fix wrong encoding when frame size equals packet size (#416)

This commit is contained in:
Alessandro Ros
2023-09-13 12:56:59 +02:00
committed by GitHub
parent 4ede58cda2
commit 73ba46591e
11 changed files with 241 additions and 153 deletions

View File

@@ -23,6 +23,30 @@ func randUint32() (uint32, error) {
return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]), nil return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]), nil
} }
func lenAggregated(nalus [][]byte, addNALU []byte) int {
ret := 1 // header
for _, nalu := range nalus {
ret += 2 // size
ret += len(nalu) // nalu
}
if addNALU != nil {
ret += 2 // size
ret += len(addNALU) // nalu
}
return ret
}
func packetCount(avail, le int) int {
n := le / avail
if (le % avail) != 0 {
n++
}
return n
}
// Encoder is a RTP/H264 encoder. // Encoder is a RTP/H264 encoder.
// Specification: https://datatracker.ietf.org/doc/html/rfc6184 // Specification: https://datatracker.ietf.org/doc/html/rfc6184
type Encoder struct { type Encoder struct {
@@ -82,7 +106,7 @@ func (e *Encoder) Encode(nalus [][]byte) ([]*rtp.Packet, error) {
// split NALUs into batches // split NALUs into batches
for _, nalu := range nalus { for _, nalu := range nalus {
if e.lenAggregated(batch, nalu) <= e.PayloadMaxSize { if lenAggregated(batch, nalu) <= e.PayloadMaxSize {
// add to existing batch // add to existing batch
batch = append(batch, nalu) batch = append(batch, nalu)
} else { } else {
@@ -147,37 +171,27 @@ func (e *Encoder) writeFragmented(nalu []byte, marker bool) ([]*rtp.Packet, erro
// (packetization-mode=1) // (packetization-mode=1)
avail := e.PayloadMaxSize - 2 avail := e.PayloadMaxSize - 2
le := len(nalu) - 1 le := len(nalu) - 1
packetCount := le / avail packetCount := packetCount(avail, le)
lastPacketSize := le % avail
if lastPacketSize > 0 {
packetCount++
}
ret := make([]*rtp.Packet, packetCount) ret := make([]*rtp.Packet, packetCount)
nri := (nalu[0] >> 5) & 0x03 nri := (nalu[0] >> 5) & 0x03
typ := nalu[0] & 0x1F typ := nalu[0] & 0x1F
nalu = nalu[1:] // remove header nalu = nalu[1:] // remove header
le = avail
start := uint8(1)
end := uint8(0)
for i := range ret { for i := range ret {
indicator := (nri << 5) | uint8(h264.NALUTypeFUA)
start := uint8(0)
if i == 0 {
start = 1
}
end := uint8(0)
le := avail
if i == (packetCount - 1) { if i == (packetCount - 1) {
end = 1 end = 1
le = lastPacketSize le = len(nalu)
} }
header := (start << 7) | (end << 6) | typ
data := make([]byte, 2+le) data := make([]byte, 2+le)
data[0] = indicator data[0] = (nri << 5) | uint8(h264.NALUTypeFUA)
data[1] = header data[1] = (start << 7) | (end << 6) | typ
copy(data[2:], nalu[:le]) copy(data[2:], nalu)
nalu = nalu[le:] nalu = nalu[le:]
ret[i] = &rtp.Packet{ ret[i] = &rtp.Packet{
@@ -192,29 +206,14 @@ func (e *Encoder) writeFragmented(nalu []byte, marker bool) ([]*rtp.Packet, erro
} }
e.sequenceNumber++ e.sequenceNumber++
start = 0
} }
return ret, nil return ret, nil
} }
func (e *Encoder) lenAggregated(nalus [][]byte, addNALU []byte) int {
ret := 1 // header
for _, nalu := range nalus {
ret += 2 // size
ret += len(nalu) // nalu
}
if addNALU != nil {
ret += 2 // size
ret += len(addNALU) // nalu
}
return ret
}
func (e *Encoder) writeAggregated(nalus [][]byte, marker bool) ([]*rtp.Packet, error) { func (e *Encoder) writeAggregated(nalus [][]byte, marker bool) ([]*rtp.Packet, error) {
payload := make([]byte, e.lenAggregated(nalus, nil)) payload := make([]byte, lenAggregated(nalus, nil))
// header // header
payload[0] = uint8(h264.NALUTypeSTAPA) payload[0] = uint8(h264.NALUTypeSTAPA)

View File

@@ -115,6 +115,38 @@ var cases = []struct {
}, },
}, },
}, },
{
"fragmented to the limit",
[][]byte{bytes.Repeat([]byte{1}, 2917)},
[]*rtp.Packet{
{
Header: rtp.Header{
Version: 2,
Marker: false,
PayloadType: 96,
SequenceNumber: 17645,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes(
[]byte{0x1c, 0x81},
bytes.Repeat([]byte{1}, 1458),
),
},
{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 17646,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes(
[]byte{0x1c, 0x41},
bytes.Repeat([]byte{1}, 1458),
),
},
},
},
{ {
"aggregated", "aggregated",
[][]byte{ [][]byte{

View File

@@ -21,6 +21,14 @@ func randUint32() (uint32, error) {
return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]), nil return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]), nil
} }
func packetCount(avail, le int) int {
n := le / avail
if (le % avail) != 0 {
n++
}
return n
}
// Encoder is a RTP/H265 encoder. // Encoder is a RTP/H265 encoder.
// Specification: https://datatracker.ietf.org/doc/html/rfc7798 // Specification: https://datatracker.ietf.org/doc/html/rfc7798
type Encoder struct { type Encoder struct {
@@ -144,34 +152,27 @@ func (e *Encoder) writeSingle(nalu []byte, marker bool) ([]*rtp.Packet, error) {
func (e *Encoder) writeFragmentationUnits(nalu []byte, marker bool) ([]*rtp.Packet, error) { func (e *Encoder) writeFragmentationUnits(nalu []byte, marker bool) ([]*rtp.Packet, error) {
avail := e.PayloadMaxSize - 3 avail := e.PayloadMaxSize - 3
le := len(nalu) - 2 le := len(nalu) - 2
n := le / avail packetCount := packetCount(avail, le)
lastPacketSize := le % avail
if lastPacketSize > 0 {
n++
}
ret := make([]*rtp.Packet, n) ret := make([]*rtp.Packet, packetCount)
head := nalu[:2] head := nalu[:2]
nalu = nalu[2:] nalu = nalu[2:]
le = avail
start := uint8(1)
end := uint8(0)
for i := range ret { for i := range ret {
start := uint8(0) if i == (packetCount - 1) {
if i == 0 { le = len(nalu)
start = 1
}
end := uint8(0)
le := avail
if i == (n - 1) {
end = 1 end = 1
le = lastPacketSize
} }
data := make([]byte, 3+le) data := make([]byte, 3+le)
data[0] = head[0]&0b10000001 | 49<<1 data[0] = head[0]&0b10000001 | 49<<1
data[1] = head[1] data[1] = head[1]
data[2] = (start << 7) | (end << 6) | (head[0]>>1)&0b111111 data[2] = (start << 7) | (end << 6) | (head[0]>>1)&0b111111
copy(data[3:], nalu[:le]) copy(data[3:], nalu)
nalu = nalu[le:] nalu = nalu[le:]
ret[i] = &rtp.Packet{ ret[i] = &rtp.Packet{
@@ -180,12 +181,13 @@ func (e *Encoder) writeFragmentationUnits(nalu []byte, marker bool) ([]*rtp.Pack
PayloadType: e.PayloadType, PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber, SequenceNumber: e.sequenceNumber,
SSRC: *e.SSRC, SSRC: *e.SSRC,
Marker: (i == (n-1) && marker), Marker: (i == (packetCount-1) && marker),
}, },
Payload: data, Payload: data,
} }
e.sequenceNumber++ e.sequenceNumber++
start = 0
} }
return ret, nil return ret, nil

View File

@@ -124,6 +124,38 @@ var cases = []struct {
}, },
}, },
}, },
{
"fragmented to the limit",
[][]byte{bytes.Repeat([]byte{1}, 2916)},
[]*rtp.Packet{
{
Header: rtp.Header{
Version: 2,
Marker: false,
PayloadType: 96,
SequenceNumber: 17645,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes(
[]byte{0x63, 0x01, 0x80},
bytes.Repeat([]byte{1}, 1457),
),
},
{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 17646,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes(
[]byte{0x63, 0x01, 0x40},
bytes.Repeat([]byte{1}, 1457),
),
},
},
},
} }
func TestEncode(t *testing.T) { func TestEncode(t *testing.T) {

View File

@@ -82,7 +82,6 @@ func (e *Encoder) packetCount(slen int) int {
if (slen % e.maxPayloadSize) != 0 { if (slen % e.maxPayloadSize) != 0 {
n++ n++
} }
return n return n
} }
@@ -95,12 +94,11 @@ func (e *Encoder) Encode(samples []byte) ([]*rtp.Packet, error) {
packetCount := e.packetCount(slen) packetCount := e.packetCount(slen)
ret := make([]*rtp.Packet, packetCount) ret := make([]*rtp.Packet, packetCount)
i := 0
pos := 0 pos := 0
payloadSize := e.maxPayloadSize payloadSize := e.maxPayloadSize
timestamp := uint32(0) timestamp := uint32(0)
for { for i := range ret {
if payloadSize > len(samples[pos:]) { if payloadSize > len(samples[pos:]) {
payloadSize = len(samples[pos:]) payloadSize = len(samples[pos:])
} }
@@ -118,13 +116,8 @@ func (e *Encoder) Encode(samples []byte) ([]*rtp.Packet, error) {
} }
e.sequenceNumber++ e.sequenceNumber++
i++
pos += payloadSize pos += payloadSize
timestamp += uint32(payloadSize / e.sampleSize) timestamp += uint32(payloadSize / e.sampleSize)
if pos == slen {
break
}
} }
return ret, nil return ret, nil

View File

@@ -29,6 +29,14 @@ func lenAggregated(frames [][]byte, frame []byte) int {
return l return l
} }
func packetCount(avail, le int) int {
n := le / avail
if (le % avail) != 0 {
n++
}
return n
}
// Encoder is a RTP/MPEG-1/2 Audio encoder. // Encoder is a RTP/MPEG-1/2 Audio encoder.
// Specification: https://datatracker.ietf.org/doc/html/rfc2250 // Specification: https://datatracker.ietf.org/doc/html/rfc2250
type Encoder struct { type Encoder struct {
@@ -127,21 +135,15 @@ func (e *Encoder) writeBatch(frames [][]byte, timestamp uint32) ([]*rtp.Packet,
func (e *Encoder) writeFragmented(frame []byte, timestamp uint32) ([]*rtp.Packet, error) { func (e *Encoder) writeFragmented(frame []byte, timestamp uint32) ([]*rtp.Packet, error) {
avail := e.PayloadMaxSize - 4 avail := e.PayloadMaxSize - 4
le := len(frame) le := len(frame)
packetCount := le / avail packetCount := packetCount(avail, le)
lastPacketSize := le % avail
if lastPacketSize > 0 {
packetCount++
}
pos := 0
ret := make([]*rtp.Packet, packetCount) ret := make([]*rtp.Packet, packetCount)
pos := 0
le = avail
for i := range ret { for i := range ret {
var le int if i == (packetCount - 1) {
if i != (packetCount - 1) { le = len(frame) - pos
le = avail
} else {
le = lastPacketSize
} }
payload := make([]byte, 4+le) payload := make([]byte, 4+le)

View File

@@ -23,6 +23,14 @@ func randUint32() (uint32, error) {
return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]), nil return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]), nil
} }
func packetCount(avail, le int) int {
n := le / avail
if (le % avail) != 0 {
n++
}
return n
}
// Encoder is a RTP/MPEG4-audio encoder. // Encoder is a RTP/MPEG4-audio encoder.
// Specification: https://datatracker.ietf.org/doc/html/rfc3640 // Specification: https://datatracker.ietf.org/doc/html/rfc3640
type Encoder struct { type Encoder struct {
@@ -132,20 +140,14 @@ func (e *Encoder) writeFragmented(au []byte, timestamp uint32) ([]*rtp.Packet, e
avail := e.PayloadMaxSize - 2 - auHeadersLenBytes avail := e.PayloadMaxSize - 2 - auHeadersLenBytes
le := len(au) le := len(au)
packetCount := le / avail packetCount := packetCount(avail, le)
lastPacketSize := le % avail
if lastPacketSize > 0 {
packetCount++
}
ret := make([]*rtp.Packet, packetCount) ret := make([]*rtp.Packet, packetCount)
le = avail
for i := range ret { for i := range ret {
var le int if i == (packetCount - 1) {
if i != (packetCount - 1) { le = len(au)
le = avail
} else {
le = lastPacketSize
} }
payload := make([]byte, 2+auHeadersLenBytes+le) payload := make([]byte, 2+auHeadersLenBytes+le)
@@ -160,7 +162,7 @@ func (e *Encoder) writeFragmented(au []byte, timestamp uint32) ([]*rtp.Packet, e
bits.WriteBits(payload[2:], &pos, 0, e.IndexLength) bits.WriteBits(payload[2:], &pos, 0, e.IndexLength)
// AU // AU
copy(payload[2+auHeadersLenBytes:], au[:le]) copy(payload[2+auHeadersLenBytes:], au)
au = au[le:] au = au[le:]
ret[i] = &rtp.Packet{ ret[i] = &rtp.Packet{
@@ -170,7 +172,7 @@ func (e *Encoder) writeFragmented(au []byte, timestamp uint32) ([]*rtp.Packet, e
SequenceNumber: e.sequenceNumber, SequenceNumber: e.sequenceNumber,
Timestamp: timestamp, Timestamp: timestamp,
SSRC: *e.SSRC, SSRC: *e.SSRC,
Marker: (i == (packetCount - 1)), Marker: (i == packetCount-1),
}, },
Payload: payload, Payload: payload,
} }

View File

@@ -229,6 +229,41 @@ var cases = []struct {
}, },
}, },
}, },
{
"fragmented to the limit",
13,
3,
3,
[][]byte{bytes.Repeat([]byte{1}, 2912)},
[]*rtp.Packet{
{
Header: rtp.Header{
Version: 2,
Marker: false,
PayloadType: 96,
SequenceNumber: 17645,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes(
[]byte{0x0, 0x10, 0x2d, 0x80},
bytes.Repeat([]byte{1}, 1456),
),
},
{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 17646,
SSRC: 0x9dbb7812,
},
Payload: mergeBytes(
[]byte{0x0, 0x10, 0x2d, 0x80},
bytes.Repeat([]byte{1}, 1456),
),
},
},
},
{ {
"aggregated followed by fragmented", "aggregated followed by fragmented",
13, 13,

View File

@@ -68,12 +68,11 @@ func (e *Encoder) Init() error {
func (e *Encoder) packetCount(auLen int, plil int) int { func (e *Encoder) packetCount(auLen int, plil int) int {
totalLen := plil + auLen totalLen := plil + auLen
packetCount := totalLen / e.PayloadMaxSize n := totalLen / e.PayloadMaxSize
lastPacketSize := totalLen % e.PayloadMaxSize if (totalLen % e.PayloadMaxSize) != 0 {
if lastPacketSize > 0 { n++
packetCount++
} }
return packetCount return n
} }
// Encode encodes an access unit into RTP packets. // Encode encodes an access unit into RTP packets.
@@ -82,29 +81,25 @@ func (e *Encoder) Encode(au []byte) ([]*rtp.Packet, error) {
plil := payloadLengthInfoEncodeSize(auLen) plil := payloadLengthInfoEncodeSize(auLen)
packetCount := e.packetCount(auLen, plil) packetCount := e.packetCount(auLen, plil)
avail := e.PayloadMaxSize - plil
ret := make([]*rtp.Packet, packetCount) ret := make([]*rtp.Packet, packetCount)
le := e.PayloadMaxSize - plil
for i := range ret { for i := range ret {
var final bool if i == (packetCount - 1) {
var l int le = len(au)
if len(au) < avail {
l = len(au)
final = true
} else {
l = avail
final = false
} }
var payload []byte var payload []byte
if i == 0 { if i == 0 {
payload = make([]byte, plil+l) payload = make([]byte, plil+le)
payloadLengthInfoEncode(plil, auLen, payload) payloadLengthInfoEncode(plil, auLen, payload)
copy(payload[plil:], au[:l]) copy(payload[plil:], au[:le])
au = au[le:]
le = e.PayloadMaxSize
} else { } else {
payload = au[:l] payload = au[:le]
au = au[le:]
} }
ret[i] = &rtp.Packet{ ret[i] = &rtp.Packet{
@@ -113,19 +108,12 @@ func (e *Encoder) Encode(au []byte) ([]*rtp.Packet, error) {
PayloadType: e.PayloadType, PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber, SequenceNumber: e.sequenceNumber,
SSRC: *e.SSRC, SSRC: *e.SSRC,
Marker: final, Marker: (i == packetCount-1),
}, },
Payload: payload, Payload: payload,
} }
e.sequenceNumber++ e.sequenceNumber++
if final {
break
}
au = au[l:]
avail = e.PayloadMaxSize
} }
return ret, nil return ret, nil

View File

@@ -4,7 +4,6 @@ import (
"bytes" "bytes"
"testing" "testing"
"github.com/bluenviron/mediacommon/pkg/codecs/mpeg4audio"
"github.com/pion/rtp" "github.com/pion/rtp"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@@ -34,25 +33,12 @@ func mergeBytes(vals ...[]byte) []byte {
} }
var cases = []struct { var cases = []struct {
name string name string
config *mpeg4audio.StreamMuxConfig au []byte
au []byte pkts []*rtp.Packet
pkts []*rtp.Packet
}{ }{
{ {
"single", "single",
&mpeg4audio.StreamMuxConfig{
Programs: []*mpeg4audio.StreamMuxConfigProgram{{
Layers: []*mpeg4audio.StreamMuxConfigLayer{{
AudioSpecificConfig: &mpeg4audio.AudioSpecificConfig{
Type: 2,
SampleRate: 48000,
ChannelCount: 2,
},
LatmBufferFullness: 255,
}},
}},
},
[]byte{1, 2, 3, 4}, []byte{1, 2, 3, 4},
[]*rtp.Packet{ []*rtp.Packet{
{ {
@@ -71,18 +57,6 @@ var cases = []struct {
}, },
{ {
"fragmented", "fragmented",
&mpeg4audio.StreamMuxConfig{
Programs: []*mpeg4audio.StreamMuxConfigProgram{{
Layers: []*mpeg4audio.StreamMuxConfigLayer{{
AudioSpecificConfig: &mpeg4audio.AudioSpecificConfig{
Type: 2,
SampleRate: 48000,
ChannelCount: 2,
},
LatmBufferFullness: 255,
}},
}},
},
bytes.Repeat([]byte{0, 1, 2, 3, 4, 5, 6, 7}, 512), bytes.Repeat([]byte{0, 1, 2, 3, 4, 5, 6, 7}, 512),
[]*rtp.Packet{ []*rtp.Packet{
{ {
@@ -129,6 +103,38 @@ var cases = []struct {
}, },
}, },
}, },
{
"fragmented to the limit",
bytes.Repeat([]byte{1}, 2908),
[]*rtp.Packet{
{
Header: rtp.Header{
Version: 2,
Marker: false,
PayloadType: 96,
SequenceNumber: 17645,
SSRC: 2646308882,
},
Payload: mergeBytes(
bytes.Repeat([]byte{0xff}, 11),
[]byte{0x67},
bytes.Repeat([]byte{1}, 1448),
),
},
{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 17646,
SSRC: 2646308882,
},
Payload: mergeBytes(
bytes.Repeat([]byte{1}, 1460),
),
},
},
},
} }
func TestEncode(t *testing.T) { func TestEncode(t *testing.T) {

View File

@@ -20,6 +20,14 @@ func randUint32() (uint32, error) {
return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]), nil return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]), nil
} }
func packetCount(avail, le int) int {
n := le / avail
if (le % avail) != 0 {
n++
}
return n
}
// Encoder is a RTP/MPEG-4 Video encoder. // Encoder is a RTP/MPEG-4 Video encoder.
// Specification: https://datatracker.ietf.org/doc/html/rfc6416 // Specification: https://datatracker.ietf.org/doc/html/rfc6416
type Encoder struct { type Encoder struct {
@@ -66,29 +74,18 @@ func (e *Encoder) Init() error {
return nil return nil
} }
func packetCount(avail, le int) int {
packetCount := le / avail
lastPacketSize := le % avail
if lastPacketSize > 0 {
packetCount++
}
return packetCount
}
// Encode encodes a frame into RTP packets. // Encode encodes a frame into RTP packets.
func (e *Encoder) Encode(frame []byte) ([]*rtp.Packet, error) { func (e *Encoder) Encode(frame []byte) ([]*rtp.Packet, error) {
avail := e.PayloadMaxSize avail := e.PayloadMaxSize
le := len(frame) le := len(frame)
packetCount := packetCount(avail, le) packetCount := packetCount(avail, le)
pos := 0
ret := make([]*rtp.Packet, packetCount) ret := make([]*rtp.Packet, packetCount)
pos := 0
le = avail
for i := range ret { for i := range ret {
var le int if i == (packetCount - 1) {
if i != (packetCount - 1) {
le = avail
} else {
le = len(frame[pos:]) le = len(frame[pos:])
} }
@@ -98,7 +95,7 @@ func (e *Encoder) Encode(frame []byte) ([]*rtp.Packet, error) {
PayloadType: e.PayloadType, PayloadType: e.PayloadType,
SequenceNumber: e.sequenceNumber, SequenceNumber: e.sequenceNumber,
SSRC: *e.SSRC, SSRC: *e.SSRC,
Marker: (i == len(ret)-1), Marker: (i == packetCount-1),
}, },
Payload: frame[pos : pos+le], Payload: frame[pos : pos+le],
} }