/* NAME h264.go DESCRIPTION See Readme.md AUTHOR Saxon Nelson-Milton LICENSE h264.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 http://www.gnu.org/licenses. */ package parser import ( "time" "bitbucket.org/ausocean/av/itut" ) const ( inputChanSize = 100000 outputBufferSize = 10000 ) // H264 provides properties and methods to allow for the parsing of a // h264 stream - i.e. to allow extraction of the individual access units type H264 struct { inputBuffer []byte isParsing bool parserOutputChanRef chan []byte userOutputChanRef chan []byte inputChan chan byte delay uint } // NewH264Parser returns an instance of the H264 struct func NewH264Parser() (p *H264) { p = new(H264) p.isParsing = true p.inputChan = make(chan byte, inputChanSize) p.delay = 0 return } // 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 *H264) Stop() { p.isParsing = false } // Start starts the parse func as a goroutine so that incoming data is interpreted func (p *H264) Start() { p.isParsing = true go p.parse() } // 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 *H264) SetDelay(delay uint) { p.delay = delay } // InputChan returns a handle to the input channel of the parser func (p *H264) InputChan() chan byte { return p.inputChan } // OutputChan returns a handle to the output chan of the parser func (p *H264) OutputChan() <-chan []byte { return p.userOutputChanRef } // 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 *H264) SetOutputChan(o chan []byte) { p.parserOutputChanRef = o p.userOutputChanRef = o } // parse interprets an incoming h264 stream and extracts individual frames // aka access units func (p *H264) parse() { outputBuffer := make([]byte, 0, outputBufferSize) searchingForEnd := false for p.isParsing { var aByte uint8 if p.isParsing { aByte = <-p.inputChan } else { return } outputBuffer = append(outputBuffer, aByte) for i := 1; aByte == 0x00 && i != 4; i++ { if p.isParsing { aByte = <-p.inputChan } else { return } outputBuffer = append(outputBuffer, aByte) 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 outputBuffer = outputBuffer[len(outputBuffer)-1-i:] searchingForEnd = false } if p.isParsing { aByte = <-p.inputChan } else { return } outputBuffer = append(outputBuffer, aByte) if nalType := aByte & 0x1F; nalType == 1 || nalType == 5 || nalType == 8 { searchingForEnd = true } } } } }