first commit

This commit is contained in:
Andrey Semochkin
2019-11-30 21:53:21 +01:00
commit 087a2b4c2d
60 changed files with 19091 additions and 0 deletions

291
format/ts/demuxer.go Normal file
View File

@@ -0,0 +1,291 @@
package ts
import (
"bufio"
"fmt"
"io"
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/codec/aacparser"
"github.com/deepch/vdk/codec/h264parser"
"github.com/deepch/vdk/format/ts/tsio"
"github.com/deepch/vdk/utils/bits/pio"
)
type Demuxer struct {
r *bufio.Reader
pkts []av.Packet
pat *tsio.PAT
pmt *tsio.PMT
streams []*Stream
tshdr []byte
stage int
}
func NewDemuxer(r io.Reader) *Demuxer {
return &Demuxer{
tshdr: make([]byte, 188),
r: bufio.NewReaderSize(r, pio.RecommendBufioSize),
}
}
func (self *Demuxer) Streams() (streams []av.CodecData, err error) {
if err = self.probe(); err != nil {
return
}
for _, stream := range self.streams {
streams = append(streams, stream.CodecData)
}
return
}
func (self *Demuxer) probe() (err error) {
if self.stage == 0 {
for {
if self.pmt != nil {
n := 0
for _, stream := range self.streams {
if stream.CodecData != nil {
n++
}
}
if n == len(self.streams) {
break
}
}
if err = self.poll(); err != nil {
return
}
}
self.stage++
}
return
}
func (self *Demuxer) ReadPacket() (pkt av.Packet, err error) {
if err = self.probe(); err != nil {
return
}
for len(self.pkts) == 0 {
if err = self.poll(); err != nil {
return
}
}
pkt = self.pkts[0]
self.pkts = self.pkts[1:]
return
}
func (self *Demuxer) poll() (err error) {
if err = self.readTSPacket(); err == io.EOF {
var n int
if n, err = self.payloadEnd(); err != nil {
return
}
if n == 0 {
err = io.EOF
}
}
return
}
func (self *Demuxer) initPMT(payload []byte) (err error) {
var psihdrlen int
var datalen int
if _, _, psihdrlen, datalen, err = tsio.ParsePSI(payload); err != nil {
return
}
self.pmt = &tsio.PMT{}
if _, err = self.pmt.Unmarshal(payload[psihdrlen : psihdrlen+datalen]); err != nil {
return
}
self.streams = []*Stream{}
for i, info := range self.pmt.ElementaryStreamInfos {
stream := &Stream{}
stream.idx = i
stream.demuxer = self
stream.pid = info.ElementaryPID
stream.streamType = info.StreamType
switch info.StreamType {
case tsio.ElementaryStreamTypeH264:
self.streams = append(self.streams, stream)
case tsio.ElementaryStreamTypeAdtsAAC:
self.streams = append(self.streams, stream)
}
}
return
}
func (self *Demuxer) payloadEnd() (n int, err error) {
for _, stream := range self.streams {
var i int
if i, err = stream.payloadEnd(); err != nil {
return
}
n += i
}
return
}
func (self *Demuxer) readTSPacket() (err error) {
var hdrlen int
var pid uint16
var start bool
var iskeyframe bool
if _, err = io.ReadFull(self.r, self.tshdr); err != nil {
return
}
if pid, start, iskeyframe, hdrlen, err = tsio.ParseTSHeader(self.tshdr); err != nil {
return
}
payload := self.tshdr[hdrlen:]
if self.pat == nil {
if pid == 0 {
var psihdrlen int
var datalen int
if _, _, psihdrlen, datalen, err = tsio.ParsePSI(payload); err != nil {
return
}
self.pat = &tsio.PAT{}
if _, err = self.pat.Unmarshal(payload[psihdrlen : psihdrlen+datalen]); err != nil {
return
}
}
} else if self.pmt == nil {
for _, entry := range self.pat.Entries {
if entry.ProgramMapPID == pid {
if err = self.initPMT(payload); err != nil {
return
}
break
}
}
} else {
for _, stream := range self.streams {
if pid == stream.pid {
if err = stream.handleTSPacket(start, iskeyframe, payload); err != nil {
return
}
break
}
}
}
return
}
func (self *Stream) addPacket(payload []byte, timedelta time.Duration) {
dts := self.dts
pts := self.pts
if dts == 0 {
dts = pts
}
demuxer := self.demuxer
pkt := av.Packet{
Idx: int8(self.idx),
IsKeyFrame: self.iskeyframe,
Time: dts + timedelta,
Data: payload,
}
if pts != dts {
pkt.CompositionTime = pts - dts
}
demuxer.pkts = append(demuxer.pkts, pkt)
}
func (self *Stream) payloadEnd() (n int, err error) {
payload := self.data
if payload == nil {
return
}
if self.datalen != 0 && len(payload) != self.datalen {
err = fmt.Errorf("ts: packet size mismatch size=%d correct=%d", len(payload), self.datalen)
return
}
self.data = nil
switch self.streamType {
case tsio.ElementaryStreamTypeAdtsAAC:
var config aacparser.MPEG4AudioConfig
delta := time.Duration(0)
for len(payload) > 0 {
var hdrlen, framelen, samples int
if config, hdrlen, framelen, samples, err = aacparser.ParseADTSHeader(payload); err != nil {
return
}
if self.CodecData == nil {
if self.CodecData, err = aacparser.NewCodecDataFromMPEG4AudioConfig(config); err != nil {
return
}
}
self.addPacket(payload[hdrlen:framelen], delta)
n++
delta += time.Duration(samples) * time.Second / time.Duration(config.SampleRate)
payload = payload[framelen:]
}
case tsio.ElementaryStreamTypeH264:
nalus, _ := h264parser.SplitNALUs(payload)
var sps, pps []byte
for _, nalu := range nalus {
if len(nalu) > 0 {
naltype := nalu[0] & 0x1f
switch {
case naltype == 7:
sps = nalu
case naltype == 8:
pps = nalu
case h264parser.IsDataNALU(nalu):
// raw nalu to avcc
b := make([]byte, 4+len(nalu))
pio.PutU32BE(b[0:4], uint32(len(nalu)))
copy(b[4:], nalu)
self.addPacket(b, time.Duration(0))
n++
}
}
}
if self.CodecData == nil && len(sps) > 0 && len(pps) > 0 {
if self.CodecData, err = h264parser.NewCodecDataFromSPSAndPPS(sps, pps); err != nil {
return
}
}
}
return
}
func (self *Stream) handleTSPacket(start bool, iskeyframe bool, payload []byte) (err error) {
if start {
if _, err = self.payloadEnd(); err != nil {
return
}
var hdrlen int
if hdrlen, _, self.datalen, self.pts, self.dts, err = tsio.ParsePESHeader(payload); err != nil {
return
}
self.iskeyframe = iskeyframe
if self.datalen == 0 {
self.data = make([]byte, 0, 4096)
} else {
self.data = make([]byte, 0, self.datalen)
}
self.data = append(self.data, payload[hdrlen:]...)
} else {
self.data = append(self.data, payload...)
}
return
}

