av/generator/MPEGTSGenerator.go

218 lines
5.9 KiB
Go
Raw Normal View History

/*
NAME
2018-02-28 16:46:59 +03:00
MPEGTSGenerator.go
DESCRIPTION
See Readme.md
AUTHOR
2018-02-28 16:46:59 +03:00
Saxon Nelson-Milton <saxon@ausocean.org>
LICENSE
2018-02-28 16:46:59 +03:00
MPEGTSGenerator.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 generator
import (
2018-03-13 08:15:42 +03:00
"bitbucket.org/ausocean/av/mpegts"
"bitbucket.org/ausocean/av/pes"
/*
2018-03-14 04:18:03 +03:00
"../mpegts"
"../pes"
*/)
2018-04-17 08:20:23 +03:00
const mpegtsPacketSize = 184
2018-02-28 17:42:00 +03:00
// TODO: really need to finish the at and pmt stuff - this is too hacky
2018-01-16 08:49:18 +03:00
var (
2018-02-28 17:42:00 +03:00
patTableStart = []byte{0, 0, 176, 13, 0, 1, 193, 0, 0, 0, 1, 240, 0, 42, 177, 4, 178}
patTable []byte
pmtTableStart = []byte{0, 2, 176, 18, 0, 1, 193, 0, 0, 0xE1, 0x00, 0xF0, 0, 0x1B, 0xE1, 0, 0xF0, 0, 0x15, 0xBD, 0x4D, 0x56}
2018-03-14 04:18:03 +03:00
pmtTable []byte
2018-01-16 08:49:18 +03:00
)
2018-02-28 17:42:00 +03:00
// genPatAndPmt generates the rest of the pat and pmt tables i.e. fills them
// with 0xFFs - because it looks ugly to hardcode above. This is called through
// NewMpegtsgenerator
func genPatAndPmt() {
2018-04-17 08:20:23 +03:00
patTable = make([]byte, 0, mpegtsPacketSize)
2018-03-13 07:33:31 +03:00
patTable = append(patTable, patTableStart...)
2018-04-17 08:20:23 +03:00
for i := len(pmtTableStart); i < mpegtsPacketSize - len(pmtTableStart); i++ {
pmtTable[i] = 255
2018-02-28 17:42:00 +03:00
}
2018-04-17 08:20:23 +03:00
pmtTable = make([]byte, 0, mpegtsPacketSize)
2018-03-13 07:33:31 +03:00
pmtTable = append(pmtTable, pmtTableStart...)
2018-04-17 08:20:23 +03:00
for i := len(patTableStart); i < mpegtsPacketSize - len(patTableStart); i++ {
pmtTable[i] = 255
2018-02-28 17:42:00 +03:00
}
}
2018-01-16 08:49:18 +03:00
const (
2018-02-28 17:42:00 +03:00
SdtPid = 17
PatPid = 0
pmtPid = 4096
videoPid = 256
streamID = 0xE0
outputChanSize = 100
2018-03-14 04:18:03 +03:00
inputChanSize = 10000
2018-02-28 17:42:00 +03:00
pesPktChanSize = 1000
2018-03-13 07:33:31 +03:00
payloadByteChanSize = 100000
2018-02-28 17:42:00 +03:00
ptsOffset = .7
maxCC = 15
2018-01-16 08:49:18 +03:00
)
2018-02-28 17:42:00 +03:00
// tsGenerator encapsulates properties of an mpegts generator.
2018-01-10 04:32:16 +03:00
type tsGenerator struct {
2018-02-28 16:46:59 +03:00
outputChan chan []byte
nalInputChan chan []byte
2018-01-08 04:12:26 +03:00
currentTsPacket *mpegts.MpegTsPacket
payloadByteChan chan byte
currentCC byte
2018-02-28 16:46:59 +03:00
currentPtsTime float64
currentPcrTime float64
fps uint
pesPktChan chan []byte
ccMap map[int]int
2018-05-06 10:32:51 +03:00
isGenerating bool
}
2018-02-28 17:42:00 +03:00
// getInputChan returns a handle to the nalInputChan (inputChan) so that nal units
// can be passed to the generator and processed
2018-02-28 16:46:59 +03:00
func (g *tsGenerator) GetInputChan() chan []byte {
return g.nalInputChan
}
2018-02-28 17:42:00 +03:00
// GetOutputChan returns a handle to the generator output chan where the mpegts
// packets will show up once ready to go
2018-02-28 16:46:59 +03:00
func (g *tsGenerator) GetOutputChan() chan []byte {
return g.outputChan
2017-12-13 09:52:18 +03:00
}
2018-02-28 17:42:00 +03:00
// NewTsGenerator returns an instance of the tsGenerator struct
2018-01-10 06:57:56 +03:00
func NewTsGenerator(fps uint) (g *tsGenerator) {
g = new(tsGenerator)
2018-02-28 17:42:00 +03:00
g.outputChan = make(chan []byte, outputChanSize)
g.nalInputChan = make(chan []byte, inputChanSize)
2018-01-10 06:57:56 +03:00
g.currentCC = 0
g.fps = fps
2018-02-28 17:42:00 +03:00
g.currentPcrTime = 0.0
g.currentPtsTime = ptsOffset
2018-03-13 07:33:31 +03:00
g.pesPktChan = make(chan []byte, pesPktChanSize)
2018-02-28 17:42:00 +03:00
g.payloadByteChan = make(chan byte, payloadByteChanSize)
2018-01-16 10:17:38 +03:00
g.ccMap = make(map[int]int, 4)
2018-01-16 08:49:18 +03:00
g.ccMap[SdtPid] = 0
g.ccMap[PatPid] = 0
2018-02-28 16:46:59 +03:00
g.ccMap[pmtPid] = 0
g.ccMap[videoPid] = 0
2018-03-13 07:33:31 +03:00
genPatAndPmt()
2018-05-06 10:32:51 +03:00
g.isGenerating = false
return
2017-12-13 09:52:18 +03:00
}
2018-02-28 17:42:00 +03:00
// getPts retuns the next presentation timestamp for the tsGenerator t.
2018-02-28 16:46:59 +03:00
func (g *tsGenerator) genPts() (pts uint64) {
2018-01-10 06:57:56 +03:00
pts = uint64(g.currentPtsTime * float64(90000))
2018-02-28 16:46:59 +03:00
g.currentPtsTime += 1.0 / float64(g.fps)
2018-01-08 04:12:26 +03:00
return
}
2018-02-28 17:42:00 +03:00
// genPcr returns the next program clock reference for the tsGenerator g
2018-02-28 16:46:59 +03:00
func (g *tsGenerator) genPcr() (pcr uint64) {
2018-01-10 06:57:56 +03:00
pcr = uint64(g.currentPcrTime * float64(90000))
2018-02-28 16:46:59 +03:00
g.currentPcrTime += 1.0 / float64(g.fps)
2018-01-08 04:12:26 +03:00
return
}
2018-02-28 17:42:00 +03:00
// Start is called when we would like generation to begin, i.e. we would like
// the generator to start taking input data and creating mpegts packets
2018-02-28 16:46:59 +03:00
func (g *tsGenerator) Start() {
2018-05-06 10:32:51 +03:00
g.isGenerating = true
2018-01-24 07:12:22 +03:00
go g.generate()
}
2018-05-06 10:32:51 +03:00
func (g *tsGenerator) Stop() {
g.isGenerating = false
}
2018-03-13 07:33:31 +03:00
// getCC returns the next continuity counter for a particular pid
2018-02-28 17:42:00 +03:00
func (g *tsGenerator) getCC(pid int) int {
temp := g.ccMap[pid]
if g.ccMap[pid]++; g.ccMap[pid] > maxCC {
g.ccMap[pid] = 0
}
return temp
}
// generate handles the incoming data and generates equivalent mpegts packets -
// sending them to the output channel
2018-01-24 07:12:22 +03:00
func (g *tsGenerator) generate() {
2018-01-17 06:48:47 +03:00
for {
select {
2018-01-10 06:57:56 +03:00
case nalUnit := <-g.nalInputChan:
2018-03-13 07:33:31 +03:00
pesPkt := pes.PESPacket{
2018-02-28 16:46:59 +03:00
StreamID: streamID,
PDI: byte(2),
PTS: g.genPts(),
Data: nalUnit,
2018-01-08 04:12:26 +03:00
HeaderLength: 5,
2018-03-13 07:33:31 +03:00
}
g.pesPktChan <- pesPkt.ToByteSlice()
case pesPkt := <-g.pesPktChan:
for ii := range pesPkt {
g.payloadByteChan <- pesPkt[ii]
}
2018-01-08 04:12:26 +03:00
pusi := true
for len(g.payloadByteChan) > 0 {
2018-01-08 04:12:26 +03:00
pkt := mpegts.MpegTsPacket{
PUSI: pusi,
2018-02-28 16:46:59 +03:00
PID: videoPid,
RAI: pusi,
CC: byte(g.getCC(videoPid)),
AFC: byte(3),
2018-01-08 04:12:26 +03:00
PCRF: pusi,
}
pkt.FillPayload(g.payloadByteChan)
2018-01-24 07:12:22 +03:00
2018-04-15 13:53:53 +03:00
// TODO: create consts for AFC parameters
2018-01-08 04:12:26 +03:00
if pusi {
2018-02-28 17:42:00 +03:00
// Create pat table
2018-03-13 07:33:31 +03:00
patPkt := mpegts.MpegTsPacket{
2018-02-28 16:46:59 +03:00
PUSI: pusi,
PID: PatPid,
CC: byte(g.getCC(PatPid)),
AFC: 1,
2018-03-13 07:33:31 +03:00
Payload: patTable,
}
g.outputChan <- patPkt.ToByteSlice()
2018-02-28 17:42:00 +03:00
// Create pmt table
2018-03-13 07:33:31 +03:00
pmtPkt := mpegts.MpegTsPacket{
2018-02-28 16:46:59 +03:00
PUSI: pusi,
PID: pmtPid,
CC: byte(g.getCC(pmtPid)),
AFC: 1,
Payload: pmtTable,
2018-03-13 07:33:31 +03:00
}
g.outputChan <- pmtPkt.ToByteSlice()
2018-02-28 17:42:00 +03:00
// If pusi then we need to gen a pcr
2018-01-10 06:57:56 +03:00
pkt.PCR = g.genPcr()
2018-01-08 04:12:26 +03:00
pusi = false
}
2018-02-28 17:42:00 +03:00
g.outputChan <- pkt.ToByteSlice()
}
}
}
2017-12-13 09:52:18 +03:00
}