Use padding to achieve bitrate probing if the initial key frame has too few packets.
BUG=4350
R=mflodman@webrtc.org
Review URL: https://webrtc-codereview.appspot.com/44879004
Cr-Commit-Position: refs/heads/master@{#9134}
diff --git a/webrtc/modules/pacing/bitrate_prober.cc b/webrtc/modules/pacing/bitrate_prober.cc
index 5475ef3..1ed6298 100644
--- a/webrtc/modules/pacing/bitrate_prober.cc
+++ b/webrtc/modules/pacing/bitrate_prober.cc
@@ -11,6 +11,7 @@
#include "webrtc/modules/pacing/bitrate_prober.h"
#include <assert.h>
+#include <algorithm>
#include <limits>
#include <sstream>
@@ -107,7 +108,11 @@
time_until_probe_ms = 0;
}
}
- return time_until_probe_ms;
+ return std::max(time_until_probe_ms, 0);
+}
+
+size_t BitrateProber::RecommendedPacketSize() const {
+ return packet_size_last_send_;
}
void BitrateProber::PacketSent(int64_t now_ms, size_t packet_size) {
diff --git a/webrtc/modules/pacing/bitrate_prober.h b/webrtc/modules/pacing/bitrate_prober.h
index 04a8580..b3f52af 100644
--- a/webrtc/modules/pacing/bitrate_prober.h
+++ b/webrtc/modules/pacing/bitrate_prober.h
@@ -38,6 +38,10 @@
// get accurate probing.
int TimeUntilNextProbe(int64_t now_ms);
+ // Returns the number of bytes that the prober recommends for the next probe
+ // packet.
+ size_t RecommendedPacketSize() const;
+
// Called to report to the prober that a packet has been sent, which helps the
// prober know when to move to the next packet in a probe.
void PacketSent(int64_t now_ms, size_t packet_size);
diff --git a/webrtc/modules/pacing/include/paced_sender.h b/webrtc/modules/pacing/include/paced_sender.h
index 645999d..730d3b7 100644
--- a/webrtc/modules/pacing/include/paced_sender.h
+++ b/webrtc/modules/pacing/include/paced_sender.h
@@ -126,6 +126,9 @@
// Process any pending packets in the queue(s).
int32_t Process() override;
+ protected:
+ virtual bool ProbingExperimentIsEnabled() const;
+
private:
// Updates the number of bytes that can be sent for the next time interval.
void UpdateBytesPerInterval(int64_t delta_time_in_ms)
diff --git a/webrtc/modules/pacing/paced_sender.cc b/webrtc/modules/pacing/paced_sender.cc
index 6186f96..f251341 100644
--- a/webrtc/modules/pacing/paced_sender.cc
+++ b/webrtc/modules/pacing/paced_sender.cc
@@ -193,7 +193,9 @@
-500 * target_rate_kbps_ / 8);
}
- int bytes_remaining() const { return bytes_remaining_; }
+ size_t bytes_remaining() const {
+ return static_cast<size_t>(std::max(0, bytes_remaining_));
+ }
int target_rate_kbps() const { return target_rate_kbps_; }
@@ -334,7 +336,7 @@
UpdateBytesPerInterval(delta_time_ms);
}
while (!packets_->Empty()) {
- if (media_budget_->bytes_remaining() <= 0 && !prober_->IsProbing()) {
+ if (media_budget_->bytes_remaining() == 0 && !prober_->IsProbing()) {
return 0;
}
@@ -355,10 +357,14 @@
}
}
- int padding_needed = padding_budget_->bytes_remaining();
- if (padding_needed > 0) {
+ size_t padding_needed;
+ if (prober_->IsProbing() && ProbingExperimentIsEnabled())
+ padding_needed = prober_->RecommendedPacketSize();
+ else
+ padding_needed = padding_budget_->bytes_remaining();
+
+ if (padding_needed > 0)
SendPadding(static_cast<size_t>(padding_needed));
- }
}
return 0;
}
@@ -386,13 +392,20 @@
size_t bytes_sent = callback_->TimeToSendPadding(padding_needed);
critsect_->Enter();
- // Update padding bytes sent.
- media_budget_->UseBudget(bytes_sent);
- padding_budget_->UseBudget(bytes_sent);
+ if (bytes_sent > 0) {
+ prober_->PacketSent(clock_->TimeInMilliseconds(), bytes_sent);
+ media_budget_->UseBudget(bytes_sent);
+ padding_budget_->UseBudget(bytes_sent);
+ }
}
void PacedSender::UpdateBytesPerInterval(int64_t delta_time_ms) {
media_budget_->IncreaseBudget(delta_time_ms);
padding_budget_->IncreaseBudget(delta_time_ms);
}
+
+bool PacedSender::ProbingExperimentIsEnabled() const {
+ return webrtc::field_trial::FindFullName("WebRTC-BitrateProbing") ==
+ "Enabled";
+}
} // namespace webrtc
diff --git a/webrtc/modules/pacing/paced_sender_unittest.cc b/webrtc/modules/pacing/paced_sender_unittest.cc
index e82a49e..0730062 100644
--- a/webrtc/modules/pacing/paced_sender_unittest.cc
+++ b/webrtc/modules/pacing/paced_sender_unittest.cc
@@ -71,22 +71,26 @@
uint16_t sequence_number,
int64_t capture_time_ms,
bool retransmission) {
+ ExpectAndCountPacket();
+ return true;
+ }
+
+ size_t TimeToSendPadding(size_t bytes) {
+ ExpectAndCountPacket();
+ return bytes;
+ }
+
+ void ExpectAndCountPacket() {
++packets_sent_;
EXPECT_FALSE(expected_deltas_.empty());
if (expected_deltas_.empty())
- return false;
+ return;
int64_t now_ms = clock_->TimeInMilliseconds();
if (prev_packet_time_ms_ >= 0) {
EXPECT_EQ(expected_deltas_.front(), now_ms - prev_packet_time_ms_);
expected_deltas_.pop_front();
}
prev_packet_time_ms_ = now_ms;
- return true;
- }
-
- size_t TimeToSendPadding(size_t bytes) {
- EXPECT_TRUE(false);
- return bytes;
}
int packets_sent() const { return packets_sent_; }
@@ -714,8 +718,6 @@
std::list<int> expected_deltas_list(expected_deltas,
expected_deltas + kNumPackets - 1);
PacedSenderProbing callback(expected_deltas_list, &clock_);
- // Probing implicitly enabled by creating a new PacedSender which defaults to
- // probing on.
send_bucket_.reset(
new PacedSender(&clock_,
&callback,
@@ -741,6 +743,58 @@
}
}
+class ProbingPacedSender : public PacedSender {
+ public:
+ ProbingPacedSender(Clock* clock,
+ Callback* callback,
+ int bitrate_kbps,
+ int max_bitrate_kbps,
+ int min_bitrate_kbps)
+ : PacedSender(clock,
+ callback,
+ bitrate_kbps,
+ max_bitrate_kbps,
+ min_bitrate_kbps) {}
+
+ bool ProbingExperimentIsEnabled() const override { return true; }
+};
+
+TEST_F(PacedSenderTest, ProbingWithTooSmallInitialFrame) {
+ const int kNumPackets = 11;
+ const int kNumDeltas = kNumPackets - 1;
+ const size_t kPacketSize = 1200;
+ const int kInitialBitrateKbps = 300;
+ uint32_t ssrc = 12346;
+ uint16_t sequence_number = 1234;
+ const int expected_deltas[kNumDeltas] = {10, 10, 10, 10, 10, 5, 5, 5, 5, 5};
+ std::list<int> expected_deltas_list(expected_deltas,
+ expected_deltas + kNumPackets - 1);
+ PacedSenderProbing callback(expected_deltas_list, &clock_);
+ send_bucket_.reset(
+ new ProbingPacedSender(&clock_, &callback, kInitialBitrateKbps,
+ kPaceMultiplier * kInitialBitrateKbps, 0));
+
+ for (int i = 0; i < kNumPackets - 5; ++i) {
+ EXPECT_FALSE(send_bucket_->SendPacket(
+ PacedSender::kNormalPriority, ssrc, sequence_number++,
+ clock_.TimeInMilliseconds(), kPacketSize, false));
+ }
+ while (callback.packets_sent() < kNumPackets) {
+ int time_until_process = send_bucket_->TimeUntilNextProcess();
+ if (time_until_process <= 0) {
+ send_bucket_->Process();
+ } else {
+ clock_.AdvanceTimeMilliseconds(time_until_process);
+ }
+ }
+
+ // Process one more time and make sure we don't send any more probes.
+ int time_until_process = send_bucket_->TimeUntilNextProcess();
+ clock_.AdvanceTimeMilliseconds(time_until_process);
+ send_bucket_->Process();
+ EXPECT_EQ(kNumPackets, callback.packets_sent());
+}
+
TEST_F(PacedSenderTest, PriorityInversion) {
uint32_t ssrc = 12346;
uint16_t sequence_number = 1234;
diff --git a/webrtc/video/end_to_end_tests.cc b/webrtc/video/end_to_end_tests.cc
index c260567..1b4e990 100644
--- a/webrtc/video/end_to_end_tests.cc
+++ b/webrtc/video/end_to_end_tests.cc
@@ -403,7 +403,8 @@
if (sent_rtp_packets_ % kPacketsBetweenLossBursts == 0)
packets_left_to_drop_ = kLossBurstSize;
- if (packets_left_to_drop_ > 0) {
+ // Never drop padding packets as those won't be retransmitted.
+ if (packets_left_to_drop_ > 0 && header.paddingLength == 0) {
--packets_left_to_drop_;
dropped_packets_.insert(header.sequenceNumber);
return DROP_PACKET;
@@ -463,11 +464,15 @@
RTPHeader header;
EXPECT_TRUE(parser_->Parse(packet, length, &header));
- EXPECT_EQ(kRedPayloadType, header.payloadType);
- int encapsulated_payload_type =
- static_cast<int>(packet[header.headerLength]);
- if (encapsulated_payload_type != kFakeSendPayloadType)
- EXPECT_EQ(kUlpfecPayloadType, encapsulated_payload_type);
+ int encapsulated_payload_type = -1;
+ if (header.payloadType == kRedPayloadType) {
+ encapsulated_payload_type =
+ static_cast<int>(packet[header.headerLength]);
+ if (encapsulated_payload_type != kFakeSendPayloadType)
+ EXPECT_EQ(kUlpfecPayloadType, encapsulated_payload_type);
+ } else {
+ EXPECT_EQ(kFakeSendPayloadType, header.payloadType);
+ }
if (protected_sequence_numbers_.count(header.sequenceNumber) != 0) {
// Retransmitted packet, should not count.
@@ -571,12 +576,16 @@
Action OnSendRtp(const uint8_t* packet, size_t length) override {
RTPHeader header;
EXPECT_TRUE(parser_->Parse(packet, length, &header));
- EXPECT_EQ(kRedPayloadType, header.payloadType);
- int encapsulated_payload_type =
- static_cast<int>(packet[header.headerLength]);
- if (encapsulated_payload_type != kFakeSendPayloadType)
- EXPECT_EQ(kUlpfecPayloadType, encapsulated_payload_type);
+ int encapsulated_payload_type = -1;
+ if (header.payloadType == kRedPayloadType) {
+ encapsulated_payload_type =
+ static_cast<int>(packet[header.headerLength]);
+ if (encapsulated_payload_type != kFakeSendPayloadType)
+ EXPECT_EQ(kUlpfecPayloadType, encapsulated_payload_type);
+ } else {
+ EXPECT_EQ(kFakeSendPayloadType, header.payloadType);
+ }
if (has_last_sequence_number_ &&
!IsNewerSequenceNumber(header.sequenceNumber,
diff --git a/webrtc/video/video_send_stream_tests.cc b/webrtc/video/video_send_stream_tests.cc
index eb79e1b..547c811 100644
--- a/webrtc/video/video_send_stream_tests.cc
+++ b/webrtc/video/video_send_stream_tests.cc
@@ -306,14 +306,22 @@
EXPECT_EQ(0, rtcp_sender.SendRTCP(feedback_state, kRtcpRr));
}
- EXPECT_EQ(kRedPayloadType, header.payloadType);
-
- uint8_t encapsulated_payload_type = packet[header.headerLength];
-
- if (encapsulated_payload_type == kUlpfecPayloadType) {
- received_fec_ = true;
+ int encapsulated_payload_type = -1;
+ if (header.payloadType == kRedPayloadType) {
+ encapsulated_payload_type =
+ static_cast<int>(packet[header.headerLength]);
+ if (encapsulated_payload_type != kFakeSendPayloadType)
+ EXPECT_EQ(kUlpfecPayloadType, encapsulated_payload_type);
} else {
- received_media_ = true;
+ EXPECT_EQ(kFakeSendPayloadType, header.payloadType);
+ }
+
+ if (encapsulated_payload_type != -1) {
+ if (encapsulated_payload_type == kUlpfecPayloadType) {
+ received_fec_ = true;
+ } else {
+ received_media_ = true;
+ }
}
if (received_media_ && received_fec_)