26
format/ts/handler.go Normal file
View File

@@ -0,0 +1,26 @@
package ts
import (
"io"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/av/avutil"
)
func Handler(h *avutil.RegisterHandler) {
h.Ext = ".ts"
h.Probe = func(b []byte) bool {
return b[0] == 0x47 && b[188] == 0x47
}
h.ReaderDemuxer = func(r io.Reader) av.Demuxer {
return NewDemuxer(r)
}
h.WriterMuxer = func(w io.Writer) av.Muxer {
return NewMuxer(w)
}
h.CodecTypes = CodecTypes
}

206
format/ts/muxer.go Normal file
View File

@@ -0,0 +1,206 @@
package ts
import (
"fmt"
"io"
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/codec/aacparser"
"github.com/deepch/vdk/codec/h264parser"
"github.com/deepch/vdk/format/ts/tsio"
)
var CodecTypes = []av.CodecType{av.H264, av.AAC}
type Muxer struct {
w io.Writer
streams []*Stream
PaddingToMakeCounterCont bool
psidata []byte
peshdr []byte
tshdr []byte
adtshdr []byte
datav [][]byte
nalus [][]byte
tswpat, tswpmt *tsio.TSWriter
}
func NewMuxer(w io.Writer) *Muxer {
return &Muxer{
w: w,
psidata: make([]byte, 188),
peshdr: make([]byte, tsio.MaxPESHeaderLength),
tshdr: make([]byte, tsio.MaxTSHeaderLength),
adtshdr: make([]byte, aacparser.ADTSHeaderLength),
nalus: make([][]byte, 16),
datav: make([][]byte, 16),
tswpmt: tsio.NewTSWriter(tsio.PMT_PID),
tswpat: tsio.NewTSWriter(tsio.PAT_PID),
}
}
func (self *Muxer) newStream(codec av.CodecData) (err error) {
ok := false
for _, c := range CodecTypes {
if codec.Type() == c {
ok = true
break
}
}
if !ok {
err = fmt.Errorf("ts: codec type=%s is not supported", codec.Type())
return
}
pid := uint16(len(self.streams) + 0x100)
stream := &Stream{
muxer: self,
CodecData: codec,
pid: pid,
tsw: tsio.NewTSWriter(pid),
}
self.streams = append(self.streams, stream)
return
}
func (self *Muxer) writePaddingTSPackets(tsw *tsio.TSWriter) (err error) {
for tsw.ContinuityCounter&0xf != 0x0 {
if err = tsw.WritePackets(self.w, self.datav[:0], 0, false, true); err != nil {
return
}
}
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) SetWriter(w io.Writer) {
self.w = w
return
}
func (self *Muxer) WritePATPMT() (err error) {
pat := tsio.PAT{
Entries: []tsio.PATEntry{
{ProgramNumber: 1, ProgramMapPID: tsio.PMT_PID},
},
}
patlen := pat.Marshal(self.psidata[tsio.PSIHeaderLength:])
n := tsio.FillPSI(self.psidata, tsio.TableIdPAT, tsio.TableExtPAT, patlen)
self.datav[0] = self.psidata[:n]
if err = self.tswpat.WritePackets(self.w, self.datav[:1], 0, false, true); err != nil {
return
}
var elemStreams []tsio.ElementaryStreamInfo
for _, stream := range self.streams {
switch stream.Type() {
case av.AAC:
elemStreams = append(elemStreams, tsio.ElementaryStreamInfo{
StreamType: tsio.ElementaryStreamTypeAdtsAAC,
ElementaryPID: stream.pid,
})
case av.H264:
elemStreams = append(elemStreams, tsio.ElementaryStreamInfo{
StreamType: tsio.ElementaryStreamTypeH264,
ElementaryPID: stream.pid,
})
}
}
pmt := tsio.PMT{
PCRPID: 0x100,
ElementaryStreamInfos: elemStreams,
}
pmtlen := pmt.Len()
if pmtlen+tsio.PSIHeaderLength > len(self.psidata) {
err = fmt.Errorf("ts: pmt too large")
return
}
pmt.Marshal(self.psidata[tsio.PSIHeaderLength:])
n = tsio.FillPSI(self.psidata, tsio.TableIdPMT, tsio.TableExtPMT, pmtlen)
self.datav[0] = self.psidata[:n]
if err = self.tswpmt.WritePackets(self.w, self.datav[:1], 0, false, true); 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) {
stream := self.streams[pkt.Idx]
pkt.Time += time.Second
switch stream.Type() {
case av.AAC:
codec := stream.CodecData.(aacparser.CodecData)
n := tsio.FillPESHeader(self.peshdr, tsio.StreamIdAAC, len(self.adtshdr)+len(pkt.Data), pkt.Time, 0)
self.datav[0] = self.peshdr[:n]
aacparser.FillADTSHeader(self.adtshdr, codec.Config, 1024, len(pkt.Data))
self.datav[1] = self.adtshdr
self.datav[2] = pkt.Data
if err = stream.tsw.WritePackets(self.w, self.datav[:3], pkt.Time, true, false); err != nil {
return
}
case av.H264:
codec := stream.CodecData.(h264parser.CodecData)
nalus := self.nalus[:0]
if pkt.IsKeyFrame {
nalus = append(nalus, codec.SPS())
nalus = append(nalus, codec.PPS())
}
pktnalus, _ := h264parser.SplitNALUs(pkt.Data)
for _, nalu := range pktnalus {
nalus = append(nalus, nalu)
}
datav := self.datav[:1]
for i, nalu := range nalus {
if i == 0 {
datav = append(datav, h264parser.AUDBytes)
} else {
datav = append(datav, h264parser.StartCodeBytes)
}
datav = append(datav, nalu)
}
n := tsio.FillPESHeader(self.peshdr, tsio.StreamIdH264, -1, pkt.Time+pkt.CompositionTime, pkt.Time)
datav[0] = self.peshdr[:n]
if err = stream.tsw.WritePackets(self.w, datav, pkt.Time, pkt.IsKeyFrame, false); err != nil {
return
}
}
return
}

