diff --git a/mpegts/MpegTs.go b/mpegts/MpegTs.go index e6c18a7f..ee6615ec 100644 --- a/mpegts/MpegTs.go +++ b/mpegts/MpegTs.go @@ -131,8 +131,8 @@ type MpegTsPacket struct { payload []byte // Mpeg ts payload } -// TODO: make payload private considering we now have FillPayload method - +// FillPayload takes a channel and fills the packets payload field until the +// channel is empty or we've the packet reaches capacity func (p *MpegTsPacket) FillPayload(channel chan byte){ p.Payload = make([]byte,0,mpegtsPayloadSize) currentPktLength := 6 + int(btb(p.PCRF))*6+int(btb(p.OPCRF))*6+ @@ -143,10 +143,14 @@ func (p *MpegTsPacket) FillPayload(channel chan byte){ } } +// btb is a simple wrapper function for tools.BoolToByte which takes a bool +// and returns an equivalent byte func btb(b bool) byte { return tools.BoolToByte(b) } +// ToByteSlice interprets the fields of the ts packet instance and outputs a +// corresponding byte slice func (p *MpegTsPacket) ToByteSlice() (output []byte) { stuffingLength := 182-len(p.Payload)-len(p.TPD)-int(btb(p.PCRF))*6- int(btb(p.OPCRF))*6 - int(btb(p.SPF)) diff --git a/nal/NalAccessUnit.go b/nal/NalAccessUnit.go deleted file mode 100644 index 0de160fb..00000000 --- a/nal/NalAccessUnit.go +++ /dev/null @@ -1,50 +0,0 @@ -/* -NAME - PES.go - -DESCRIPTION - See Readme.md - -AUTHOR - Saxon Nelson-Milton - -LICENSE - PES.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 nal - -type NalAccessUnit struct { - SPS []byte - PPS []byte - SEI [][]byte - Data [][]byte -} - -func (u *NalAccessUnit) AsAnnexB() (output []byte) { - startCode := []byte{ 0x00,0x00,0x01} - AUD := []byte{0x09, 0xF0} - format := [][]byte{startCode, AUD, startCode, u.SPS, startCode, u.PPS } - for i := range format { - output = append(output,format[i]...) - } - for i := range u.SEI { - output = append(output, append(startCode,u.SEI[i]...)...) - } - for i := range u.Data { - output = append(output, append(startCode,u.Data[i]...)...) - } - return -} diff --git a/nal/NalUnit.go b/nal/NalUnit.go deleted file mode 100644 index 5af02fe4..00000000 --- a/nal/NalUnit.go +++ /dev/null @@ -1,101 +0,0 @@ -/* -NAME - PES.go - -DESCRIPTION - See Readme.md - -AUTHOR - Saxon Nelson-Milton - -LICENSE - PES.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 nal - -type NALUnit interface { - ToByteSlice() []byte - GetType() byte -} - -type NALSpsPps struct { - Data []byte -} - -type NALFragment struct { - ThreeNUBs byte - FragmentType byte - Start bool - End bool - Reserved bool - FiveNUBs byte - Data []byte -} - - -func GetNalType(unit []byte) byte { - return unit[0] & 0x1F -} - -/* -First byte: [ 3 NAL UNIT BITS | 5 FRAGMENT TYPE BITS] -Second byte: [ START BIT | END BIT | RESERVED BIT | 5 NAL UNIT BITS] -Other bytes: [... VIDEO FRAGMENT DATA...] -*/ -func ParseNALFragment(unit []byte) (u *NALFragment) { - u = new(NALFragment) - u.ThreeNUBs = (unit[0] & 0xE0) >> 5 - u.FragmentType = unit[0] & 0x1F - u.Start = (unit[1] & 0x80) != 0 - u.End = (unit[1] & 0x40) != 0 - u.Reserved = (unit[1] & 0x20) != 0 - u.FiveNUBs = unit[1] & 0x1F - u.Data = make([]byte,len(unit[2:])) - copy(u.Data[:],unit[2:]) - return -} - -func ParseNALSpsPps(unit []byte)(u *NALSpsPps){ - u = new(NALSpsPps) - u.Data = make([]byte,len(unit)) - copy(u.Data[:],unit[:]) - return -} - -func (u *NALFragment) ToByteSlice() (output []byte) { - output = make([]byte, 2+len(u.Data)) - output[0] = ( u.ThreeNUBs << 5 ) | u.FragmentType - output[1] = boolToByte( u.Start ) << 7 | - boolToByte( u.End ) << 6 | - boolToByte( u.Reserved ) << 5 | - u.FiveNUBs - copy(output[2:],u.Data) - return -} - -func (u *NALFragment) GetType() byte { - return GetNalType(u.ToByteSlice()) -} - -func (u *NALSpsPps) GetType() byte { - return GetNalType(u.ToByteSlice()) -} - -func (u *NALSpsPps) ToByteSlice() (output []byte){ - output = make([]byte,len(u.Data)) - output = u.Data - return -} diff --git a/nal/nal_test.go b/nal/nal_test.go deleted file mode 100644 index 6f13ea07..00000000 --- a/nal/nal_test.go +++ /dev/null @@ -1,118 +0,0 @@ -/* -NAME - MpegTs.go - provides a data structure intended to encapsulate the properties - of an MpegTs packet. - -DESCRIPTION - See Readme.md - -AUTHOR - Saxon 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 nal - -import ( - "testing" -) - -var parseInput = []byte{ - 0x6C, // 3NalUnitBits = 101(5), Fragment type = 1100 (type = 12 ) - 0x94, // starbit = 1, endbit = 0, Reservedbit = 0, 5NalUnitBits = 10100 (20) - 0x8E, // 10001110 random frame byte - 0x26, // 00100110 random frame byte - 0xD0, // 11010000 random frame byte -} - -var expectedParsing = []interface{}{ - byte(3), - byte(12), - bool(true), - bool(false), - bool(false), - byte(20), - []byte{0x8E, 0x26, 0xD0}, -} - -const ( - nalTestType = 12 -) - -func TestNalFragmentParsing(t *testing.T) { - nalUnit := ParseNALFragment(parseInput) - value := reflect.ValueOf(*nalUnit) - length := value.NumField() - fields := make([]interface{}, length) - for ii := 0; ii < length; ii++ { - fields[ii] = value.Field(ii).Interface() - } - for ii := range fields { - if !reflect.DeepEqual(fields[ii], expectedParsing[ii]) { - t.Errorf("Bad Parsing! Field: %v wanted: %v got: %v\n", ii, expectedParsing[ii], - fields[ii]) - } - } -} - -func TestNalFragmentToByteSlice(t *testing.T) { - nalUnit := ParseNALFragment(parseInput) - output := nalUnit.ToByteSlice() - for ii := range output { - if output[ii] != parseInput[ii] { - t.Errorf("Bad conversion to byte slice at %vth byte! wanted: %v got: %v", - parseInput[ii], output[ii]) - } - } -} - -func TestNalFragmentType(t *testing.T) { - nalUnit := ParseNALFragment(parseInput) - nalType := nalUnit.GetType() - if nalType != nalTestType { - t.Errorf("Returned wrong type!") - } -} - -func TestNalSpsPpsParsing(t *testing.T) { - nalSpsPps := ParseNALSpsPps(parseInput) - for ii := range parseInput { - if nalSpsPps.Data[ii] != parseInput[ii] { - t.Errorf("Bad Parsing! Byte: %v wanted: %v got: %v\n", ii, parseInput[ii], - nalSpsPps.Data[ii]) - } - } -} - -func TestNalSpsPpsToByteSlice(t *testing.T) { - nalSpsPps := ParseNALSpsPps(parseInput) - nalSpsPpsByteSlice := nalSpsPps.ToByteSlice() - for ii := range parseInput { - if nalSpsPpsByteSlice[ii] != parseInput[ii] { - t.Errorf("Bad conversion to byte slice! Byte: %v wanted: %v got: %v\n", ii, - parseInput[ii], nalSpsPpsByteSlice[ii]) - } - } -} - -func TestNalSpsPpsType(t *testing.T) { - nalSpsPps := ParseNALSpsPps(parseInput) - if nalSpsPps.GetType() != nalTestType { - t.Errorf("Returned wrong type!") - } -} diff --git a/parser/H264Parser.go b/parser/H264Parser.go index 4d341213..f939291f 100644 --- a/parser/H264Parser.go +++ b/parser/H264Parser.go @@ -1,16 +1,15 @@ /* NAME - RtpToTsConverter.go - provides utilities for the conversion of Rtp packets - to equivalent MpegTs packets. + H264Parser.go DESCRIPTION See Readme.md AUTHOR - Saxon Nelson-Milton + Saxon Nelson-Milton LICENSE - RtpToTsConverter.go is Copyright (C) 2017 the Australian Ocean Lab (AusOcean) + H264Parser.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 @@ -28,74 +27,96 @@ LICENSE package parser import ( - //"bitbucket.org/ausocean/av/itut" - "../itut" - _"fmt" - "time" + //"bitbucket.org/ausocean/av/itut" + "../itut" + _ "fmt" + "time" ) +const ( + inputChanSize = 100000 + outputBufferSize = 10000 +) + +// h264Parser provides properties and methods to allow for the parsing of a +// h264 stream - i.e. to allow extraction of the individual access units type h264Parser struct { - inputBuffer []byte - isParsing bool - parserOutputChanRef chan []byte - userOutputChanRef chan []byte - inputChan chan byte - delay uint + inputBuffer []byte + isParsing bool + parserOutputChanRef chan []byte + userOutputChanRef chan []byte + inputChan chan byte + delay uint } +// NewH264Parser returns an instance of the h264Parser struct func NewH264Parser() (p *h264Parser) { - p = new(h264Parser) - p.isParsing = true - p.inputChan = make(chan byte, 100000) - p.delay = 0 - return + p = new(h264Parser) + p.isParsing = true + p.inputChan = make(chan byte, inputChanSize) + p.delay = 0 + return } -func (p* h264Parser)Stop(){ - p.isParsing = false +// Stop simply sets the isParsing flag to false to indicate to the parser that +// we don't want to interpret incoming data anymore - this will also make the +// parser jump out of the parse func +func (p *h264Parser) Stop() { + p.isParsing = false } -func (p *h264Parser)Start(){ - go p.parse() +// Start starts the parse func as a goroutine so that incoming data is interpreted +func (p *h264Parser) Start() { + go p.parse() } -func (p *h264Parser)SetDelay(delay uint){ - p.delay = delay +// SetDelay sets a delay inbetween each buffer output. Useful if we're parsing +// a file but want to replicate the speed of incoming video frames from a +// camera +func (p *h264Parser) SetDelay(delay uint) { + p.delay = delay } -func (p *h264Parser)GetInputChan() chan byte { - return p.inputChan +// GetInputChan returns a handle to the input channel of the parser +func (p *h264Parser) GetInputChan() chan byte { + return p.inputChan } -func (p *h264Parser)GetOutputChan() chan []byte { - return p.userOutputChanRef +// GetOutputChan returns a handle to the output chan of the parser +func (p *h264Parser) GetOutputChan() chan []byte { + return p.userOutputChanRef } -func (p *h264Parser)SetOutputChan(aChan chan []byte){ - p.parserOutputChanRef = aChan - p.userOutputChanRef = aChan +// SetOutputChan sets the parser output chan to the passed output chan. This is +// useful if we want the parser output to go directly to a generator of some sort +// for packetization. +func (p *h264Parser) SetOutputChan(aChan chan []byte) { + p.parserOutputChanRef = aChan + p.userOutputChanRef = aChan } -func (p *h264Parser)parse() { - outputBuffer := make([]byte, 0, 10000) +// parse interprets an incoming h264 stream and extracts individual frames +// aka access units +func (p *h264Parser) parse() { + outputBuffer := make([]byte, 0, outputBufferSize) searchingForEnd := false for p.isParsing { - aByte := <-p.inputChan + aByte := <-p.inputChan outputBuffer = append(outputBuffer, aByte) - for i:=1; aByte == 0x00 && i != 4; i++ { + for i := 1; aByte == 0x00 && i != 4; i++ { aByte = <-p.inputChan outputBuffer = append(outputBuffer, aByte) - if ( aByte == 0x01 && i == 2 ) || ( aByte == 0x01 && i == 3 ) { + if (aByte == 0x01 && i == 2) || (aByte == 0x01 && i == 3) { if searchingForEnd { - output := append(append(itut.StartCode1(),itut.AUD()...),outputBuffer[:len(outputBuffer)-(i+1)]...) - time.Sleep(time.Duration(p.delay)*time.Millisecond) - p.parserOutputChanRef<-output + output := append(append(itut.StartCode1(), itut.AUD()...), outputBuffer[:len(outputBuffer)-(i+1)]...) + time.Sleep(time.Duration(p.delay) * time.Millisecond) + p.parserOutputChanRef <- output outputBuffer = outputBuffer[len(outputBuffer)-1-i:] searchingForEnd = false } aByte = <-p.inputChan outputBuffer = append(outputBuffer, aByte) - if nalType := aByte & 0x1F; nalType == 1 || nalType == 5 || nalType == 8{ + if nalType := aByte & 0x1F; nalType == 1 || nalType == 5 || nalType == 8 { searchingForEnd = true } } diff --git a/parser/H264Writer.go b/parser/H264Writer.go index 7874a567..334dbe28 100644 --- a/parser/H264Writer.go +++ b/parser/H264Writer.go @@ -1,16 +1,17 @@ /* +NOTE: this file is in progress... + NAME - RtpToTsConverter.go - provides utilities for the conversion of Rtp packets - to equivalent MpegTs packets. + H264Writer.go DESCRIPTION See Readme.md AUTHOR - Saxon Nelson-Milton + Saxon Nelson-Milton LICENSE - RtpToTsConverter.go is Copyright (C) 2017 the Australian Ocean Lab (AusOcean) + H264Writer.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 @@ -26,6 +27,8 @@ LICENSE along with revid in gpl.txt. If not, see [GNU licenses](http://www.gnu.org/licenses). */ +// TODO: complete this file + package parser import ( diff --git a/parser/MJPEGParser.go b/parser/MJPEGParser.go index a65074fd..79985cbf 100644 --- a/parser/MJPEGParser.go +++ b/parser/MJPEGParser.go @@ -1,64 +1,94 @@ +/* +NAME + MJPEGParser.go + +DESCRIPTION + See Readme.md + +AUTHOR + Saxon Nelson-Milton + +LICENSE + MJPEGParser.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 parser import ( - //"bitbucket.org/ausocean/av/itut" - _"fmt" + //"bitbucket.org/ausocean/av/itut" + _ "fmt" +) + +const ( + frameStartCode = 0xD8 ) type mjpegParser struct { - inputBuffer []byte - isParsing bool - parserOutputChanRef chan []byte - userOutputChanRef chan []byte - inputChan chan byte - delay uint + inputBuffer []byte + isParsing bool + parserOutputChanRef chan []byte + userOutputChanRef chan []byte + inputChan chan byte + delay uint } -func NewMJPEGParser(inputChanLen int) (p *mjpegParser){ - p = new(mjpegParser) - p.isParsing = true - p.inputChan = make(chan byte, inputChanLen ) - return +func NewMJPEGParser(inputChanLen int) (p *mjpegParser) { + p = new(mjpegParser) + p.isParsing = true + p.inputChan = make(chan byte, inputChanLen) + return } -func (p *mjpegParser)Stop(){ - p.isParsing = false +func (p *mjpegParser) Stop() { + p.isParsing = false } -func (p *mjpegParser)Start(){ - go p.parse() +func (p *mjpegParser) Start() { + go p.parse() } -func (p *mjpegParser)SetDelay(delay uint){ - p.delay = delay +func (p *mjpegParser) SetDelay(delay uint) { + p.delay = delay } - -func (p *mjpegParser)GetInputChan() chan byte { - return p.inputChan +func (p *mjpegParser) GetInputChan() chan byte { + return p.inputChan } -func (p *mjpegParser)GetOutputChan() chan []byte { - return p.userOutputChanRef +func (p *mjpegParser) GetOutputChan() chan []byte { + return p.userOutputChanRef } -func (p *mjpegParser)SetOutputChan(aChan chan []byte){ - p.parserOutputChanRef = aChan - p.userOutputChanRef = aChan +func (p *mjpegParser) SetOutputChan(aChan chan []byte) { + p.parserOutputChanRef = aChan + p.userOutputChanRef = aChan } -func (p *mjpegParser)parse() { - var outputBuffer []byte +func (p *mjpegParser) parse() { + var outputBuffer []byte for p.isParsing { - aByte := <-p.inputChan + aByte := <-p.inputChan outputBuffer = append(outputBuffer, aByte) - if aByte == 0xFF && len(outputBuffer) != 0 { - aByte := <-p.inputChan - outputBuffer = append(outputBuffer, aByte) - if aByte == 0xD8 { - p.parserOutputChanRef<-outputBuffer[:len(outputBuffer)-2] - outputBuffer = outputBuffer[len(outputBuffer)-2:] - } - } - } + if aByte == 0xFF && len(outputBuffer) != 0 { + aByte := <-p.inputChan + outputBuffer = append(outputBuffer, aByte) + if aByte == frameStartCode { + p.parserOutputChanRef <- outputBuffer[:len(outputBuffer)-2] + outputBuffer = outputBuffer[len(outputBuffer)-2:] + } + } + } }