Skip to content

Commit

Permalink
Muxer: increase Continuity Counter of PAT and PMT (#29)
Browse files Browse the repository at this point in the history
Countinuity counter of PAT and PMT must be increased after every
delivery in order to avoid errors when playing TS files with VLC
or hls.js.

PAT and PMT buffers are reused in order to save RAM.
  • Loading branch information
aler9 authored Jul 5, 2021
1 parent 7a46f0b commit ddb96a7
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 18 deletions.
19 changes: 11 additions & 8 deletions muxer.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ type Muxer struct {
nextPID uint16
patVersion wrappingCounter
pmtVersion wrappingCounter
patCC wrappingCounter
pmtCC wrappingCounter

patBytes bytes.Buffer
pmtBytes bytes.Buffer
Expand Down Expand Up @@ -83,6 +85,9 @@ func NewMuxer(ctx context.Context, w io.Writer, opts ...func(*Muxer)) *Muxer {
patVersion: newWrappingCounter(0b11111),
pmtVersion: newWrappingCounter(0b11111),

patCC: newWrappingCounter(0b1111),
pmtCC: newWrappingCounter(0b1111),

esContexts: map[uint16]*esContext{},
}

Expand Down Expand Up @@ -285,16 +290,12 @@ func (m *Muxer) retransmitTables(force bool) (int, error) {
func (m *Muxer) WriteTables() (int, error) {
bytesWritten := 0

if m.patBytes.Len() != m.packetSize {
if err := m.generatePAT(); err != nil {
return bytesWritten, err
}
if err := m.generatePAT(); err != nil {
return bytesWritten, err
}

if m.pmtBytes.Len() != m.packetSize {
if err := m.generatePMT(); err != nil {
return bytesWritten, err
}
if err := m.generatePMT(); err != nil {
return bytesWritten, err
}

n, err := m.w.Write(m.patBytes.Bytes())
Expand Down Expand Up @@ -351,6 +352,7 @@ func (m *Muxer) generatePAT() error {
HasPayload: true,
PayloadUnitStartIndicator: true,
PID: PIDPAT,
ContinuityCounter: uint8(m.patCC.get()),
},
Payload: m.buf.Bytes(),
}
Expand Down Expand Up @@ -411,6 +413,7 @@ func (m *Muxer) generatePMT() error {
HasPayload: true,
PayloadUnitStartIndicator: true,
PID: pmtStartPID, // FIXME multiple programs support
ContinuityCounter: uint8(m.pmtCC.get()),
},
Payload: m.buf.Bytes(),
}
Expand Down
20 changes: 10 additions & 10 deletions muxer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import (
"testing"
)

func patExpectedBytes(versionNumber uint8) []byte {
func patExpectedBytes(versionNumber uint8, cc uint8) []byte {
buf := bytes.Buffer{}
w := astikit.NewBitsWriter(astikit.BitsWriterOptions{Writer: &buf})
w.Write(uint8(syncByte))
w.Write("010") // no transport error, payload start, no priority
w.WriteN(PIDPAT, 13)
w.Write("0001") // no scrambling, no AF, payload present
w.Write("0000") // CC
w.WriteN(cc, 4)

w.Write(uint16(0)) // Table ID
w.Write("1011") // Syntax section indicator, private bit, reserved
Expand Down Expand Up @@ -50,13 +50,13 @@ func TestMuxer_generatePAT(t *testing.T) {
err := muxer.generatePAT()
assert.NoError(t, err)
assert.Equal(t, MpegTsPacketSize, muxer.patBytes.Len())
assert.Equal(t, patExpectedBytes(0), muxer.patBytes.Bytes())
assert.Equal(t, patExpectedBytes(0, 0), muxer.patBytes.Bytes())

// to check version number increment
err = muxer.generatePAT()
assert.NoError(t, err)
assert.Equal(t, MpegTsPacketSize, muxer.patBytes.Len())
assert.Equal(t, patExpectedBytes(1), muxer.patBytes.Bytes())
assert.Equal(t, patExpectedBytes(1, 1), muxer.patBytes.Bytes())
}

func pmtExpectedBytesVideoOnly(versionNumber uint8) []byte {
Expand Down Expand Up @@ -99,14 +99,14 @@ func pmtExpectedBytesVideoOnly(versionNumber uint8) []byte {
return buf.Bytes()
}

func pmtExpectedBytesVideoAndAudio(versionNumber uint8) []byte {
func pmtExpectedBytesVideoAndAudio(versionNumber uint8, cc uint8) []byte {
buf := bytes.Buffer{}
w := astikit.NewBitsWriter(astikit.BitsWriterOptions{Writer: &buf})
w.Write(uint8(syncByte))
w.Write("010") // no transport error, payload start, no priority
w.WriteN(pmtStartPID, 13)
w.Write("0001") // no scrambling, no AF, payload present
w.Write("0000") // CC
w.WriteN(cc, 4)

w.Write(uint16(PSITableIDPMT)) // Table ID
w.Write("1011") // Syntax section indicator, private bit, reserved
Expand Down Expand Up @@ -172,7 +172,7 @@ func TestMuxer_generatePMT(t *testing.T) {
err = muxer.generatePMT()
assert.NoError(t, err)
assert.Equal(t, MpegTsPacketSize, muxer.pmtBytes.Len())
assert.Equal(t, pmtExpectedBytesVideoAndAudio(1), muxer.pmtBytes.Bytes())
assert.Equal(t, pmtExpectedBytesVideoAndAudio(1, 1), muxer.pmtBytes.Bytes())
}

func TestMuxer_WriteTables(t *testing.T) {
Expand All @@ -190,7 +190,7 @@ func TestMuxer_WriteTables(t *testing.T) {
assert.Equal(t, 2*MpegTsPacketSize, n)
assert.Equal(t, n, buf.Len())

expectedBytes := append(patExpectedBytes(0), pmtExpectedBytesVideoOnly(0)...)
expectedBytes := append(patExpectedBytes(0, 0), pmtExpectedBytesVideoOnly(0)...)
assert.Equal(t, expectedBytes, buf.Bytes())
}

Expand Down Expand Up @@ -316,6 +316,6 @@ func TestMuxer_WritePayload(t *testing.T) {
assert.Equal(t, 0, buf.Len()%MpegTsPacketSize)

bs := buf.Bytes()
assert.Equal(t, patExpectedBytes(0), bs[:MpegTsPacketSize])
assert.Equal(t, pmtExpectedBytesVideoAndAudio(0), bs[MpegTsPacketSize:MpegTsPacketSize*2])
assert.Equal(t, patExpectedBytes(0, 0), bs[:MpegTsPacketSize])
assert.Equal(t, pmtExpectedBytesVideoAndAudio(0, 0), bs[MpegTsPacketSize:MpegTsPacketSize*2])
}

0 comments on commit ddb96a7

Please sign in to comment.