27
format/ts/stream.go Normal file
View File

@@ -0,0 +1,27 @@
package ts
import (
"time"
"github.com/deepch/vdk/av"
"github.com/deepch/vdk/format/ts/tsio"
)
type Stream struct {
av.CodecData
demuxer *Demuxer
muxer *Muxer
pid uint16
streamId uint8
streamType uint8
tsw *tsio.TSWriter
idx int
iskeyframe bool
pts, dts time.Duration
data []byte
datalen int
}

View File

@@ -0,0 +1,55 @@
package tsio
var ieeeCrc32Tbl = []uint32{
0x00000000, 0xB71DC104, 0x6E3B8209, 0xD926430D, 0xDC760413, 0x6B6BC517,
0xB24D861A, 0x0550471E, 0xB8ED0826, 0x0FF0C922, 0xD6D68A2F, 0x61CB4B2B,
0x649B0C35, 0xD386CD31, 0x0AA08E3C, 0xBDBD4F38, 0x70DB114C, 0xC7C6D048,
0x1EE09345, 0xA9FD5241, 0xACAD155F, 0x1BB0D45B, 0xC2969756, 0x758B5652,
0xC836196A, 0x7F2BD86E, 0xA60D9B63, 0x11105A67, 0x14401D79, 0xA35DDC7D,
0x7A7B9F70, 0xCD665E74, 0xE0B62398, 0x57ABE29C, 0x8E8DA191, 0x39906095,
0x3CC0278B, 0x8BDDE68F, 0x52FBA582, 0xE5E66486, 0x585B2BBE, 0xEF46EABA,
0x3660A9B7, 0x817D68B3, 0x842D2FAD, 0x3330EEA9, 0xEA16ADA4, 0x5D0B6CA0,
0x906D32D4, 0x2770F3D0, 0xFE56B0DD, 0x494B71D9, 0x4C1B36C7, 0xFB06F7C3,
0x2220B4CE, 0x953D75CA, 0x28803AF2, 0x9F9DFBF6, 0x46BBB8FB, 0xF1A679FF,
0xF4F63EE1, 0x43EBFFE5, 0x9ACDBCE8, 0x2DD07DEC, 0x77708634, 0xC06D4730,
0x194B043D, 0xAE56C539, 0xAB068227, 0x1C1B4323, 0xC53D002E, 0x7220C12A,
0xCF9D8E12, 0x78804F16, 0xA1A60C1B, 0x16BBCD1F, 0x13EB8A01, 0xA4F64B05,
0x7DD00808, 0xCACDC90C, 0x07AB9778, 0xB0B6567C, 0x69901571, 0xDE8DD475,
0xDBDD936B, 0x6CC0526F, 0xB5E61162, 0x02FBD066, 0xBF469F5E, 0x085B5E5A,
0xD17D1D57, 0x6660DC53, 0x63309B4D, 0xD42D5A49, 0x0D0B1944, 0xBA16D840,
0x97C6A5AC, 0x20DB64A8, 0xF9FD27A5, 0x4EE0E6A1, 0x4BB0A1BF, 0xFCAD60BB,
0x258B23B6, 0x9296E2B2, 0x2F2BAD8A, 0x98366C8E, 0x41102F83, 0xF60DEE87,
0xF35DA999, 0x4440689D, 0x9D662B90, 0x2A7BEA94, 0xE71DB4E0, 0x500075E4,
0x892636E9, 0x3E3BF7ED, 0x3B6BB0F3, 0x8C7671F7, 0x555032FA, 0xE24DF3FE,
0x5FF0BCC6, 0xE8ED7DC2, 0x31CB3ECF, 0x86D6FFCB, 0x8386B8D5, 0x349B79D1,
0xEDBD3ADC, 0x5AA0FBD8, 0xEEE00C69, 0x59FDCD6D, 0x80DB8E60, 0x37C64F64,
0x3296087A, 0x858BC97E, 0x5CAD8A73, 0xEBB04B77, 0x560D044F, 0xE110C54B,
0x38368646, 0x8F2B4742, 0x8A7B005C, 0x3D66C158, 0xE4408255, 0x535D4351,
0x9E3B1D25, 0x2926DC21, 0xF0009F2C, 0x471D5E28, 0x424D1936, 0xF550D832,
0x2C769B3F, 0x9B6B5A3B, 0x26D61503, 0x91CBD407, 0x48ED970A, 0xFFF0560E,
0xFAA01110, 0x4DBDD014, 0x949B9319, 0x2386521D, 0x0E562FF1, 0xB94BEEF5,
0x606DADF8, 0xD7706CFC, 0xD2202BE2, 0x653DEAE6, 0xBC1BA9EB, 0x0B0668EF,
0xB6BB27D7, 0x01A6E6D3, 0xD880A5DE, 0x6F9D64DA, 0x6ACD23C4, 0xDDD0E2C0,
0x04F6A1CD, 0xB3EB60C9, 0x7E8D3EBD, 0xC990FFB9, 0x10B6BCB4, 0xA7AB7DB0,
0xA2FB3AAE, 0x15E6FBAA, 0xCCC0B8A7, 0x7BDD79A3, 0xC660369B, 0x717DF79F,
0xA85BB492, 0x1F467596, 0x1A163288, 0xAD0BF38C, 0x742DB081, 0xC3307185,
0x99908A5D, 0x2E8D4B59, 0xF7AB0854, 0x40B6C950, 0x45E68E4E, 0xF2FB4F4A,
0x2BDD0C47, 0x9CC0CD43, 0x217D827B, 0x9660437F, 0x4F460072, 0xF85BC176,
0xFD0B8668, 0x4A16476C, 0x93300461, 0x242DC565, 0xE94B9B11, 0x5E565A15,
0x87701918, 0x306DD81C, 0x353D9F02, 0x82205E06, 0x5B061D0B, 0xEC1BDC0F,
0x51A69337, 0xE6BB5233, 0x3F9D113E, 0x8880D03A, 0x8DD09724, 0x3ACD5620,
0xE3EB152D, 0x54F6D429, 0x7926A9C5, 0xCE3B68C1, 0x171D2BCC, 0xA000EAC8,
0xA550ADD6, 0x124D6CD2, 0xCB6B2FDF, 0x7C76EEDB, 0xC1CBA1E3, 0x76D660E7,
0xAFF023EA, 0x18EDE2EE, 0x1DBDA5F0, 0xAAA064F4, 0x738627F9, 0xC49BE6FD,
0x09FDB889, 0xBEE0798D, 0x67C63A80, 0xD0DBFB84, 0xD58BBC9A, 0x62967D9E,
0xBBB03E93, 0x0CADFF97, 0xB110B0AF, 0x060D71AB, 0xDF2B32A6, 0x6836F3A2,
0x6D66B4BC, 0xDA7B75B8, 0x035D36B5, 0xB440F7B1, 0x00000001,
}
func calcCRC32(crc uint32, data []byte) uint32 {
for _, b := range data {
crc = ieeeCrc32Tbl[b^byte(crc)] ^ (crc >> 8)
}
return crc
}

