Index: webrtc/video/end_to_end_tests.cc |
diff --git a/webrtc/video/end_to_end_tests.cc b/webrtc/video/end_to_end_tests.cc |
index 305dcb0aaf8f3029c2f0d254fcbd83388265ac38..01f132ff27f4eb5dfaeb0f9fc4e5bc4326dcca7e 100644 |
--- a/webrtc/video/end_to_end_tests.cc |
+++ b/webrtc/video/end_to_end_tests.cc |
@@ -20,6 +20,7 @@ |
#include "webrtc/call.h" |
#include "webrtc/call/transport_adapter.h" |
#include "webrtc/frame_callback.h" |
+#include "webrtc/modules/rtp_rtcp/source/byte_io.h" |
#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" |
#include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h" |
#include "webrtc/modules/video_coding/codecs/vp9/include/vp9.h" |
@@ -1335,19 +1336,20 @@ TEST_F(EndToEndTest, SendsAndReceivesMultipleStreams) { |
} |
TEST_F(EndToEndTest, AssignsTransportSequenceNumbers) { |
- // TODO(sprang): Extend this to verify received values once send-side BWE |
- // is in place. |
- |
static const int kExtensionId = 5; |
class RtpExtensionHeaderObserver : public test::DirectTransport { |
public: |
- RtpExtensionHeaderObserver() |
+ RtpExtensionHeaderObserver(const uint32_t& first_media_ssrc, |
+ const std::map<uint32_t, uint32_t>& ssrc_map) |
: done_(EventWrapper::Create()), |
parser_(RtpHeaderParser::Create()), |
- last_seq_(0), |
+ first_media_ssrc_(first_media_ssrc), |
+ rtx_to_media_ssrcs_(ssrc_map), |
padding_observed_(false), |
- rtx_padding_observed_(false) { |
+ rtx_padding_observed_(false), |
+ retransmit_observed_(false), |
+ started_(false) { |
parser_->RegisterRtpHeaderExtension(kRtpExtensionTransportSequenceNumber, |
kExtensionId); |
} |
@@ -1356,54 +1358,110 @@ TEST_F(EndToEndTest, AssignsTransportSequenceNumbers) { |
bool SendRtp(const uint8_t* data, |
size_t length, |
const PacketOptions& options) override { |
- if (IsDone()) |
- return false; |
+ { |
+ rtc::CritScope cs(&lock_); |
- RTPHeader header; |
- EXPECT_TRUE(parser_->Parse(data, length, &header)); |
- if (header.extension.hasTransportSequenceNumber) { |
- EXPECT_EQ(options.packet_id, |
- header.extension.transportSequenceNumber); |
- if (!streams_observed_.empty()) { |
- EXPECT_EQ(static_cast<uint16_t>(last_seq_ + 1), |
+ if (IsDone()) |
+ return false; |
+ |
+ if (started_) { |
+ RTPHeader header; |
+ EXPECT_TRUE(parser_->Parse(data, length, &header)); |
+ bool drop_packet = false; |
+ |
+ EXPECT_TRUE(header.extension.hasTransportSequenceNumber); |
+ EXPECT_EQ(options.packet_id, |
header.extension.transportSequenceNumber); |
- } |
- last_seq_ = header.extension.transportSequenceNumber; |
- |
- size_t payload_length = |
- length - (header.headerLength + header.paddingLength); |
- if (payload_length == 0) { |
- padding_observed_ = true; |
- } else if (header.payloadType == kSendRtxPayloadType) { |
- rtx_padding_observed_ = true; |
- } else { |
- streams_observed_.insert(header.ssrc); |
- } |
+ if (!streams_observed_.empty()) { |
+ // Unwrap packet id and verify uniqueness. |
+ int64_t packet_id = unwrapper_.Unwrap(options.packet_id); |
+ EXPECT_TRUE(received_packed_ids_.insert(packet_id).second); |
+ } |
- if (IsDone()) |
- done_->Set(); |
+ // Drop (up to) every 17th packet, so we get retransmits. |
+ // Only drop media, and not on the first stream (otherwise it will be |
+ // hard to distinguish from padding, which is always sent on the first |
+ // stream). |
+ if (header.payloadType != kSendRtxPayloadType && |
+ header.ssrc != first_media_ssrc_ && |
+ header.extension.transportSequenceNumber % 17 == 0) { |
+ dropped_seq_[header.ssrc].insert(header.sequenceNumber); |
+ drop_packet = true; |
+ } |
+ |
+ size_t payload_length = |
+ length - (header.headerLength + header.paddingLength); |
+ if (payload_length == 0) { |
+ padding_observed_ = true; |
+ } else if (header.payloadType == kSendRtxPayloadType) { |
+ uint16_t original_sequence_number = |
+ ByteReader<uint16_t>::ReadBigEndian(&data[header.headerLength]); |
+ uint32_t original_ssrc = |
+ rtx_to_media_ssrcs_.find(header.ssrc)->second; |
+ std::set<uint16_t>* seq_no_map = &dropped_seq_[original_ssrc]; |
+ auto it = seq_no_map->find(original_sequence_number); |
+ if (it != seq_no_map->end()) { |
+ retransmit_observed_ = true; |
+ seq_no_map->erase(it); |
+ } else { |
+ rtx_padding_observed_ = true; |
+ } |
+ } else { |
+ streams_observed_.insert(header.ssrc); |
+ } |
+ |
+ if (IsDone()) |
+ done_->Set(); |
+ |
+ if (drop_packet) |
+ return true; |
+ } |
} |
+ |
return test::DirectTransport::SendRtp(data, length, options); |
} |
bool IsDone() { |
- return streams_observed_.size() == MultiStreamTest::kNumStreams && |
- padding_observed_ && rtx_padding_observed_; |
+ bool observed_types_ok = |
+ streams_observed_.size() == MultiStreamTest::kNumStreams && |
+ padding_observed_ && retransmit_observed_ && rtx_padding_observed_; |
+ if (!observed_types_ok) |
+ return false; |
+ // We should not have any gaps in the sequence number range. |
+ size_t seqno_range = |
+ *received_packed_ids_.rbegin() - *received_packed_ids_.begin() + 1; |
+ return seqno_range == received_packed_ids_.size(); |
} |
- EventTypeWrapper Wait() { return done_->Wait(kDefaultTimeoutMs); } |
+ EventTypeWrapper Wait() { |
+ { |
+ // Can't be sure until this point that rtx_to_media_ssrcs_ etc have |
+ // been initialized and are OK to read. |
+ rtc::CritScope cs(&lock_); |
+ started_ = true; |
+ } |
+ return done_->Wait(kDefaultTimeoutMs); |
+ } |
+ rtc::CriticalSection lock_; |
rtc::scoped_ptr<EventWrapper> done_; |
rtc::scoped_ptr<RtpHeaderParser> parser_; |
- uint16_t last_seq_; |
+ SequenceNumberUnwrapper unwrapper_; |
+ std::set<int64_t> received_packed_ids_; |
std::set<uint32_t> streams_observed_; |
+ std::map<uint32_t, std::set<uint16_t>> dropped_seq_; |
+ const uint32_t& first_media_ssrc_; |
+ const std::map<uint32_t, uint32_t>& rtx_to_media_ssrcs_; |
bool padding_observed_; |
bool rtx_padding_observed_; |
+ bool retransmit_observed_; |
+ bool started_; |
}; |
class TransportSequenceNumberTester : public MultiStreamTest { |
public: |
- TransportSequenceNumberTester() : observer_(nullptr) {} |
+ TransportSequenceNumberTester() |
+ : first_media_ssrc_(0), observer_(nullptr) {} |
virtual ~TransportSequenceNumberTester() {} |
protected: |
@@ -1431,24 +1489,33 @@ TEST_F(EndToEndTest, AssignsTransportSequenceNumbers) { |
// Configure RTX for redundant payload padding. |
send_config->rtp.nack.rtp_history_ms = kNackRtpHistoryMs; |
- send_config->rtp.rtx.ssrcs.push_back(kSendRtxSsrcs[0]); |
+ send_config->rtp.rtx.ssrcs.push_back(kSendRtxSsrcs[stream_index]); |
send_config->rtp.rtx.payload_type = kSendRtxPayloadType; |
+ rtx_to_media_ssrcs_[kSendRtxSsrcs[stream_index]] = |
+ send_config->rtp.ssrcs[0]; |
+ |
+ if (stream_index == 0) |
+ first_media_ssrc_ = send_config->rtp.ssrcs[0]; |
} |
void UpdateReceiveConfig( |
size_t stream_index, |
VideoReceiveStream::Config* receive_config) override { |
+ receive_config->rtp.nack.rtp_history_ms = kNackRtpHistoryMs; |
receive_config->rtp.extensions.clear(); |
receive_config->rtp.extensions.push_back( |
RtpExtension(RtpExtension::kTransportSequenceNumber, kExtensionId)); |
} |
virtual test::DirectTransport* CreateSendTransport() { |
- observer_ = new RtpExtensionHeaderObserver(); |
+ observer_ = new RtpExtensionHeaderObserver(first_media_ssrc_, |
+ rtx_to_media_ssrcs_); |
return observer_; |
} |
private: |
+ uint32_t first_media_ssrc_; |
+ std::map<uint32_t, uint32_t> rtx_to_media_ssrcs_; |
RtpExtensionHeaderObserver* observer_; |
} tester; |