diff --git a/packets/MpegTs.go b/packets/MpegTs.go index 99a76f7f..e124b1e2 100644 --- a/packets/MpegTs.go +++ b/packets/MpegTs.go @@ -69,11 +69,6 @@ type MpegTsPacket struct { Payload []byte } -func boolToByte( in bool ) (out uint8){ - if in { out = 1 } - return -} - func (p *MpegTsPacket) ToByteSlice() (output []byte) { output = make([]byte,188) output[0] = p.SyncByte @@ -86,6 +81,7 @@ func (p *MpegTsPacket) ToByteSlice() (output []byte) { for ii := 4; ii-4 < len(p.AF); ii++ { output[ii] = p.AF[ii-4] } + //copy(output[4:4+len(p.AF)],p.AF) //headerSize := packetLength-len(p.Payload) headerSize := 4 + len(p.AF) for ii := headerSize; ii < packetLength; ii++ { diff --git a/packets/NAL.go b/packets/NAL.go deleted file mode 100644 index 297c7c05..00000000 --- a/packets/NAL.go +++ /dev/null @@ -1,20 +0,0 @@ -package packets - -type NALUnit struct { - ThreeNUBs byte - FragmentType byte - Start bool - End bool - Reserved bool - FiveNUBs byte - data []byte -} - -func ParseNALUnit(data []byte) (u *NALUnit) { - u = new(NALUnit) - -} - -func (u *NALUnit) ToByteSlice() (output []byte) { - -} diff --git a/packets/Nal.go b/packets/Nal.go new file mode 100644 index 00000000..07b77084 --- /dev/null +++ b/packets/Nal.go @@ -0,0 +1,69 @@ +/* +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 packets + +type NALUnit struct { + ThreeNUBs byte + FragmentType byte + Start bool + End bool + Reserved bool + FiveNUBs byte + Data []byte +} + +/* +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 ParseNALUnit(unit []byte) (u *NALUnit) { + u = new(NALUnit) + 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 = unit[2:] + return +} + +func GetNalType(unit []byte) byte { + return unit[0] & 0x1F +} + +func (u *NALUnit) 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 +} diff --git a/packets/PES.go b/packets/Pes.go similarity index 61% rename from packets/PES.go rename to packets/Pes.go index 0759e403..4b260970 100644 --- a/packets/PES.go +++ b/packets/Pes.go @@ -48,5 +48,30 @@ type PESPacket struct { } func (p *PESPacket) ToByteSlice() (output []byte) { - + output = make([]byte, 6+p.Length) + output[0] = 0x00 + output[1] = 0x00 + output[2] = 0x01 + output[3] = p.StreamID + output[4] = byte(( p.Length & 0xFF00 ) >> 8) + output[5] = byte( p.Length & 0x00FF ) + output[6] = 0x10 << 6 | + p.ScramblingControl << 4 | + boolToByte(p.Priority) << 3 | + boolToByte(p.DAI) << 2 | + boolToByte(p.Copyright) << 1 | + boolToByte(p.Original) + output[7] = p.PDI << 6 | + boolToByte(p.ESCR) << 5 | + boolToByte(p.ESRate) << 4 | + boolToByte(p.DSMTrickMode) << 3 | + boolToByte(p.ACI) << 2 | + boolToByte(p.CRC) << 1 | + boolToByte(p.Ext) + output[8] = p.HeaderLength + optFieldsOffset := 9+len(p.optFieldsLen) + copy(output[9:optFieldsOffset],p.OptFields) + copy(output[optFieldsOffset:optFieldsOffset + len(p.Stuffing)],p.Stuffing) + dataOffset := 9+p.HeaderLength + copy(output[dataOffset:dataOffset+len(p.Data)],p.Data) } diff --git a/packets/RtpToTsConverter.go b/packets/RtpToTsConverter.go index 7820d932..2d9e9569 100644 --- a/packets/RtpToTsConverter.go +++ b/packets/RtpToTsConverter.go @@ -53,54 +53,93 @@ func NewRtpToTsConverter() (c *rtpToTsConverter) { } func (c* rtpToTsConverter) Convert(rtpSession *Session) { + nalUnitChan := make(chan *NALUnit, 1000) + // Get nal units from incoming rtp + go func(){ + for { + rtpPacket := <-rtpSession.RtpChan + if GetNalType( rtpPacket.Payload ) == 28 { + nalUnitChan<-ParseNALUnit(rtpPacket.Payload) + } + } + }() + // Create PES packets from NAl units + pesPktChan := make(chan []byte, 100) + go func(){ + pesDataChan := make(chan []byte, 100) + for { + nalUnit:=<-nalUnitChan + if nalUnit.Start { + pesDataChanLen = len(pesDataChan) + if pesDataChanLen > 0 { + pesPkt := new(PESPacket) + pesPkt.StreamID = 0xE0 + pesPkt.Length = 24 + pesDataChanLen + pesPkt.ScramblingControl = 0 + pesPkt.Priority = true + pesPkt.DAI = false + pesPkt.Copyright = false + pesPkt.Original = true + pesPkt.PDI = false + pesPkt.ESCR = false + pesPkt.ESRate = false + pesPkt.DSMTrickMode = false + pesPkt.ACI = false + pesPkt.CRC = false + pesPkt.Ext = false + pesPkt.HeaderLength = 0 + pesPkt.Data = make([]byte,pesDataChanLen) + for ii:=0; ii 0 { + lengthOfByteChan := len(c.payloadByteChan) + c.currentTsPacket = new(MpegTsPacket) + c.currentTsPacket.SyncByte = 0x47 + c.currentTsPacket.TEI = false + c.currentTsPacket.PUSI = false + if firstPacket { // if it's the start of the payload + c.currentTsPacket.PUSI = true + firstPacket = false } - - firstPacket:=true - for len(c.payloadByteChan) > 0 { - lengthOfByteChan := len(c.payloadByteChan) - c.currentTsPacket = new(MpegTsPacket) - c.currentTsPacket.SyncByte = 0x47 - c.currentTsPacket.TEI = false - c.currentTsPacket.PUSI = false - if firstPacket { // if it's the start of the payload - c.currentTsPacket.PUSI = true - firstPacket = false - } - c.currentTsPacket.Priority = false - c.currentTsPacket.PID = 256 - c.currentTsPacket.TSC = 0 - c.currentTsPacket.CC = c.currentCC - if c.currentCC++; c.currentCC > 15 { c.currentCC = 0 } - payloadLength := 182 - if lengthOfByteChan < 182 { - payloadLength = lengthOfByteChan - } - c.currentTsPacket.AFC = 3 - stuffingLength := 182-payloadLength - c.currentTsPacket.AF = make([]byte,2 + stuffingLength) // adaptationfield flag length = 16 - c.currentTsPacket.AF[0] = byte(1 + stuffingLength) - c.currentTsPacket.AF[1] = 0 - if c.currentTsPacket.PUSI { - c.currentTsPacket.AF[1] = 0x40 - } - - for ii := 0; ii < stuffingLength; ii++ { - c.currentTsPacket.AF[2+ii] = 0xFF - } - fmt.Printf("AF length: %v\n",c.currentTsPacket.AF[0]) - fmt.Printf("AF length2: %v\n",len(c.currentTsPacket.AF)) - c.currentTsPacket.Payload = make([]byte, payloadLength) - for ii:=0; ii < payloadLength; ii++ { - c.currentTsPacket.Payload[ii] = <-c.payloadByteChan - } - c.tsChan<-c.currentTsPacket + c.currentTsPacket.Priority = false + c.currentTsPacket.PID = 256 + c.currentTsPacket.TSC = 0 + c.currentTsPacket.CC = c.currentCC + if c.currentCC++; c.currentCC > 15 { c.currentCC = 0 } + payloadLength := 182 + if lengthOfByteChan < 182 { + payloadLength = lengthOfByteChan } + c.currentTsPacket.AFC = 3 + stuffingLength := 182-payloadLength + c.currentTsPacket.AF = make([]byte,2 + stuffingLength) // adaptationfield flag length = 16 + c.currentTsPacket.AF[0] = byte(1 + stuffingLength) + c.currentTsPacket.AF[1] = 0 + if c.currentTsPacket.PUSI { + c.currentTsPacket.AF[1] = 0x40 + } + for ii := 0; ii < stuffingLength; ii++ { + c.currentTsPacket.AF[2+ii] = 0xFF + } + c.currentTsPacket.Payload = make([]byte, payloadLength) + for ii:=0; ii < payloadLength; ii++ { + c.currentTsPacket.Payload[ii] = <-c.payloadByteChan + } + c.tsChan<-c.currentTsPacket } } } diff --git a/packets/helpers.go b/packets/helpers.go new file mode 100644 index 00000000..cfb4079c --- /dev/null +++ b/packets/helpers.go @@ -0,0 +1,35 @@ +/* +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 packets + +func boolToByte( in bool ) (out uint8){ + if in { out = 1 } + return +} diff --git a/packets/nal_test.go b/packets/nal_test.go new file mode 100644 index 00000000..40c58df4 --- /dev/null +++ b/packets/nal_test.go @@ -0,0 +1,51 @@ +package packets + +import ( + "testing" + "reflect" +) + +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 +} + +expectedParsing := []interface{}{ + 5, + 12, + true, + false, + false, + 20, + {0x8E,0x26,0xD0} +} + +func TestParsing(t *testing.T) { + nalUnit := ParseNALUnit(parseInput) + value := reflect.ValueOf(naleUnit) + length := value.NumField() + fields := make([]interface{}, length) + for ii := 0; ii < length; ii++ { + fields[ii] = value.Field(i).Interface() + } + + for ii := range fields { + if fields[ii] != expectedParsing[ii] { + t.Errorf("Bad Parsing! wanted: %v got: %v\n",expectedParsing[ii],fields[ii]) + } + } +} + +func TestToByteSlice(t *testing.T) { + nalUnit := ParseNALUnit(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]) + } + } +}