mirror of https://bitbucket.org/ausocean/av.git
Merged in improve-ts-encoder-performance (pull request #83)
Improve ts encoder performance Approved-by: kortschak <dan@kortschak.io>
This commit is contained in:
commit
af6c8d875f
|
@ -122,8 +122,7 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
psiPacketSize = 184
|
psiSndCnt = 7
|
||||||
psiSendCount = 7
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// timeLocation holds time and location data
|
// timeLocation holds time and location data
|
||||||
|
@ -197,6 +196,8 @@ type Encoder struct {
|
||||||
clock time.Duration
|
clock time.Duration
|
||||||
frameInterval time.Duration
|
frameInterval time.Duration
|
||||||
ptsOffset time.Duration
|
ptsOffset time.Duration
|
||||||
|
tsSpace [PacketSize]byte
|
||||||
|
pesSpace [pes.MaxPesSize]byte
|
||||||
|
|
||||||
psiCount int
|
psiCount int
|
||||||
|
|
||||||
|
@ -240,7 +241,7 @@ func (e *Encoder) Encode(nalu []byte) error {
|
||||||
Data: nalu,
|
Data: nalu,
|
||||||
HeaderLength: 5,
|
HeaderLength: 5,
|
||||||
}
|
}
|
||||||
buf := pesPkt.Bytes()
|
buf := pesPkt.Bytes(e.pesSpace[:pes.MaxPesSize])
|
||||||
|
|
||||||
pusi := true
|
pusi := true
|
||||||
for len(buf) != 0 {
|
for len(buf) != 0 {
|
||||||
|
@ -268,7 +269,7 @@ func (e *Encoder) Encode(nalu []byte) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
e.psiCount--
|
e.psiCount--
|
||||||
_, err := e.dst.Write(pkt.Bytes())
|
_, err := e.dst.Write(pkt.Bytes(e.tsSpace[:PacketSize]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -288,9 +289,9 @@ func (e *Encoder) writePSI() error {
|
||||||
PID: patPid,
|
PID: patPid,
|
||||||
CC: e.ccFor(patPid),
|
CC: e.ccFor(patPid),
|
||||||
AFC: hasPayload,
|
AFC: hasPayload,
|
||||||
Payload: addPadding(patTable),
|
Payload: patTable,
|
||||||
}
|
}
|
||||||
_, err := e.dst.Write(patPkt.Bytes())
|
_, err := e.dst.Write(patPkt.Bytes(e.tsSpace[:PacketSize]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -311,25 +312,16 @@ func (e *Encoder) writePSI() error {
|
||||||
PID: pmtPid,
|
PID: pmtPid,
|
||||||
CC: e.ccFor(pmtPid),
|
CC: e.ccFor(pmtPid),
|
||||||
AFC: hasPayload,
|
AFC: hasPayload,
|
||||||
Payload: addPadding(pmtTable),
|
Payload: pmtTable,
|
||||||
}
|
}
|
||||||
_, err = e.dst.Write(pmtPkt.Bytes())
|
_, err = e.dst.Write(pmtPkt.Bytes(e.tsSpace[:PacketSize]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
e.psiCount = psiSendCount
|
e.psiCount = psiSndCnt
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// addPadding adds an appropriate amount of padding to a pat or pmt table for
|
|
||||||
// addition to an mpegts packet.
|
|
||||||
func addPadding(d []byte) []byte {
|
|
||||||
for len(d) < psiPacketSize {
|
|
||||||
d = append(d, 0xff)
|
|
||||||
}
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
// tick advances the clock one frame interval.
|
// tick advances the clock one frame interval.
|
||||||
func (e *Encoder) tick() {
|
func (e *Encoder) tick() {
|
||||||
e.clock += e.frameInterval
|
e.clock += e.frameInterval
|
||||||
|
|
|
@ -33,8 +33,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
mpegTsSize = 188
|
PacketSize = 188
|
||||||
mpegtsPayloadSize = 176
|
PayloadSize = 176
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -130,13 +130,13 @@ type Packet struct {
|
||||||
// FindPMT will take a clip of mpegts and try to find a PMT table - if one
|
// FindPMT will take a clip of mpegts and try to find a PMT table - if one
|
||||||
// is found, then it is returned, otherwise nil and an error is returned.
|
// is found, then it is returned, otherwise nil and an error is returned.
|
||||||
func FindPMT(d []byte) (p []byte, err error) {
|
func FindPMT(d []byte) (p []byte, err error) {
|
||||||
if len(d) < mpegTsSize {
|
if len(d) < PacketSize {
|
||||||
return nil, errors.New("Mmpegts data not of valid length")
|
return nil, errors.New("Mmpegts data not of valid length")
|
||||||
}
|
}
|
||||||
for i := 0; i < len(d); i += mpegTsSize {
|
for i := 0; i < len(d); i += PacketSize {
|
||||||
pid := (uint16(d[i+1]&0x1f) << 8) | uint16(d[i+2])
|
pid := (uint16(d[i+1]&0x1f) << 8) | uint16(d[i+2])
|
||||||
if pid == pmtPid {
|
if pid == pmtPid {
|
||||||
p = d[i+4 : i+mpegTsSize]
|
p = d[i+4 : i+PacketSize]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,9 +146,9 @@ func FindPMT(d []byte) (p []byte, err error) {
|
||||||
// FillPayload takes a channel and fills the packets Payload field until the
|
// FillPayload takes a channel and fills the packets Payload field until the
|
||||||
// channel is empty or we've the packet reaches capacity
|
// channel is empty or we've the packet reaches capacity
|
||||||
func (p *Packet) FillPayload(data []byte) int {
|
func (p *Packet) FillPayload(data []byte) int {
|
||||||
currentPktLength := 6 + asInt(p.PCRF)*6 + asInt(p.OPCRF)*6 +
|
currentPktLen := 6 + asInt(p.PCRF)*6 + asInt(p.OPCRF)*6 +
|
||||||
asInt(p.SPF)*1 + asInt(p.TPDF)*1 + len(p.TPD)
|
asInt(p.SPF)*1 + asInt(p.TPDF)*1 + len(p.TPD)
|
||||||
p.Payload = make([]byte, mpegtsPayloadSize-currentPktLength)
|
p.Payload = make([]byte, PayloadSize-currentPktLen)
|
||||||
return copy(p.Payload, data)
|
return copy(p.Payload, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +168,11 @@ func asByte(b bool) byte {
|
||||||
|
|
||||||
// ToByteSlice interprets the fields of the ts packet instance and outputs a
|
// ToByteSlice interprets the fields of the ts packet instance and outputs a
|
||||||
// corresponding byte slice
|
// corresponding byte slice
|
||||||
func (p *Packet) Bytes() []byte {
|
func (p *Packet) Bytes(buf []byte) []byte {
|
||||||
|
if buf == nil || cap(buf) != PacketSize {
|
||||||
|
buf = make([]byte, 0, PacketSize)
|
||||||
|
}
|
||||||
|
buf = buf[:0]
|
||||||
stuffingLength := 182 - len(p.Payload) - len(p.TPD) - asInt(p.PCRF)*6 -
|
stuffingLength := 182 - len(p.Payload) - len(p.TPD) - asInt(p.PCRF)*6 -
|
||||||
asInt(p.OPCRF)*6 - asInt(p.SPF)
|
asInt(p.OPCRF)*6 - asInt(p.SPF)
|
||||||
var stuffing []byte
|
var stuffing []byte
|
||||||
|
@ -179,7 +183,6 @@ func (p *Packet) Bytes() []byte {
|
||||||
stuffing[i] = 0xFF
|
stuffing[i] = 0xFF
|
||||||
}
|
}
|
||||||
afl := 1 + asInt(p.PCRF)*6 + asInt(p.OPCRF)*6 + asInt(p.SPF) + asInt(p.TPDF) + len(p.TPD) + len(stuffing)
|
afl := 1 + asInt(p.PCRF)*6 + asInt(p.OPCRF)*6 + asInt(p.SPF) + asInt(p.TPDF) + len(p.TPD) + len(stuffing)
|
||||||
buf := make([]byte, 0, mpegTsSize)
|
|
||||||
buf = append(buf, []byte{
|
buf = append(buf, []byte{
|
||||||
0x47,
|
0x47,
|
||||||
(asByte(p.TEI)<<7 | asByte(p.PUSI)<<6 | asByte(p.Priority)<<5 | byte((p.PID&0xFF00)>>8)),
|
(asByte(p.TEI)<<7 | asByte(p.PUSI)<<6 | asByte(p.Priority)<<5 | byte((p.PID&0xFF00)>>8)),
|
||||||
|
|
|
@ -26,7 +26,7 @@ LICENSE
|
||||||
|
|
||||||
package pes
|
package pes
|
||||||
|
|
||||||
const maxPesSize = 10000
|
const MaxPesSize = 10000
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The below data struct encapsulates the fields of an PES packet. Below is
|
The below data struct encapsulates the fields of an PES packet. Below is
|
||||||
|
@ -92,8 +92,11 @@ type Packet struct {
|
||||||
Data []byte // Pes packet data
|
Data []byte // Pes packet data
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Packet) Bytes() []byte {
|
func (p *Packet) Bytes(buf []byte) []byte {
|
||||||
buf := make([]byte, 0, maxPesSize)
|
if buf == nil || cap(buf) != MaxPesSize {
|
||||||
|
buf = make([]byte, 0, MaxPesSize)
|
||||||
|
}
|
||||||
|
buf = buf[:0]
|
||||||
buf = append(buf, []byte{
|
buf = append(buf, []byte{
|
||||||
0x00, 0x00, 0x01,
|
0x00, 0x00, 0x01,
|
||||||
p.StreamID,
|
p.StreamID,
|
||||||
|
|
|
@ -46,7 +46,7 @@ func TestPesToByteSlice(t *testing.T) {
|
||||||
Stuff: []byte{0xFF, 0xFF},
|
Stuff: []byte{0xFF, 0xFF},
|
||||||
Data: []byte{0xEA, 0x4B, 0x12},
|
Data: []byte{0xEA, 0x4B, 0x12},
|
||||||
}
|
}
|
||||||
got := pkt.Bytes()
|
got := pkt.Bytes(nil)
|
||||||
want := []byte{
|
want := []byte{
|
||||||
0x00, // packet start code prefix byte 1
|
0x00, // packet start code prefix byte 1
|
||||||
0x00, // packet start code prefix byte 2
|
0x00, // packet start code prefix byte 2
|
||||||
|
|
|
@ -68,6 +68,13 @@ func UpdateTime(dst []byte, t uint64) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SyntaxSecLenFrom takes a byte slice representation of a psi and extracts
|
||||||
|
// it's syntax section length
|
||||||
|
func SyntaxSecLenFrom(p []byte) (l uint8) {
|
||||||
|
l = uint8(p[syntaxSecLenIndx]) - crcSize
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// TimeFrom takes a byte slice representation of a psi-pmt and extracts it's
|
// TimeFrom takes a byte slice representation of a psi-pmt and extracts it's
|
||||||
// timestamp, returning as a uint64 if it exists, otherwise returning 0 and nil
|
// timestamp, returning as a uint64 if it exists, otherwise returning 0 and nil
|
||||||
// if it does not exist
|
// if it does not exist
|
||||||
|
@ -121,3 +128,15 @@ func trimTo(d []byte, t byte) []byte {
|
||||||
}
|
}
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// addPadding adds an appropriate amount of padding to a pat or pmt table for
|
||||||
|
// addition to an mpegts packet
|
||||||
|
func addPadding(d []byte) []byte {
|
||||||
|
t := make([]byte, PacketSize)
|
||||||
|
copy(t, d)
|
||||||
|
padding := t[len(d):]
|
||||||
|
for i := range padding {
|
||||||
|
padding[i] = 0xff
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
|
@ -26,6 +26,10 @@ LICENSE
|
||||||
|
|
||||||
package psi
|
package psi
|
||||||
|
|
||||||
|
const (
|
||||||
|
PacketSize = 184 // packet size of a psi.
|
||||||
|
)
|
||||||
|
|
||||||
// Lengths of section definitions
|
// Lengths of section definitions
|
||||||
const (
|
const (
|
||||||
ESSDDefLen = 5
|
ESSDDefLen = 5
|
||||||
|
@ -58,6 +62,12 @@ const (
|
||||||
LocationDataSize = 32 // bytes
|
LocationDataSize = 32 // bytes
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Other misc consts
|
||||||
|
const (
|
||||||
|
syntaxSecLenIndx = 3
|
||||||
|
crcSize = 4
|
||||||
|
)
|
||||||
|
|
||||||
// Program specific information
|
// Program specific information
|
||||||
type PSI struct {
|
type PSI struct {
|
||||||
Pf byte // Point field
|
Pf byte // Point field
|
||||||
|
@ -126,6 +136,7 @@ func (p *PSI) Bytes() []byte {
|
||||||
out[3] = byte(p.Sl)
|
out[3] = byte(p.Sl)
|
||||||
out = append(out, p.Tss.Bytes()...)
|
out = append(out, p.Tss.Bytes()...)
|
||||||
out = addCrc(out)
|
out = addCrc(out)
|
||||||
|
out = addPadding(out)
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -130,7 +130,7 @@ const (
|
||||||
|
|
||||||
// err message
|
// err message
|
||||||
const (
|
const (
|
||||||
errCmp = "Incorrect output, for: %v wanted: %v, got: %v"
|
errCmp = "Incorrect output, for: %v \nwant: %v, \ngot: %v"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Test time to bytes test Data
|
// Test time to bytes test Data
|
||||||
|
@ -282,9 +282,7 @@ var bytesTests = []struct {
|
||||||
func TestBytes(t *testing.T) {
|
func TestBytes(t *testing.T) {
|
||||||
for _, test := range bytesTests {
|
for _, test := range bytesTests {
|
||||||
got := test.input.Bytes()
|
got := test.input.Bytes()
|
||||||
// Remove crc32s
|
if !bytes.Equal(got, addPadding(addCrc(test.want))) {
|
||||||
got = got[:len(got)-4]
|
|
||||||
if !bytes.Equal(got, test.want) {
|
|
||||||
t.Errorf("unexpected error for test %v: got:%v want:%v", test.name, got,
|
t.Errorf("unexpected error for test %v: got:%v want:%v", test.name, got,
|
||||||
test.want)
|
test.want)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue