add h264 utilities

This commit is contained in:
aler9
2021-09-23 17:37:35 +02:00
parent 239b71d975
commit 69967d6918
18 changed files with 695 additions and 69 deletions

1
go.mod
View File

@@ -3,6 +3,7 @@ module github.com/aler9/gortsplib
go 1.15
require (
github.com/asticode/go-astits v1.9.0
github.com/icza/bitio v1.0.0
github.com/pion/rtcp v1.2.4
github.com/pion/rtp v1.6.1

7
go.sum
View File

@@ -1,3 +1,7 @@
github.com/asticode/go-astikit v0.20.0 h1:+7N+J4E4lWx2QOkRdOf6DafWJMv6O4RRfgClwQokrH8=
github.com/asticode/go-astikit v0.20.0/go.mod h1:h4ly7idim1tNhaVkdVBeXQZEE3L0xblP7fCWbgwipF0=
github.com/asticode/go-astits v1.9.0 h1:69cilL0/7uwsxdGNQgwmVBu6JP0aMicXSm91ukJDjgQ=
github.com/asticode/go-astits v1.9.0/go.mod h1:DkOWmBNQpnr9mv24KfZjq4JawCFX1FCqjLVGvO0DygQ=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/icza/bitio v1.0.0 h1:squ/m1SHyFeCA6+6Gyol1AxV9nmPPlJFT8c2vKdj3U8=
@@ -12,9 +16,11 @@ github.com/pion/rtp v1.6.1 h1:2Y2elcVBrahYnHKN2X7rMHX/r1R4TEBMP1LaVu/wNhk=
github.com/pion/rtp v1.6.1/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
github.com/pion/sdp/v3 v3.0.2 h1:UNnSPVaMM+Pdu/mR9UvAyyo6zkdYbKeuOooCwZvTl/g=
github.com/pion/sdp/v3 v3.0.2/go.mod h1:bNiSknmJE0HYBprTHXKPQ3+JjacTv5uap92ueJZKsRk=
github.com/pkg/profile v1.4.0/go.mod h1:NWz/XGvpEW1FyYQ7fCx4dqYBLlfTcE+A9FLAkNKqjFE=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b h1:k+E048sYJHyVnsr1GDrRZWQ32D2C7lWs9JRc0bel53A=
@@ -27,5 +33,6 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

79
pkg/h264/annexb.go Normal file
View File

@@ -0,0 +1,79 @@
package h264
import (
"fmt"
)
// DecodeAnnexB decodes NALUs from the Annex-B stream format.
func DecodeAnnexB(byts []byte) ([][]byte, error) {
bl := len(byts)
// check initial delimiter
n := func() int {
if bl < 3 || byts[0] != 0x00 || byts[1] != 0x00 {
return -1
}
if byts[2] == 0x01 {
return 3
}
if bl < 4 || byts[2] != 0x00 || byts[3] != 0x01 {
return -1
}
return 4
}()
if n < 0 {
return nil, fmt.Errorf("input doesn't start with a delimiter")
}
var ret [][]byte
zeros := 0
start := n
delimStart := 0
for i := n; i < bl; i++ {
switch byts[i] {
case 0:
if zeros == 0 {
delimStart = i
}
zeros++
case 1:
if zeros == 2 || zeros == 3 {
nalu := byts[start:delimStart]
if len(nalu) == 0 {
return nil, fmt.Errorf("empty NALU")
}
ret = append(ret, nalu)
start = i + 1
}
zeros = 0
default:
zeros = 0
}
}
nalu := byts[start:bl]
if len(nalu) == 0 {
return nil, fmt.Errorf("empty NALU")
}
ret = append(ret, nalu)
return ret, nil
}
// EncodeAnnexB encodes NALUs into the Annex-B stream format.
func EncodeAnnexB(nalus [][]byte) ([]byte, error) {
var ret []byte
for _, nalu := range nalus {
ret = append(ret, []byte{0x00, 0x00, 0x00, 0x01}...)
ret = append(ret, nalu...)
}
return ret, nil
}

115
pkg/h264/annexb_test.go Normal file
View File

@@ -0,0 +1,115 @@
package h264
import (
"testing"
"github.com/stretchr/testify/require"
)
var casesAnnexB = []struct {
name string
encin []byte
encout []byte
dec [][]byte
}{
{
"2 zeros, single",
[]byte{0x00, 0x00, 0x01, 0xaa, 0xbb},
[]byte{0x00, 0x00, 0x00, 0x01, 0xaa, 0xbb},
[][]byte{
{0xaa, 0xbb},
},
},
{
"2 zeros, multiple",
[]byte{
0x00, 0x00, 0x01, 0xaa, 0xbb, 0x00, 0x00, 0x01,
0xcc, 0xdd, 0x00, 0x00, 0x01, 0xee, 0xff,
},
[]byte{
0x00, 0x00, 0x00, 0x01, 0xaa, 0xbb, 0x00, 0x00,
0x00, 0x01, 0xcc, 0xdd, 0x00, 0x00, 0x00, 0x01,
0xee, 0xff,
},
[][]byte{
{0xaa, 0xbb},
{0xcc, 0xdd},
{0xee, 0xff},
},
},
{
"3 zeros, single",
[]byte{0x00, 0x00, 0x00, 0x01, 0xaa, 0xbb},
[]byte{0x00, 0x00, 0x00, 0x01, 0xaa, 0xbb},
[][]byte{
{0xaa, 0xbb},
},
},
{
"3 zeros, multiple",
[]byte{
0x00, 0x00, 0x00, 0x01, 0xaa, 0xbb, 0x00, 0x00,
0x00, 0x01, 0xcc, 0xdd, 0x00, 0x00, 0x00, 0x01,
0xee, 0xff,
},
[]byte{
0x00, 0x00, 0x00, 0x01, 0xaa, 0xbb, 0x00, 0x00,
0x00, 0x01, 0xcc, 0xdd, 0x00, 0x00, 0x00, 0x01,
0xee, 0xff,
},
[][]byte{
{0xaa, 0xbb},
{0xcc, 0xdd},
{0xee, 0xff},
},
},
}
func TestAnnexBDecode(t *testing.T) {
for _, ca := range casesAnnexB {
t.Run(ca.name, func(t *testing.T) {
dec, err := DecodeAnnexB(ca.encin)
require.NoError(t, err)
require.Equal(t, ca.dec, dec)
})
}
}
func TestAnnexBEncode(t *testing.T) {
for _, ca := range casesAnnexB {
t.Run(ca.name, func(t *testing.T) {
enc, err := EncodeAnnexB(ca.dec)
require.NoError(t, err)
require.Equal(t, ca.encout, enc)
})
}
}
func TestAnnexBDecodeError(t *testing.T) {
for _, ca := range []struct {
name string
enc []byte
}{
{
"empty",
[]byte{},
},
{
"missing initial delimiter",
[]byte{0xaa, 0xbb},
},
{
"empty initial",
[]byte{0x00, 0x00, 0x01},
},
{
"empty 2nd",
[]byte{0x00, 0x00, 0x01, 0xaa, 0x00, 0x00, 0x01},
},
} {
t.Run(ca.name, func(t *testing.T) {
_, err := DecodeAnnexB(ca.enc)
require.Error(t, err)
})
}
}

View File

@@ -0,0 +1,90 @@
package h264
// AntiCompetitionAdd adds the anti-competition bytes to a NALU.
func AntiCompetitionAdd(nalu []byte) []byte {
var ret []byte
step := 0
start := 0
for i, b := range nalu {
switch step {
case 0:
if b == 0 {
step++
}
case 1:
if b == 0 {
step++
} else {
step = 0
}
case 2:
switch b {
case 3, 2, 1, 0:
ret = append(ret, nalu[start:i-2]...)
ret = append(ret, []byte{0x00, 0x00, 0x03, b}...)
step = 0
start = i + 1
default:
step = 0
}
}
}
ret = append(ret, nalu[start:]...)
return ret
}
// AntiCompetitionRemove removes the anti-competition bytes from a NALU.
func AntiCompetitionRemove(nalu []byte) []byte {
// 0x00 0x00 0x03 0x00 -> 0x00 0x00 0x00
// 0x00 0x00 0x03 0x01 -> 0x00 0x00 0x01
// 0x00 0x00 0x03 0x02 -> 0x00 0x00 0x02
// 0x00 0x00 0x03 0x03 -> 0x00 0x00 0x03
var ret []byte
step := 0
start := 0
for i, b := range nalu {
switch step {
case 0:
if b == 0 {
step++
}
case 1:
if b == 0 {
step++
} else {
step = 0
}
case 2:
if b == 3 {
step++
} else {
step = 0
}
case 3:
switch b {
case 3, 2, 1, 0:
ret = append(ret, nalu[start:i-3]...)
ret = append(ret, []byte{0x00, 0x00, b}...)
step = 0
start = i + 1
default:
step = 0
}
}
}
ret = append(ret, nalu[start:]...)
return ret
}

View File

@@ -0,0 +1,47 @@
package h264
import (
"testing"
"github.com/stretchr/testify/require"
)
var casesAntiCompetition = []struct {
name string
unproc []byte
proc []byte
}{
{
"base",
[]byte{
0x00, 0x00, 0x00,
0x00, 0x00, 0x01,
0x00, 0x00, 0x02,
0x00, 0x00, 0x03,
},
[]byte{
0x00, 0x00, 0x03, 0x00,
0x00, 0x00, 0x03, 0x01,
0x00, 0x00, 0x03, 0x02,
0x00, 0x00, 0x03, 0x03,
},
},
}
func TestAntiCompetitionAdd(t *testing.T) {
for _, ca := range casesAntiCompetition {
t.Run(ca.name, func(t *testing.T) {
proc := AntiCompetitionAdd(ca.unproc)
require.Equal(t, ca.proc, proc)
})
}
}
func TestAntiCompetitionRemove(t *testing.T) {
for _, ca := range casesAntiCompetition {
t.Run(ca.name, func(t *testing.T) {
unproc := AntiCompetitionRemove(ca.proc)
require.Equal(t, ca.unproc, unproc)
})
}
}

55
pkg/h264/avcc.go Normal file
View File

@@ -0,0 +1,55 @@
package h264
import (
"encoding/binary"
"fmt"
)
// DecodeAVCC decodes NALUs from the AVCC stream format.
func DecodeAVCC(byts []byte) ([][]byte, error) {
var ret [][]byte
for len(byts) > 0 {
if len(byts) < 4 {
return nil, fmt.Errorf("invalid length")
}
le := binary.BigEndian.Uint32(byts)
byts = byts[4:]
if len(byts) < int(le) {
return nil, fmt.Errorf("invalid length")
}
ret = append(ret, byts[:le])
byts = byts[le:]
}
if len(ret) == 0 {
return nil, fmt.Errorf("no NALUs decoded")
}
return ret, nil
}
// EncodeAVCC encodes NALUs into the AVCC stream format.
func EncodeAVCC(nalus [][]byte) ([]byte, error) {
le := 0
for _, nalu := range nalus {
le += 4 + len(nalu)
}
ret := make([]byte, le)
pos := 0
for _, nalu := range nalus {
ln := len(nalu)
binary.BigEndian.PutUint32(ret[pos:], uint32(ln))
pos += 4
copy(ret[pos:], nalu)
pos += ln
}
return ret, nil
}

85
pkg/h264/avcc_test.go Normal file
View File

@@ -0,0 +1,85 @@
package h264
import (
"testing"
"github.com/stretchr/testify/require"
)
var casesAVCC = []struct {
name string
enc []byte
dec [][]byte
}{
{
"single",
[]byte{
0x00, 0x00, 0x00, 0x03,
0xaa, 0xbb, 0xcc,
},
[][]byte{
{0xaa, 0xbb, 0xcc},
},
},
{
"multiple",
[]byte{
0x00, 0x00, 0x00, 0x02,
0xaa, 0xbb,
0x00, 0x00, 0x00, 0x02,
0xcc, 0xdd,
0x00, 0x00, 0x00, 0x02,
0xee, 0xff,
},
[][]byte{
{0xaa, 0xbb},
{0xcc, 0xdd},
{0xee, 0xff},
},
},
}
func TestAVCCDecode(t *testing.T) {
for _, ca := range casesAVCC {
t.Run(ca.name, func(t *testing.T) {
dec, err := DecodeAVCC(ca.enc)
require.NoError(t, err)
require.Equal(t, ca.dec, dec)
})
}
}
func TestAVCCEncode(t *testing.T) {
for _, ca := range casesAVCC {
t.Run(ca.name, func(t *testing.T) {
enc, err := EncodeAVCC(ca.dec)
require.NoError(t, err)
require.Equal(t, ca.enc, enc)
})
}
}
func TestAVCCDecodeError(t *testing.T) {
for _, ca := range []struct {
name string
enc []byte
}{
{
"empty",
[]byte{},
},
{
"invalid length",
[]byte{0x01},
},
{
"invalid length",
[]byte{0x00, 0x00, 0x00, 0x03},
},
} {
t.Run(ca.name, func(t *testing.T) {
_, err := DecodeAVCC(ca.enc)
require.Error(t, err)
})
}
}

61
pkg/h264/dtsestimator.go Normal file
View File

@@ -0,0 +1,61 @@
package h264
import (
"time"
)
// DTSEstimator is a DTS estimator.
type DTSEstimator struct {
initializing int
prevDTS time.Duration
prevPTS time.Duration
prevPrevPTS time.Duration
}
// NewDTSEstimator allocates a DTSEstimator.
func NewDTSEstimator() *DTSEstimator {
return &DTSEstimator{
initializing: 2,
}
}
// Feed provides PTS to the estimator, and returns the estimated DTS.
func (d *DTSEstimator) Feed(pts time.Duration) time.Duration {
switch d.initializing {
case 2:
d.initializing--
return 0
case 1:
d.initializing--
d.prevPTS = pts
d.prevDTS = time.Millisecond
return time.Millisecond
}
dts := func() time.Duration {
// P or I frame
if pts > d.prevPTS {
// previous frame was B
// use the DTS of the previous frame
if d.prevPTS < d.prevPrevPTS {
return d.prevPTS
}
// previous frame was P or I
// use two frames ago plus a small quantity
// to avoid non-monotonous DTS with B-frames
return d.prevPrevPTS + time.Millisecond
}
// B Frame
// increase by a small quantity
return d.prevDTS + time.Millisecond
}()
d.prevPrevPTS = d.prevPTS
d.prevPTS = pts
d.prevDTS = dts
return dts
}

View File

@@ -0,0 +1,32 @@
package h264
import (
"testing"
"time"
"github.com/stretchr/testify/require"
)
func TestDTSEstimator(t *testing.T) {
est := NewDTSEstimator()
// initial state
dts := est.Feed(0)
require.Equal(t, time.Duration(0), dts)
// b-frame
dts = est.Feed(1*time.Second - 200*time.Millisecond)
require.Equal(t, time.Millisecond, dts)
// b-frame
dts = est.Feed(1*time.Second - 400*time.Millisecond)
require.Equal(t, 2*time.Millisecond, dts)
// p-frame
dts = est.Feed(1 * time.Second)
require.Equal(t, 1*time.Second-400*time.Millisecond, dts)
// p-frame
dts = est.Feed(1*time.Second + 200*time.Millisecond)
require.Equal(t, 1*time.Second-399*time.Millisecond, dts)
}

2
pkg/h264/h264.go Normal file
View File

@@ -0,0 +1,2 @@
// Package h264 contains utilities to work with H264 streams.
package h264

69
pkg/h264/nalutype.go Normal file
View File

@@ -0,0 +1,69 @@
package h264
import (
"fmt"
)
// NALUType is the type of a NALU.
type NALUType uint8
// standard NALU types.
const (
NALUTypeNonIDR NALUType = 1
NALUTypeDataPartitionA NALUType = 2
NALUTypeDataPartitionB NALUType = 3
NALUTypeDataPartitionC NALUType = 4
NALUTypeIDR NALUType = 5
NALUTypeSEI NALUType = 6
NALUTypeSPS NALUType = 7
NALUTypePPS NALUType = 8
NALUTypeAccessUnitDelimiter NALUType = 9
NALUTypeEndOfSequence NALUType = 10
NALUTypeEndOfStream NALUType = 11
NALUTypeFillerData NALUType = 12
NALUTypeSPSExtension NALUType = 13
NALUTypePrefix NALUType = 14
NALUTypeSubsetSPS NALUType = 15
NALUTypeReserved16 NALUType = 16
NALUTypeReserved17 NALUType = 17
NALUTypeReserved18 NALUType = 18
NALUTypeSliceLayerWithoutPartitioning NALUType = 19
NALUTypeSliceExtension NALUType = 20
NALUTypeSliceExtensionDepth NALUType = 21
NALUTypeReserved22 NALUType = 22
NALUTypeReserved23 NALUType = 23
)
var naluTypelabels = map[NALUType]string{
NALUTypeNonIDR: "NonIDR",
NALUTypeDataPartitionA: "DataPartitionA",
NALUTypeDataPartitionB: "DataPartitionB",
NALUTypeDataPartitionC: "DataPartitionC",
NALUTypeIDR: "IDR",
NALUTypeSEI: "SEI",
NALUTypeSPS: "SPS",
NALUTypePPS: "PPS",
NALUTypeAccessUnitDelimiter: "AccessUnitDelimiter",
NALUTypeEndOfSequence: "EndOfSequence",
NALUTypeEndOfStream: "EndOfStream",
NALUTypeFillerData: "FillerData",
NALUTypeSPSExtension: "SPSExtension",
NALUTypePrefix: "Prefix",
NALUTypeSubsetSPS: "SubsetSPS",
NALUTypeReserved16: "Reserved16",
NALUTypeReserved17: "Reserved17",
NALUTypeReserved18: "Reserved18",
NALUTypeSliceLayerWithoutPartitioning: "SliceLayerWithoutPartitioning",
NALUTypeSliceExtension: "SliceExtension",
NALUTypeSliceExtensionDepth: "SliceExtensionDepth",
NALUTypeReserved22: "Reserved22",
NALUTypeReserved23: "Reserved23",
}
// String implements fmt.Stringer.
func (nt NALUType) String() string {
if l, ok := naluTypelabels[nt]; ok {
return l
}
return fmt.Sprintf("unknown (%d)", nt)
}

13
pkg/h264/nalutype_test.go Normal file
View File

@@ -0,0 +1,13 @@
package h264
import (
"strings"
"testing"
"github.com/stretchr/testify/require"
)
func TestNALUType(t *testing.T) {
require.NotEqual(t, true, strings.HasPrefix(NALUType(10).String(), "unknown"))
require.Equal(t, true, strings.HasPrefix(NALUType(50).String(), "unknown"))
}

View File

@@ -38,7 +38,7 @@ func (d *Decoder) decodeTimestamp(ts uint32) time.Duration {
// It returns the AUs and the PTS of the first AU.
// The PTS of subsequent AUs can be calculated by adding time.Second*1000/clockRate.
func (d *Decoder) Decode(byts []byte) ([][]byte, time.Duration, error) {
pkt := rtp.Packet{}
var pkt rtp.Packet
err := pkt.Unmarshal(byts)
if err != nil {
d.isDecodingFragmented = false

View File

@@ -9,6 +9,8 @@ import (
"time"
"github.com/pion/rtp"
"github.com/aler9/gortsplib/pkg/h264"
)
// ErrMorePacketsNeeded is returned when more packets are needed.
@@ -55,7 +57,7 @@ func (d *Decoder) decodeTimestamp(ts uint32) time.Duration {
// Decode decodes NALUs from a RTP/H264 packet.
// It returns the decoded NALUs and their PTS.
func (d *Decoder) Decode(byts []byte) ([][]byte, time.Duration, error) {
pkt := rtp.Packet{}
var pkt rtp.Packet
err := pkt.Unmarshal(byts)
if err != nil {
d.isDecodingFragmented = false
@@ -197,13 +199,13 @@ func (d *Decoder) ReadSPSPPS(r io.Reader) ([]byte, []byte, error) {
for _, nalu := range nalus {
switch naluType(nalu[0] & 0x1F) {
case naluTypeSPS:
case naluType(h264.NALUTypeSPS):
sps = append([]byte(nil), nalu...)
if sps != nil && pps != nil {
return sps, pps, nil
}
case naluTypePPS:
case naluType(h264.NALUTypePPS):
pps = append([]byte(nil), nalu...)
if sps != nil && pps != nil {
return sps, pps, nil

View File

@@ -1,77 +1,43 @@
package rtph264
// naluType is the type of a NALU.
type naluType uint8
import (
"fmt"
"strings"
// NALU types, augmented for RTP.
"github.com/aler9/gortsplib/pkg/h264"
)
type naluType h264.NALUType
// additional NALU types for RTP/H264.
const (
naluTypeNonIDR naluType = 1
naluTypeDataPartitionA naluType = 2
naluTypeDataPartitionB naluType = 3
naluTypeDataPartitionC naluType = 4
naluTypeIDR naluType = 5
naluTypeSEI naluType = 6
naluTypeSPS naluType = 7
naluTypePPS naluType = 8
naluTypeAccessUnitDelimiter naluType = 9
naluTypeEndOfSequence naluType = 10
naluTypeEndOfStream naluType = 11
naluTypeFillerData naluType = 12
naluTypeSPSExtension naluType = 13
naluTypePrefix naluType = 14
naluTypeSubsetSPS naluType = 15
naluTypeReserved16 naluType = 16
naluTypeReserved17 naluType = 17
naluTypeReserved18 naluType = 18
naluTypeSliceLayerWithoutPartitioning naluType = 19
naluTypeSliceExtension naluType = 20
naluTypeSliceExtensionDepth naluType = 21
naluTypeReserved22 naluType = 22
naluTypeReserved23 naluType = 23
naluTypeSTAPA naluType = 24
naluTypeSTAPB naluType = 25
naluTypeMTAP16 naluType = 26
naluTypeMTAP24 naluType = 27
naluTypeFUA naluType = 28
naluTypeFUB naluType = 29
naluTypeSTAPA naluType = 24
naluTypeSTAPB naluType = 25
naluTypeMTAP16 naluType = 26
naluTypeMTAP24 naluType = 27
naluTypeFUA naluType = 28
naluTypeFUB naluType = 29
)
var naluLabels = map[naluType]string{
naluTypeNonIDR: "NonIDR",
naluTypeDataPartitionA: "DataPartitionA",
naluTypeDataPartitionB: "DataPartitionB",
naluTypeDataPartitionC: "DataPartitionC",
naluTypeIDR: "IDR",
naluTypeSEI: "SEI",
naluTypeSPS: "SPS",
naluTypePPS: "PPS",
naluTypeAccessUnitDelimiter: "AccessUnitDelimiter",
naluTypeEndOfSequence: "EndOfSequence",
naluTypeEndOfStream: "EndOfStream",
naluTypeFillerData: "FillerData",
naluTypeSPSExtension: "SPSExtension",
naluTypePrefix: "Prefix",
naluTypeSubsetSPS: "SubsetSPS",
naluTypeReserved16: "Reserved16",
naluTypeReserved17: "Reserved17",
naluTypeReserved18: "Reserved18",
naluTypeSliceLayerWithoutPartitioning: "SliceLayerWithoutPartitioning",
naluTypeSliceExtension: "SliceExtension",
naluTypeSliceExtensionDepth: "SliceExtensionDepth",
naluTypeReserved22: "Reserved22",
naluTypeReserved23: "Reserved23",
naluTypeSTAPA: "STAPA",
naluTypeSTAPB: "STAPB",
naluTypeMTAP16: "MTAP16",
naluTypeMTAP24: "MTAP24",
naluTypeFUA: "FUA",
naluTypeFUB: "FUB",
naluTypeSTAPA: "STAP-A",
naluTypeSTAPB: "STAP-B",
naluTypeMTAP16: "MTAP-16",
naluTypeMTAP24: "MTAP-24",
naluTypeFUA: "FU-A",
naluTypeFUB: "FU-B",
}
// String implements fmt.Stringer.
func (nt naluType) String() string {
p := h264.NALUType(nt).String()
if !strings.HasPrefix(p, "unknown") {
return p
}
if l, ok := naluLabels[nt]; ok {
return l
}
return "unknown"
return fmt.Sprintf("unknown (%d)", nt)
}

View File

@@ -1,12 +1,14 @@
package rtph264
import (
"strings"
"testing"
"github.com/stretchr/testify/require"
)
func TestNALUType(t *testing.T) {
require.NotEqual(t, "unknown", naluType(10).String())
require.Equal(t, "unknown", naluType(50).String())
require.NotEqual(t, true, strings.HasPrefix(naluType(10).String(), "unknown"))
require.NotEqual(t, true, strings.HasPrefix(naluType(26).String(), "unknown"))
require.Equal(t, true, strings.HasPrefix(naluType(50).String(), "unknown"))
}

View File

@@ -431,7 +431,7 @@ func TestDecodeErrors(t *testing.T) {
0x80, 0xe0, 0x44, 0xed, 0x88, 0x77, 0x6a, 0x15,
0x9d, 0xbb, 0x78, 0x12, byte(naluTypeMTAP16),
}},
"packet type not supported (MTAP16)",
"packet type not supported (MTAP-16)",
},
} {
t.Run(ca.name, func(t *testing.T) {