mirror of https://bitbucket.org/ausocean/av.git
Got mpegts working!
This commit is contained in:
parent
da11e2888f
commit
18019992f7
|
@ -31,6 +31,7 @@ package mpegts
|
|||
import (
|
||||
"../tools"
|
||||
"errors"
|
||||
_"fmt"
|
||||
)
|
||||
|
||||
/*
|
||||
|
@ -106,7 +107,6 @@ type MpegTsPacket struct {
|
|||
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
|
||||
|
@ -121,23 +121,40 @@ type MpegTsPacket struct {
|
|||
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) FillPayload(channel chan byte){
|
||||
p.Payload = []byte{}
|
||||
currentPktLength := 6 + int(tools.BoolToByte(p.PCRF))*6+int(tools.BoolToByte(p.OPCRF))*6+
|
||||
int(tools.BoolToByte(p.SPF))*1+int(tools.BoolToByte(p.TPDF))*1+len(p.TPD)
|
||||
for len(channel) > 0 && (currentPktLength+len(p.Payload)) < 188 {
|
||||
nextByte := <-channel
|
||||
p.Payload = append(p.Payload,nextByte)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *MpegTsPacket) ToByteSlice() (output []byte, err error) {
|
||||
stuffingLength := 182-len(p.Payload)-len(p.TPD)-int(tools.BoolToByte(p.PCRF))*6-
|
||||
int(tools.BoolToByte(p.OPCRF))*6 - int(tools.BoolToByte(p.SPF))
|
||||
stuffing := make([]byte,stuffingLength)
|
||||
for i := range stuffing {
|
||||
stuffing[i] = 0xFF
|
||||
}
|
||||
afl := 1+int(tools.BoolToByte(p.PCRF))*6+int(tools.BoolToByte(p.OPCRF))*
|
||||
6+int(tools.BoolToByte(p.SPF))*1+int(tools.BoolToByte(p.TPDF))*1+len(p.TPD)+len(stuffing)
|
||||
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,
|
||||
byte(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)))
|
||||
output = append(output, byte((p.PCR<<15)>>uint(i)))
|
||||
}
|
||||
for i := 40; p.OPCRF && i >= 0; i-=8 {
|
||||
output = append(output, byte(p.OPCR>>uint(i)))
|
||||
|
@ -148,7 +165,8 @@ func (p *MpegTsPacket) ToByteSlice() (output []byte, err error) {
|
|||
if p.TPDF {
|
||||
output = append(output, append([]byte{p.TPDL}, p.TPD...)...)
|
||||
}
|
||||
output = append(output, append(p.Ext, append(p.Stuff, p.Payload...)...)...)
|
||||
|
||||
output = append(output, append(p.Ext, append(stuffing, p.Payload...)...)...)
|
||||
if len(output) != 188 {
|
||||
err = errors.New("Length of MPEG-TS packet is not 188! Something is wrong!")
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ func TestPesToByteSlice(t *testing.T) {
|
|||
StreamID: 0xE0, // StreamID
|
||||
PDI: byte(2),
|
||||
PTS: 100000,
|
||||
HeaderLength: byte(11),
|
||||
HeaderLength: byte(10),
|
||||
Stuff: []byte{0xFF,0xFF,},
|
||||
Data: []byte{ 0xEA, 0x4B, 0x12, },
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ func TestPesToByteSlice(t *testing.T) {
|
|||
0x00, // PES packet length byte 2
|
||||
0x80, // Marker bits,ScramblingControl, Priority, DAI, Copyright, Original
|
||||
0x80, // PDI, ESCR, ESRate, DSMTrickMode, ACI, CRC, Ext
|
||||
byte(11), // header length
|
||||
byte(10), // header length
|
||||
0x21, // PCR byte 1
|
||||
0x00, // pcr byte 2
|
||||
0x07, // pcr byte 3
|
||||
|
|
|
@ -47,7 +47,8 @@ import (
|
|||
"time"
|
||||
"os"
|
||||
|
||||
"../packets"
|
||||
"../tscreator"
|
||||
"../tools"
|
||||
|
||||
"bitbucket.org/ausocean/av/ringbuffer"
|
||||
|
||||
|
@ -80,6 +81,7 @@ const (
|
|||
rtspUrl = "rtsp://192.168.0.50:8554/CH002.sdp"
|
||||
rtpUrl = "rtsp://192.168.0.50:8554/CH002.sdp/track1"
|
||||
inputFileName = "testInput.h264"
|
||||
framesPerSec = 25
|
||||
)
|
||||
|
||||
// flag values
|
||||
|
@ -219,7 +221,7 @@ func input(input string, output string) {
|
|||
return
|
||||
}
|
||||
}
|
||||
converter := packets.NewRtpToTsConverter()
|
||||
converter := tscreator.NewTsCreator(framesPerSec)
|
||||
// Open the h264 file
|
||||
file, err := os.Open(inputFileName)
|
||||
if err != nil {
|
||||
|
@ -237,7 +239,7 @@ func input(input string, output string) {
|
|||
}
|
||||
|
||||
// Start parsing the h264 file and send nal access units to the converter
|
||||
go packets.ParseH264Buffer(buffer,converter.NalInputChan)
|
||||
go tools.ParseH264Buffer(buffer,converter.NalInputChan)
|
||||
go converter.Convert()
|
||||
clipSize := 0
|
||||
packetCount := 0
|
||||
|
@ -250,7 +252,7 @@ func input(input string, output string) {
|
|||
{71,64,0,16,0,0,176,13,0,1,193,0,0,0,1,240,0,42,177,4,178,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255},
|
||||
/*PMT*/{71,80,0,16,
|
||||
/*Start of payload*/
|
||||
0,2,176,18,0,1,193,0,0,255,255,240,0,27,225,0,240,0,193,91,65,224,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255},
|
||||
0,2,176,18,0,1,193,0,0,0xE1,0x00,0xF0,0,0x1B,0xE1,0,0xF0,0,0x15,0xBD,0x4D,0x56,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255},
|
||||
}
|
||||
|
||||
donePSI := false
|
||||
|
@ -271,7 +273,10 @@ func input(input string, output string) {
|
|||
} else {
|
||||
donePSI = true
|
||||
packet := <-converter.TsChan
|
||||
packetByteSlice := packet.ToByteSlice()
|
||||
packetByteSlice,err := packet.ToByteSlice()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
copy(clip[clipSize:upperBound],packetByteSlice)
|
||||
}
|
||||
//fmt.Println(clip[clipSize:upperBound])
|
||||
|
|
Binary file not shown.
|
@ -50,7 +50,7 @@ func GetStartBit(p *rtp.RtpPacket) byte {
|
|||
return (p.Payload[1] & 0x80) >> 7
|
||||
}
|
||||
|
||||
func getEndBit(p *rtp.RtpPacket) byte {
|
||||
func GetEndBit(p *rtp.RtpPacket) byte {
|
||||
return (p.Payload[1] & 0x40) >> 6
|
||||
}
|
||||
|
||||
|
|
|
@ -26,50 +26,70 @@ LICENSE
|
|||
along with revid in gpl.txt. If not, see [GNU licenses](http://www.gnu.org/licenses).
|
||||
*/
|
||||
|
||||
package packets
|
||||
package tscreator
|
||||
|
||||
import (
|
||||
_ "fmt"
|
||||
"os"
|
||||
_"os"
|
||||
"../mpegts"
|
||||
"../pes"
|
||||
"../tools"
|
||||
"../rtp"
|
||||
)
|
||||
|
||||
type RtpToTsConverter interface {
|
||||
type TsConverter interface {
|
||||
Convert()
|
||||
}
|
||||
|
||||
type rtpToTsConverter struct {
|
||||
TsChan <-chan *MpegTsPacket
|
||||
tsChan chan<- *MpegTsPacket
|
||||
InputChan chan<- RtpPacket
|
||||
inputChan <-chan RtpPacket
|
||||
type tsCreator struct {
|
||||
TsChan <-chan *mpegts.MpegTsPacket
|
||||
tsChan chan<- *mpegts.MpegTsPacket
|
||||
InputChan chan<- rtp.RtpPacket
|
||||
inputChan <-chan rtp.RtpPacket
|
||||
NalInputChan chan<- []byte
|
||||
nalInputChan <-chan []byte
|
||||
currentTsPacket *MpegTsPacket
|
||||
currentTsPacket *mpegts.MpegTsPacket
|
||||
payloadByteChan chan byte
|
||||
currentCC byte
|
||||
currentPtsTime float64
|
||||
currentPcrTime float64
|
||||
fps uint
|
||||
}
|
||||
|
||||
func NewRtpToTsConverter() (c *rtpToTsConverter) {
|
||||
c = new(rtpToTsConverter)
|
||||
tsChan := make(chan *MpegTsPacket, 100)
|
||||
func NewTsCreator(fps uint) (c *tsCreator) {
|
||||
c = new(tsCreator)
|
||||
tsChan := make(chan *mpegts.MpegTsPacket, 100)
|
||||
c.TsChan = tsChan
|
||||
c.tsChan = tsChan
|
||||
inputChan := make(chan RtpPacket, 100)
|
||||
inputChan := make(chan rtp.RtpPacket, 100)
|
||||
c.InputChan = inputChan
|
||||
c.inputChan = inputChan
|
||||
nalInputChan := make(chan []byte, 10000)
|
||||
c.NalInputChan = nalInputChan
|
||||
c.nalInputChan = nalInputChan
|
||||
c.currentCC = 0
|
||||
c.fps = fps
|
||||
c.currentPcrTime = .0
|
||||
c.currentPtsTime = .7
|
||||
return
|
||||
}
|
||||
|
||||
func (c *rtpToTsConverter) Convert() {
|
||||
file, _ := os.Create("video")
|
||||
func (c* tsCreator) genPts()(pts uint64){
|
||||
pts = uint64(c.currentPtsTime * float64(90000))
|
||||
c.currentPtsTime += 1.0/float64(c.fps)
|
||||
return
|
||||
}
|
||||
|
||||
func (c* tsCreator) genPcr()(pcr uint64){
|
||||
pcr = uint64(c.currentPcrTime * float64(90000))
|
||||
c.currentPcrTime += 1.0/float64(c.fps)
|
||||
return
|
||||
}
|
||||
|
||||
func (c *tsCreator) Convert() {
|
||||
pesPktChan := make(chan []byte, 1000)
|
||||
pesDataChan := make(chan byte, 50000)
|
||||
payloadByteChan := make(chan byte, 100000)
|
||||
var rtpBuffer [](*RtpPacket)
|
||||
var rtpBuffer [](*rtp.RtpPacket)
|
||||
for {
|
||||
select {
|
||||
default:
|
||||
|
@ -88,28 +108,24 @@ func (c *rtpToTsConverter) Convert() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(rtpBuffer) > 200 {
|
||||
// Discard everything before a type 7
|
||||
for GetOctectType(rtpBuffer[0]) != 7 {
|
||||
for tools.GetOctectType(rtpBuffer[0]) != 7 {
|
||||
rtpBuffer = rtpBuffer[1:]
|
||||
}
|
||||
// get sps
|
||||
sps := make([]byte, len(rtpBuffer[0].Payload))
|
||||
copy(sps[:], rtpBuffer[0].Payload[:])
|
||||
rtpBuffer = rtpBuffer[1:]
|
||||
// get pps
|
||||
pps := make([]byte, len(rtpBuffer[0].Payload))
|
||||
copy(pps[:], rtpBuffer[0].Payload[:])
|
||||
rtpBuffer = rtpBuffer[1:]
|
||||
// get sei
|
||||
sei := make([]byte, len(rtpBuffer[0].Payload))
|
||||
copy(sei[:], rtpBuffer[0].Payload[:])
|
||||
rtpBuffer = rtpBuffer[1:]
|
||||
// while we haven't reached the next sps in the buffer
|
||||
for GetOctectType(rtpBuffer[0]) != 7 {
|
||||
switch GetOctectType(rtpBuffer[0]) {
|
||||
for tools.GetOctectType(rtpBuffer[0]) != 7 {
|
||||
switch tools.GetOctectType(rtpBuffer[0]) {
|
||||
case 28:
|
||||
if GetStartBit(rtpBuffer[0]) == 1 {
|
||||
if tools.GetStartBit(rtpBuffer[0]) == 1 {
|
||||
var buffer []byte
|
||||
buffer = append(buffer, []byte{0x00, 0x00, 0x01}...)
|
||||
buffer = append(buffer, []byte{0x09, 0x10}...)
|
||||
|
@ -125,10 +141,9 @@ func (c *rtpToTsConverter) Convert() {
|
|||
rtpBuffer = rtpBuffer[1:]
|
||||
for {
|
||||
buffer = append(buffer, rtpBuffer[0].Payload[2:]...)
|
||||
if getEndBit(rtpBuffer[0]) == 1 {
|
||||
if tools.GetEndBit(rtpBuffer[0]) == 1 {
|
||||
rtpBuffer = rtpBuffer[1:]
|
||||
c.NalInputChan <- buffer
|
||||
file.Write(buffer)
|
||||
break
|
||||
}
|
||||
rtpBuffer = rtpBuffer[1:]
|
||||
|
@ -149,73 +164,42 @@ func (c *rtpToTsConverter) Convert() {
|
|||
buffer = append(buffer, rtpBuffer[0].Payload[2:]...)
|
||||
rtpBuffer = rtpBuffer[1:]
|
||||
c.NalInputChan <- buffer
|
||||
file.Write(buffer)
|
||||
default:
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
case nalUnit := <-c.nalInputChan:
|
||||
for ii := range nalUnit {
|
||||
pesDataChan <- nalUnit[ii]
|
||||
}
|
||||
pesDataChanLen := len(nalUnit)
|
||||
pesPkt := new(PESPacket)
|
||||
pesPkt.StreamID = 0xE0
|
||||
pesPkt.Length = uint16(3 + pesDataChanLen)
|
||||
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, pesDataChanLen)
|
||||
for ii := 0; ii < pesDataChanLen; ii++ {
|
||||
pesPkt.Data[ii] = <-pesDataChan
|
||||
pesPkt := pes.PESPacket{
|
||||
StreamID: 0xE0,
|
||||
PDI: byte(2),
|
||||
PTS: c.genPts(),
|
||||
Data: nalUnit,
|
||||
HeaderLength: 5,
|
||||
}
|
||||
pesPktChan <- pesPkt.ToByteSlice()
|
||||
case pesPkt := <-pesPktChan:
|
||||
for ii := range pesPkt {
|
||||
payloadByteChan <- pesPkt[ii]
|
||||
}
|
||||
firstPacket := true
|
||||
pusi := true
|
||||
for len(payloadByteChan) > 0 {
|
||||
lengthOfByteChan := len(payloadByteChan)
|
||||
c.currentTsPacket = new(MpegTsPacket)
|
||||
c.currentTsPacket.SyncByte = 0x47
|
||||
c.currentTsPacket.PUSI = false
|
||||
if firstPacket { // if it's the start of the payload
|
||||
c.currentTsPacket.PUSI = true
|
||||
firstPacket = false
|
||||
pkt := mpegts.MpegTsPacket{
|
||||
PUSI: pusi,
|
||||
PID: 256,
|
||||
RAI: pusi,
|
||||
CC: c.currentCC,
|
||||
AFC: byte(3),
|
||||
PCRF: pusi,
|
||||
}
|
||||
pkt.FillPayload(payloadByteChan)
|
||||
if pusi {
|
||||
pkt.PCR = c.genPcr()
|
||||
pusi = false
|
||||
}
|
||||
c.currentTsPacket.PID = 256
|
||||
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)
|
||||
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] = <-payloadByteChan
|
||||
}
|
||||
c.tsChan <- c.currentTsPacket
|
||||
c.tsChan <- &pkt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue