722 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			722 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package tsio
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"time"
 | |
| 
 | |
| 	"git.r-2.top/kunmeng/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
 | |
| 	ElementaryStreamTypeAlignmentDescriptor = 0x06
 | |
| 	ElementaryStreamTypeH265                = 0x24
 | |
| )
 | |
| 
 | |
| 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
 | |
| }
 | |
| 
 | |
| // TSHeader func
 | |
| type TSHeader struct {
 | |
| 	PID                    uint
 | |
| 	PCR                    uint64
 | |
| 	OPCR                   uint64
 | |
| 	ContinuityCounter      uint
 | |
| 	PayloadUnitStart       bool
 | |
| 	DiscontinuityIndicator bool
 | |
| 	RandomAccessIndicator  bool
 | |
| 	HeaderLength           uint
 | |
| }
 | |
| 
 | |
| // WriteTSHeader func
 | |
| func WriteTSHeader(w io.Writer, element TSHeader, dataLength int) (written int, err error) {
 | |
| 	var flags, extFlags uint
 | |
| 
 | |
| 	flags = 0x47 << 24
 | |
| 	flags |= 0x10
 | |
| 	if element.PayloadUnitStart {
 | |
| 		flags |= 0x400000
 | |
| 	}
 | |
| 
 | |
| 	flags |= (element.PID & 0x1fff) << 8
 | |
| 	flags |= element.ContinuityCounter & 0xf
 | |
| 
 | |
| 	const PCR = 0x10
 | |
| 	const OPCR = 0x08
 | |
| 	const EXT = 0x20
 | |
| 
 | |
| 	if element.PCR != 0 {
 | |
| 		extFlags |= PCR
 | |
| 	}
 | |
| 	if element.OPCR != 0 {
 | |
| 		extFlags |= OPCR
 | |
| 	}
 | |
| 	if element.RandomAccessIndicator {
 | |
| 		extFlags |= 0x40
 | |
| 	}
 | |
| 	if element.DiscontinuityIndicator {
 | |
| 		extFlags |= 0x80
 | |
| 	}
 | |
| 	if extFlags != 0 {
 | |
| 		flags |= EXT
 | |
| 	}
 | |
| 	if dataLength < 184 {
 | |
| 		flags |= EXT
 | |
| 	}
 | |
| 	if err = WriteUInt(w, flags, 4); err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 	written += 4
 | |
| 
 | |
| 	if flags&EXT != 0 {
 | |
| 		var length uint
 | |
| 		length = 1
 | |
| 		if extFlags&PCR != 0 {
 | |
| 			length += 6
 | |
| 		}
 | |
| 		if extFlags&OPCR != 0 {
 | |
| 			length += 6
 | |
| 		}
 | |
| 		paddingLength := 0
 | |
| 		if int(length)+5+dataLength < 188 {
 | |
| 			paddingLength = 188 - dataLength - 5 - int(length)
 | |
| 			length = 188 - uint(dataLength) - 5
 | |
| 		}
 | |
| 		if err = WriteUInt(w, length, 1); err != nil {
 | |
| 			return
 | |
| 		}
 | |
| 		if err = WriteUInt(w, extFlags, 1); err != nil {
 | |
| 			return
 | |
| 		}
 | |
| 		if extFlags&PCR != 0 {
 | |
| 			if err = WriteUInt64(w, PCRToUInt(element.PCR), 6); err != nil {
 | |
| 				return
 | |
| 			}
 | |
| 		}
 | |
| 		if extFlags&OPCR != 0 {
 | |
| 			if err = WriteUInt64(w, PCRToUInt(element.OPCR), 6); err != nil {
 | |
| 				return
 | |
| 			}
 | |
| 		}
 | |
| 		if paddingLength > 0 {
 | |
| 			if err = WriteRepeatVal(w, 0xff, paddingLength); err != nil {
 | |
| 				return
 | |
| 			}
 | |
| 		}
 | |
| 		written += int(length) + 1
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 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
 | |
| }
 | |
| func makeRepeatValBytes(val byte, n int) []byte {
 | |
| 	b := make([]byte, n)
 | |
| 	for i := range b {
 | |
| 		b[i] = val
 | |
| 	}
 | |
| 	return b
 | |
| }
 | |
| 
 | |
| // WriteRepeatVal func
 | |
| func WriteRepeatVal(w io.Writer, val byte, n int) (err error) {
 | |
| 	_, err = w.Write(makeRepeatValBytes(val, n))
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // WriteUInt64 func
 | |
| func WriteUInt64(w io.Writer, val uint64, n int) (err error) {
 | |
| 	var b [8]byte
 | |
| 	for i := n - 1; i >= 0; i-- {
 | |
| 		b[i] = byte(val)
 | |
| 		val >>= 8
 | |
| 	}
 | |
| 	if _, err = w.Write(b[:n]); err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // WriteUInt func
 | |
| func WriteUInt(w io.Writer, val uint, n int) (err error) {
 | |
| 	return WriteUInt64(w, uint64(val), n)
 | |
| }
 | |
| 
 | |
| // PCRToUInt func
 | |
| func PCRToUInt(pcr uint64) uint64 {
 | |
| 	base := pcr / 300
 | |
| 	ext := pcr % 300
 | |
| 	return base<<15 | 0x3f<<9 | ext
 | |
| }
 | 
