Skip to content

Commit 446662d

Browse files
authored
Merge pull request #36 from edgeware/avc-improvements
AVC improvements
2 parents d91f2bd + a963a24 commit 446662d

File tree

13 files changed

+173
-34
lines changed

13 files changed

+173
-34
lines changed

README.md

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,4 @@ Some code in pkg/bits comes from or is based on https://github.com/tcnksm/go-cas
144144

145145
## Versions
146146

147-
| Version | Highlight |
148-
| ------ | --------- |
149-
| 0.14.0 | Added functions to use Annex B byte stream for AVC/H.264 |
150-
| 0.13.0 | Removed third-party log library |
151-
| 0.12.0 | Complete parsing of AVC/H.264 SPS and PPS |
152-
147+
See [Versions.md](Versions.md).

Versions.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Versions
2+
3+
| Version | Highlight |
4+
| ------ | --------- |
5+
6+
| 0.15.0 | Support for multiple SPS and more explicit NALU types for AVC |
7+
| 0.14.0 | Added functions to use Annex B byte stream for AVC/H.264 |
8+
| 0.13.0 | Removed third-party log library |
9+
| 0.12.0 | Complete parsing of AVC/H.264 SPS and PPS |

avc/avc.go

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@ package avc
22

33
import (
44
"encoding/binary"
5+
"fmt"
56
)
67

78
// NalType - AVC nal type
89
type NalType uint16
910

1011
const (
11-
// NALU_IDR - IDR Random Access Picture NAL Unit
12+
// NALU_NON_IDR - Non-IDR Slice NAL unit
13+
NALU_NON_IDR = NalType(1)
14+
// NALU_IDR - IDR Random Access Slice NAL Unit
1215
NALU_IDR = NalType(5)
1316
// NALU_SEI - Supplementary Enhancement Information NAL Unit
1417
NALU_SEI = NalType(6)
@@ -18,10 +21,12 @@ const (
1821
NALU_PPS = NalType(8)
1922
// NALU_AUD - AccessUnitDelimiter NAL Unit
2023
NALU_AUD = NalType(9)
24+
// NALU_EO_SEQ - End of Sequence NAL Unit
25+
NALU_EO_SEQ = NalType(10)
26+
// NALU_EO_STREAM - End of Stream NAL Unit
27+
NALU_EO_STREAM = NalType(11)
2128
// NALU_FILL - Filler NAL Unit
2229
NALU_FILL = NalType(12)
23-
// ExtendedSAR - Extended Sample Aspect Ratio Code
24-
ExtendedSAR = 255
2530
)
2631

2732
func (a NalType) String() string {
@@ -37,7 +42,7 @@ func (a NalType) String() string {
3742
case NALU_AUD:
3843
return "AUD"
3944
default:
40-
return "other"
45+
return fmt.Sprintf("Other %d", a)
4146
}
4247
}
4348

@@ -103,3 +108,25 @@ func HasParameterSets(b []byte) bool {
103108
}
104109
return false
105110
}
111+
112+
// GetParameterSets - get (multiple) SPS and PPS from a sample
113+
func GetParameterSets(sample []byte) (sps [][]byte, pps [][]byte) {
114+
sampleLength := uint32(len(sample))
115+
var pos uint32 = 0
116+
for {
117+
if pos >= sampleLength {
118+
break
119+
}
120+
nalLength := binary.BigEndian.Uint32(sample[pos : pos+4])
121+
pos += 4
122+
nalHdr := sample[pos]
123+
switch GetNalType(nalHdr) {
124+
case NALU_SPS:
125+
sps = append(sps, sample[pos:pos+nalLength])
126+
case NALU_PPS:
127+
pps = append(pps, sample[pos:pos+nalLength])
128+
}
129+
pos += nalLength
130+
}
131+
return sps, pps
132+
}

avc/avcdecoderconfigurationrecord.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ type AVCDecConfRec struct {
2626
}
2727

