Skip to content

Commit 7d97c9b

Browse files
committed
Refactored samplebuilder logic
Many corner cases would cause samplebuilder to fail and return invalid results. This refactoring is more reliable in all cases. Fixed bug in H264 writer by reusing the packet object in H264 writer.
1 parent 6af3914 commit 7d97c9b

File tree

11 files changed

+511
-193
lines changed

11 files changed

+511
-193
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ Check out the **[contributing wiki](https://github.com/pion/webrtc/wiki/Contribu
275275
* [Jin Gong](https://github.com/cgojin)
276276
* [yusuke](https://github.com/yusukem99)
277277
* [Patryk Rogalski](https://github.com/digitalix)
278+
* [Robin Raymond](https://github.com/robin-raymond)
278279

279280
### License
280281
MIT License - see [LICENSE](LICENSE) for full text

examples/custom-logger/main.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ import (
1414
// customLogger satisfies the interface logging.LeveledLogger
1515
// a logger is created per subsystem in Pion, so you can have custom
1616
// behavior per subsystem (ICE, DTLS, SCTP...)
17-
type customLogger struct {
18-
}
17+
type customLogger struct{}
1918

2019
// Print all messages except trace
2120
func (c customLogger) Trace(msg string) {}
@@ -41,8 +40,7 @@ func (c customLogger) Errorf(format string, args ...interface{}) {
4140
// customLoggerFactory satisfies the interface logging.LoggerFactory
4241
// This allows us to create different loggers per subsystem. So we can
4342
// add custom behavior
44-
type customLoggerFactory struct {
45-
}
43+
type customLoggerFactory struct{}
4644

4745
func (c customLoggerFactory) NewLogger(subsystem string) logging.LeveledLogger {
4846
fmt.Printf("Creating logger for %s \n", subsystem)

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ require (
1212
github.com/pion/logging v0.2.2
1313
github.com/pion/randutil v0.1.0
1414
github.com/pion/rtcp v1.2.6
15-
github.com/pion/rtp v1.6.2
15+
github.com/pion/rtp v1.6.5
1616
github.com/pion/sctp v1.7.12
1717
github.com/pion/sdp/v3 v3.0.4
1818
github.com/pion/srtp/v2 v2.0.2

go.sum

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,9 @@ github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
5353
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
5454
github.com/pion/rtcp v1.2.6 h1:1zvwBbyd0TeEuuWftrd/4d++m+/kZSeiguxU61LFWpo=
5555
github.com/pion/rtcp v1.2.6/go.mod h1:52rMNPWFsjr39z9B9MhnkqhPLoeHTv1aN63o/42bWE0=
56-
github.com/pion/rtp v1.6.2 h1:iGBerLX6JiDjB9NXuaPzHyxHFG9JsIEdgwTC0lp5n/U=
5756
github.com/pion/rtp v1.6.2/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
57+
github.com/pion/rtp v1.6.5 h1:o2cZf8OascA5HF/b0PAbTxRKvOWxTQxWYt7SlToxFGI=
58+
github.com/pion/rtp v1.6.5/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko=
5859
github.com/pion/sctp v1.7.10/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0=
5960
github.com/pion/sctp v1.7.12 h1:GsatLufywVruXbZZT1CKg+Jr8ZTkwiPnmUC/oO9+uuY=
6061
github.com/pion/sctp v1.7.12/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s=

pkg/media/h264writer/h264writer.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ type (
1818
// Therefore, only 1-23, 24 (STAP-A), 28 (FU-A) NAL types are allowed.
1919
// https://tools.ietf.org/html/rfc6184#section-5.2
2020
H264Writer struct {
21-
writer io.Writer
22-
hasKeyFrame bool
21+
writer io.Writer
22+
hasKeyFrame bool
23+
cachedPacket *codecs.H264Packet
2324
}
2425
)
2526

@@ -53,7 +54,11 @@ func (h *H264Writer) WriteRTP(packet *rtp.Packet) error {
5354
}
5455
}
5556

56-
data, err := (&codecs.H264Packet{}).Unmarshal(packet.Payload)
57+
if h.cachedPacket == nil {
58+
h.cachedPacket = &codecs.H264Packet{}
59+
}
60+
61+
data, err := h.cachedPacket.Unmarshal(packet.Payload)
5762
if err != nil {
5863
return err
5964
}
@@ -65,6 +70,7 @@ func (h *H264Writer) WriteRTP(packet *rtp.Packet) error {
6570

6671
// Close closes the underlying writer
6772
func (h *H264Writer) Close() error {
73+
h.cachedPacket = nil
6874
if h.writer != nil {
6975
if closer, ok := h.writer.(io.Closer); ok {
7076
return closer.Close()

pkg/media/h264writer/h264writer_test.go

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,51 +59,61 @@ func TestWriteRTP(t *testing.T) {
5959
hasKeyFrame bool
6060
wantBytes []byte
6161
wantErr error
62+
reuseWriter bool
6263
}{
6364
{
6465
"When given an empty payload; it should return nil",
6566
[]byte{},
6667
false,
6768
[]byte{},
6869
nil,
70+
false,
6971
},
7072
{
7173
"When no keyframe is defined; it should discard the packet",
7274
[]byte{0x25, 0x90, 0x90},
7375
false,
7476
[]byte{},
7577
nil,
78+
false,
7679
},
7780
{
7881
"When a valid Single NAL Unit packet is given; it should unpack it without error",
7982
[]byte{0x27, 0x90, 0x90},
8083
true,
8184
[]byte{0x00, 0x00, 0x00, 0x01, 0x27, 0x90, 0x90},
8285
nil,
86+
false,
8387
},
8488
{
8589
"When a valid STAP-A packet is given; it should unpack it without error",
8690
[]byte{0x38, 0x00, 0x03, 0x27, 0x90, 0x90, 0x00, 0x05, 0x28, 0x90, 0x90, 0x90, 0x90},
8791
true,
8892
[]byte{0x00, 0x00, 0x00, 0x01, 0x27, 0x90, 0x90, 0x00, 0x00, 0x00, 0x01, 0x28, 0x90, 0x90, 0x90, 0x90},
8993
nil,
94+
false,
9095
},
9196
{
9297
"When a valid FU-A start packet is given; it should unpack it without error",
9398
[]byte{0x3C, 0x85, 0x90, 0x90, 0x90},
9499
true,
95-
[]byte{0x00, 0x00, 0x00, 0x01, 0x25, 0x90, 0x90, 0x90},
100+
[]byte{},
96101
nil,
102+
true,
97103
},
98104
{
99105
"When a valid FU-A end packet is given; it should unpack it without error",
100106
[]byte{0x3C, 0x45, 0x90, 0x90, 0x90},
101107
true,
102-
[]byte{0x90, 0x90, 0x90},
108+
[]byte{0x00, 0x00, 0x00, 0x01, 0x25, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90},
103109
nil,
110+
false,
104111
},
105112
}
106113

114+
var reuseWriter *bytes.Buffer
115+
var reuseH264Writer *H264Writer
116+
107117
for _, tt := range tests {
108118
tt := tt
109119
t.Run(tt.name, func(t *testing.T) {
@@ -112,6 +122,12 @@ func TestWriteRTP(t *testing.T) {
112122
hasKeyFrame: tt.hasKeyFrame,
113123
writer: writer,
114124
}
125+
if reuseWriter != nil {
126+
writer = reuseWriter
127+
}
128+
if reuseH264Writer != nil {
129+
h264Writer = reuseH264Writer
130+
}
115131
packet := &rtp.Packet{
116132
Payload: tt.payload,
117133
}
@@ -120,7 +136,14 @@ func TestWriteRTP(t *testing.T) {
120136

121137
assert.Equal(t, tt.wantErr, err)
122138
assert.True(t, bytes.Equal(tt.wantBytes, writer.Bytes()))
123-
assert.Nil(t, h264Writer.Close())
139+
if !tt.reuseWriter {
140+
assert.Nil(t, h264Writer.Close())
141+
reuseWriter = nil
142+
reuseH264Writer = nil
143+
} else {
144+
reuseWriter = writer
145+
reuseH264Writer = h264Writer
146+
}
124147
})
125148
}
126149
}

pkg/media/media.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,11 @@ import (
99

1010
// A Sample contains encoded media and timing information
1111
type Sample struct {
12-
Data []byte
13-
Timestamp time.Time
14-
Duration time.Duration
12+
Data []byte
13+
Timestamp time.Time
14+
Duration time.Duration
15+
PacketTimestamp uint32
16+
PrevDroppedPackets uint16
1517
}
1618

1719
// Writer defines an interface to handle
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// Package samplebuilder provides functionality to reconstruct media frames from RTP packets.
2+
package samplebuilder
3+
4+
import "math"
5+
6+
type sampleSequenceLocation struct {
7+
// head is the first packet in a sequence
8+
head uint16
9+
// tail is always set to one after the final sequence number,
10+
// so if head == tail then the sequence is empty
11+
tail uint16
12+
}
13+
14+
func (l sampleSequenceLocation) empty() bool {
15+
return l.head == l.tail
16+
}
17+
18+
func (l sampleSequenceLocation) hasData() bool {
19+
return l.head != l.tail
20+
}
21+
22+
func (l sampleSequenceLocation) count() uint16 {
23+
return seqnumDistance(l.head, l.tail)
24+
}
25+
26+
const (
27+
slCompareVoid = iota
28+
slCompareBefore
29+
slCompareInside
30+
slCompareAfter
31+
)
32+
33+
func minUint32(x, y uint32) uint32 {
34+
if x < y {
35+
return x
36+
}
37+
return y
38+
}
39+
40+
// Distance between two seqnums
41+
func seqnumDistance32(x, y uint32) uint32 {
42+
diff := int32(x - y)
43+
if diff < 0 {
44+
return uint32(-diff)
45+
}
46+
47+
return uint32(diff)
48+
}
49+
50+
func (l sampleSequenceLocation) compare(pos uint16) int {
51+
if l.empty() {
52+
return slCompareVoid
53+
}
54+
55+
head32 := uint32(l.head)
56+
count32 := uint32(l.count())
57+
tail32 := head32 + count32
58+
59+
// pos32 is possibly two values, the normal value or a wrap
60+
// around the start value, figure out which it is...
61+
62+
pos32Normal := uint32(pos)
63+
pos32Wrap := uint32(pos) + math.MaxUint16 + 1
64+
65+
distNormal := minUint32(seqnumDistance32(head32, pos32Normal), seqnumDistance32(tail32, pos32Normal))
66+
distWrap := minUint32(seqnumDistance32(head32, pos32Wrap), seqnumDistance32(tail32, pos32Wrap))
67+
68+
pos32 := pos32Normal
69+
if distWrap < distNormal {
70+
pos32 = pos32Wrap
71+
}
72+
73+
if pos32 < head32 {
74+
return slCompareBefore
75+
}
76+
77+
if pos32 >= tail32 {
78+
return slCompareAfter
79+
}
80+
81+
return slCompareInside
82+
}

0 commit comments

Comments
 (0)