/* NAME RtpToTsConverter.go - provides utilities for the conversion of Rtp packets to equivalent MpegTs packets. DESCRIPTION See Readme.md AUTHOR Saxon Nelson-Milton LICENSE RtpToTsConverter.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 tsgenerator import ( _"fmt" _"os" "bitbucket.org/ausocean/av/mpegts" "bitbucket.org/ausocean/av/pes" "bitbucket.org/ausocean/av/tools" "bitbucket.org/ausocean/av/rtp" ) var ( PatTable = []byte{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,} PmtTable = []byte{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,} ) const ( SdtPid = 17 PatPid = 0 PmtPid = 4096 VideoPid = 256 ) type TsGenerator interface { generate() GetNalInputChan() chan<- []byte GetTsOutputChan() <-chan *mpegts.MpegTsPacket Start() genPts()(pts uint64) genPcr()(pts uint64) } type tsGenerator struct { TsChan <-chan *mpegts.MpegTsPacket tsChan chan<- *mpegts.MpegTsPacket InputChan chan<- rtp.RtpPacket inputChan <-chan rtp.RtpPacket NalInputChan chan<- []byte nalInputChan <-chan []byte currentTsPacket *mpegts.MpegTsPacket payloadByteChan chan byte currentCC byte currentPtsTime float64 currentPcrTime float64 fps uint pesPktChan chan []byte ccMap map[int]int } func (g *tsGenerator)GetNalInputChan() chan<- []byte { return g.NalInputChan } func (g *tsGenerator)GetTsOutputChan() <-chan *mpegts.MpegTsPacket { return g.TsChan } func NewTsGenerator(fps uint) (g *tsGenerator) { g = new(tsGenerator) tsChan := make(chan *mpegts.MpegTsPacket, 100) g.TsChan = tsChan g.tsChan = tsChan inputChan := make(chan rtp.RtpPacket, 100) g.InputChan = inputChan g.inputChan = inputChan nalInputChan := make(chan []byte, 10000) g.NalInputChan = nalInputChan g.nalInputChan = nalInputChan g.currentCC = 0 g.fps = fps g.currentPcrTime = .0 g.currentPtsTime = .7 g.pesPktChan = make(chan []byte, 1000) g.payloadByteChan = make(chan byte, 100000) g.ccMap = make(map[int]int, 4) g.ccMap[SdtPid] = 0 g.ccMap[PatPid] = 0 g.ccMap[PmtPid] = 0 g.ccMap[VideoPid] = 0 return } func (g *tsGenerator) genPts()(pts uint64){ pts = uint64(g.currentPtsTime * float64(90000)) g.currentPtsTime += 1.0/float64(g.fps) return } func (g *tsGenerator) genPcr()(pcr uint64){ pcr = uint64(g.currentPcrTime * float64(90000)) g.currentPcrTime += 1.0/float64(g.fps) return } func (g *tsGenerator) Start(){ go g.generate() } func (g *tsGenerator) generate() { var rtpBuffer [](*rtp.RtpPacket) for { select { case rtpPacket := <-g.inputChan: rtpBuffer = append(rtpBuffer, &rtpPacket) if len(rtpBuffer) > 2 { // if there's something weird going on with sequence numbers then // insertion sort if rtpPacket.SequenceNumber < rtpBuffer[len(rtpBuffer)-2].SequenceNumber { for i := 1; i < len(rtpBuffer); i++ { for j := i; j > 0 && rtpBuffer[j].SequenceNumber < rtpBuffer[j-1].SequenceNumber; j-- { temp := rtpBuffer[j] rtpBuffer[j] = rtpBuffer[j-1] rtpBuffer[j-1] = temp } } } } if len(rtpBuffer) > 200 { for tools.GetOctectType(rtpBuffer[0]) != 7 { rtpBuffer = rtpBuffer[1:] } sps := make([]byte, len(rtpBuffer[0].Payload)) copy(sps[:], rtpBuffer[0].Payload[:]) rtpBuffer = rtpBuffer[1:] pps := make([]byte, len(rtpBuffer[0].Payload)) copy(pps[:], rtpBuffer[0].Payload[:]) rtpBuffer = rtpBuffer[1:] sei := make([]byte, len(rtpBuffer[0].Payload)) copy(sei[:], rtpBuffer[0].Payload[:]) rtpBuffer = rtpBuffer[1:] for tools.GetOctectType(rtpBuffer[0]) != 7 { switch tools.GetOctectType(rtpBuffer[0]) { case 28: if tools.GetStartBit(rtpBuffer[0]) == 1 { var buffer []byte buffer = append(buffer, []byte{0x00, 0x00, 0x01}...) buffer = append(buffer, []byte{0x09, 0x10}...) buffer = append(buffer, []byte{0x00, 0x00, 0x01}...) buffer = append(buffer, sps...) buffer = append(buffer, []byte{0x00, 0x00, 0x01}...) buffer = append(buffer, pps...) buffer = append(buffer, []byte{0x00, 0x00, 0x01}...) buffer = append(buffer, sei...) buffer = append(buffer, []byte{0x00, 0x00, 0x01}...) buffer = append(buffer, rtpBuffer[0].Payload[0]&0xE0|rtpBuffer[0].Payload[1]&0x1F) buffer = append(buffer, rtpBuffer[0].Payload[2:]...) rtpBuffer = rtpBuffer[1:] for { buffer = append(buffer, rtpBuffer[0].Payload[2:]...) if tools.GetEndBit(rtpBuffer[0]) == 1 { rtpBuffer = rtpBuffer[1:] g.NalInputChan <- buffer break } rtpBuffer = rtpBuffer[1:] } } case 1: var buffer []byte buffer = append(buffer, []byte{0x00, 0x00, 0x01}...) buffer = append(buffer, []byte{0x09, 0x10}...) buffer = append(buffer, []byte{0x00, 0x00, 0x01}...) buffer = append(buffer, sps...) buffer = append(buffer, []byte{0x00, 0x00, 0x01}...) buffer = append(buffer, pps...) buffer = append(buffer, []byte{0x00, 0x00, 0x01}...) buffer = append(buffer, sei...) buffer = append(buffer, []byte{0x00, 0x00, 0x01}...) buffer = append(buffer, rtpBuffer[0].Payload[0]&0xE0|rtpBuffer[0].Payload[1]&0x1F) buffer = append(buffer, rtpBuffer[0].Payload[2:]...) rtpBuffer = rtpBuffer[1:] g.NalInputChan <- buffer default: } } } case nalUnit := <-g.nalInputChan: pesPkt := pes.PESPacket{ StreamID: 0xE0, PDI: byte(2), PTS: g.genPts(), Data: nalUnit, HeaderLength: 5, } g.pesPktChan <- pesPkt.ToByteSlice() case pesPkt := <-g.pesPktChan: for ii := range pesPkt { g.payloadByteChan <- pesPkt[ii] } pusi := true for len(g.payloadByteChan) > 0 { pkt := mpegts.MpegTsPacket{ PUSI: pusi, PID: VideoPid, RAI: pusi, CC: byte(g.getCC(VideoPid)), AFC: byte(3), PCRF: pusi, } pkt.FillPayload(g.payloadByteChan) if pusi { // Create pat table and send off patPkt := mpegts.MpegTsPacket{ PUSI: pusi, PID: PatPid, CC: byte(g.getCC(PatPid)), AFC: 1, Payload: PatTable, } g.tsChan <- &patPkt // Create pmt table and send off pmtPkt := mpegts.MpegTsPacket{ PUSI: pusi, PID: PmtPid, CC: byte(g.getCC(PmtPid)), AFC: 1, Payload: PmtTable, } g.tsChan <- &pmtPkt pkt.PCR = g.genPcr() pusi = false } g.tsChan <- &pkt } } } } func (g *tsGenerator) getCC(pid int) int { temp := g.ccMap[pid] if g.ccMap[pid]++; g.ccMap[pid] > 15 { g.ccMap[pid] = 0 } return temp }