mirror of https://bitbucket.org/ausocean/av.git
Updating remote
This commit is contained in:
parent
e52918e417
commit
bec2280cbf
|
@ -69,11 +69,6 @@ type MpegTsPacket struct {
|
||||||
Payload []byte
|
Payload []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func boolToByte( in bool ) (out uint8){
|
|
||||||
if in { out = 1 }
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *MpegTsPacket) ToByteSlice() (output []byte) {
|
func (p *MpegTsPacket) ToByteSlice() (output []byte) {
|
||||||
output = make([]byte,188)
|
output = make([]byte,188)
|
||||||
output[0] = p.SyncByte
|
output[0] = p.SyncByte
|
||||||
|
@ -86,6 +81,7 @@ func (p *MpegTsPacket) ToByteSlice() (output []byte) {
|
||||||
for ii := 4; ii-4 < len(p.AF); ii++ {
|
for ii := 4; ii-4 < len(p.AF); ii++ {
|
||||||
output[ii] = p.AF[ii-4]
|
output[ii] = p.AF[ii-4]
|
||||||
}
|
}
|
||||||
|
//copy(output[4:4+len(p.AF)],p.AF)
|
||||||
//headerSize := packetLength-len(p.Payload)
|
//headerSize := packetLength-len(p.Payload)
|
||||||
headerSize := 4 + len(p.AF)
|
headerSize := 4 + len(p.AF)
|
||||||
for ii := headerSize; ii < packetLength; ii++ {
|
for ii := headerSize; ii < packetLength; ii++ {
|
||||||
|
|
|
@ -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) {
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
NAME
|
||||||
|
PES.go -
|
||||||
|
DESCRIPTION
|
||||||
|
See Readme.md
|
||||||
|
|
||||||
|
AUTHOR
|
||||||
|
Saxon Nelson-Milton <saxon.milton@gmail.com>
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
|
@ -48,5 +48,30 @@ type PESPacket struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *PESPacket) ToByteSlice() (output []byte) {
|
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)
|
||||||
}
|
}
|
|
@ -53,54 +53,93 @@ func NewRtpToTsConverter() (c *rtpToTsConverter) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c* rtpToTsConverter) Convert(rtpSession *Session) {
|
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<pesDataChanLen; ii++ {
|
||||||
|
pesPkt.Data[i] = <-pesDataChan
|
||||||
|
}
|
||||||
|
pesPktChan<-pesPkt.ToByteSlice()
|
||||||
|
}
|
||||||
|
pesDataChan<-nalUnit.ToByteSlice()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
// Create mpegts using pes packets
|
||||||
for {
|
for {
|
||||||
select{
|
pesPkt := <-pesPktChan
|
||||||
default:
|
for ii:=range pesPkt {
|
||||||
case rtpPacket := <-rtpSession.RtpChan:
|
c.payloadByteChan<-pesPkt[ii]
|
||||||
for ii := range rtpPacket.Payload {
|
}
|
||||||
c.payloadByteChan<-rtpPacket.Payload[ii]
|
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
|
||||||
firstPacket:=true
|
c.currentTsPacket.PID = 256
|
||||||
for len(c.payloadByteChan) > 0 {
|
c.currentTsPacket.TSC = 0
|
||||||
lengthOfByteChan := len(c.payloadByteChan)
|
c.currentTsPacket.CC = c.currentCC
|
||||||
c.currentTsPacket = new(MpegTsPacket)
|
if c.currentCC++; c.currentCC > 15 { c.currentCC = 0 }
|
||||||
c.currentTsPacket.SyncByte = 0x47
|
payloadLength := 182
|
||||||
c.currentTsPacket.TEI = false
|
if lengthOfByteChan < 182 {
|
||||||
c.currentTsPacket.PUSI = false
|
payloadLength = lengthOfByteChan
|
||||||
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.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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 <saxon.milton@gmail.com>
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
|
@ -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])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue