Skip to content

Commit

Permalink
h264: support empty NALUs inside AVCC
Browse files Browse the repository at this point in the history
  • Loading branch information
aler9 committed Sep 24, 2023
1 parent 7fae03f commit 5354429
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 19 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Definitions and functions shared between [gortsplib](https://github.com/bluenvir
|[ITU-T Rec. T-871, JPEG File Interchange Format](https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-T.871-201105-I!!PDF-E&type=items)|JPEG codec|
|[ITU-T Rec. H.264 (08/2021)](https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-H.264-202108-I!!PDF-E&type=items)|H264 codec|
|[ITU-T Rec. H.265 (08/2021)](https://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-H.265-202108-I!!PDF-E&type=items)|H265 codec|
|ISO 14496-15, Coding of audio-visual objects, Part 15, Advanced Video Coding (AVC) file format|H264 and H265 inside MP4|
|[VP9 Bitstream & Decoding Process Specification v0.6](https://storage.googleapis.com/downloads.webmproject.org/docs/vp9/vp9-bitstream-specification-v0.6-20160331-draft.pdf)|VP9 codec|
|[VP9 Codec ISO Media File Format Binding](https://www.webmproject.org/vp9/mp4/)|VP9 inside MP4|
|[AV1 Bitstream & Decoding Process](https://aomediacodec.github.io/av1-spec/av1-spec.pdf)|AV1 codec|
Expand Down
44 changes: 25 additions & 19 deletions pkg/codecs/h264/avcc.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package h264

import (
"errors"
"fmt"
)

// ErrAVCCNoNALUs is returned by AVCCUnmarshal when no NALUs have been decoded.
var ErrAVCCNoNALUs = errors.New("AVCC unit doesn't contain any NALU")

// AVCCUnmarshal decodes an access unit from the AVCC stream format.
// Specification: ?
// Specification: ISO 14496-15, section 5.3.4.2.1
func AVCCUnmarshal(buf []byte) ([][]byte, error) {
bl := len(buf)
pos := 0
Expand All @@ -21,33 +25,35 @@ func AVCCUnmarshal(buf []byte) ([][]byte, error) {
l := int(uint32(buf[pos])<<24 | uint32(buf[pos+1])<<16 | uint32(buf[pos+2])<<8 | uint32(buf[pos+3]))
pos += 4

if l == 0 {
return nil, fmt.Errorf("invalid NALU")
}
if l != 0 {
if (auSize + l) > MaxAccessUnitSize {
return nil, fmt.Errorf("access unit size (%d) is too big, maximum is %d", auSize+l, MaxAccessUnitSize)
}

if (auSize + l) > MaxAccessUnitSize {
return nil, fmt.Errorf("access unit size (%d) is too big, maximum is %d", auSize+l, MaxAccessUnitSize)
}
if (naluCount + 1) > MaxNALUsPerAccessUnit {
return nil, fmt.Errorf("NALU count (%d) exceeds maximum allowed (%d)",
len(ret)+1, MaxNALUsPerAccessUnit)
}

Check warning on line 36 in pkg/codecs/h264/avcc.go

View check run for this annotation

Codecov / codecov/patch

pkg/codecs/h264/avcc.go#L34-L36

Added lines #L34 - L36 were not covered by tests

if (naluCount + 1) > MaxNALUsPerAccessUnit {
return nil, fmt.Errorf("NALU count (%d) exceeds maximum allowed (%d)",
len(ret)+1, MaxNALUsPerAccessUnit)
}
if (bl - pos) < l {
return nil, fmt.Errorf("invalid length")
}

if (bl - pos) < l {
return nil, fmt.Errorf("invalid length")
ret = append(ret, buf[pos:pos+l])
auSize += l
naluCount++
pos += l
}

ret = append(ret, buf[pos:pos+l])
auSize += l
naluCount++
pos += l

if (bl - pos) == 0 {
break
}
}

if ret == nil {
return nil, ErrAVCCNoNALUs
}

return ret, nil
}

Expand All @@ -60,7 +66,7 @@ func avccMarshalSize(au [][]byte) int {
}

// AVCCMarshal encodes an access unit into the AVCC stream format.
// Specification: ?
// Specification: ISO 14496-15, section 5.3.4.2.1
func AVCCMarshal(au [][]byte) ([]byte, error) {
buf := make([]byte, avccMarshalSize(au))
pos := 0
Expand Down
19 changes: 19 additions & 0 deletions pkg/codecs/h264/avcc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,25 @@ func TestAVCCUnmarshal(t *testing.T) {
}
}

// issue mediamtx/2375
func TestAVCCUnmarshalEmpty(t *testing.T) {
dec, err := AVCCUnmarshal([]byte{
0x0, 0x0, 0x0, 0x0,
})

require.Equal(t, ErrAVCCNoNALUs, err)
require.Equal(t, [][]byte(nil), dec)

dec, err = AVCCUnmarshal([]byte{
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x1, 0x2, 0x3,
})

require.NoError(t, err)
require.Equal(t, [][]byte{
{1, 2, 3},
}, dec)
}

func TestAVCCMarshal(t *testing.T) {
for _, ca := range casesAVCC {
t.Run(ca.name, func(t *testing.T) {
Expand Down

0 comments on commit 5354429

Please sign in to comment.