Cleaned up & tested

Cleaned up file/folder structure and got the new and improved mpegts stuff working. Now to looking at the pes stuff.
This commit is contained in:
Unknown 2018-01-08 01:02:56 +10:30
parent 484f21e4d3
commit 862c3a67e2
19 changed files with 646 additions and 825 deletions

View File

@ -2,6 +2,10 @@
av is a collection of tools and packages written in Go for audio-video processing.
# Authors
Alan Noble
Saxon A. Nelson-Milton <saxon.milton@gmail.com>
# Description
* revid: a tool for re-muxing and re-directing video streams.

View File

@ -7,7 +7,7 @@ DESCRIPTION
See Readme.md
AUTHOR
Saxon Nelson-Milton <saxon.milton@gmail.com>
Saxon A. Nelson-Milton <saxon.milton@gmail.com>
LICENSE
MpegTs.go is Copyright (C) 2017 the Australian Ocean Lab (AusOcean)
@ -26,7 +26,12 @@ LICENSE
along with revid in gpl.txt. If not, see [GNU licenses](http://www.gnu.org/licenses).
*/
package packets
package mpegts
import (
"../tools"
"errors"
)
/*
The below data struct encapsulates the fields of an MPEG-TS packet. Below is
@ -94,59 +99,58 @@ the formatting of an MPEG-TS packet for reference!
----------------------------------------------------------------------------
*/
type MpegTsPacket struct {
TEI bool // Transport Error Indicator
PUSI bool // Payload Unit Start Indicator
Priority bool // Tranposrt priority indicator
PID uint16 // Packet identifier
TSC byte // Transport Scrambling Control
AFC byte // Adaption Field Control
CC byte // Continuity Counter
AFL byte // Adaptation field length
DI bool // Discontinouty indicator
RAI bool // random access indicator
ESPI bool // Elementary stream priority indicator
PCRF bool // PCR flag
OPCRF bool // OPCR flag
SPF bool // Splicing point flag
TPDF bool // Transport private data flag
AFEF bool // Adaptation field extension flag
PCR uint64 // Program clock reference
OPCR uint64 // Original program clock reference
SC byte // Splice countdown
TPDL byte // Tranposrt private data length
TPD []byte // Private data
Extension []byte // Adaptation field extension
Stuffing []byte // Stuffing bytes
Payload []byte // Mpeg ts payload
TEI bool // Transport Error Indicator
PUSI bool // Payload Unit Start Indicator
Priority bool // Tranposrt priority indicator
PID uint16 // Packet identifier
TSC byte // Transport Scrambling Control
AFC byte // Adaption Field Control
CC byte // Continuity Counter
AFL byte // Adaptation field length
DI bool // Discontinouty indicator
RAI bool // random access indicator
ESPI bool // Elementary stream priority indicator
PCRF bool // PCR flag
OPCRF bool // OPCR flag
SPF bool // Splicing point flag
TPDF bool // Transport private data flag
AFEF bool // Adaptation field extension flag
PCR uint64 // Program clock reference
OPCR uint64 // Original program clock reference
SC byte // Splice countdown
TPDL byte // Tranposrt private data length
TPD []byte // Private data
Ext []byte // Adaptation field extension
Stuff []byte // Stuffing bytes
Payload []byte // Mpeg ts payload
}
func (p *MpegTsPacket) ToByteSlice() (output []byte) {
output = make([]byte, 188)
output[0] = 0x47
output[1] = boolToByte(p.TEI) << 7 | boolToByte(p.PUSI) << 6 |
boolToByte(p.Priority) << 5 | byte((p.PID&0xFF00) >> 8)
output[2] = byte(p.PID & 0x00FF)
output[3] = p.TSC << 6 | p.AFC << 4 | p.CC
output[4] = p.AFL
output[5] = boolToByte(p.DI) << 7 | boolToByte(p.RAI) << 6 | boolToByte(p.ESPI) << 5 |
boolToByte(p.PCRF) << 4 | boolToByte(p.OPCRF) << 3 | boolToByte(p.SPF) << 2 |
boolToByte(TPDF) << 1 | boolToByte(AFEF)
currentIndex := 6
for ; p.PCRf && currentIndex < 12; currentIndex++ {
output[currentIndex] = p.PCR >> (22 - 2*currentIndex)
func (p *MpegTsPacket) ToByteSlice() (output []byte, err error) {
output = append(output, []byte{
0x47,
(tools.BoolToByte(p.TEI)<<7 | tools.BoolToByte(p.PUSI)<<6 | tools.BoolToByte(p.Priority)<<5 |
byte((p.PID&0xFF00)>>8)),
byte(p.PID & 0x00FF),
(p.TSC<<6 | p.AFC<<4 | p.CC),
p.AFL,
(tools.BoolToByte(p.DI)<<7 | tools.BoolToByte(p.RAI)<<6 | tools.BoolToByte(p.ESPI)<<5 | tools.BoolToByte(p.PCRF)<<4 |
tools.BoolToByte(p.OPCRF)<<3 | tools.BoolToByte(p.SPF)<<2 | tools.BoolToByte(p.TPDF)<<1 | tools.BoolToByte(p.AFEF)),
}...)
for i := 40; p.PCRF && i >= 0; i-=8 {
output = append(output, byte(p.PCR>>uint(i)))
}
startIndex := currentIndex
for ; p.OPCRF && ( currentIndex < ( startIndex + 6 )); currentIndex++ {
output[currentIndex] = p.OPCR >> (10 - 2*(currentIndex-startIndex))
for i := 40; p.OPCRF && i >= 0; i-=8 {
output = append(output, byte(p.OPCR>>uint(i)))
}
if p.SPF {
output[(currentIndex++)-1] = p.SC
output = append(output, p.SC)
}
if p.TPDF {
output[(currentIndex++)-1] = p.TPDL
copy(output[currentIndex:len(p.TPD)],p.TPD[:]]
currentIndex += len(p.TPD)
output = append(output, append([]byte{p.TPDL}, p.TPD...)...)
}
copy()
output = append(output, append(p.Ext, append(p.Stuff, p.Payload...)...)...)
if len(output) != 188 {
err = errors.New("Length of MPEG-TS packet is not 188! Something is wrong!")
}
return
}

72
mpegts/mpegts_test.go Normal file
View File

@ -0,0 +1,72 @@
/*
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 mpegts
import (
"testing"
_"fmt"
)
// Just ensure that we can create a byte slice with a mpegts packet correctly
func TestMpegTsToByteSlice(t *testing.T){
payload := []byte{0x56,0xA2,0x78,0x89,0x67}
pcr := 100000 // => 100000
stuffing := make([]byte,171)
for i := range stuffing {
stuffing[i] = 0xFF
}
tsPkt := MpegTsPacket{
PUSI: true,
PID: uint16(256),
AFC: byte(3),
AFL: 7+171,
CC: byte(6),
PCRF: true,
PCR: uint64(pcr),
Stuff: stuffing,
Payload: payload,
}
expectedOutput := []byte{ 0x47, 0x41, 0x00, 0x36, byte(178),0x10}
for i := 40; i >= 0; i-= 8 {
expectedOutput = append(expectedOutput,byte(pcr>>uint(i)))
}
for i := 0; i < 171; i++ {
expectedOutput = append(expectedOutput, 0xFF)
}
expectedOutput = append(expectedOutput,payload...)
tsPktAsByteSlice, err := tsPkt.ToByteSlice()
if err != nil {
t.Errorf("Should not have got error!")
}
for i := 0; i < 188; i++ {
if tsPktAsByteSlice[i] != expectedOutput[i] {
t.Errorf("Conversion to byte slice bad! Byte: %v Wanted: %v Got: %v", i, expectedOutput[i], tsPktAsByteSlice[i])
}
}
}

50
nal/NalAccessUnit.go Normal file
View File

@ -0,0 +1,50 @@
/*
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 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
}

View File

@ -24,7 +24,7 @@ LICENSE
along with revid in gpl.txt. If not, see [GNU licenses](http://www.gnu.org/licenses).
*/
package packets
package nal
type NALUnit interface {
ToByteSlice() []byte

118
nal/nal_test.go Normal file
View File

@ -0,0 +1,118 @@
/*
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 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!")
}
}

View File

@ -1,24 +0,0 @@
package packets
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
}

View File

@ -1,566 +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 <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
import (
//"bytes"
"fmt"
"io"
"log"
"net"
"reflect"
"testing"
"time"
_"math/rand"
"github.com/beatgammit/rtsp"
)
/*******************************************************
Testing stuff related to connection i.e. rtsp, rtp, rtcp
********************************************************/
const (
rtpPort = 17300
rtcpPort = 17319
rtspUrl = "rtsp://192.168.0.50:8554/CH002.sdp"
rtpUrl = "rtsp://192.168.0.50:8554/CH002.sdp/track1"
)
/* Let's see if we can connect to an rtsp device then read an rtp stream,
and then convert the rtp packets to mpegts packets and output. */
func TestRTSP(t *testing.T) {
sess := rtsp.NewSession()
res, err := sess.Options(rtspUrl)
if err != nil {
t.Errorf("Shouldn't have got error: %v\n", err)
}
res, err = sess.Describe(rtspUrl)
if err != nil {
log.Fatalln(err)
t.Errorf("Shouldn't have got error: %v\n", err)
}
p, err := rtsp.ParseSdp(&io.LimitedReader{R: res.Body, N: res.ContentLength})
if err != nil {
t.Errorf("Shouldn't have got error: %v\n", err)
}
log.Printf("%+v", p)
res, err = sess.Setup(rtpUrl, fmt.Sprintf("RTP/AVP;unicast;client_port=%d-%d", rtpPort, rtcpPort))
if err != nil {
t.Errorf("Shouldn't have got error: %v\n", err)
}
log.Println(res)
res, err = sess.Play(rtspUrl, res.Header.Get("Session"))
if err != nil {
t.Errorf("Shouldn't have got error: %v\n", err)
}
log.Println(res)
}
func TestRTP(t *testing.T) {
sess := rtsp.NewSession()
res, err := sess.Options(rtspUrl)
if err != nil {
t.Errorf("Shouldn't have got error: %v\n", err)
}
res, err = sess.Describe(rtspUrl)
if err != nil {
log.Fatalln(err)
t.Errorf("Shouldn't have got error: %v\n", err)
}
p, err := rtsp.ParseSdp(&io.LimitedReader{R: res.Body, N: res.ContentLength})
if err != nil {
t.Errorf("Shouldn't have got error: %v\n", err)
}
log.Printf("%+v", p)
res, err = sess.Setup(rtpUrl, fmt.Sprintf("RTP/AVP;unicast;client_port=%d-%d", rtpPort, rtcpPort))
if err != nil {
t.Errorf("Shouldn't have got error: %v\n", err)
}
log.Println(res)
res, err = sess.Play(rtspUrl, res.Header.Get("Session"))
if err != nil {
t.Errorf("Shouldn't have got error: %v\n", err)
}
log.Println(res)
// create udp connection for rtp stuff
rtpLaddr, err := net.ResolveUDPAddr("udp", "192.168.0.109:17300")
if err != nil {
t.Errorf("Local rtp addr not set! %v\n", err)
}
rtpAddr, err := net.ResolveUDPAddr("udp", "192.168.0.50:17300")
if err != nil {
t.Errorf("Resolving rtp address didn't work! %v\n", err)
}
rtpConn, err := net.DialUDP("udp", rtpLaddr, rtpAddr)
if err != nil {
t.Errorf("Conncection not established! %v\n", err)
}
// Create udp connection for rtcp stuff
rtcpLaddr, err := net.ResolveUDPAddr("udp", "192.168.0.109:17319")
if err != nil {
t.Errorf("Local RTCP address not resolved! %v\n", err)
}
rtcpAddr, err := net.ResolveUDPAddr("udp", "192.168.0.50:17301")
if err != nil {
t.Errorf("Remote RTCP address not resolved! %v\n", err)
}
rtcpConn, err := net.DialUDP("udp", rtcpLaddr, rtcpAddr)
if err != nil {
t.Errorf("Connection not established! %v\n", err)
}
// let's create a session that will store useful stuff from the connections
rtpSession := NewSession(rtpConn, rtcpConn)
time.Sleep(2 * time.Second)
select {
default:
t.Errorf("Should have got rtpPacket!")
case rtpPacket := <-rtpSession.RtpChan:
fmt.Printf("RTP packet: %v\n", rtpPacket)
}
}
/*******************************************************
Testing stuff related to the Nal.go file
********************************************************/
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!")
}
}
/*******************************************************
Pes Packet testing!
********************************************************/
const (
dataLength = 3 // bytes
)
func TestPesToByteSlice(t *testing.T) {
pesPkt := PESPacket{
byte(0xE0), // StreamID
uint16(6), // Length
byte(0), // ScramblingControl
bool(true), // Priority
bool(false), // DAI
bool(false), // copyright
bool(true), // Original
byte(0), // PDI
bool(false), // Escr
bool(false), // ESRate
bool(false), // DSMTrickMode
bool(false), // ACI
bool(false), // CRC
bool(false), // Ext
byte(0), // header length
[]byte{},
[]byte{},
[]byte{ // data
0xEA,
0x4B,
0x12,
},
}
pesExpectedOutput := []byte{
0x00, // packet start code prefix byte 1
0x00, // packet start code prefix byte 2
0x01, // packet start code prefix byte 3
0xE0, // stream ID
0x00, // PES Packet length byte 1
0x06, // PES packet length byte 2
0x89, // Marker bits,ScramblingControl, Priority, DAI, Copyright, Original
0x00, // PDI, ESCR, ESRate, DSMTrickMode, ACI, CRC, Ext
0x00, // header length
0xEA, // data byte 1
0x4B, // data byte 2
0x12, // data byte 3
}
pesPktAsByteSlice := pesPkt.ToByteSlice()
for ii := range pesPktAsByteSlice {
if pesPktAsByteSlice[ii] != pesExpectedOutput[ii] {
t.Errorf("Conversion to byte slice bad! Byte: %v Wanted: %v Got: %v",
ii, pesExpectedOutput[ii], pesPktAsByteSlice[ii])
}
}
}
/*******************************************************
Mpegts testing
********************************************************/
func TestMpegTsToByteSlice(t *testing.T){
afRemainderLength := 180
afField := make([]byte, afRemainderLength+1)
afField[0] = byte(afRemainderLength)
afField[1] = byte(0)
for i := 2; i < len(afField); i++ {
afField[i] = 0xFF
}
tsPkt := MpegTsPacket{
byte(0x47), // sync byte
bool(false), // TEI
bool(false), // PUSI
bool(false), // Priority
uint16(256), // PID
byte(0), // TSC
byte(3), // AFC
byte(6), // CC
afField, // AF
[]byte{ // data
0x67,
0xB2,
0xE3,
},
}
expectedOutput := []byte{
0x47,
0x01,
0x00,
0x36,
byte(afRemainderLength),
0x00,
// this is where stuffing is, expect that to be 0xFF
0x67,
0xB2,
0xE3,
}
tsPktAsByteSlice := tsPkt.ToByteSlice()
for ii := 0; ii < 6; ii++ {
if tsPktAsByteSlice[ii] != expectedOutput[ii] {
t.Errorf("Conversion to byte slice bad! Byte: %v Wanted: %v Got: %v",
ii, expectedOutput[ii], tsPktAsByteSlice[ii])
}
}
// Check that the stuffing is all there
for ii := 6; ii < 185; ii++ {
if tsPktAsByteSlice[ii] != 0xFF {
t.Errorf("Conversion to byte slice bad! Byte: %v Wanted: %v Got: %v",
ii, byte(0xFF), tsPktAsByteSlice[ii])
}
}
for ii := 185; ii < 188; ii++ {
if tsPktAsByteSlice[ii] != expectedOutput[ii-185+6] {
t.Errorf("Conversion to byte slice bad! Byte: %v Wanted: %v Got: %v",
ii, expectedOutput[ii], tsPktAsByteSlice[ii])
}
}
}
/*******************************************************
RtpToTsConverter testing
********************************************************/
/*
func TestRtpToTsConverter(t *testing.T){
converter := NewRtpToTsConverter()
go converter.Convert()
// Create first rtp packet
rtpPacket1 := new(RtpPacket)
rtpPacket1.Version = 2
rtpPacket1.Padding = false
rtpPacket1.Ext = false
rtpPacket1.CC = 0
rtpPacket1.Marker = true
rtpPacket1.PayloadType = 0xE3
rtpPacket1.SequenceNumber = 1
rtpPacket1.Timestamp = 200
rtpPacket1.SyncSource = 0
rtpPacket1.CSRC = nil
rtpPacket1.ExtHeader = 0
rtpPacket1.ExtData = nil
nalFragment := new(NALFragment)
nalFragment.ThreeNUBs = 0x02
nalFragment.FragmentType = byte(28)
nalFragment.Start = true
nalFragment.End = false
nalFragment.Reserved = true
nalFragment.FiveNUBs = 0x03
nalFragment.Data = make([]byte,98)
rand.Seed(int64(time.Now().Nanosecond()))
for i := range nalFragment.Data {
nalFragment.Data[i] = byte(rand.Intn(255))
fmt.Printf(" %v ", nalFragment.Data[i])
}
rtpPacket1.Payload = make([]byte,100)
copy(rtpPacket1.Payload[:], nalFragment.ToByteSlice())
fmt.Println(rtpPacket1.Payload)
converter.InputChan<-(*rtpPacket1)
// Create second rtp packet
rtpPacket2 := new(RtpPacket)
rtpPacket2.Version = 2
rtpPacket2.Padding = false
rtpPacket2.Ext = false
rtpPacket2.CC = 0
rtpPacket2.Marker = false
rtpPacket2.PayloadType = 0xE3
rtpPacket2.SequenceNumber = 2
rtpPacket2.Timestamp = 300
rtpPacket2.SyncSource = 0
rtpPacket2.CSRC = nil
rtpPacket2.ExtHeader = 0
rtpPacket2.ExtData = nil
nalFragment = new(NALFragment)
nalFragment.ThreeNUBs = 0x02
nalFragment.FragmentType = byte(28)
nalFragment.Start = false
nalFragment.End = true
nalFragment.Reserved = true
nalFragment.FiveNUBs = 0x03
nalFragment.Data = make([]byte,198)
for i := range nalFragment.Data {
nalFragment.Data[i] = byte(rand.Intn(255))
}
rtpPacket2.Payload = make([]byte,200)
copy(rtpPacket2.Payload[:], nalFragment.ToByteSlice())
converter.InputChan<-(*rtpPacket2)
// Create first expected tsPacket
afField := make([]byte, 2)
afField[0] = byte(1)
afField[1] = byte(0)
pesPkt := new(PESPacket)
pesPkt.StreamID = 0xE0
pesPkt.Length = uint16( 3 + 300 )
pesPkt.ScramblingControl = 0
pesPkt.Priority = true
pesPkt.DAI = false
pesPkt.Copyright = false
pesPkt.Original = true
pesPkt.PDI = 0
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,300)
for ii:=0; ii<100; ii++ {
pesPkt.Data[ii] = rtpPacket1.Payload[ii]
}
for ii:=100; ii <300; ii++ {
pesPkt.Data[ii] = rtpPacket2.Payload[ii-100]
}
pesPacketAsByteSlice := pesPkt.ToByteSlice()
data := make([]byte, 182)
copy(data[:],pesPacketAsByteSlice[:182])
expectedPkt1 := MpegTsPacket{
byte(0x47), // sync byte
bool(false), // TEI
bool(true), // PUSI
bool(false), // Priority
uint16(256), // PID
byte(0), // TSC
byte(3), // AFC
byte(0), // CC
afField, // AF
data,
}
data = make([]byte, len(pesPacketAsByteSlice)-182)
copy(data[:], pesPacketAsByteSlice[182:])
afField = make([]byte, 2+(182-(len(pesPacketAsByteSlice)-182)))
afField[0] = byte(1+(182-(len(pesPacketAsByteSlice)-182)))
afField[1] = byte(0)
for ii := 2; ii < len(afField); ii++ {
afField[ii] = 0xFF
}
expectedPkt2 := MpegTsPacket{
byte(0x47), // sync byte
bool(false), // TEI
bool(false), // PUSI
bool(false), // Priority
uint16(256), // PID
byte(0), // TSC
byte(3), // AFC
byte(1), // CC
afField, // AF
data,
}
// Now let's get our two Ts packets from the converter and see if they're G
tsPacket := <-converter.TsChan
expectedPkt1AsByteSlice := expectedPkt1.ToByteSlice()
tsPacketAsByteSlice := tsPacket.ToByteSlice()
for ii := range expectedPkt1AsByteSlice {
if expectedPkt1AsByteSlice[ii] != tsPacketAsByteSlice[ii] {
t.Errorf("Not equal! Byte: %v Exptected: %v Got: %v\n",ii,
expectedPkt1AsByteSlice[ii],tsPacketAsByteSlice[ii])
}
}
fmt.Printf("Expected packet: %v\n", expectedPkt1.ToByteSlice())
fmt.Printf("Got packet: %v\n", tsPacket.ToByteSlice())
tsPacket = <-converter.TsChan
expectedPkt2AsByteSlice := expectedPkt2.ToByteSlice()
tsPacketAsByteSlice = tsPacket.ToByteSlice()
for ii := range expectedPkt2AsByteSlice {
if expectedPkt2AsByteSlice[ii] != tsPacketAsByteSlice[ii] {
t.Errorf("Not equal! Byte: %v Exptected: %v Got: %v\n",ii,
expectedPkt2AsByteSlice[ii],tsPacketAsByteSlice[ii])
}
}
fmt.Printf("Expected packet: %v\n", expectedPkt2.ToByteSlice())
fmt.Printf("Got packet: %v\n", tsPacket.ToByteSlice())
}
*/
func TestH264Parsing(t *testing.T) {
// Using file
/*
file, err := os.Open(fileName)
if err != nil {
panic("Could not open file!")
return
}
stats, err := file.Stat()
if err != nil {
panic("Could not get file stats!")
}
buffer := make([]byte, stats.Size())
_, err = file.Read(buffer)
if err != nil {
panic("Could not read file!")
}
*/
// straight from buffer
someData := []byte{
0,0,1,7,59,100,45,82,93,0,0,1,8,23,78,65,0,0,1,6,45,34,23,3,2,0,0,1,5,3,4,5,
56,76,4,234,78,65,34,34,43,0,0,1,7,67,10,45,8,93,0,0,1,8,23,7,5,0,0,1,6,
4,34,2,3,2,0,0,1,1,3,4,5,5,76,4,234,78,65,34,34,43,45,
}
nalAccess1 := []byte{
0,0,1,9,240,0,0,1,7,59,100,45,82,93,0,0,1,8,23,78,65,0,0,1,6,45,34,23,3,2,0,0,1,5,3,4,5,
56,76,4,234,78,65,34,34,43,
}
nalAccess2 := []byte{
0,0,1,9,240,0,0,1,7,67,10,45,8,93,0,0,1,8,23,7,5,0,0,1,6,
4,34,2,3,2,0,0,1,1,3,4,5,5,76,4,234,78,65,34,34,43,45,
}
aChannel := make(chan []byte, 10)
var nalAccessChan chan<- []byte
nalAccessChan = aChannel
go ParseH264Buffer(someData,nalAccessChan)
anAccessUnit := <-aChannel
for i := range anAccessUnit {
if anAccessUnit[i] != nalAccess1[i] {
t.Errorf("Should have been equal!")
}
}
anAccessUnit = <-aChannel
for i := range anAccessUnit {
if anAccessUnit[i] != nalAccess2[i] {
t.Errorf("Should have been equal!")
}
}
}

View File

@ -5,7 +5,7 @@ DESCRIPTION
See Readme.md
AUTHOR
Saxon Nelson-Milton <saxon.milton@gmail.com>
Saxon A. Nelson-Milton <saxon.milton@gmail.com>
LICENSE
PES.go is Copyright (C) 2017 the Australian Ocean Lab (AusOcean)
@ -24,8 +24,13 @@ LICENSE
along with revid in gpl.txt. If not, see [GNU licenses](http://www.gnu.org/licenses).
*/
package packets
package pes
/*
The below data struct encapsulates the fields of an PES packet. Below is
the formatting of a PES packet for reference!
PES Packet Formatting
============================================================================
| octet no | bit 0 | bit 1 | bit 2 | bit 3 | bit 4 | bit 5 | bit 6 | bit 7 |
============================================================================
@ -61,52 +66,38 @@ package packets
----------------------------------------------------------------------------
*/
type PESPacket struct {
StreamID byte
Length uint16
SC byte // Scrambling control
Priority bool // Priority Indicator
DAI bool // Data alginment indicator
Copyright bool // Copyright indicator
Original bool // Original data indicator
PDI byte // PTS DTS indicator
ESCRF bool // Elementary stream clock reference flag
ESRF bool // Elementary stream rate reference flag
DSMTMF bool // Dsm trick mode flag
ACI bool // Additional copy info flag
CRC bool //
EF bool // Extension flag
HeaderLength byte // Pes header length
OptFields []byte // Optional fields
Stuffing []byte // Stuffing bytes
Data []byte // Pes packet data
StreamID byte // Type of stream
Length uint16 // Pes packet length in bytes after this field
SC byte // Scrambling control
Priority bool // Priority Indicator
DAI bool // Data alginment indicator
Copyright bool // Copyright indicator
Original bool // Original data indicator
PDI byte // PTS DTS indicator
ESCRF bool // Elementary stream clock reference flag
ESRF bool // Elementary stream rate reference flag
DSMTMF bool // Dsm trick mode flag
ACIF bool // Additional copy info flag
CRCF bool // Not sure
EF bool // Extension flag
HeaderLength byte // Pes header length
OptFields []byte // Optional fields
Stuffing []byte // Stuffing bytes
Data []byte // Pes packet data
}
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] = 0x2 << 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.OptFields)
copy(output[9:optFieldsOffset],p.OptFields)
copy(output[optFieldsOffset:optFieldsOffset + len(p.Stuffing)],p.Stuffing)
dataOffset := 9+int(p.HeaderLength)
copy(output[dataOffset:dataOffset+len(p.Data)],p.Data)
output = append(output, []byte{
0x00, 0x00, 0x01,
p.StreamID,
byte((p.Length & 0xFF00) >> 8),
byte(p.Length & 0x00FF),
(0x2<<6 | p.SC<<4 | boolToByte(p.Priority)<<3 | boolToByte(p.DAI)<<2 |
boolToByte(p.Copyright)<<1 | boolToByte(p.Original)),
(p.PDI<<6 | boolToByte(p.ESCRF)<<5 | boolToByte(p.ESRF)<<4 | boolToByte(p.DSMTMF)<<3 |
boolToByte(p.ACIF)<<2 | boolToByte(p.CRCF)<<1 | boolToByte(p.EF)),
p.HeaderLength,
}...)
output = append(output, append(p.OptFields, append(p.Stuffing, p.Data...)...)...)
return
}

85
pes/pes_test.go Normal file
View File

@ -0,0 +1,85 @@
/*
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 pes
import (
"testing"
)
const (
dataLength = 3 // bytes
)
func TestPesToByteSlice(t *testing.T) {
pesPkt := PESPacket{
byte(0xE0), // StreamID
uint16(6), // Length
byte(0), // ScramblingControl
bool(true), // Priority
bool(false), // DAI
bool(false), // copyright
bool(true), // Original
byte(0), // PDI
bool(false), // Escr
bool(false), // ESRate
bool(false), // DSMTrickMode
bool(false), // ACI
bool(false), // CRC
bool(false), // Ext
byte(0), // header length
[]byte{},
[]byte{},
[]byte{ // data
0xEA,
0x4B,
0x12,
},
}
pesExpectedOutput := []byte{
0x00, // packet start code prefix byte 1
0x00, // packet start code prefix byte 2
0x01, // packet start code prefix byte 3
0xE0, // stream ID
0x00, // PES Packet length byte 1
0x06, // PES packet length byte 2
0x89, // Marker bits,ScramblingControl, Priority, DAI, Copyright, Original
0x00, // PDI, ESCR, ESRate, DSMTrickMode, ACI, CRC, Ext
0x00, // header length
0xEA, // data byte 1
0x4B, // data byte 2
0x12, // data byte 3
}
pesPktAsByteSlice := pesPkt.ToByteSlice()
for ii := range pesPktAsByteSlice {
if pesPktAsByteSlice[ii] != pesExpectedOutput[ii] {
t.Errorf("Conversion to byte slice bad! Byte: %v Wanted: %v Got: %v",
ii, pesExpectedOutput[ii], pesPktAsByteSlice[ii])
}
}
}

View File

@ -1,22 +0,0 @@
package main
import "fmt"
func main(){
data := []byte{ 2,176,18,0,1,193,0,0,225,0,240,0,27,225,0,240,0}
crc32 := 0xffffffff
for i := 1 + data[0]; i < len(data); i++ {
b := data[i]
for bit := 0; bit < 8; bit++ {
if (crc32 >= 0x80000000) != (b >= 0x90) {
crc32 = (crc32 << 1) ^ 0x04C11DB7
} else {
crc32 = crc32 << 1
b <<= 1
}
}
}
fmt.Println(crc32)
}

View File

@ -1,104 +0,0 @@
package main
import (
//"bytes"
"flag"
"fmt"
"io"
"log"
"net"
_"time"
"../packet"
"github.com/beatgammit/rtsp"
)
func init() {
flag.Parse()
}
func main() {
if len(flag.Args()) >= 1 {
rtspUrl := flag.Args()[0]
rtpUrl := flag.Args()[1]
sess := rtsp.NewSession()
res, err := sess.Options(rtspUrl)
if err != nil {
log.Fatalln(err)
}
fmt.Println("Options:")
fmt.Println(res)
res, err = sess.Describe(rtspUrl)
if err != nil {
log.Fatalln(err)
}
fmt.Println("Describe:")
fmt.Println(res)
p, err := rtsp.ParseSdp(&io.LimitedReader{R: res.Body, N: res.ContentLength})
if err != nil {
log.Fatalln(err)
}
log.Printf("%+v", p)
fmt.Println("Setting up!")
rtpPort, rtcpPort := 17300, 17319
res, err = sess.Setup(rtpUrl, fmt.Sprintf("RTP/AVP;unicast;client_port=%d-%d", rtpPort, rtcpPort))
if err != nil {
log.Fatalln(err)
}
log.Println(res)
fmt.Println("Playing !")
res, err = sess.Play(rtspUrl, res.Header.Get("Session"))
if err != nil {
log.Fatalln(err)
}
log.Println(res)
// create udp connection for rtp stuff
rtpLaddr, err := net.ResolveUDPAddr("udp", "192.168.0.109:17300")
if err != nil {
fmt.Println("Local rtp addr not set!")
}
rtpAddr, err := net.ResolveUDPAddr("udp", "192.168.0.50:17300")
if err != nil {
fmt.Println("Resolving rtp address didn't work!")
}
rtpConn, err := net.DialUDP("udp", rtpLaddr, rtpAddr)
if err != nil {
fmt.Println("Rtp dial didn't work!")
}
// Create udp connection for rtcp stuff
rtcpLaddr, err := net.ResolveUDPAddr("udp", "192.168.0.109:17319")
if err != nil {
fmt.Println("Local ")
}
rtcpAddr, err := net.ResolveUDPAddr("udp", "192.168.0.50:17301")
if err != nil {
fmt.Println("resolving rtcp address didn't work!")
}
rtcpConn, err := net.DialUDP("udp", rtcpLaddr, rtcpAddr)
if err != nil {
fmt.Println("Rtcp dial didnt't work!")
}
// let's create a session that will store useful stuff from the connections
rtpSession := packet.NewSession(rtpConn, rtcpConn)
converter := packet.NewRtpToTsConverter()
go converter.Convert()
for {
select{
default:
case rtpPacket := <-rtpSession.RtpChan:
converter.RtpChan<-rtpPacket
case tsPacket:=<-converter.TsChan:
fmt.Println(tsPacket.ToByteSlice())
}
}
}
}

136
revid/revid_test.go Normal file
View File

@ -0,0 +1,136 @@
/*
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 revid
import (
"testing"
)
/*******************************************************
Testing stuff related to connection i.e. rtsp, rtp, rtcp
********************************************************/
const (
rtpPort = 17300
rtcpPort = 17319
rtspUrl = "rtsp://192.168.0.50:8554/CH002.sdp"
rtpUrl = "rtsp://192.168.0.50:8554/CH002.sdp/track1"
)
/* Let's see if we can connect to an rtsp device then read an rtp stream,
and then convert the rtp packets to mpegts packets and output. */
func TestRTSP(t *testing.T) {
sess := rtsp.NewSession()
res, err := sess.Options(rtspUrl)
if err != nil {
t.Errorf("Shouldn't have got error: %v\n", err)
}
res, err = sess.Describe(rtspUrl)
if err != nil {
log.Fatalln(err)
t.Errorf("Shouldn't have got error: %v\n", err)
}
p, err := rtsp.ParseSdp(&io.LimitedReader{R: res.Body, N: res.ContentLength})
if err != nil {
t.Errorf("Shouldn't have got error: %v\n", err)
}
log.Printf("%+v", p)
res, err = sess.Setup(rtpUrl, fmt.Sprintf("RTP/AVP;unicast;client_port=%d-%d", rtpPort, rtcpPort))
if err != nil {
t.Errorf("Shouldn't have got error: %v\n", err)
}
log.Println(res)
res, err = sess.Play(rtspUrl, res.Header.Get("Session"))
if err != nil {
t.Errorf("Shouldn't have got error: %v\n", err)
}
log.Println(res)
}
func TestRTP(t *testing.T) {
sess := rtsp.NewSession()
res, err := sess.Options(rtspUrl)
if err != nil {
t.Errorf("Shouldn't have got error: %v\n", err)
}
res, err = sess.Describe(rtspUrl)
if err != nil {
log.Fatalln(err)
t.Errorf("Shouldn't have got error: %v\n", err)
}
p, err := rtsp.ParseSdp(&io.LimitedReader{R: res.Body, N: res.ContentLength})
if err != nil {
t.Errorf("Shouldn't have got error: %v\n", err)
}
log.Printf("%+v", p)
res, err = sess.Setup(rtpUrl, fmt.Sprintf("RTP/AVP;unicast;client_port=%d-%d", rtpPort, rtcpPort))
if err != nil {
t.Errorf("Shouldn't have got error: %v\n", err)
}
log.Println(res)
res, err = sess.Play(rtspUrl, res.Header.Get("Session"))
if err != nil {
t.Errorf("Shouldn't have got error: %v\n", err)
}
log.Println(res)
// create udp connection for rtp stuff
rtpLaddr, err := net.ResolveUDPAddr("udp", "192.168.0.109:17300")
if err != nil {
t.Errorf("Local rtp addr not set! %v\n", err)
}
rtpAddr, err := net.ResolveUDPAddr("udp", "192.168.0.50:17300")
if err != nil {
t.Errorf("Resolving rtp address didn't work! %v\n", err)
}
rtpConn, err := net.DialUDP("udp", rtpLaddr, rtpAddr)
if err != nil {
t.Errorf("Conncection not established! %v\n", err)
}
// Create udp connection for rtcp stuff
rtcpLaddr, err := net.ResolveUDPAddr("udp", "192.168.0.109:17319")
if err != nil {
t.Errorf("Local RTCP address not resolved! %v\n", err)
}
rtcpAddr, err := net.ResolveUDPAddr("udp", "192.168.0.50:17301")
if err != nil {
t.Errorf("Remote RTCP address not resolved! %v\n", err)
}
rtcpConn, err := net.DialUDP("udp", rtcpLaddr, rtcpAddr)
if err != nil {
t.Errorf("Connection not established! %v\n", err)
}
// let's create a session that will store useful stuff from the connections
rtpSession := NewSession(rtpConn, rtcpConn)
time.Sleep(2 * time.Second)
select {
default:
t.Errorf("Should have got rtpPacket!")
case rtpPacket := <-rtpSession.RtpChan:
fmt.Printf("RTP packet: %v\n", rtpPacket)
}
}

View File

View File

@ -29,7 +29,7 @@ OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISE
OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package packets
package rtp
import (
"net"

View File

@ -26,30 +26,31 @@ LICENSE
along with revid in gpl.txt. If not, see [GNU licenses](http://www.gnu.org/licenses).
*/
package packets
package tools
import (
_"os"
_"fmt"
"reflect"
"../rtp"
)
func boolToByte(in bool) (out uint8) {
func BoolToByte(in bool) (out byte) {
if in {
out = 1
}
return
}
func GetOctectType(p *RtpPacket) byte {
func GetOctectType(p *rtp.RtpPacket) byte {
return p.Payload[0] & 0x1F
}
func GetStartBit(p *RtpPacket) byte {
func GetStartBit(p *rtp.RtpPacket) byte {
return (p.Payload[1] & 0x80) >> 7
}
func getEndBit(p *RtpPacket) byte {
func getEndBit(p *rtp.RtpPacket) byte {
return (p.Payload[1] & 0x40) >> 6
}

83
tools/tools_test.go Normal file
View File

@ -0,0 +1,83 @@
/*
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 tools
import (
"testing"
)
func TestH264Parsing(t *testing.T){
// Using file
/*
file, err := os.Open(fileName)
if err != nil {
panic("Could not open file!")
return
}
stats, err := file.Stat()
if err != nil {
panic("Could not get file stats!")
}
buffer := make([]byte, stats.Size())
_, err = file.Read(buffer)
if err != nil {
panic("Could not read file!")
}
*/
// straight from buffer
someData := []byte{
0,0,1,7,59,100,45,82,93,0,0,1,8,23,78,65,0,0,1,6,45,34,23,3,2,0,0,1,5,3,4,5,
56,76,4,234,78,65,34,34,43,0,0,1,7,67,10,45,8,93,0,0,1,8,23,7,5,0,0,1,6,
4,34,2,3,2,0,0,1,1,3,4,5,5,76,4,234,78,65,34,34,43,45,
}
nalAccess1 := []byte{
0,0,1,9,240,0,0,1,7,59,100,45,82,93,0,0,1,8,23,78,65,0,0,1,6,45,34,23,3,2,0,0,1,5,3,4,5,
56,76,4,234,78,65,34,34,43,
}
nalAccess2 := []byte{
0,0,1,9,240,0,0,1,7,67,10,45,8,93,0,0,1,8,23,7,5,0,0,1,6,
4,34,2,3,2,0,0,1,1,3,4,5,5,76,4,234,78,65,34,34,43,45,
}
aChannel := make(chan []byte, 10)
var nalAccessChan chan<- []byte
nalAccessChan = aChannel
go ParseH264Buffer(someData,nalAccessChan)
anAccessUnit := <-aChannel
for i := range anAccessUnit {
if anAccessUnit[i] != nalAccess1[i] {
t.Errorf("Should have been equal!")
}
}
anAccessUnit = <-aChannel
for i := range anAccessUnit {
if anAccessUnit[i] != nalAccess2[i] {
t.Errorf("Should have been equal!")
}
}
}

View File

@ -190,15 +190,12 @@ func (c *rtpToTsConverter) Convert() {
lengthOfByteChan := len(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
@ -211,10 +208,6 @@ func (c *rtpToTsConverter) Convert() {
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] = 0x00
}
for ii := 0; ii < stuffingLength; ii++ {
c.currentTsPacket.AF[2+ii] = 0xFF
}