2828
// CreateAVCDecConfRec - Create an AVCDecConfRec based on SPS and PPS
29-
func CreateAVCDecConfRec(spsNALU []byte, ppsNALUs [][]byte) AVCDecConfRec {
30-
sps, err := ParseSPSNALUnit(spsNALU, false) // false -> parse only start of VUI
29+
func CreateAVCDecConfRec(spsNALUs [][]byte, ppsNALUs [][]byte) AVCDecConfRec {
30+
31+
sps, err := ParseSPSNALUnit(spsNALUs[0], false) // false -> parse only start of VUI
3132
if err != nil {
3233
panic("Could not parse SPS")
3334
}
@@ -36,7 +37,7 @@ func CreateAVCDecConfRec(spsNALU []byte, ppsNALUs [][]byte) AVCDecConfRec {
3637
AVCProfileIndication: byte(sps.Profile),
3738
ProfileCompatibility: byte(sps.ProfileCompatibility),
3839
AVCLevelIndication: byte(sps.Level),
39-
SPSnalus: [][]byte{spsNALU},
40+
SPSnalus: spsNALUs,
4041
PPSnalus: ppsNALUs,
4142
ChromaFormat: 1,
4243
BitDepthLumaMinus1: 0,

avc/sps.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import (
88
"github.com/edgeware/mp4ff/bits"
99
)
1010

11+
// extendedSAR - Extended Sample Aspect Ratio Code
12+
const extendedSAR = 255
13+
1114
var ErrNotSPS = errors.New("Not an SPS NAL unit")
1215

1316
// SPS - AVC SPS parameters
@@ -355,7 +358,7 @@ func parseVUI(reader *bits.EBSPReader, parseVUIBeyondAspectRatio bool) (*VUIPara
355358
if err != nil {
356359
return nil, err
357360
}
358-
if aspectRatioIDC == ExtendedSAR {
361+
if aspectRatioIDC == extendedSAR {
359362
vui.SampleAspectRatioWidth, err = reader.Read(16)
360363
if err != nil {
361364
return nil, err

bits/bits.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func NewReader(rd io.Reader) *Reader {
8080
func (r *Reader) Read(n int) (uint, error) {
8181
var err error
8282

83-
for r.n <= n {
83+
for r.n < n {
8484
r.v <<= 8
8585
var b uint8
8686
err = binary.Read(r.rd, binary.BigEndian, &b)
@@ -112,7 +112,7 @@ func (r *Reader) ReadFlag() (bool, error) {
112112
func (r *Reader) MustRead(n int) uint {
113113
var err error
114114

115-
for r.n <= n {
115+
for r.n < n {
116116
r.v <<= 8
117117
var b uint8
118118
err = binary.Read(r.rd, binary.BigEndian, &b)

bits/ebsp.go

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,11 @@ func (r *EBSPReader) MustRead(n int) uint {
5050
}
5151
r.pos++
5252
r.zeroCount = 0
53+
}
54+
if b != 0 {
55+
r.zeroCount = 0
5356
} else {
54-
if b != 0 {
55-
r.zeroCount = 0
56-
} else {
57-
r.zeroCount++
58-
}
57+
r.zeroCount++
5958
}
6059
r.v |= uint(b)
6160

@@ -151,12 +150,11 @@ func (r *EBSPReader) Read(n int) (uint, error) {
151150
}
152151
r.pos++
153152
r.zeroCount = 0
153+
}
154+
if b != 0 {
155+
r.zeroCount = 0
154156
} else {
155-
if b != 0 {
156-
r.zeroCount = 0
157-
} else {
158-
r.zeroCount++
159-
}
157+
r.zeroCount++
160158
}
161159
r.v |= uint(b)
162160

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,11 @@ func TestEbspParser(t *testing.T) {
7070
"27640020ac2ec05005bb011000000300100000078e840016e300005b8d8bdef83b438627",
7171
"27640020ac2ec05005bb0110000000100000078e840016e300005b8d8bdef83b438627",
7272
},
73+
{
74+
"Long zero sequence",
75+
"00000300000300",
76+
"0000000000",
77+
},
7378
}
7479

7580
for _, c := range cases {
@@ -212,3 +217,34 @@ func TestReadTrailingRbspBits(t *testing.T) {
212217
t.Errorf("Not at end after reading rbsp_trailing_bits")
213218
}
214219
}
220+
221+
func TestEBSPWriter(t *testing.T) {
222+
testCases := []struct {
223+
in []byte
224+
out []byte
225+
}{
226+
{
227+
in: []byte{0, 0, 0, 1},
228+
out: []byte{0, 0, 3, 0, 1},
229+
},
230+
{
231+
in: []byte{1, 0, 0, 2},
232+
out: []byte{1, 0, 0, 3, 2},
233+
},
234+
{
235+
in: []byte{0, 0, 0, 0, 0},
236+
out: []byte{0, 0, 3, 0, 0, 3, 0},
237+
},
238+
}
239+
for _, tc := range testCases {
240+
buf := bytes.Buffer{}
241+
w := NewEBSPWriter(&buf)
242+
for _, b := range tc.in {
243+
w.Write(uint(b), 8)
244+
}
245+
diff := deep.Equal(buf.Bytes(), tc.out)
246+
if diff != nil {
247+
t.Errorf("Got %v but wanted %d", buf.Bytes(), tc.out)
248+
}
249+
}
250+
}

bits/ebsp_writer.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package bits
2+
3+
import (
4+
"encoding/binary"
5+
"io"
6+
)
7+
8+
// EbspWriter writes bits into underlying io.Writer and inserts
9+
// start-code emulation prevention bytes as necessary
10+
// Stops writing at first error.
11+
// Errors that have occured can later be checked with Error().
12+
type EBSPWriter struct {
13+
n int // current number of bits
14+
v uint // current accumulated value
15+
err error // The first error caused by any write operation
16+
nr0 int // Number preceeding zero bytes
17+
18+
wr io.Writer
19+
}
20+
21+
// NewWriter - returns a new Writer
22+
func NewEBSPWriter(w io.Writer) *EBSPWriter {
23+
return &EBSPWriter{
24+
wr: w,
25+
}
26+
}
27+
28+
// Write - write n bits from bits and save error state
29+
func (w *EBSPWriter) Write(bits uint, n int) {
30+
if w.err != nil {
31+
return
32+
}
33+
w.v <<= uint(n)
34+
w.v |= bits & mask(n)
35+
w.n += n
36+
for w.n >= 8 {
37+
b := (w.v >> (uint(w.n) - 8)) & mask(8)
38+
if w.nr0 == 2 && b <= 3 {
39+
if err := binary.Write(w.wr, binary.BigEndian, uint8(3)); err != nil {
40+
w.err = err
41+
return
42+
}
43+
w.nr0 = 0
44+
}
45+
if err := binary.Write(w.wr, binary.BigEndian, uint8(b)); err != nil {
46+
w.err = err
47+
return
48+
}
49+
if b == 0 {
50+
w.nr0++
51+
}
52+
w.n -= 8
53+
}
54+
w.v &= mask(8)
55+
}
56+
57+
// Write - write rbsp trailing bits (a 1 followed by zeros to a byte boundary)
58+
func (w *EBSPWriter) WriteRbspTrailingBits() {
59+
w.Write(1, 1)
60+
w.StuffByteWithZeros()
61+
}
62+
63+
// StuffByteWithZeros - write zero bits until byte boundary (0-7bits)
64+
func (w *EBSPWriter) StuffByteWithZeros() {
65+
if w.n > 0 {
66+
w.Write(0, 8-w.n)
67+
}
68+
}

examples/initcreator/main.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,15 @@ func main() {
2525
}
2626

2727
func writeVideoAVCInitSegment() error {
28-
spsNALU, _ := hex.DecodeString(sps1nalu)
28+
sps, _ := hex.DecodeString(sps1nalu)
29+
spsNALUs := [][]byte{sps}
2930
pps, _ := hex.DecodeString(pps1nalu)
3031
ppsNALUs := [][]byte{pps}
3132

3233
videoTimescale := uint32(180000)
3334
init := mp4.CreateEmptyMP4Init(videoTimescale, "video", "und")
3435
trak := init.Moov.Trak[0]
35-
trak.SetAVCDescriptor("avc1", spsNALU, ppsNALUs)
36+
trak.SetAVCDescriptor("avc1", spsNALUs, ppsNALUs)
3637
width := trak.Mdia.Minf.Stbl.Stsd.AvcX.Width
3738
height := trak.Mdia.Minf.Stbl.Stsd.AvcX.Height
3839
if width != 1280 || height != 720 {

0 commit comments

Comments
 (0)