mirror of
https://github.com/aler9/gortsplib
synced 2025-10-31 02:26:57 +08:00
rtph264: support encoding aggregated packets
This commit is contained in:
@@ -63,15 +63,17 @@ func (e *Encoder) Encode(at *AUAndTimestamp) ([]byte, error) {
|
|||||||
return nil, fmt.Errorf("data is too big")
|
return nil, fmt.Errorf("data is too big")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
payload := make([]byte, 4+len(at.AU))
|
||||||
|
|
||||||
// AU-headers-length
|
// AU-headers-length
|
||||||
payload := []byte{0x00, 0x10}
|
payload[0] = 0x00
|
||||||
|
payload[1] = 0x10
|
||||||
|
|
||||||
// AU-header
|
// AU-header
|
||||||
header := make([]byte, 2)
|
binary.BigEndian.PutUint16(payload[2:], uint16(len(at.AU))<<3)
|
||||||
binary.BigEndian.PutUint16(header, uint16(len(at.AU))<<3)
|
|
||||||
payload = append(payload, header...)
|
|
||||||
|
|
||||||
payload = append(payload, at.AU...)
|
// AU
|
||||||
|
copy(payload[4:], at.AU)
|
||||||
|
|
||||||
rpkt := rtp.Packet{
|
rpkt := rtp.Packet{
|
||||||
Header: rtp.Header{
|
Header: rtp.Header{
|
||||||
@@ -80,11 +82,11 @@ func (e *Encoder) Encode(at *AUAndTimestamp) ([]byte, error) {
|
|||||||
SequenceNumber: e.sequenceNumber,
|
SequenceNumber: e.sequenceNumber,
|
||||||
Timestamp: e.encodeTimestamp(at.Timestamp),
|
Timestamp: e.encodeTimestamp(at.Timestamp),
|
||||||
SSRC: e.ssrc,
|
SSRC: e.ssrc,
|
||||||
|
Marker: true,
|
||||||
},
|
},
|
||||||
Payload: payload,
|
Payload: payload,
|
||||||
}
|
}
|
||||||
e.sequenceNumber++
|
e.sequenceNumber++
|
||||||
rpkt.Header.Marker = true
|
|
||||||
|
|
||||||
frame, err := rpkt.Marshal()
|
frame, err := rpkt.Marshal()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package rtph264
|
package rtph264
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/binary"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -55,16 +56,60 @@ func (e *Encoder) encodeTimestamp(ts time.Duration) uint32 {
|
|||||||
return e.initialTs + uint32(ts.Seconds()*rtpClockRate)
|
return e.initialTs + uint32(ts.Seconds()*rtpClockRate)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encode encodes a NALU into RTP/H264 packets.
|
// Encode encodes NALUs into RTP/H264 packets.
|
||||||
// It always returns at least one RTP/H264 packet.
|
// It always returns at least one RTP/H264 packet.
|
||||||
func (e *Encoder) Encode(nt *NALUAndTimestamp) ([][]byte, error) {
|
// RTP/H264 packets can be:
|
||||||
// if the NALU fits into a single RTP packet, use a single payload
|
// * single
|
||||||
if len(nt.NALU) < rtpPayloadMaxSize {
|
// * fragmented (FU-A)
|
||||||
return e.writeSingle(nt)
|
// * aggregated (STAP-A)
|
||||||
|
func (e *Encoder) Encode(nts []*NALUAndTimestamp) ([][]byte, error) {
|
||||||
|
var rets [][]byte
|
||||||
|
var batch []*NALUAndTimestamp
|
||||||
|
|
||||||
|
// split packets into batches
|
||||||
|
for _, nt := range nts {
|
||||||
|
// packets can be contained into a single aggregation unit
|
||||||
|
if e.lenAggregated(batch, nt) < rtpPayloadMaxSize {
|
||||||
|
// add packet to batch
|
||||||
|
batch = append(batch, nt)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// write last batch
|
||||||
|
if batch != nil {
|
||||||
|
pkts, err := e.writeBatch(batch)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rets = append(rets, pkts...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise, split the NALU into multiple fragmentation payloads
|
// initialize new batch
|
||||||
return e.writeFragmented(nt)
|
batch = []*NALUAndTimestamp{nt}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write last batch
|
||||||
|
pkts, err := e.writeBatch(batch)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
rets = append(rets, pkts...)
|
||||||
|
|
||||||
|
return rets, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) writeBatch(nts []*NALUAndTimestamp) ([][]byte, error) {
|
||||||
|
if len(nts) == 1 {
|
||||||
|
// the NALU fits into a single RTP packet, use a single payload
|
||||||
|
if len(nts[0].NALU) < rtpPayloadMaxSize {
|
||||||
|
return e.writeSingle(nts[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
// split the NALU into multiple fragmentation payloads
|
||||||
|
return e.writeFragmented(nts[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.writeAggregated(nts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Encoder) writeSingle(nt *NALUAndTimestamp) ([][]byte, error) {
|
func (e *Encoder) writeSingle(nt *NALUAndTimestamp) ([][]byte, error) {
|
||||||
@@ -75,13 +120,12 @@ func (e *Encoder) writeSingle(nt *NALUAndTimestamp) ([][]byte, error) {
|
|||||||
SequenceNumber: e.sequenceNumber,
|
SequenceNumber: e.sequenceNumber,
|
||||||
Timestamp: e.encodeTimestamp(nt.Timestamp),
|
Timestamp: e.encodeTimestamp(nt.Timestamp),
|
||||||
SSRC: e.ssrc,
|
SSRC: e.ssrc,
|
||||||
|
Marker: true,
|
||||||
},
|
},
|
||||||
Payload: nt.NALU,
|
Payload: nt.NALU,
|
||||||
}
|
}
|
||||||
e.sequenceNumber++
|
e.sequenceNumber++
|
||||||
|
|
||||||
rpkt.Header.Marker = true
|
|
||||||
|
|
||||||
frame, err := rpkt.Marshal()
|
frame, err := rpkt.Marshal()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -152,3 +196,54 @@ func (e *Encoder) writeFragmented(nt *NALUAndTimestamp) ([][]byte, error) {
|
|||||||
|
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) lenAggregated(batch []*NALUAndTimestamp, additionalEl *NALUAndTimestamp) int {
|
||||||
|
ret := 1 // header
|
||||||
|
|
||||||
|
for _, bnt := range batch {
|
||||||
|
ret += 2 // size
|
||||||
|
ret += len(bnt.NALU) // unit
|
||||||
|
}
|
||||||
|
|
||||||
|
if additionalEl != nil {
|
||||||
|
ret += 2 // size
|
||||||
|
ret += len(additionalEl.NALU) // unit
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *Encoder) writeAggregated(nts []*NALUAndTimestamp) ([][]byte, error) {
|
||||||
|
payload := make([]byte, e.lenAggregated(nts, nil))
|
||||||
|
payload[0] = uint8(codech264.NALUTypeStapA) // header
|
||||||
|
pos := 1
|
||||||
|
|
||||||
|
for _, nt := range nts {
|
||||||
|
naluLen := len(nt.NALU)
|
||||||
|
binary.BigEndian.PutUint16(payload[pos:], uint16(naluLen))
|
||||||
|
pos += 2
|
||||||
|
|
||||||
|
copy(payload[pos:], nt.NALU)
|
||||||
|
pos += naluLen
|
||||||
|
}
|
||||||
|
|
||||||
|
rpkt := rtp.Packet{
|
||||||
|
Header: rtp.Header{
|
||||||
|
Version: rtpVersion,
|
||||||
|
PayloadType: e.payloadType,
|
||||||
|
SequenceNumber: e.sequenceNumber,
|
||||||
|
Timestamp: e.encodeTimestamp(nts[0].Timestamp),
|
||||||
|
SSRC: e.ssrc,
|
||||||
|
Marker: true,
|
||||||
|
},
|
||||||
|
Payload: payload,
|
||||||
|
}
|
||||||
|
e.sequenceNumber++
|
||||||
|
|
||||||
|
frame, err := rpkt.Marshal()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return [][]byte{frame}, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -33,18 +33,20 @@ func (f readerFunc) Read(p []byte) (int, error) {
|
|||||||
|
|
||||||
var cases = []struct {
|
var cases = []struct {
|
||||||
name string
|
name string
|
||||||
dec *NALUAndTimestamp
|
dec []*NALUAndTimestamp
|
||||||
enc [][]byte
|
enc [][]byte
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"single",
|
"single",
|
||||||
&NALUAndTimestamp{
|
[]*NALUAndTimestamp{
|
||||||
|
{
|
||||||
Timestamp: 25 * time.Millisecond,
|
Timestamp: 25 * time.Millisecond,
|
||||||
NALU: mergeBytes(
|
NALU: mergeBytes(
|
||||||
[]byte{0x05},
|
[]byte{0x05},
|
||||||
bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 8),
|
bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 8),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
},
|
||||||
[][]byte{
|
[][]byte{
|
||||||
mergeBytes(
|
mergeBytes(
|
||||||
[]byte{
|
[]byte{
|
||||||
@@ -57,13 +59,15 @@ var cases = []struct {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"negative timestamp",
|
"negative timestamp",
|
||||||
&NALUAndTimestamp{
|
[]*NALUAndTimestamp{
|
||||||
|
{
|
||||||
Timestamp: -20 * time.Millisecond,
|
Timestamp: -20 * time.Millisecond,
|
||||||
NALU: mergeBytes(
|
NALU: mergeBytes(
|
||||||
[]byte{0x05},
|
[]byte{0x05},
|
||||||
bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 8),
|
bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 8),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
},
|
||||||
[][]byte{
|
[][]byte{
|
||||||
mergeBytes(
|
mergeBytes(
|
||||||
[]byte{
|
[]byte{
|
||||||
@@ -76,33 +80,159 @@ var cases = []struct {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fragmented",
|
"fragmented",
|
||||||
&NALUAndTimestamp{
|
[]*NALUAndTimestamp{
|
||||||
|
{
|
||||||
Timestamp: 55 * time.Millisecond,
|
Timestamp: 55 * time.Millisecond,
|
||||||
NALU: mergeBytes(
|
NALU: mergeBytes(
|
||||||
[]byte{0x05},
|
[]byte{0x05},
|
||||||
bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 256),
|
bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 256),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
},
|
||||||
[][]byte{
|
[][]byte{
|
||||||
mergeBytes(
|
mergeBytes(
|
||||||
[]byte{
|
[]byte{
|
||||||
0x80, 0x60, 0x44, 0xed, 0x88, 0x77, 0x79, 0xab,
|
0x80, 0x60, 0x44, 0xed, 0x88, 0x77, 0x79, 0xab,
|
||||||
0x9d, 0xbb, 0x78, 0x12, 0x1c, 0x85, 0x00, 0x01,
|
0x9d, 0xbb, 0x78, 0x12, 0x1c, 0x85,
|
||||||
0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
|
||||||
},
|
},
|
||||||
bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 181),
|
bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 182),
|
||||||
[]byte{0x00, 0x01},
|
[]byte{0x00, 0x01},
|
||||||
),
|
),
|
||||||
mergeBytes(
|
mergeBytes(
|
||||||
[]byte{
|
[]byte{
|
||||||
0x80, 0xe0, 0x44, 0xee, 0x88, 0x77, 0x79, 0xab,
|
0x80, 0xe0, 0x44, 0xee, 0x88, 0x77, 0x79, 0xab,
|
||||||
0x9d, 0xbb, 0x78, 0x12, 0x1c, 0x45, 0x02, 0x03,
|
0x9d, 0xbb, 0x78, 0x12, 0x1c, 0x45,
|
||||||
0x04, 0x05, 0x06, 0x07,
|
|
||||||
},
|
},
|
||||||
|
[]byte{0x02, 0x03, 0x04, 0x05, 0x06, 0x07},
|
||||||
bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 73),
|
bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 73),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"aggregated",
|
||||||
|
[]*NALUAndTimestamp{
|
||||||
|
{
|
||||||
|
NALU: []byte{0x09, 0xF0},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
NALU: []byte{
|
||||||
|
0x41, 0x9a, 0x24, 0x6c, 0x41, 0x4f, 0xfe, 0xd6,
|
||||||
|
0x8c, 0xb0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
||||||
|
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||||
|
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
||||||
|
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
||||||
|
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||||
|
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
||||||
|
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
||||||
|
0x00, 0x00, 0x6d, 0x40,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[][]byte{
|
||||||
|
{
|
||||||
|
0x80, 0xe0, 0x44, 0xed, 0x88, 0x77, 0x66, 0x55,
|
||||||
|
0x9d, 0xbb, 0x78, 0x12, 0x18, 0x00, 0x02, 0x09,
|
||||||
|
0xf0, 0x00, 0x44, 0x41, 0x9a, 0x24, 0x6c, 0x41,
|
||||||
|
0x4f, 0xfe, 0xd6, 0x8c, 0xb0, 0x00, 0x00, 0x03,
|
||||||
|
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||||
|
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
||||||
|
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
||||||
|
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||||
|
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
||||||
|
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
||||||
|
0x00, 0x00, 0x03, 0x00, 0x00, 0x6d, 0x40,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aggregated followed by single",
|
||||||
|
[]*NALUAndTimestamp{
|
||||||
|
{
|
||||||
|
NALU: []byte{0x09, 0xF0},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
NALU: []byte{
|
||||||
|
0x41, 0x9a, 0x24, 0x6c, 0x41, 0x4f, 0xfe, 0xd6,
|
||||||
|
0x8c, 0xb0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
||||||
|
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||||
|
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
||||||
|
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
||||||
|
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||||
|
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
||||||
|
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
||||||
|
0x00, 0x00, 0x6d, 0x40,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
NALU: mergeBytes(
|
||||||
|
[]byte{0x08},
|
||||||
|
bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 175),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[][]byte{
|
||||||
|
{
|
||||||
|
0x80, 0xe0, 0x44, 0xed, 0x88, 0x77, 0x66, 0x55,
|
||||||
|
0x9d, 0xbb, 0x78, 0x12, 0x18, 0x00, 0x02, 0x09,
|
||||||
|
0xf0, 0x00, 0x44, 0x41, 0x9a, 0x24, 0x6c, 0x41,
|
||||||
|
0x4f, 0xfe, 0xd6, 0x8c, 0xb0, 0x00, 0x00, 0x03,
|
||||||
|
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||||
|
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
||||||
|
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
||||||
|
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
||||||
|
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
||||||
|
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
||||||
|
0x00, 0x00, 0x03, 0x00, 0x00, 0x6d, 0x40,
|
||||||
|
},
|
||||||
|
mergeBytes(
|
||||||
|
[]byte{
|
||||||
|
0x80, 0xe0, 0x44, 0xee, 0x88, 0x77, 0x66, 0x55,
|
||||||
|
0x9d, 0xbb, 0x78, 0x12, 0x08,
|
||||||
|
},
|
||||||
|
bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 175),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fragmented followed by aggregated",
|
||||||
|
[]*NALUAndTimestamp{
|
||||||
|
{
|
||||||
|
NALU: mergeBytes(
|
||||||
|
[]byte{0x05},
|
||||||
|
bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 256),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
NALU: []byte{0x09, 0xF0},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
NALU: []byte{0x09, 0xF0},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[][]byte{
|
||||||
|
mergeBytes(
|
||||||
|
[]byte{
|
||||||
|
0x80, 0x60, 0x44, 0xed, 0x88, 0x77, 0x66, 0x55,
|
||||||
|
0x9d, 0xbb, 0x78, 0x12, 0x1c, 0x85,
|
||||||
|
},
|
||||||
|
bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 182),
|
||||||
|
[]byte{0x00, 0x01},
|
||||||
|
),
|
||||||
|
mergeBytes(
|
||||||
|
[]byte{
|
||||||
|
0x80, 0xe0, 0x44, 0xee, 0x88, 0x77, 0x66, 0x55,
|
||||||
|
0x9d, 0xbb, 0x78, 0x12, 0x1c, 0x45,
|
||||||
|
},
|
||||||
|
[]byte{0x02, 0x03, 0x04, 0x05, 0x06, 0x07},
|
||||||
|
bytes.Repeat([]byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, 73),
|
||||||
|
),
|
||||||
|
{
|
||||||
|
0x80, 0xe0, 0x44, 0xef, 0x88, 0x77, 0x66, 0x55,
|
||||||
|
0x9d, 0xbb, 0x78, 0x12, 0x18, 0x00, 0x02, 0x09,
|
||||||
|
0xf0, 0x00, 0x02, 0x09, 0xf0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEncode(t *testing.T) {
|
func TestEncode(t *testing.T) {
|
||||||
@@ -135,71 +265,22 @@ func TestDecode(t *testing.T) {
|
|||||||
d := NewDecoder()
|
d := NewDecoder()
|
||||||
|
|
||||||
// send an initial packet downstream
|
// send an initial packet downstream
|
||||||
// in order to correctly compute the timestamp
|
// in order to compute the timestamp,
|
||||||
|
// which is relative to the initial packet
|
||||||
_, err := d.Decode([]byte{
|
_, err := d.Decode([]byte{
|
||||||
0x80, 0xe0, 0x44, 0xed, 0x88, 0x77, 0x66, 0x55,
|
0x80, 0xe0, 0x44, 0xed, 0x88, 0x77, 0x66, 0x55,
|
||||||
0x9d, 0xbb, 0x78, 0x12, 0x06, 0x00,
|
0x9d, 0xbb, 0x78, 0x12, 0x06, 0x00,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
for _, dec0 := range ca.dec {
|
||||||
dec, err := d.Read(r)
|
dec, err := d.Read(r)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, ca.dec, dec)
|
require.Equal(t, dec0, dec)
|
||||||
|
}
|
||||||
|
|
||||||
_, err = d.Read(r)
|
_, err = d.Read(r)
|
||||||
require.Equal(t, io.EOF, err)
|
require.Equal(t, io.EOF, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDecodeAggregated(t *testing.T) {
|
|
||||||
sent := false
|
|
||||||
r := readerFunc(func(p []byte) (int, error) {
|
|
||||||
if sent {
|
|
||||||
return 0, io.EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
sent = true
|
|
||||||
pkt := []byte{
|
|
||||||
0x80, 0xe0, 0x0e, 0x6a, 0x48, 0xf1, 0x7d, 0xb9,
|
|
||||||
0x23, 0xe6, 0x5d, 0x50, 0x18, 0x00, 0x02, 0x09,
|
|
||||||
0xf0, 0x00, 0x44, 0x41, 0x9a, 0x24, 0x6c, 0x41,
|
|
||||||
0x4f, 0xfe, 0xd6, 0x8c, 0xb0, 0x00, 0x00, 0x03,
|
|
||||||
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
|
||||||
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
|
||||||
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
|
||||||
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
|
||||||
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
|
||||||
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
|
||||||
0x00, 0x00, 0x03, 0x00, 0x00, 0x6d, 0x40,
|
|
||||||
}
|
|
||||||
return copy(p, pkt), nil
|
|
||||||
})
|
|
||||||
|
|
||||||
d := NewDecoder()
|
|
||||||
|
|
||||||
nt, err := d.Read(r)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, &NALUAndTimestamp{
|
|
||||||
NALU: []byte{0x09, 0xF0},
|
|
||||||
}, nt)
|
|
||||||
|
|
||||||
nt, err = d.Read(r)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, &NALUAndTimestamp{
|
|
||||||
NALU: []byte{
|
|
||||||
0x41, 0x9a, 0x24, 0x6c, 0x41, 0x4f, 0xfe, 0xd6,
|
|
||||||
0x8c, 0xb0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
|
||||||
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
|
||||||
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
|
||||||
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
|
||||||
0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00,
|
|
||||||
0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00,
|
|
||||||
0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
|
||||||
0x00, 0x00, 0x6d, 0x40,
|
|
||||||
},
|
|
||||||
}, nt)
|
|
||||||
|
|
||||||
_, err = d.Read(r)
|
|
||||||
require.Equal(t, io.EOF, err)
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user