mirror of
https://github.com/pion/webrtc.git
synced 2025-10-05 07:06:51 +08:00
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.
This commit is contained in:
@@ -11,22 +11,25 @@ import (
|
||||
)
|
||||
|
||||
type sampleBuilderTest struct {
|
||||
message string
|
||||
packets []*rtp.Packet
|
||||
withHeadChecker bool
|
||||
headBytes []byte
|
||||
samples []*media.Sample
|
||||
timestamps []uint32
|
||||
maxLate uint16
|
||||
message string
|
||||
packets []*rtp.Packet
|
||||
withHeadChecker bool
|
||||
headBytes []byte
|
||||
samples []*media.Sample
|
||||
maxLate uint16
|
||||
maxLateTimestamp uint32
|
||||
}
|
||||
|
||||
type fakeDepacketizer struct {
|
||||
}
|
||||
type fakeDepacketizer struct{}
|
||||
|
||||
func (f *fakeDepacketizer) Unmarshal(r []byte) ([]byte, error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (f *fakeDepacketizer) IsDetectedFinalPacketInSequence(rtpPacketMarketBit bool) bool {
|
||||
return rtpPacketMarketBit
|
||||
}
|
||||
|
||||
type fakePartitionHeadChecker struct {
|
||||
headBytes []byte
|
||||
}
|
||||
@@ -47,24 +50,82 @@ func TestSampleBuilder(t *testing.T) {
|
||||
packets: []*rtp.Packet{
|
||||
{Header: rtp.Header{SequenceNumber: 5000, Timestamp: 5}, Payload: []byte{0x01}},
|
||||
},
|
||||
samples: []*media.Sample{},
|
||||
timestamps: []uint32{},
|
||||
maxLate: 50,
|
||||
samples: []*media.Sample{},
|
||||
maxLate: 50,
|
||||
maxLateTimestamp: 0,
|
||||
},
|
||||
{
|
||||
message: "SampleBuilder should emit one packet, we had three packets with unique timestamps",
|
||||
message: "SampleBuilder shouldn't emit anything if only one RTP packet has been pushed even if the market bit is set",
|
||||
packets: []*rtp.Packet{
|
||||
{Header: rtp.Header{SequenceNumber: 5000, Timestamp: 5, Marker: true}, Payload: []byte{0x01}},
|
||||
},
|
||||
samples: []*media.Sample{},
|
||||
maxLate: 50,
|
||||
maxLateTimestamp: 0,
|
||||
},
|
||||
{
|
||||
message: "SampleBuilder should emit two packets, we had three packets with unique timestamps",
|
||||
packets: []*rtp.Packet{
|
||||
{Header: rtp.Header{SequenceNumber: 5000, Timestamp: 5}, Payload: []byte{0x01}},
|
||||
{Header: rtp.Header{SequenceNumber: 5001, Timestamp: 6}, Payload: []byte{0x02}},
|
||||
{Header: rtp.Header{SequenceNumber: 5002, Timestamp: 7}, Payload: []byte{0x03}},
|
||||
},
|
||||
samples: []*media.Sample{
|
||||
{Data: []byte{0x02}, Duration: time.Second},
|
||||
{Data: []byte{0x01}, Duration: time.Second, PacketTimestamp: 5},
|
||||
{Data: []byte{0x02}, Duration: time.Second, PacketTimestamp: 6},
|
||||
},
|
||||
timestamps: []uint32{
|
||||
6,
|
||||
maxLate: 50,
|
||||
maxLateTimestamp: 0,
|
||||
},
|
||||
{
|
||||
message: "SampleBuilder should emit one packet, we had a packet end of sequence marker and run out of space",
|
||||
packets: []*rtp.Packet{
|
||||
{Header: rtp.Header{SequenceNumber: 5000, Timestamp: 5, Marker: true}, Payload: []byte{0x01}},
|
||||
{Header: rtp.Header{SequenceNumber: 5002, Timestamp: 7}, Payload: []byte{0x02}},
|
||||
{Header: rtp.Header{SequenceNumber: 5004, Timestamp: 9}, Payload: []byte{0x03}},
|
||||
{Header: rtp.Header{SequenceNumber: 5006, Timestamp: 11}, Payload: []byte{0x04}},
|
||||
{Header: rtp.Header{SequenceNumber: 5008, Timestamp: 13}, Payload: []byte{0x05}},
|
||||
{Header: rtp.Header{SequenceNumber: 5010, Timestamp: 15}, Payload: []byte{0x06}},
|
||||
{Header: rtp.Header{SequenceNumber: 5012, Timestamp: 17}, Payload: []byte{0x07}},
|
||||
},
|
||||
maxLate: 50,
|
||||
samples: []*media.Sample{
|
||||
{Data: []byte{0x01}, Duration: time.Second * 2, PacketTimestamp: 5},
|
||||
},
|
||||
maxLate: 5,
|
||||
maxLateTimestamp: 0,
|
||||
},
|
||||
{
|
||||
message: "SampleBuilder shouldn't emit any packet, we do not have a valid end of sequence and run out of space",
|
||||
packets: []*rtp.Packet{
|
||||
{Header: rtp.Header{SequenceNumber: 5000, Timestamp: 5}, Payload: []byte{0x01}},
|
||||
{Header: rtp.Header{SequenceNumber: 5002, Timestamp: 7}, Payload: []byte{0x02}},
|
||||
{Header: rtp.Header{SequenceNumber: 5004, Timestamp: 9}, Payload: []byte{0x03}},
|
||||
{Header: rtp.Header{SequenceNumber: 5006, Timestamp: 11}, Payload: []byte{0x04}},
|
||||
{Header: rtp.Header{SequenceNumber: 5008, Timestamp: 13}, Payload: []byte{0x05}},
|
||||
{Header: rtp.Header{SequenceNumber: 5010, Timestamp: 15}, Payload: []byte{0x06}},
|
||||
{Header: rtp.Header{SequenceNumber: 5012, Timestamp: 17}, Payload: []byte{0x07}},
|
||||
},
|
||||
samples: []*media.Sample{},
|
||||
maxLate: 5,
|
||||
maxLateTimestamp: 0,
|
||||
},
|
||||
{
|
||||
message: "SampleBuilder should emit one packet, we had a packet end of sequence marker and run out of space",
|
||||
packets: []*rtp.Packet{
|
||||
{Header: rtp.Header{SequenceNumber: 5000, Timestamp: 5, Marker: true}, Payload: []byte{0x01}},
|
||||
{Header: rtp.Header{SequenceNumber: 5002, Timestamp: 7, Marker: true}, Payload: []byte{0x02}},
|
||||
{Header: rtp.Header{SequenceNumber: 5004, Timestamp: 9}, Payload: []byte{0x03}},
|
||||
{Header: rtp.Header{SequenceNumber: 5006, Timestamp: 11}, Payload: []byte{0x04}},
|
||||
{Header: rtp.Header{SequenceNumber: 5008, Timestamp: 13}, Payload: []byte{0x05}},
|
||||
{Header: rtp.Header{SequenceNumber: 5010, Timestamp: 15}, Payload: []byte{0x06}},
|
||||
{Header: rtp.Header{SequenceNumber: 5012, Timestamp: 17}, Payload: []byte{0x07}},
|
||||
},
|
||||
samples: []*media.Sample{
|
||||
{Data: []byte{0x01}, Duration: time.Second * 2, PacketTimestamp: 5},
|
||||
{Data: []byte{0x02}, Duration: time.Second * 2, PacketTimestamp: 7, PrevDroppedPackets: 1},
|
||||
},
|
||||
maxLate: 5,
|
||||
maxLateTimestamp: 0,
|
||||
},
|
||||
{
|
||||
message: "SampleBuilder should emit one packet, we had two packets but two with duplicate timestamps",
|
||||
@@ -75,12 +136,11 @@ func TestSampleBuilder(t *testing.T) {
|
||||
{Header: rtp.Header{SequenceNumber: 5003, Timestamp: 7}, Payload: []byte{0x04}},
|
||||
},
|
||||
samples: []*media.Sample{
|
||||
{Data: []byte{0x02, 0x03}, Duration: time.Second},
|
||||
{Data: []byte{0x01}, Duration: time.Second, PacketTimestamp: 5},
|
||||
{Data: []byte{0x02, 0x03}, Duration: time.Second, PacketTimestamp: 6},
|
||||
},
|
||||
timestamps: []uint32{
|
||||
6,
|
||||
},
|
||||
maxLate: 50,
|
||||
maxLate: 50,
|
||||
maxLateTimestamp: 0,
|
||||
},
|
||||
{
|
||||
message: "SampleBuilder shouldn't emit a packet because we have a gap before a valid one",
|
||||
@@ -89,26 +149,22 @@ func TestSampleBuilder(t *testing.T) {
|
||||
{Header: rtp.Header{SequenceNumber: 5007, Timestamp: 6}, Payload: []byte{0x02}},
|
||||
{Header: rtp.Header{SequenceNumber: 5008, Timestamp: 7}, Payload: []byte{0x03}},
|
||||
},
|
||||
samples: []*media.Sample{},
|
||||
timestamps: []uint32{},
|
||||
maxLate: 50,
|
||||
samples: []*media.Sample{},
|
||||
maxLate: 50,
|
||||
maxLateTimestamp: 0,
|
||||
},
|
||||
{
|
||||
message: "SampleBuilder should emit a packet after a gap if PartitionHeadChecker assumes it head",
|
||||
message: "SampleBuilder shouldn't emit a packet after a gap as there are gaps and have not reached maxLate yet",
|
||||
packets: []*rtp.Packet{
|
||||
{Header: rtp.Header{SequenceNumber: 5000, Timestamp: 5}, Payload: []byte{0x01}},
|
||||
{Header: rtp.Header{SequenceNumber: 5007, Timestamp: 6}, Payload: []byte{0x02}},
|
||||
{Header: rtp.Header{SequenceNumber: 5008, Timestamp: 7}, Payload: []byte{0x03}},
|
||||
},
|
||||
withHeadChecker: true,
|
||||
headBytes: []byte{0x02},
|
||||
samples: []*media.Sample{
|
||||
{Data: []byte{0x02}, Duration: 0},
|
||||
},
|
||||
timestamps: []uint32{
|
||||
6,
|
||||
},
|
||||
maxLate: 50,
|
||||
withHeadChecker: true,
|
||||
headBytes: []byte{0x02},
|
||||
samples: []*media.Sample{},
|
||||
maxLate: 50,
|
||||
maxLateTimestamp: 0,
|
||||
},
|
||||
{
|
||||
message: "SampleBuilder shouldn't emit a packet after a gap if PartitionHeadChecker doesn't assume it head",
|
||||
@@ -117,11 +173,11 @@ func TestSampleBuilder(t *testing.T) {
|
||||
{Header: rtp.Header{SequenceNumber: 5007, Timestamp: 6}, Payload: []byte{0x02}},
|
||||
{Header: rtp.Header{SequenceNumber: 5008, Timestamp: 7}, Payload: []byte{0x03}},
|
||||
},
|
||||
withHeadChecker: true,
|
||||
headBytes: []byte{},
|
||||
samples: []*media.Sample{},
|
||||
timestamps: []uint32{},
|
||||
maxLate: 50,
|
||||
withHeadChecker: true,
|
||||
headBytes: []byte{},
|
||||
samples: []*media.Sample{},
|
||||
maxLate: 50,
|
||||
maxLateTimestamp: 0,
|
||||
},
|
||||
{
|
||||
message: "SampleBuilder should emit multiple valid packets",
|
||||
@@ -134,18 +190,34 @@ func TestSampleBuilder(t *testing.T) {
|
||||
{Header: rtp.Header{SequenceNumber: 5005, Timestamp: 6}, Payload: []byte{0x06}},
|
||||
},
|
||||
samples: []*media.Sample{
|
||||
{Data: []byte{0x02}, Duration: time.Second},
|
||||
{Data: []byte{0x03}, Duration: time.Second},
|
||||
{Data: []byte{0x04}, Duration: time.Second},
|
||||
{Data: []byte{0x05}, Duration: time.Second},
|
||||
{Data: []byte{0x01}, Duration: time.Second, PacketTimestamp: 1},
|
||||
{Data: []byte{0x02}, Duration: time.Second, PacketTimestamp: 2},
|
||||
{Data: []byte{0x03}, Duration: time.Second, PacketTimestamp: 3},
|
||||
{Data: []byte{0x04}, Duration: time.Second, PacketTimestamp: 4},
|
||||
{Data: []byte{0x05}, Duration: time.Second, PacketTimestamp: 5},
|
||||
},
|
||||
timestamps: []uint32{
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
maxLate: 50,
|
||||
maxLateTimestamp: 0,
|
||||
},
|
||||
{
|
||||
message: "SampleBuilder should skip time stamps too old",
|
||||
packets: []*rtp.Packet{
|
||||
{Header: rtp.Header{SequenceNumber: 5000, Timestamp: 1}, Payload: []byte{0x01}},
|
||||
{Header: rtp.Header{SequenceNumber: 5001, Timestamp: 2}, Payload: []byte{0x02}},
|
||||
{Header: rtp.Header{SequenceNumber: 5002, Timestamp: 3}, Payload: []byte{0x03}},
|
||||
{Header: rtp.Header{SequenceNumber: 5013, Timestamp: 4000}, Payload: []byte{0x04}},
|
||||
{Header: rtp.Header{SequenceNumber: 5014, Timestamp: 4000}, Payload: []byte{0x05}},
|
||||
{Header: rtp.Header{SequenceNumber: 5015, Timestamp: 4002}, Payload: []byte{0x06}},
|
||||
{Header: rtp.Header{SequenceNumber: 5016, Timestamp: 7000}, Payload: []byte{0x04}},
|
||||
{Header: rtp.Header{SequenceNumber: 5017, Timestamp: 7001}, Payload: []byte{0x05}},
|
||||
},
|
||||
maxLate: 50,
|
||||
samples: []*media.Sample{
|
||||
{Data: []byte{0x04, 0x05}, Duration: time.Second * time.Duration(2), PacketTimestamp: 4000, PrevDroppedPackets: 13},
|
||||
},
|
||||
withHeadChecker: true,
|
||||
headBytes: []byte{0x04},
|
||||
maxLate: 50,
|
||||
maxLateTimestamp: 2000,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -159,6 +231,11 @@ func TestSampleBuilder(t *testing.T) {
|
||||
&fakePartitionHeadChecker{headBytes: t.headBytes},
|
||||
))
|
||||
}
|
||||
if t.maxLateTimestamp != 0 {
|
||||
opts = append(opts, WithMaxTimeDelay(
|
||||
time.Millisecond*time.Duration(int64(t.maxLateTimestamp)),
|
||||
))
|
||||
}
|
||||
|
||||
s := New(t.maxLate, &fakeDepacketizer{}, 1, opts...)
|
||||
samples := []*media.Sample{}
|
||||
@@ -169,35 +246,7 @@ func TestSampleBuilder(t *testing.T) {
|
||||
for sample := s.Pop(); sample != nil; sample = s.Pop() {
|
||||
samples = append(samples, sample)
|
||||
}
|
||||
|
||||
assert.Equal(samples, t.samples, t.message)
|
||||
}
|
||||
})
|
||||
t.Run("PopWithTimestamp", func(t *testing.T) {
|
||||
assert := assert.New(t)
|
||||
|
||||
for _, t := range testData {
|
||||
var opts []Option
|
||||
if t.withHeadChecker {
|
||||
opts = append(opts, WithPartitionHeadChecker(
|
||||
&fakePartitionHeadChecker{headBytes: t.headBytes},
|
||||
))
|
||||
}
|
||||
|
||||
s := New(t.maxLate, &fakeDepacketizer{}, 1, opts...)
|
||||
samples := []*media.Sample{}
|
||||
timestamps := []uint32{}
|
||||
|
||||
for _, p := range t.packets {
|
||||
s.Push(p)
|
||||
}
|
||||
for sample, timestamp := s.PopWithTimestamp(); sample != nil; sample, timestamp = s.PopWithTimestamp() {
|
||||
samples = append(samples, sample)
|
||||
timestamps = append(timestamps, timestamp)
|
||||
}
|
||||
|
||||
assert.Equal(samples, t.samples, t.message)
|
||||
assert.Equal(timestamps, t.timestamps, t.message)
|
||||
assert.Equal(t.samples, samples, t.message)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -210,12 +259,18 @@ func TestSampleBuilderMaxLate(t *testing.T) {
|
||||
s.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 0, Timestamp: 1}, Payload: []byte{0x01}})
|
||||
s.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 1, Timestamp: 2}, Payload: []byte{0x01}})
|
||||
s.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 2, Timestamp: 3}, Payload: []byte{0x01}})
|
||||
assert.Equal(s.Pop(), &media.Sample{Data: []byte{0x01}, Duration: time.Second}, "Failed to build samples before gap")
|
||||
assert.Equal(&media.Sample{Data: []byte{0x01}, Duration: time.Second, PacketTimestamp: 1}, s.Pop(), "Failed to build samples before gap")
|
||||
|
||||
s.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 5000, Timestamp: 500}, Payload: []byte{0x02}})
|
||||
s.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 5001, Timestamp: 501}, Payload: []byte{0x02}})
|
||||
s.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 5002, Timestamp: 502}, Payload: []byte{0x02}})
|
||||
assert.Equal(s.Pop(), &media.Sample{Data: []byte{0x02}, Duration: time.Second}, "Failed to build samples after large gap")
|
||||
|
||||
assert.Equal(&media.Sample{Data: []byte{0x01}, Duration: time.Second, PacketTimestamp: 2}, s.Pop(), "Failed to build samples after large gap")
|
||||
assert.Equal((*media.Sample)(nil), s.Pop(), "Failed to build samples after large gap")
|
||||
|
||||
s.Push(&rtp.Packet{Header: rtp.Header{SequenceNumber: 6000, Timestamp: 600}, Payload: []byte{0x03}})
|
||||
assert.Equal(&media.Sample{Data: []byte{0x02}, Duration: time.Second, PacketTimestamp: 500, PrevDroppedPackets: 4998}, s.Pop(), "Failed to build samples after large gap")
|
||||
assert.Equal(&media.Sample{Data: []byte{0x02}, Duration: time.Second, PacketTimestamp: 501}, s.Pop(), "Failed to build samples after large gap")
|
||||
}
|
||||
|
||||
func TestSeqnumDistance(t *testing.T) {
|
||||
@@ -285,6 +340,7 @@ func TestSampleBuilderWithPacketReleaseHandler(t *testing.T) {
|
||||
{Header: rtp.Header{SequenceNumber: 11, Timestamp: 120}, Payload: []byte{0x02}},
|
||||
{Header: rtp.Header{SequenceNumber: 12, Timestamp: 121}, Payload: []byte{0x03}},
|
||||
{Header: rtp.Header{SequenceNumber: 13, Timestamp: 122}, Payload: []byte{0x04}},
|
||||
{Header: rtp.Header{SequenceNumber: 21, Timestamp: 200}, Payload: []byte{0x05}},
|
||||
}
|
||||
s := New(10, &fakeDepacketizer{}, 1, WithPacketReleaseHandler(fakePacketReleaseHandler))
|
||||
s.Push(&pkts[0])
|
||||
@@ -298,13 +354,14 @@ func TestSampleBuilderWithPacketReleaseHandler(t *testing.T) {
|
||||
// Test packets released after samples built.
|
||||
s.Push(&pkts[2])
|
||||
s.Push(&pkts[3])
|
||||
s.Push(&pkts[4])
|
||||
if s.Pop() == nil {
|
||||
t.Errorf("Should have some sample here.")
|
||||
}
|
||||
if len(released) != 2 {
|
||||
if len(released) < 3 {
|
||||
t.Errorf("packet built with sample is not released")
|
||||
}
|
||||
if len(released) >= 2 && released[1].SequenceNumber != pkts[2].SequenceNumber {
|
||||
if len(released) >= 2 && released[2].SequenceNumber != pkts[2].SequenceNumber {
|
||||
t.Errorf("Unexpected packet released by samples built")
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user