133 lines
2.9 KiB
Go
133 lines
2.9 KiB
Go
package ts
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"github.com/nareix/av"
|
|
"github.com/nareix/codec/aacparser"
|
|
"github.com/nareix/codec/h264parser"
|
|
"io"
|
|
)
|
|
|
|
type Muxer struct {
|
|
W io.Writer
|
|
streams []*Stream
|
|
}
|
|
|
|
func (self *Muxer) NewStream() av.Stream {
|
|
stream := &Stream{
|
|
mux: self,
|
|
tsw: &TSWriter{
|
|
DiscontinuityIndicator: true,
|
|
PID: uint(len(self.streams) + 0x100),
|
|
},
|
|
}
|
|
self.streams = append(self.streams, stream)
|
|
return stream
|
|
}
|
|
|
|
func (self *Muxer) WriteHeader() (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)
|
|
|
|
tswPMT := &TSWriter{
|
|
PID: 0x1000,
|
|
DiscontinuityIndicator: true,
|
|
}
|
|
tswPAT := &TSWriter{
|
|
PID: 0,
|
|
DiscontinuityIndicator: true,
|
|
}
|
|
if err = tswPAT.WriteTo(self.W, bufPAT.Bytes()); err != nil {
|
|
return
|
|
}
|
|
if err = tswPMT.WriteTo(self.W, bufPMT.Bytes()); err != nil {
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (self *Muxer) WritePacket(streamIndex int, pkt av.Packet) (err error) {
|
|
stream := self.streams[streamIndex]
|
|
|
|
if stream.Type() == av.AAC {
|
|
data := pkt.Data
|
|
if !aacparser.IsADTSFrame(data) {
|
|
data = append(aacparser.MakeADTSHeader(stream.AACCodecInfo.MPEG4AudioConfig, 1024, len(data)), data...)
|
|
}
|
|
|
|
buf := &bytes.Buffer{}
|
|
pes := PESHeader{
|
|
StreamId: StreamIdAAC,
|
|
PTS: timeToPesTs(stream.time),
|
|
}
|
|
WritePESHeader(buf, pes, len(data))
|
|
buf.Write(data)
|
|
|
|
stream.tsw.RandomAccessIndicator = true
|
|
stream.tsw.PCR = timeToPCR(stream.time)
|
|
if err = stream.tsw.WriteTo(self.W, buf.Bytes()); err != nil {
|
|
return
|
|
}
|
|
|
|
stream.time += pkt.Duration
|
|
|
|
} else if stream.Type() == av.H264 {
|
|
buf := &bytes.Buffer{}
|
|
pes := PESHeader{
|
|
StreamId: StreamIdH264,
|
|
PTS: timeToPesTs(stream.time + pkt.CompositionTime),
|
|
DTS: timeToPesTs(stream.time),
|
|
}
|
|
WritePESHeader(buf, pes, 0)
|
|
|
|
nalus, _ := h264parser.SplitNALUs(pkt.Data)
|
|
if pkt.IsKeyFrame {
|
|
sps := stream.H264CodecInfo.Record.SPS[0]
|
|
pps := stream.H264CodecInfo.Record.PPS[0]
|
|
nalus = append([][]byte{sps, pps}, nalus...)
|
|
}
|
|
h264parser.WalkNALUsAnnexb(nalus, func(b []byte) {
|
|
buf.Write(b)
|
|
})
|
|
|
|
stream.tsw.RandomAccessIndicator = pkt.IsKeyFrame
|
|
stream.tsw.PCR = timeToPCR(stream.time)
|
|
if err = stream.tsw.WriteTo(self.W, buf.Bytes()); err != nil {
|
|
return
|
|
}
|
|
|
|
stream.time += pkt.Duration
|
|
|
|
} else {
|
|
err = fmt.Errorf("unknown stream type=%d", stream.Type())
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|