You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
joy4/format/ts/tsio/tsio.go

590 lines
11 KiB
Go

4 years ago
package tsio
import (
"fmt"
"io"
"time"
"gitlab.com/ics_cinnamon/joy4/utils/bits/pio"
4 years ago
)
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
}