197 lines
4.3 KiB
Go
197 lines
4.3 KiB
Go
package ts
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"github.com/nareix/joy4/av"
|
|
"github.com/nareix/joy4/codec/aacparser"
|
|
"github.com/nareix/joy4/codec/h264parser"
|
|
"io"
|
|
)
|
|
|
|
type Muxer struct {
|
|
W io.Writer
|
|
streams []*Stream
|
|
PaddingToMakeCounterCont bool
|
|
|
|
tswPAT *TSWriter
|
|
tswPMT *TSWriter
|
|
}
|
|
|
|
func (self *Muxer) isCodecSupported(codec av.CodecData) bool {
|
|
switch codec.Type() {
|
|
case av.H264, av.AAC:
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func (self *Muxer) newStream(codec av.CodecData) (err error) {
|
|
if !self.isCodecSupported(codec) {
|
|
err = fmt.Errorf("codec type=%x is not supported", codec.Type())
|
|
return
|
|
}
|
|
|
|
stream := &Stream{
|
|
muxer: self,
|
|
CodecData: codec,
|
|
tsw: &TSWriter{
|
|
DiscontinuityIndicator: true,
|
|
PID: uint(len(self.streams) + 0x100),
|
|
},
|
|
}
|
|
self.streams = append(self.streams, stream)
|
|
return
|
|
}
|
|
|
|
func (self *Muxer) writePaddingTSPackets(tsw *TSWriter) (err error) {
|
|
for tsw.ContinuityCounter&0xf != 0x0 {
|
|
header := TSHeader{
|
|
PID: tsw.PID,
|
|
ContinuityCounter: tsw.ContinuityCounter,
|
|
}
|
|
if _, err = WriteTSHeader(self.W, header, 0); err != nil {
|
|
return
|
|
}
|
|
tsw.ContinuityCounter++
|
|
}
|
|
return
|
|
}
|
|
|
|
func (self *Muxer) WriteTrailer() (err error) {
|
|
if self.PaddingToMakeCounterCont {
|
|
for _, stream := range self.streams {
|
|
if err = self.writePaddingTSPackets(stream.tsw); err != nil {
|
|
return
|
|
}
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (self *Muxer) WritePATPMT() (err error) {
|
|
bufPAT := &bytes.Buffer{}
|
|
bufPMT := &bytes.Buffer{}
|
|
|
|
pat := PAT{
|
|
Entries: []PATEntry{
|
|
{ProgramNumber: 1, ProgramMapPID: 0x1000},
|
|
},
|
|
}
|
|
WritePAT(bufPAT, pat)
|
|
|
|
var elemStreams []ElementaryStreamInfo
|
|
for _, stream := range self.streams {
|
|
switch stream.Type() {
|
|
case av.AAC:
|
|
elemStreams = append(elemStreams, ElementaryStreamInfo{StreamType: ElementaryStreamTypeAdtsAAC, ElementaryPID: stream.tsw.PID})
|
|
case av.H264:
|
|
elemStreams = append(elemStreams, ElementaryStreamInfo{StreamType: ElementaryStreamTypeH264, ElementaryPID: stream.tsw.PID})
|
|
}
|
|
}
|
|
|
|
pmt := PMT{
|
|
PCRPID: 0x100,
|
|
ElementaryStreamInfos: elemStreams,
|
|
}
|
|
WritePMT(bufPMT, pmt)
|
|
|
|
self.tswPMT = &TSWriter{
|
|
PID: 0x1000,
|
|
DiscontinuityIndicator: true,
|
|
}
|
|
self.tswPAT = &TSWriter{
|
|
PID: 0,
|
|
DiscontinuityIndicator: true,
|
|
}
|
|
if err = self.tswPAT.WriteTo(self.W, bufPAT.Bytes()); err != nil {
|
|
return
|
|
}
|
|
if err = self.tswPMT.WriteTo(self.W, bufPMT.Bytes()); err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (self *Muxer) WriteHeader(streams []av.CodecData) (err error) {
|
|
self.streams = []*Stream{}
|
|
for _, stream := range streams {
|
|
if err = self.newStream(stream); err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
if err = self.WritePATPMT(); err != nil {
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func (self *Muxer) WritePacket(pkt av.Packet) (err error) {
|
|
if false {
|
|
fmt.Println("ts:", "in", pkt.Idx, pkt.Time, "len", len(pkt.Data))
|
|
}
|
|
if err = self.writePacket(pkt); err != nil {
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func (self *Muxer) writePacket(pkt av.Packet) (err error) {
|
|
stream := self.streams[pkt.Idx]
|
|
|
|
switch stream.Type() {
|
|
case av.AAC:
|
|
codec := stream.CodecData.(aacparser.CodecData)
|
|
data := pkt.Data
|
|
data = append(aacparser.MakeADTSHeader(codec.Config, 1024, len(data)), data...)
|
|
|
|
buf := &bytes.Buffer{}
|
|
pes := PESHeader{
|
|
StreamId: StreamIdAAC,
|
|
PTS: timeToPesTs(pkt.Time),
|
|
}
|
|
WritePESHeader(buf, pes, len(data))
|
|
buf.Write(data)
|
|
|
|
stream.tsw.RandomAccessIndicator = true
|
|
stream.tsw.PCR = timeToPCR(pkt.Time)
|
|
if err = stream.tsw.WriteTo(self.W, buf.Bytes()); err != nil {
|
|
return
|
|
}
|
|
|
|
case av.H264:
|
|
codec := stream.CodecData.(h264parser.CodecData)
|
|
buf := &bytes.Buffer{}
|
|
pes := PESHeader{
|
|
StreamId: StreamIdH264,
|
|
PTS: timeToPesTs(pkt.Time + pkt.CompositionTime),
|
|
DTS: timeToPesTs(pkt.Time),
|
|
}
|
|
WritePESHeader(buf, pes, 0)
|
|
|
|
if typ := h264parser.CheckNALUsType(pkt.Data); typ != h264parser.NALU_RAW {
|
|
err = fmt.Errorf("ts: h264 nalu format=%d invalid", typ)
|
|
return
|
|
}
|
|
nalus := [][]byte{}
|
|
if pkt.IsKeyFrame {
|
|
nalus = append([][]byte{codec.SPS(), codec.PPS()})
|
|
}
|
|
nalus = append(nalus, pkt.Data)
|
|
h264parser.WalkNALUsAnnexb(nalus, func(b []byte) {
|
|
buf.Write(b)
|
|
})
|
|
|
|
stream.tsw.RandomAccessIndicator = pkt.IsKeyFrame
|
|
stream.tsw.PCR = timeToPCR(pkt.Time)
|
|
if err = stream.tsw.WriteTo(self.W, buf.Bytes()); err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|