589
format/ts/tsio/tsio.go Normal file
View File

@@ -0,0 +1,589 @@
package tsio
import (
"fmt"
"io"
"time"
"github.com/deepch/vdk/utils/bits/pio"
)
const (
StreamIdH264 = 0xe0
StreamIdAAC = 0xc0
)
const (
PAT_PID = 0
PMT_PID = 0x1000
)
const TableIdPMT = 2
const TableExtPMT = 1
const TableIdPAT = 0
const TableExtPAT = 1
const MaxPESHeaderLength = 19
const MaxTSHeaderLength = 12
var ErrPESHeader = fmt.Errorf("invalid PES header")
var ErrPSIHeader = fmt.Errorf("invalid PSI header")
var ErrParsePMT = fmt.Errorf("invalid PMT")
var ErrParsePAT = fmt.Errorf("invalid PAT")
const (
ElementaryStreamTypeH264 = 0x1B
ElementaryStreamTypeAdtsAAC = 0x0F
)
type PATEntry struct {
ProgramNumber uint16
NetworkPID uint16
ProgramMapPID uint16
}
type PAT struct {
Entries []PATEntry
}
func (self PAT) Len() (n int) {
return len(self.Entries) * 4
}
func (self PAT) Marshal(b []byte) (n int) {
for _, entry := range self.Entries {
pio.PutU16BE(b[n:], entry.ProgramNumber)
n += 2
if entry.ProgramNumber == 0 {
pio.PutU16BE(b[n:], entry.NetworkPID&0x1fff|7<<13)
n += 2
} else {
pio.PutU16BE(b[n:], entry.ProgramMapPID&0x1fff|7<<13)
n += 2
}
}
return
}
func (self *PAT) Unmarshal(b []byte) (n int, err error) {
for n < len(b) {
if n+4 <= len(b) {
var entry PATEntry
entry.ProgramNumber = pio.U16BE(b[n:])
n += 2
if entry.ProgramNumber == 0 {
entry.NetworkPID = pio.U16BE(b[n:]) & 0x1fff
n += 2
} else {
entry.ProgramMapPID = pio.U16BE(b[n:]) & 0x1fff
n += 2
}
self.Entries = append(self.Entries, entry)
} else {
break
}
}
if n < len(b) {
err = ErrParsePAT
return
}
return
}
type Descriptor struct {
Tag uint8
Data []byte
}
type ElementaryStreamInfo struct {
StreamType uint8
ElementaryPID uint16
Descriptors []Descriptor
}
type PMT struct {
PCRPID uint16
ProgramDescriptors []Descriptor
ElementaryStreamInfos []ElementaryStreamInfo
}
func (self PMT) Len() (n int) {
// 111(3)
// PCRPID(13)
n += 2
// desclen(16)
n += 2
for _, desc := range self.ProgramDescriptors {
n += 2 + len(desc.Data)
}
for _, info := range self.ElementaryStreamInfos {
// streamType
n += 1
// Reserved(3)
// Elementary PID(13)
n += 2
// Reserved(6)
// ES Info length length(10)
n += 2
for _, desc := range info.Descriptors {
n += 2 + len(desc.Data)
}
}
return
}
func (self PMT) fillDescs(b []byte, descs []Descriptor) (n int) {
for _, desc := range descs {
b[n] = desc.Tag
n++
b[n] = uint8(len(desc.Data))
n++
copy(b[n:], desc.Data)
n += len(desc.Data)
}
return
}
func (self PMT) Marshal(b []byte) (n int) {
// 111(3)
// PCRPID(13)
pio.PutU16BE(b[n:], self.PCRPID|7<<13)
n += 2
hold := n
n += 2
pos := n
n += self.fillDescs(b[n:], self.ProgramDescriptors)
desclen := n - pos
pio.PutU16BE(b[hold:], uint16(desclen)|0xf<<12)
for _, info := range self.ElementaryStreamInfos {
b[n] = info.StreamType
n++
// Reserved(3)
// Elementary PID(13)
pio.PutU16BE(b[n:], info.ElementaryPID|7<<13)
n += 2
hold := n
n += 2
pos := n
n += self.fillDescs(b[n:], info.Descriptors)
desclen := n - pos
pio.PutU16BE(b[hold:], uint16(desclen)|0x3c<<10)
}
return
}
func (self PMT) parseDescs(b []byte) (descs []Descriptor, err error) {
n := 0
for n < len(b) {
if n+2 <= len(b) {
desc := Descriptor{}
desc.Tag = b[n]
desc.Data = make([]byte, b[n+1])
n += 2
if n+len(desc.Data) < len(b) {
copy(desc.Data, b[n:])
descs = append(descs, desc)
n += len(desc.Data)
} else {
break
}
} else {
break
}
}
if n < len(b) {
err = ErrParsePMT
return
}
return
}
func (self *PMT) Unmarshal(b []byte) (n int, err error) {
if len(b) < n+4 {
err = ErrParsePMT
return
}
// 111(3)
// PCRPID(13)
self.PCRPID = pio.U16BE(b[0:2]) & 0x1fff
n += 2
// Reserved(4)=0xf
// Reserved(2)=0x0
// Program info length(10)
desclen := int(pio.U16BE(b[2:4]) & 0x3ff)
n += 2
if desclen > 0 {
if len(b) < n+desclen {
err = ErrParsePMT
return
}
if self.ProgramDescriptors, err = self.parseDescs(b[n : n+desclen]); err != nil {
return
}
n += desclen
}
for n < len(b) {
if len(b) < n+5 {
err = ErrParsePMT
return
}
var info ElementaryStreamInfo
info.StreamType = b[n]
n++
// Reserved(3)
// Elementary PID(13)
info.ElementaryPID = pio.U16BE(b[n:]) & 0x1fff
n += 2
// Reserved(6)
// ES Info length(10)
desclen := int(pio.U16BE(b[n:]) & 0x3ff)
n += 2
if desclen > 0 {
if len(b) < n+desclen {
err = ErrParsePMT
return
}
if info.Descriptors, err = self.parseDescs(b[n : n+desclen]); err != nil {
return
}
n += desclen
}
self.ElementaryStreamInfos = append(self.ElementaryStreamInfos, info)
}
return
}
func ParsePSI(h []byte) (tableid uint8, tableext uint16, hdrlen int, datalen int, err error) {
if len(h) < 8 {
err = ErrPSIHeader
return
}
// pointer(8)
pointer := h[0]
hdrlen++
if pointer > 0 {
hdrlen += int(pointer)
if len(h) < hdrlen {
err = ErrPSIHeader
return
}
}
if len(h) < hdrlen+12 {
err = ErrPSIHeader
return
}
// table_id(8)
tableid = h[hdrlen]
hdrlen++
// section_syntax_indicator(1)=1,private_bit(1)=0,reserved(2)=3,unused(2)=0,section_length(10)
datalen = int(pio.U16BE(h[hdrlen:]))&0x3ff - 9
hdrlen += 2
if datalen < 0 {
err = ErrPSIHeader
return
}
// Table ID extension(16)
tableext = pio.U16BE(h[hdrlen:])
hdrlen += 2
// resverd(2)=3
// version(5)
// Current_next_indicator(1)
hdrlen++
// section_number(8)
hdrlen++
// last_section_number(8)
hdrlen++
// data
// crc(32)
return
}
const PSIHeaderLength = 9
func FillPSI(h []byte, tableid uint8, tableext uint16, datalen int) (n int) {
// pointer(8)
h[n] = 0
n++
// table_id(8)
h[n] = tableid
n++
// section_syntax_indicator(1)=1,private_bit(1)=0,reserved(2)=3,unused(2)=0,section_length(10)
pio.PutU16BE(h[n:], uint16(0xa<<12|2+3+4+datalen))
n += 2
// Table ID extension(16)
pio.PutU16BE(h[n:], tableext)
n += 2
// resverd(2)=3,version(5)=0,Current_next_indicator(1)=1
h[n] = 0x3<<6 | 1
n++
// section_number(8)
h[n] = 0
n++
// last_section_number(8)
h[n] = 0
n++
n += datalen
crc := calcCRC32(0xffffffff, h[1:n])
pio.PutU32LE(h[n:], crc)
n += 4
return
}
func TimeToPCR(tm time.Duration) (pcr uint64) {
// base(33)+resverd(6)+ext(9)
ts := uint64(tm * PCR_HZ / time.Second)
base := ts / 300
ext := ts % 300
pcr = base<<15 | 0x3f<<9 | ext
return
}
func PCRToTime(pcr uint64) (tm time.Duration) {
base := pcr >> 15
ext := pcr & 0x1ff
ts := base*300 + ext
tm = time.Duration(ts) * time.Second / time.Duration(PCR_HZ)
return
}
func TimeToTs(tm time.Duration) (v uint64) {
ts := uint64(tm * PTS_HZ / time.Second)
// 0010 PTS 32..30 1 PTS 29..15 1 PTS 14..00 1
v = ((ts>>30)&0x7)<<33 | ((ts>>15)&0x7fff)<<17 | (ts&0x7fff)<<1 | 0x100010001
return
}
func TsToTime(v uint64) (tm time.Duration) {
// 0010 PTS 32..30 1 PTS 29..15 1 PTS 14..00 1
ts := (((v >> 33) & 0x7) << 30) | (((v >> 17) & 0x7fff) << 15) | ((v >> 1) & 0x7fff)
tm = time.Duration(ts) * time.Second / time.Duration(PTS_HZ)
return
}
const (
PTS_HZ = 90000
PCR_HZ = 27000000
)
func ParsePESHeader(h []byte) (hdrlen int, streamid uint8, datalen int, pts, dts time.Duration, err error) {
if h[0] != 0 || h[1] != 0 || h[2] != 1 {
err = ErrPESHeader
return
}
streamid = h[3]
flags := h[7]
hdrlen = int(h[8]) + 9
datalen = int(pio.U16BE(h[4:6]))
if datalen > 0 {
datalen -= int(h[8]) + 3
}
const PTS = 1 << 7
const DTS = 1 << 6
if flags&PTS != 0 {
if len(h) < 14 {
err = ErrPESHeader
return
}
pts = TsToTime(pio.U40BE(h[9:14]))
if flags&DTS != 0 {
if len(h) < 19 {
err = ErrPESHeader
return
}
dts = TsToTime(pio.U40BE(h[14:19]))
}
}
return
}
func FillPESHeader(h []byte, streamid uint8, datalen int, pts, dts time.Duration) (n int) {
h[0] = 0
h[1] = 0
h[2] = 1
h[3] = streamid
const PTS = 1 << 7
const DTS = 1 << 6
var flags uint8
if pts != 0 {
flags |= PTS
if dts != 0 {
flags |= DTS
}
}
if flags&PTS != 0 {
n += 5
}
if flags&DTS != 0 {
n += 5
}
// packet_length(16) if zero then variable length
// Specifies the number of bytes remaining in the packet after this field. Can be zero.
// If the PES packet length is set to zero, the PES packet can be of any length.
// A value of zero for the PES packet length can be used only when the PES packet payload is a **video** elementary stream.
var pktlen uint16
if datalen >= 0 {
pktlen = uint16(datalen + n + 3)
}
pio.PutU16BE(h[4:6], pktlen)
h[6] = 2<<6 | 1 // resverd(6,2)=2,original_or_copy(0,1)=1
h[7] = flags
h[8] = uint8(n)
// pts(40)?
// dts(40)?
if flags&PTS != 0 {
if flags&DTS != 0 {
pio.PutU40BE(h[9:14], TimeToTs(pts)|3<<36)
pio.PutU40BE(h[14:19], TimeToTs(dts)|1<<36)
} else {
pio.PutU40BE(h[9:14], TimeToTs(pts)|2<<36)
}
}
n += 9
return
}
type TSWriter struct {
w io.Writer
ContinuityCounter uint
tshdr []byte
}
func NewTSWriter(pid uint16) *TSWriter {
w := &TSWriter{}
w.tshdr = make([]byte, 188)
w.tshdr[0] = 0x47
pio.PutU16BE(w.tshdr[1:3], pid&0x1fff)
for i := 6; i < 188; i++ {
w.tshdr[i] = 0xff
}
return w
}
func (self *TSWriter) WritePackets(w io.Writer, datav [][]byte, pcr time.Duration, sync bool, paddata bool) (err error) {
datavlen := pio.VecLen(datav)
writev := make([][]byte, len(datav))
writepos := 0
for writepos < datavlen {
self.tshdr[1] = self.tshdr[1] & 0x1f
self.tshdr[3] = byte(self.ContinuityCounter)&0xf | 0x30
self.tshdr[5] = 0 // flags
hdrlen := 6
self.ContinuityCounter++
if writepos == 0 {
self.tshdr[1] = 0x40 | self.tshdr[1] // Payload Unit Start Indicator
if pcr != 0 {
hdrlen += 6
self.tshdr[5] = 0x10 | self.tshdr[5] // PCR flag (Discontinuity indicator 0x80)
pio.PutU48BE(self.tshdr[6:12], TimeToPCR(pcr))
}
if sync {
self.tshdr[5] = 0x40 | self.tshdr[5] // Random Access indicator
}
}
padtail := 0
end := writepos + 188 - hdrlen
if end > datavlen {
if paddata {
padtail = end - datavlen
} else {
hdrlen += end - datavlen
}
end = datavlen
}
n := pio.VecSliceTo(datav, writev, writepos, end)
self.tshdr[4] = byte(hdrlen) - 5 // length
if _, err = w.Write(self.tshdr[:hdrlen]); err != nil {
return
}
for i := 0; i < n; i++ {
if _, err = w.Write(writev[i]); err != nil {
return
}
}
if padtail > 0 {
if _, err = w.Write(self.tshdr[188-padtail : 188]); err != nil {
return
}
}
writepos = end
}
return
}
func ParseTSHeader(tshdr []byte) (pid uint16, start bool, iskeyframe bool, hdrlen int, err error) {
// https://en.wikipedia.org/wiki/MPEG_transport_stream
if tshdr[0] != 0x47 {
err = fmt.Errorf("tshdr sync invalid")
return
}
pid = uint16((tshdr[1]&0x1f))<<8 | uint16(tshdr[2])
start = tshdr[1]&0x40 != 0
hdrlen += 4
if tshdr[3]&0x20 != 0 {
hdrlen += int(tshdr[4]) + 1
iskeyframe = tshdr[5]&0x40 != 0
}
return
}