Skip to content

Commit

Permalink
SampleBuilder: keep track of padding packets
Browse files Browse the repository at this point in the history
We store the last timestamp as a nil value at first to distinguish
between no timestamp received and subsequent timestamps.
  • Loading branch information
tmatth committed Jan 4, 2023
1 parent 336fb31 commit 46b9041
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 13 deletions.
1 change: 1 addition & 0 deletions pkg/media/media.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ type Sample struct {
Duration time.Duration
PacketTimestamp uint32
PrevDroppedPackets uint16
PrevPaddingPackets uint16
}

// Writer defines an interface to handle
Expand Down
20 changes: 20 additions & 0 deletions pkg/media/samplebuilder/samplebuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,13 @@ type SampleBuilder struct {
// prepared contains the samples that have been processed to date
prepared sampleSequenceLocation

lastSampleTimestamp *uint32

// number of packets forced to be dropped
droppedPackets uint16

// number of padding packets detected and dropped (this will be a subset of `droppedPackets`)
paddingPackets uint16
}

// New constructs a new SampleBuilder.
Expand Down Expand Up @@ -185,6 +190,7 @@ const secondToNanoseconds = 1000000000
// buildSample creates a sample from a valid collection of RTP Packets by
// walking forwards building a sample if everything looks good clear and
// update buffer+values
// nolint: gocognit
func (s *SampleBuilder) buildSample(purgingBuffers bool) *media.Sample {
if s.active.empty() {
s.active = s.filled
Expand Down Expand Up @@ -242,7 +248,17 @@ func (s *SampleBuilder) buildSample(purgingBuffers bool) *media.Sample {
// prior to decoding all the packets, check if this packet
// would end being disposed anyway
if !s.depacketizer.IsPartitionHead(s.buffer[consume.head].Payload) {
// libwebrtc may send empty padding packets to smooth out send rate. These packets do not carry any media payloads.
isPadding := false
for i := consume.head; i != consume.tail; i++ {
if s.lastSampleTimestamp != nil && *s.lastSampleTimestamp == s.buffer[i].Timestamp && len(s.buffer[i].Payload) == 0 {
isPadding = true
}
}
s.droppedPackets += consume.count()
if isPadding {
s.paddingPackets += consume.count()
}
s.purgeConsumedLocation(consume, true)
s.purgeConsumedBuffers()
return nil
Expand All @@ -264,9 +280,13 @@ func (s *SampleBuilder) buildSample(purgingBuffers bool) *media.Sample {
Duration: time.Duration((float64(samples)/float64(s.sampleRate))*secondToNanoseconds) * time.Nanosecond,
PacketTimestamp: sampleTimestamp,
PrevDroppedPackets: s.droppedPackets,
PrevPaddingPackets: s.paddingPackets,
}

s.droppedPackets = 0
s.paddingPackets = 0
s.lastSampleTimestamp = new(uint32)
*s.lastSampleTimestamp = sampleTimestamp

s.preparedSamples[s.prepared.tail] = sample
s.prepared.tail++
Expand Down
29 changes: 16 additions & 13 deletions pkg/media/samplebuilder/samplebuilder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ func (f *fakeDepacketizer) IsPartitionHead(payload []byte) bool {
return true
}

// skip padding
if len(payload) < 1 {
return false
}
Expand Down Expand Up @@ -233,21 +234,23 @@ func TestSampleBuilder(t *testing.T) {
// shamelessly stolen from webrtc-rs
message: "Sample builder should recognize padding packets",
packets: []*rtp.Packet{
{Header: rtp.Header{SequenceNumber: 5000, Timestamp: 1}, Payload: []byte{1}},
{Header: rtp.Header{SequenceNumber: 5001, Timestamp: 1}, Payload: []byte{2}},
{Header: rtp.Header{SequenceNumber: 5002, Timestamp: 1, Marker: true}, Payload: []byte{3}},
{Header: rtp.Header{SequenceNumber: 5003, Timestamp: 1}, Payload: []byte{}},
{Header: rtp.Header{SequenceNumber: 5004, Timestamp: 1}, Payload: []byte{}},
{Header: rtp.Header{SequenceNumber: 5005, Timestamp: 3}, Payload: []byte{1}},
{Header: rtp.Header{SequenceNumber: 5006, Timestamp: 3, Marker: true}, Payload: []byte{7}},
{Header: rtp.Header{SequenceNumber: 5007, Timestamp: 4}, Payload: []byte{1}},
{Header: rtp.Header{SequenceNumber: 5000, Timestamp: 1}, Payload: []byte{1}}, // 1st packet
{Header: rtp.Header{SequenceNumber: 5001, Timestamp: 1}, Payload: []byte{2}}, // 2nd packet
{Header: rtp.Header{SequenceNumber: 5002, Timestamp: 1, Marker: true}, Payload: []byte{3}}, // 3rd packet
{Header: rtp.Header{SequenceNumber: 5003, Timestamp: 1}, Payload: []byte{}}, // Padding packet 1
{Header: rtp.Header{SequenceNumber: 5004, Timestamp: 1}, Payload: []byte{}}, // Padding packet 2
{Header: rtp.Header{SequenceNumber: 5005, Timestamp: 3}, Payload: []byte{1}}, // 6th packet
{Header: rtp.Header{SequenceNumber: 5006, Timestamp: 3, Marker: true}, Payload: []byte{7}}, // 7th packet
{Header: rtp.Header{SequenceNumber: 5007, Timestamp: 4}, Payload: []byte{1}}, // 7th packet
},
withHeadChecker: true,
headBytes: []byte{1},
samples: []*media.Sample{
{Data: []byte{1, 2, 3}, Duration: 0, PacketTimestamp: 1},
{Data: []byte{1, 2, 3}, Duration: 0, PacketTimestamp: 1, PrevDroppedPackets: 0}, // first sample
},
maxLate: 50,
maxLate: 50,
maxLateTimestamp: 2000,
extraPopAttempts: 1,
},
{
// shamelessly stolen from webrtc-rs
Expand All @@ -260,10 +263,10 @@ func TestSampleBuilder(t *testing.T) {
{Header: rtp.Header{SequenceNumber: 5004, Timestamp: 2, Marker: true}, Payload: []byte{1}}, // 3rd valid packet
{Header: rtp.Header{SequenceNumber: 5005, Timestamp: 3}, Payload: []byte{1}}, // 4th valid packet, start of next sample
},
withHeadChecker: true,
headBytes: []byte{1},
withHeadChecker: true,
headBytes: []byte{1},
samples: []*media.Sample{
{Data: []byte{1, 2}, Duration: 0, PacketTimestamp: 1, PrevDroppedPackets: 0}, // 1st sample
{Data: []byte{1, 2}, Duration: 0, PacketTimestamp: 1, PrevDroppedPackets: 0}, // 1st sample
},
maxLate: 50,
maxLateTimestamp: 2000,
Expand Down

0 comments on commit 46b9041

Please sign in to comment.