Refactor NACK bitrate allocation

Nack bitrate allocation should not be done on a per-rtp-module basis,
but rather shared bitrate pool per call. This CL moves allocation to the
pacer and cleans up a bunch if bitrate stats handling.

BUG=
R=danilchap@webrtc.org, stefan@webrtc.org, tommi@webrtc.org

Review URL: https://codereview.webrtc.org/2061423003 .

Cr-Commit-Position: refs/heads/master@{#13416}
This commit is contained in:
Erik Språng 2016-07-08 18:15:14 +02:00
parent 108ecec51c
commit 5fc59e810b
40 changed files with 603 additions and 620 deletions

View File

@ -442,6 +442,7 @@ if (rtc_include_tests) {
"base/proxy_unittest.cc",
"base/proxydetect_unittest.cc",
"base/random_unittest.cc",
"base/rate_limiter_unittest.cc",
"base/rate_statistics_unittest.cc",
"base/ratelimiter_unittest.cc",
"base/ratetracker_unittest.cc",

View File

@ -141,6 +141,8 @@ static_library("rtc_base_approved") {
"race_checker.h",
"random.cc",
"random.h",
"rate_limiter.cc",
"rate_limiter.h",
"rate_statistics.cc",
"rate_statistics.h",
"ratetracker.cc",

View File

@ -74,6 +74,8 @@
'random.h',
'rate_statistics.cc',
'rate_statistics.h',
'rate_limiter.cc',
'rate_limiter.h',
'ratetracker.cc',
'ratetracker.h',
'refcount.h',

View File

@ -0,0 +1,65 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/base/rate_limiter.h"
#include "webrtc/system_wrappers/include/clock.h"
namespace webrtc {
RateLimiter::RateLimiter(Clock* clock, int64_t max_window_ms)
: clock_(clock),
current_rate_(max_window_ms, RateStatistics::kBpsScale),
window_size_ms_(max_window_ms),
max_rate_bps_(std::numeric_limits<uint32_t>::max()) {}
RateLimiter::~RateLimiter() {}
// Usage note: This class is intended be usable in a scenario where different
// threads may call each of the the different method. For instance, a network
// thread trying to send data calling TryUseRate(), the bandwidth estimator
// calling SetMaxRate() and a timed maintenance thread periodically updating
// the RTT.
bool RateLimiter::TryUseRate(size_t packet_size_bytes) {
rtc::CritScope cs(&lock_);
int64_t now_ms = clock_->TimeInMilliseconds();
rtc::Optional<uint32_t> current_rate = current_rate_.Rate(now_ms);
if (current_rate) {
// If there is a current rate, check if adding bytes would cause maximum
// bitrate target to be exceeded. If there is NOT a valid current rate,
// allow allocating rate even if target is exceeded. This prevents
// problems
// at very low rates, where for instance retransmissions would never be
// allowed due to too high bitrate caused by a single packet.
size_t bitrate_addition_bps =
(packet_size_bytes * 8 * 1000) / window_size_ms_;
if (*current_rate + bitrate_addition_bps > max_rate_bps_)
return false;
}
current_rate_.Update(packet_size_bytes, now_ms);
return true;
}
void RateLimiter::SetMaxRate(uint32_t max_rate_bps) {
rtc::CritScope cs(&lock_);
max_rate_bps_ = max_rate_bps;
}
// Set the window size over which to measure the current bitrate.
// For retransmissions, this is typically the RTT.
bool RateLimiter::SetWindowSize(int64_t window_size_ms) {
rtc::CritScope cs(&lock_);
window_size_ms_ = window_size_ms;
return current_rate_.SetWindowSize(window_size_ms,
clock_->TimeInMilliseconds());
}
} // namespace webrtc

View File

@ -0,0 +1,56 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef WEBRTC_BASE_RATE_LIMITER_H_
#define WEBRTC_BASE_RATE_LIMITER_H_
#include <limits>
#include "webrtc/base/constructormagic.h"
#include "webrtc/base/criticalsection.h"
#include "webrtc/base/rate_statistics.h"
namespace webrtc {
class Clock;
// Class used to limit a bitrate, making sure the average does not exceed a
// maximum as measured over a sliding window. This class is thread safe; all
// methods will acquire (the same) lock befeore executing.
class RateLimiter {
public:
RateLimiter(Clock* clock, int64_t max_window_ms);
~RateLimiter();
// Try to use rate to send bytes. Returns true on success and if so updates
// current rate.
bool TryUseRate(size_t packet_size_bytes);
// Set the maximum bitrate, in bps, that this limiter allows to send.
void SetMaxRate(uint32_t max_rate_bps);
// Set the window size over which to measure the current bitrate.
// For example, irt retransmissions, this is typically the RTT.
// Returns true on success and false if window_size_ms is out of range.
bool SetWindowSize(int64_t window_size_ms);
private:
Clock* const clock_;
rtc::CriticalSection lock_;
RateStatistics current_rate_ GUARDED_BY(lock_);
int64_t window_size_ms_ GUARDED_BY(lock_);
uint32_t max_rate_bps_ GUARDED_BY(lock_);
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RateLimiter);
};
} // namespace webrtc
#endif // WEBRTC_BASE_RATE_LIMITER_H_

View File

@ -0,0 +1,205 @@
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include <algorithm>
#include <memory>
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/base/event.h"
#include "webrtc/base/platform_thread.h"
#include "webrtc/base/rate_limiter.h"
#include "webrtc/base/task_queue.h"
#include "webrtc/system_wrappers/include/clock.h"
namespace webrtc {
class RateLimitTest : public ::testing::Test {
public:
RateLimitTest()
: clock_(0), rate_limiter(new RateLimiter(&clock_, kWindowSizeMs)) {}
virtual ~RateLimitTest() {}
void SetUp() override { rate_limiter->SetMaxRate(kMaxRateBps); }
protected:
static constexpr int64_t kWindowSizeMs = 1000;
static constexpr uint32_t kMaxRateBps = 100000;
// Bytes needed to completely saturate the rate limiter.
static constexpr size_t kRateFillingBytes =
(kMaxRateBps * kWindowSizeMs) / (8 * 1000);
SimulatedClock clock_;
std::unique_ptr<RateLimiter> rate_limiter;
};
TEST_F(RateLimitTest, IncreasingMaxRate) {
// Fill rate, extend window to full size.
EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
clock_.AdvanceTimeMilliseconds(kWindowSizeMs - 1);
EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
// All rate consumed.
EXPECT_FALSE(rate_limiter->TryUseRate(1));
// Double the available rate and fill that too.
rate_limiter->SetMaxRate(kMaxRateBps * 2);
EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes));
// All rate consumed again.
EXPECT_FALSE(rate_limiter->TryUseRate(1));
}
TEST_F(RateLimitTest, DecreasingMaxRate) {
// Fill rate, extend window to full size.
EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
clock_.AdvanceTimeMilliseconds(kWindowSizeMs - 1);
EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
// All rate consumed.
EXPECT_FALSE(rate_limiter->TryUseRate(1));
// Halve the available rate and move window so half of the data falls out.
rate_limiter->SetMaxRate(kMaxRateBps / 2);
clock_.AdvanceTimeMilliseconds(1);
// All rate still consumed.
EXPECT_FALSE(rate_limiter->TryUseRate(1));
}
TEST_F(RateLimitTest, ChangingWindowSize) {
// Fill rate, extend window to full size.
EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
clock_.AdvanceTimeMilliseconds(kWindowSizeMs - 1);
EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
// All rate consumed.
EXPECT_FALSE(rate_limiter->TryUseRate(1));
// Decrease window size so half of the data falls out.
rate_limiter->SetWindowSize(kWindowSizeMs / 2);
// Average rate should still be the same, so rate is still all consumed.
EXPECT_FALSE(rate_limiter->TryUseRate(1));
// Increase window size again. Now the rate is only half used (removed data
// points don't come back to life).
rate_limiter->SetWindowSize(kWindowSizeMs);
EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes / 2));
// All rate consumed again.
EXPECT_FALSE(rate_limiter->TryUseRate(1));
}
TEST_F(RateLimitTest, SingleUsageAlwaysOk) {
// Using more bytes than can fit in a window is OK for a single packet.
EXPECT_TRUE(rate_limiter->TryUseRate(kRateFillingBytes + 1));
}
TEST_F(RateLimitTest, WindowSizeLimits) {
EXPECT_TRUE(rate_limiter->SetWindowSize(1));
EXPECT_FALSE(rate_limiter->SetWindowSize(0));
EXPECT_TRUE(rate_limiter->SetWindowSize(kWindowSizeMs));
EXPECT_FALSE(rate_limiter->SetWindowSize(kWindowSizeMs + 1));
}
static const int64_t kMaxTimeoutMs = 30000;
class ThreadTask {
public:
explicit ThreadTask(RateLimiter* rate_limiter)
: rate_limiter_(rate_limiter),
start_signal_(false, false),
end_signal_(false, false) {}
virtual ~ThreadTask() {}
void Run() {
start_signal_.Wait(kMaxTimeoutMs);
DoRun();
end_signal_.Set();
}
virtual void DoRun() = 0;
RateLimiter* const rate_limiter_;
rtc::Event start_signal_;
rtc::Event end_signal_;
};
bool RunTask(void* thread_task) {
reinterpret_cast<ThreadTask*>(thread_task)->Run();
return false;
}
TEST_F(RateLimitTest, MultiThreadedUsage) {
// Simple sanity test, with different threads calling the various methods.
// Runs a few simple tasks, each on its own thread, but coordinated with
// events so that they run in a serialized order. Intended to catch data
// races when run with tsan et al.
// Half window size, double rate -> same amount of bytes needed to fill rate.
class SetWindowSizeTask : public ThreadTask {
public:
explicit SetWindowSizeTask(RateLimiter* rate_limiter)
: ThreadTask(rate_limiter) {}
virtual ~SetWindowSizeTask() {}
void DoRun() override {
EXPECT_TRUE(rate_limiter_->SetWindowSize(kWindowSizeMs / 2));
}
} set_window_size_task(rate_limiter.get());
rtc::PlatformThread thread1(RunTask, &set_window_size_task, "Thread1");
thread1.Start();
class SetMaxRateTask : public ThreadTask {
public:
explicit SetMaxRateTask(RateLimiter* rate_limiter)
: ThreadTask(rate_limiter) {}
virtual ~SetMaxRateTask() {}
void DoRun() override { rate_limiter_->SetMaxRate(kMaxRateBps * 2); }
} set_max_rate_task(rate_limiter.get());
rtc::PlatformThread thread2(RunTask, &set_max_rate_task, "Thread2");
thread2.Start();
class UseRateTask : public ThreadTask {
public:
UseRateTask(RateLimiter* rate_limiter, SimulatedClock* clock)
: ThreadTask(rate_limiter), clock_(clock) {}
virtual ~UseRateTask() {}
void DoRun() override {
EXPECT_TRUE(rate_limiter_->TryUseRate(kRateFillingBytes / 2));
clock_->AdvanceTimeMilliseconds((kWindowSizeMs / 2) - 1);
EXPECT_TRUE(rate_limiter_->TryUseRate(kRateFillingBytes / 2));
}
SimulatedClock* const clock_;
} use_rate_task(rate_limiter.get(), &clock_);
rtc::PlatformThread thread3(RunTask, &use_rate_task, "Thread3");
thread3.Start();
set_window_size_task.start_signal_.Set();
EXPECT_TRUE(set_window_size_task.end_signal_.Wait(kMaxTimeoutMs));
set_max_rate_task.start_signal_.Set();
EXPECT_TRUE(set_max_rate_task.end_signal_.Wait(kMaxTimeoutMs));
use_rate_task.start_signal_.Set();
EXPECT_TRUE(use_rate_task.end_signal_.Wait(kMaxTimeoutMs));
// All rate consumed.
EXPECT_FALSE(rate_limiter->TryUseRate(1));
thread1.Stop();
thread2.Stop();
thread3.Stop();
}
} // namespace webrtc

View File

@ -61,8 +61,10 @@ void RateStatistics::Update(size_t count, int64_t now_ms) {
++num_samples_;
}
rtc::Optional<uint32_t> RateStatistics::Rate(int64_t now_ms) {
EraseOld(now_ms);
rtc::Optional<uint32_t> RateStatistics::Rate(int64_t now_ms) const {
// Yeah, this const_cast ain't pretty, but the alternative is to declare most
// of the members as mutable...
const_cast<RateStatistics*>(this)->EraseOld(now_ms);
// If window is a single bucket or there is only one sample in a data set that
// has not grown to the full window size, treat this as rate unavailable.
@ -112,7 +114,7 @@ bool RateStatistics::SetWindowSize(int64_t window_size_ms, int64_t now_ms) {
return true;
}
bool RateStatistics::IsInitialized() {
bool RateStatistics::IsInitialized() const {
return oldest_time_ != -max_window_size_ms_;
}

View File

@ -20,22 +20,37 @@ namespace webrtc {
class RateStatistics {
public:
static constexpr float kBpsScale = 8000.0f;
// max_window_size_ms = Maximum window size in ms for the rate estimation.
// Initial window size is set to this, but may be changed
// to something lower by calling SetWindowSize().
// scale = coefficient to convert counts/ms to desired units,
// ex: if counts represents bytes, use 8*1000 to go to bits/s
// scale = coefficient to convert counts/ms to desired unit
// ex: kBpsScale (8000) for bits/s if count represents bytes.
RateStatistics(int64_t max_window_size_ms, float scale);
~RateStatistics();
// Reset instance to original state.
void Reset();
// Update rate with a new data point, moving averaging window as needed.
void Update(size_t count, int64_t now_ms);
rtc::Optional<uint32_t> Rate(int64_t now_ms);
// Note that despite this being a const method, it still updates the internal
// state (moves averaging window), but it doesn't make any alterations that
// are observable from the other methods, as long as supplied timestamps are
// from a monotonic clock. Ie, it doesn't matter if this call moves the
// window, since any subsequent call to Update or Rate would still have moved
// the window as much or more.
rtc::Optional<uint32_t> Rate(int64_t now_ms) const;
// Update the size of the averaging window. The maximum allowed value for
// window_size_ms is max_window_size_ms as supplied in the constructor.
bool SetWindowSize(int64_t window_size_ms, int64_t now_ms);
private:
void EraseOld(int64_t now_ms);
bool IsInitialized();
bool IsInitialized() const;
// Counters are kept in buckets (circular buffer), with one bucket
// per millisecond.

View File

@ -122,7 +122,8 @@ size_t GenerateRtpPacket(uint32_t extensions_bitvector,
nullptr, // FrameCountObserver*
nullptr, // SendSideDelayObserver*
nullptr, // RtcEventLog*
nullptr); // SendPacketObserver*
nullptr, // SendPacketObserver*
nullptr); // NackRateLimiter*
std::vector<uint32_t> csrcs;
for (unsigned i = 0; i < csrcs_count; i++) {

View File

@ -252,11 +252,10 @@ class RtcpPacketTypeCounterObserver {
// Rate statistics for a stream.
struct BitrateStatistics {
BitrateStatistics() : bitrate_bps(0), packet_rate(0), timestamp_ms(0) {}
BitrateStatistics() : bitrate_bps(0), packet_rate(0) {}
uint32_t bitrate_bps; // Bitrate in bits per second.
uint32_t packet_rate; // Packet rate in packets per second.
uint64_t timestamp_ms; // Ntp timestamp in ms at time of rate estimation.
};
// Callback, used to notify an observer whenever new rates have been estimated.
@ -264,8 +263,8 @@ class BitrateStatisticsObserver {
public:
virtual ~BitrateStatisticsObserver() {}
virtual void Notify(const BitrateStatistics& total_stats,
const BitrateStatistics& retransmit_stats,
virtual void Notify(uint32_t total_bitrate_bps,
uint32_t retransmit_bitrate_bps,
uint32_t ssrc) = 0;
};

View File

@ -17,6 +17,7 @@
#include "webrtc/base/checks.h"
#include "webrtc/base/constructormagic.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/rate_limiter.h"
#include "webrtc/base/socket.h"
#include "webrtc/base/thread_annotations.h"
#include "webrtc/modules/bitrate_controller/include/bitrate_controller.h"
@ -32,6 +33,8 @@ namespace webrtc {
namespace {
static const uint32_t kTimeOffsetSwitchThreshold = 30;
static const int64_t kMinRetransmitWindowSizeMs = 30;
static const int64_t kMaxRetransmitWindowSizeMs = 1000;
// Makes sure that the bitrate and the min, max values are in valid range.
static void ClampBitrates(int* bitrate_bps,
@ -164,6 +167,8 @@ CongestionController::CongestionController(
new WrappingBitrateEstimator(remote_bitrate_observer, clock_)),
bitrate_controller_(
BitrateController::CreateBitrateController(clock_, event_log)),
retransmission_rate_limiter_(
new RateLimiter(clock, kMaxRetransmitWindowSizeMs)),
remote_estimator_proxy_(clock_, packet_router_.get()),
transport_feedback_adapter_(bitrate_controller_.get(), clock_),
min_bitrate_bps_(RemoteBitrateEstimator::kDefaultMinBitrateBps),
@ -191,6 +196,8 @@ CongestionController::CongestionController(
// construction.
bitrate_controller_(
BitrateController::CreateBitrateController(clock_, event_log)),
retransmission_rate_limiter_(
new RateLimiter(clock, kMaxRetransmitWindowSizeMs)),
remote_estimator_proxy_(clock_, packet_router_.get()),
transport_feedback_adapter_(bitrate_controller_.get(), clock_),
min_bitrate_bps_(RemoteBitrateEstimator::kDefaultMinBitrateBps),
@ -266,6 +273,10 @@ CongestionController::GetTransportFeedbackObserver() {
return &transport_feedback_adapter_;
}
RateLimiter* CongestionController::GetRetransmissionRateLimiter() {
return retransmission_rate_limiter_.get();
}
void CongestionController::SetAllocatedSendBitrateLimits(
int min_send_bitrate_bps,
int max_padding_bitrate_bps) {
@ -299,6 +310,14 @@ void CongestionController::OnSentPacket(const rtc::SentPacket& sent_packet) {
void CongestionController::OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) {
remote_bitrate_estimator_->OnRttUpdate(avg_rtt_ms, max_rtt_ms);
transport_feedback_adapter_.OnRttUpdate(avg_rtt_ms, max_rtt_ms);
int64_t nack_window_size_ms = max_rtt_ms;
if (nack_window_size_ms > kMaxRetransmitWindowSizeMs) {
nack_window_size_ms = kMaxRetransmitWindowSizeMs;
} else if (nack_window_size_ms < kMinRetransmitWindowSizeMs) {
nack_window_size_ms = kMinRetransmitWindowSizeMs;
}
retransmission_rate_limiter_->SetWindowSize(nack_window_size_ms);
}
int64_t CongestionController::TimeUntilNextProcess() {
@ -323,8 +342,10 @@ void CongestionController::MaybeTriggerOnNetworkChanged() {
int64_t rtt;
bool estimate_changed = bitrate_controller_->GetNetworkParameters(
&bitrate_bps, &fraction_loss, &rtt);
if (estimate_changed)
if (estimate_changed) {
pacer_->SetEstimatedBitrate(bitrate_bps);
retransmission_rate_limiter_->SetMaxRate(bitrate_bps);
}
bitrate_bps = IsNetworkDown() || IsSendQueueFull() ? 0 : bitrate_bps;

View File

@ -31,6 +31,7 @@ namespace webrtc {
class BitrateController;
class Clock;
class ProcessThread;
class RateLimiter;
class RemoteBitrateEstimator;
class RemoteBitrateObserver;
class RtcEventLog;
@ -80,6 +81,7 @@ class CongestionController : public CallStatsObserver, public Module {
virtual PacedSender* pacer() { return pacer_.get(); }
virtual PacketRouter* packet_router() { return packet_router_.get(); }
virtual TransportFeedbackObserver* GetTransportFeedbackObserver();
RateLimiter* GetRetransmissionRateLimiter();
// SetAllocatedSendBitrateLimits sets bitrates limits imposed by send codec
// settings.
@ -118,6 +120,7 @@ class CongestionController : public CallStatsObserver, public Module {
const std::unique_ptr<PacedSender> pacer_;
const std::unique_ptr<RemoteBitrateEstimator> remote_bitrate_estimator_;
const std::unique_ptr<BitrateController> bitrate_controller_;
const std::unique_ptr<RateLimiter> retransmission_rate_limiter_;
RemoteEstimatorProxy remote_estimator_proxy_;
TransportFeedbackAdapter transport_feedback_adapter_;
int min_bitrate_bps_;

View File

@ -19,8 +19,6 @@ source_set("rtp_rtcp") {
"include/rtp_rtcp.h",
"include/rtp_rtcp_defines.h",
"mocks/mock_rtp_rtcp.h",
"source/bitrate.cc",
"source/bitrate.h",
"source/byte_io.h",
"source/dtmf_queue.cc",
"source/dtmf_queue.h",

View File

@ -46,7 +46,7 @@ class StreamStatistician {
typedef std::map<uint32_t, StreamStatistician*> StatisticianMap;
class ReceiveStatistics : public Module {
class ReceiveStatistics {
public:
virtual ~ReceiveStatistics() {}
@ -89,8 +89,6 @@ class NullReceiveStatistics : public ReceiveStatistics {
size_t packet_length) override;
StatisticianMap GetActiveStatisticians() const override;
StreamStatistician* GetStatistician(uint32_t ssrc) const override;
int64_t TimeUntilNextProcess() override;
void Process() override;
void SetMaxReorderingThreshold(int max_reordering_threshold) override;
void RegisterRtcpStatisticsCallback(
RtcpStatisticsCallback* callback) override;

View File

@ -23,11 +23,12 @@
namespace webrtc {
// Forward declarations.
class RateLimiter;
class ReceiveStatistics;
class RemoteBitrateEstimator;
class RtcEventLog;
class RtpReceiver;
class Transport;
class RtcEventLog;
RTPExtensionType StringToRtpExtensionType(const std::string& extension);
@ -79,6 +80,7 @@ class RtpRtcp : public Module {
SendSideDelayObserver* send_side_delay_observer;
RtcEventLog* event_log;
SendPacketObserver* send_packet_observer;
RateLimiter* retransmission_rate_limiter;
RTC_DISALLOW_COPY_AND_ASSIGN(Configuration);
};
@ -615,11 +617,6 @@ class RtpRtcp : public Module {
*
***************************************************************************/
/*
* Set the target send bitrate
*/
virtual void SetTargetSendBitrate(uint32_t bitrate_bps) = 0;
/*
* Turn on/off generic FEC
*/

View File

@ -26,8 +26,6 @@
'include/rtp_receiver.h',
'include/rtp_rtcp.h',
'include/rtp_rtcp_defines.h',
'source/bitrate.cc',
'source/bitrate.h',
'source/byte_io.h',
'source/fec_receiver_impl.cc',
'source/fec_receiver_impl.h',

View File

@ -1,121 +0,0 @@
/*
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/modules/rtp_rtcp/source/bitrate.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
namespace webrtc {
Bitrate::Bitrate(Clock* clock, Observer* observer)
: clock_(clock),
packet_rate_(0),
bitrate_(0),
bitrate_next_idx_(0),
time_last_rate_update_(0),
bytes_count_(0),
packet_count_(0),
observer_(observer) {
memset(packet_rate_array_, 0, sizeof(packet_rate_array_));
memset(bitrate_diff_ms_, 0, sizeof(bitrate_diff_ms_));
memset(bitrate_array_, 0, sizeof(bitrate_array_));
}
Bitrate::~Bitrate() {}
void Bitrate::Update(const size_t bytes) {
rtc::CritScope cs(&crit_);
bytes_count_ += bytes;
packet_count_++;
}
uint32_t Bitrate::PacketRate() const {
rtc::CritScope cs(&crit_);
return packet_rate_;
}
uint32_t Bitrate::BitrateLast() const {
rtc::CritScope cs(&crit_);
return bitrate_;
}
uint32_t Bitrate::BitrateNow() const {
rtc::CritScope cs(&crit_);
int64_t now = clock_->TimeInMilliseconds();
int64_t diff_ms = now - time_last_rate_update_;
if (diff_ms > 10000) { // 10 seconds.
// Too high difference, ignore.
return bitrate_;
}
int64_t bits_since_last_rate_update = 8 * bytes_count_ * 1000;
// We have to consider the time when the measurement was done:
// ((bits/sec * sec) + (bits)) / sec.
int64_t bitrate = (static_cast<uint64_t>(bitrate_) * 1000 +
bits_since_last_rate_update) / (1000 + diff_ms);
return static_cast<uint32_t>(bitrate);
}
int64_t Bitrate::time_last_rate_update() const {
rtc::CritScope cs(&crit_);
return time_last_rate_update_;
}
// Triggered by timer.
void Bitrate::Process() {
BitrateStatistics stats;
{
rtc::CritScope cs(&crit_);
int64_t now = clock_->CurrentNtpInMilliseconds();
int64_t diff_ms = now - time_last_rate_update_;
if (diff_ms < 100) {
// Not enough data, wait...
return;
}
if (diff_ms > 10000) { // 10 seconds.
// Too high difference, ignore.
time_last_rate_update_ = now;
bytes_count_ = 0;
packet_count_ = 0;
return;
}
packet_rate_array_[bitrate_next_idx_] = (packet_count_ * 1000) / diff_ms;
bitrate_array_[bitrate_next_idx_] = 8 * ((bytes_count_ * 1000) / diff_ms);
bitrate_diff_ms_[bitrate_next_idx_] = diff_ms;
bitrate_next_idx_++;
if (bitrate_next_idx_ >= 10) {
bitrate_next_idx_ = 0;
}
int64_t sum_diffMS = 0;
int64_t sum_bitrateMS = 0;
int64_t sum_packetrateMS = 0;
for (int i = 0; i < 10; i++) {
sum_diffMS += bitrate_diff_ms_[i];
sum_bitrateMS += bitrate_array_[i] * bitrate_diff_ms_[i];
sum_packetrateMS += packet_rate_array_[i] * bitrate_diff_ms_[i];
}
time_last_rate_update_ = now;
bytes_count_ = 0;
packet_count_ = 0;
packet_rate_ = static_cast<uint32_t>(sum_packetrateMS / sum_diffMS);
bitrate_ = static_cast<uint32_t>(sum_bitrateMS / sum_diffMS);
stats.bitrate_bps = bitrate_;
stats.packet_rate = packet_rate_;
stats.timestamp_ms = now;
}
if (observer_)
observer_->BitrateUpdated(stats);
}
} // namespace webrtc

View File

@ -1,77 +0,0 @@
/*
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_BITRATE_H_
#define WEBRTC_MODULES_RTP_RTCP_SOURCE_BITRATE_H_
#include <stdio.h>
#include <list>
#include "webrtc/base/criticalsection.h"
#include "webrtc/common_types.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_rtcp_config.h"
#include "webrtc/typedefs.h"
namespace webrtc {
class Clock;
class Bitrate {
public:
class Observer;
Bitrate(Clock* clock, Observer* observer);
virtual ~Bitrate();
// Calculates rates.
void Process();
// Update with a packet.
void Update(const size_t bytes);
// Packet rate last second, updated roughly every 100 ms.
uint32_t PacketRate() const;
// Bitrate last second, updated roughly every 100 ms.
uint32_t BitrateLast() const;
// Bitrate last second, updated now.
uint32_t BitrateNow() const;
int64_t time_last_rate_update() const;
class Observer {
public:
Observer() {}
virtual ~Observer() {}
virtual void BitrateUpdated(const BitrateStatistics& stats) = 0;
};
protected:
Clock* clock_;
private:
rtc::CriticalSection crit_;
uint32_t packet_rate_;
uint32_t bitrate_;
uint8_t bitrate_next_idx_;
int64_t packet_rate_array_[10];
int64_t bitrate_array_[10];
int64_t bitrate_diff_ms_[10];
int64_t time_last_rate_update_;
size_t bytes_count_;
uint32_t packet_count_;
Observer* const observer_;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_BITRATE_H_

View File

@ -15,6 +15,7 @@
#include <set>
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/base/rate_limiter.h"
#include "webrtc/common_types.h"
#include "webrtc/modules/rtp_rtcp/include/receive_statistics.h"
#include "webrtc/modules/rtp_rtcp/include/rtp_header_parser.h"
@ -34,6 +35,7 @@ const int kTestNumberOfRtxPackets = 149;
const int kNumFrames = 30;
const int kPayloadType = 123;
const int kRtxPayloadType = 98;
const int64_t kMaxRttMs = 1000;
class VerifyingRtxReceiver : public NullRtpData {
public:
@ -168,11 +170,12 @@ class RtpRtcpRtxNackTest : public ::testing::Test {
protected:
RtpRtcpRtxNackTest()
: rtp_payload_registry_(RTPPayloadStrategy::CreateStrategy(false)),
rtp_rtcp_module_(NULL),
rtp_rtcp_module_(nullptr),
transport_(kTestSsrc + 1),
receiver_(),
payload_data_length(sizeof(payload_data)),
fake_clock(123456) {}
fake_clock(123456),
retranmission_rate_limiter_(&fake_clock, kMaxRttMs) {}
~RtpRtcpRtxNackTest() {}
void SetUp() override {
@ -182,6 +185,7 @@ class RtpRtcpRtxNackTest : public ::testing::Test {
receive_statistics_.reset(ReceiveStatistics::Create(&fake_clock));
configuration.receive_statistics = receive_statistics_.get();
configuration.outgoing_transport = &transport_;
configuration.retransmission_rate_limiter = &retranmission_rate_limiter_;
rtp_rtcp_module_ = RtpRtcp::CreateRtpRtcp(configuration);
rtp_feedback_.reset(new TestRtpFeedback(rtp_rtcp_module_));
@ -288,6 +292,7 @@ class RtpRtcpRtxNackTest : public ::testing::Test {
uint8_t payload_data[65000];
size_t payload_data_length;
SimulatedClock fake_clock;
RateLimiter retranmission_rate_limiter_;
};
TEST_F(RtpRtcpRtxNackTest, LongNackList) {

View File

@ -14,7 +14,7 @@
#include <cstdlib>
#include "webrtc/modules/rtp_rtcp/source/bitrate.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_rtcp_config.h"
#include "webrtc/modules/rtp_rtcp/source/time_util.h"
namespace webrtc {
@ -29,7 +29,8 @@ StreamStatisticianImpl::StreamStatisticianImpl(
RtcpStatisticsCallback* rtcp_callback,
StreamDataCountersCallback* rtp_callback)
: clock_(clock),
incoming_bitrate_(clock, NULL),
incoming_bitrate_(kStatisticsProcessIntervalMs,
RateStatistics::kBpsScale),
ssrc_(0),
max_reordering_threshold_(kDefaultMaxReorderingThreshold),
jitter_q4_(0),
@ -61,7 +62,7 @@ void StreamStatisticianImpl::UpdateCounters(const RTPHeader& header,
rtc::CritScope cs(&stream_lock_);
bool in_order = InOrderPacketInternal(header.sequenceNumber);
ssrc_ = header.ssrc;
incoming_bitrate_.Update(packet_length);
incoming_bitrate_.Update(packet_length, clock_->TimeInMilliseconds());
receive_counters_.transmitted.AddPacket(packet_length, header);
if (!in_order && retransmitted) {
receive_counters_.retransmitted.AddPacket(packet_length, header);
@ -300,12 +301,7 @@ void StreamStatisticianImpl::GetReceiveStreamDataCounters(
uint32_t StreamStatisticianImpl::BitrateReceived() const {
rtc::CritScope cs(&stream_lock_);
return incoming_bitrate_.BitrateNow();
}
void StreamStatisticianImpl::ProcessBitrate() {
rtc::CritScope cs(&stream_lock_);
incoming_bitrate_.Process();
return incoming_bitrate_.Rate(clock_->TimeInMilliseconds()).value_or(0);
}
void StreamStatisticianImpl::LastReceiveTimeNtp(uint32_t* secs,
@ -376,7 +372,6 @@ ReceiveStatistics* ReceiveStatistics::Create(Clock* clock) {
ReceiveStatisticsImpl::ReceiveStatisticsImpl(Clock* clock)
: clock_(clock),
last_rate_update_ms_(0),
rtcp_stats_callback_(NULL),
rtp_stats_callback_(NULL) {}
@ -452,23 +447,6 @@ void ReceiveStatisticsImpl::SetMaxReorderingThreshold(
}
}
void ReceiveStatisticsImpl::Process() {
rtc::CritScope cs(&receive_statistics_lock_);
for (StatisticianImplMap::iterator it = statisticians_.begin();
it != statisticians_.end(); ++it) {
it->second->ProcessBitrate();
}
last_rate_update_ms_ = clock_->TimeInMilliseconds();
}
int64_t ReceiveStatisticsImpl::TimeUntilNextProcess() {
rtc::CritScope cs(&receive_statistics_lock_);
int64_t time_since_last_update = clock_->TimeInMilliseconds() -
last_rate_update_ms_;
return std::max<int64_t>(
kStatisticsProcessIntervalMs - time_since_last_update, 0);
}
void ReceiveStatisticsImpl::RegisterRtcpStatisticsCallback(
RtcpStatisticsCallback* callback) {
rtc::CritScope cs(&receive_statistics_lock_);
@ -525,10 +503,6 @@ StreamStatistician* NullReceiveStatistics::GetStatistician(
void NullReceiveStatistics::SetMaxReorderingThreshold(
int max_reordering_threshold) {}
int64_t NullReceiveStatistics::TimeUntilNextProcess() { return 0; }
void NullReceiveStatistics::Process() {}
void NullReceiveStatistics::RegisterRtcpStatisticsCallback(
RtcpStatisticsCallback* callback) {}

View File

@ -17,7 +17,7 @@
#include <map>
#include "webrtc/base/criticalsection.h"
#include "webrtc/modules/rtp_rtcp/source/bitrate.h"
#include "webrtc/base/rate_statistics.h"
#include "webrtc/system_wrappers/include/ntp_time.h"
namespace webrtc {
@ -44,7 +44,6 @@ class StreamStatisticianImpl : public StreamStatistician {
bool retransmitted);
void FecPacketReceived(const RTPHeader& header, size_t packet_length);
void SetMaxReorderingThreshold(int max_reordering_threshold);
void ProcessBitrate();
virtual void LastReceiveTimeNtp(uint32_t* secs, uint32_t* frac) const;
private:
@ -57,9 +56,9 @@ class StreamStatisticianImpl : public StreamStatistician {
void NotifyRtpCallback() LOCKS_EXCLUDED(stream_lock_);
void NotifyRtcpCallback() LOCKS_EXCLUDED(stream_lock_);
Clock* clock_;
Clock* const clock_;
rtc::CriticalSection stream_lock_;
Bitrate incoming_bitrate_;
RateStatistics incoming_bitrate_;
uint32_t ssrc_;
int max_reordering_threshold_; // In number of packets or sequence numbers.
@ -108,10 +107,6 @@ class ReceiveStatisticsImpl : public ReceiveStatistics,
StreamStatistician* GetStatistician(uint32_t ssrc) const override;
void SetMaxReorderingThreshold(int max_reordering_threshold) override;
// Implement Module.
void Process() override;
int64_t TimeUntilNextProcess() override;
void RegisterRtcpStatisticsCallback(
RtcpStatisticsCallback* callback) override;
@ -127,9 +122,8 @@ class ReceiveStatisticsImpl : public ReceiveStatistics,
typedef std::map<uint32_t, StreamStatisticianImpl*> StatisticianImplMap;
Clock* clock_;
Clock* const clock_;
rtc::CriticalSection receive_statistics_lock_;
int64_t last_rate_update_ms_;
StatisticianImplMap statisticians_;
RtcpStatisticsCallback* rtcp_stats_callback_;

View File

@ -13,7 +13,6 @@
#include "webrtc/base/onetimeevent.h"
#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "webrtc/modules/rtp_rtcp/source/bitrate.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_receiver_strategy.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
#include "webrtc/typedefs.h"

View File

@ -63,7 +63,8 @@ RtpRtcp::Configuration::Configuration()
send_frame_count_observer(nullptr),
send_side_delay_observer(nullptr),
event_log(nullptr),
send_packet_observer(nullptr) {}
send_packet_observer(nullptr),
retransmission_rate_limiter(nullptr) {}
RtpRtcp* RtpRtcp::CreateRtpRtcp(const RtpRtcp::Configuration& configuration) {
if (configuration.clock) {
@ -89,7 +90,8 @@ ModuleRtpRtcpImpl::ModuleRtpRtcpImpl(const Configuration& configuration)
configuration.send_frame_count_observer,
configuration.send_side_delay_observer,
configuration.event_log,
configuration.send_packet_observer),
configuration.send_packet_observer,
configuration.retransmission_rate_limiter),
rtcp_sender_(configuration.audio,
configuration.clock,
configuration.receive_statistics,
@ -820,10 +822,6 @@ int32_t ModuleRtpRtcpImpl::SendREDPayloadType(int8_t* payload_type) const {
return rtp_sender_.RED(payload_type);
}
void ModuleRtpRtcpImpl::SetTargetSendBitrate(uint32_t bitrate_bps) {
rtp_sender_.SetTargetBitrate(bitrate_bps);
}
int32_t ModuleRtpRtcpImpl::SetKeyFrameRequestMethod(
const KeyFrameRequestMethod method) {
key_frame_req_method_ = method;

View File

@ -285,8 +285,6 @@ class ModuleRtpRtcpImpl : public RtpRtcp {
// Send a request for a keyframe.
int32_t RequestKeyFrame() override;
void SetTargetSendBitrate(uint32_t bitrate_bps) override;
void SetGenericFECStatus(bool enable,
uint8_t payload_type_red,
uint8_t payload_type_fec) override;

View File

@ -15,6 +15,7 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/base/rate_limiter.h"
#include "webrtc/common_types.h"
#include "webrtc/modules/rtp_rtcp/include/rtp_header_parser.h"
#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h"
@ -37,6 +38,7 @@ const int64_t kOneWayNetworkDelayMs = 100;
const uint8_t kBaseLayerTid = 0;
const uint8_t kHigherLayerTid = 1;
const uint16_t kSequenceNumber = 100;
const int64_t kMaxRttMs = 1000;
class RtcpRttStatsTestImpl : public RtcpRttStats {
public:
@ -99,7 +101,9 @@ class SendTransport : public Transport,
class RtpRtcpModule : public RtcpPacketTypeCounterObserver {
public:
explicit RtpRtcpModule(SimulatedClock* clock)
: receive_statistics_(ReceiveStatistics::Create(clock)) {
: receive_statistics_(ReceiveStatistics::Create(clock)),
remote_ssrc_(0),
retransmission_rate_limiter_(clock, kMaxRttMs) {
RtpRtcp::Configuration config;
config.audio = false;
config.clock = clock;
@ -107,6 +111,7 @@ class RtpRtcpModule : public RtcpPacketTypeCounterObserver {
config.receive_statistics = receive_statistics_.get();
config.rtcp_packet_type_counter_observer = this;
config.rtt_stats = &rtt_stats_;
config.retransmission_rate_limiter = &retransmission_rate_limiter_;
impl_.reset(new ModuleRtpRtcpImpl(config));
impl_->SetRTCPStatus(RtcpMode::kCompound);
@ -121,6 +126,7 @@ class RtpRtcpModule : public RtcpPacketTypeCounterObserver {
RtcpRttStatsTestImpl rtt_stats_;
std::unique_ptr<ModuleRtpRtcpImpl> impl_;
uint32_t remote_ssrc_;
RateLimiter retransmission_rate_limiter_;
void SetRemoteSsrc(uint32_t ssrc) {
remote_ssrc_ = ssrc;

View File

@ -16,6 +16,7 @@
#include "webrtc/base/checks.h"
#include "webrtc/base/logging.h"
#include "webrtc/base/rate_limiter.h"
#include "webrtc/base/trace_event.h"
#include "webrtc/base/timeutils.h"
#include "webrtc/call.h"
@ -33,6 +34,7 @@ namespace webrtc {
static const size_t kMaxPaddingLength = 224;
static const int kSendSideDelayWindowMs = 1000;
static const uint32_t kAbsSendTimeFraction = 18;
static const int kBitrateStatisticsWindowMs = 1000;
namespace {
@ -63,47 +65,6 @@ uint32_t ConvertMsTo24Bits(int64_t time_ms) {
}
} // namespace
RTPSender::BitrateAggregator::BitrateAggregator(
BitrateStatisticsObserver* bitrate_callback)
: callback_(bitrate_callback),
total_bitrate_observer_(*this),
retransmit_bitrate_observer_(*this),
ssrc_(0) {}
void RTPSender::BitrateAggregator::OnStatsUpdated() const {
if (callback_) {
callback_->Notify(total_bitrate_observer_.statistics(),
retransmit_bitrate_observer_.statistics(), ssrc_);
}
}
Bitrate::Observer* RTPSender::BitrateAggregator::total_bitrate_observer() {
return &total_bitrate_observer_;
}
Bitrate::Observer* RTPSender::BitrateAggregator::retransmit_bitrate_observer() {
return &retransmit_bitrate_observer_;
}
void RTPSender::BitrateAggregator::set_ssrc(uint32_t ssrc) {
ssrc_ = ssrc;
}
RTPSender::BitrateAggregator::BitrateObserver::BitrateObserver(
const BitrateAggregator& aggregator)
: aggregator_(aggregator) {}
// Implements Bitrate::Observer.
void RTPSender::BitrateAggregator::BitrateObserver::BitrateUpdated(
const BitrateStatistics& stats) {
statistics_ = stats;
aggregator_.OnStatsUpdated();
}
const BitrateStatistics&
RTPSender::BitrateAggregator::BitrateObserver::statistics() const {
return statistics_;
}
RTPSender::RTPSender(
bool audio,
Clock* clock,
@ -115,13 +76,12 @@ RTPSender::RTPSender(
FrameCountObserver* frame_count_observer,
SendSideDelayObserver* send_side_delay_observer,
RtcEventLog* event_log,
SendPacketObserver* send_packet_observer)
SendPacketObserver* send_packet_observer,
RateLimiter* retransmission_rate_limiter)
: clock_(clock),
// TODO(holmer): Remove this conversion?
clock_delta_ms_(clock_->TimeInMilliseconds() - rtc::TimeMillis()),
random_(clock_->TimeInMicroseconds()),
bitrates_(bitrate_callback),
total_bitrate_sent_(clock, bitrates_.total_bitrate_observer()),
audio_configured_(audio),
audio_(audio ? new RTPSenderAudio(clock, this) : nullptr),
video_(audio ? nullptr : new RTPSenderVideo(clock, this)),
@ -140,18 +100,18 @@ RTPSender::RTPSender(
rotation_(kVideoRotation_0),
video_rotation_active_(false),
transport_sequence_number_(0),
// NACK.
nack_byte_count_times_(),
nack_byte_count_(),
nack_bitrate_(clock, bitrates_.retransmit_bitrate_observer()),
playout_delay_active_(false),
packet_history_(clock),
// Statistics
rtp_stats_callback_(NULL),
rtp_stats_callback_(nullptr),
total_bitrate_sent_(kBitrateStatisticsWindowMs,
RateStatistics::kBpsScale),
nack_bitrate_sent_(kBitrateStatisticsWindowMs, RateStatistics::kBpsScale),
frame_count_observer_(frame_count_observer),
send_side_delay_observer_(send_side_delay_observer),
event_log_(event_log),
send_packet_observer_(send_packet_observer),
bitrate_callback_(bitrate_callback),
// RTP variables
start_timestamp_forced_(false),
start_timestamp_(0),
@ -166,9 +126,7 @@ RTPSender::RTPSender(
last_packet_marker_bit_(false),
csrcs_(),
rtx_(kRtxOff),
target_bitrate_(0) {
memset(nack_byte_count_times_, 0, sizeof(nack_byte_count_times_));
memset(nack_byte_count_, 0, sizeof(nack_byte_count_));
retransmission_rate_limiter_(retransmission_rate_limiter) {
// We need to seed the random generator for BuildPaddingPacket() below.
// TODO(holmer,tommi): Note that TimeInMilliseconds might return 0 on Mac
// early on in the process.
@ -178,7 +136,6 @@ RTPSender::RTPSender(
ssrc_rtx_ = ssrc_db_->CreateSSRC();
RTC_DCHECK(ssrc_rtx_ != 0);
bitrates_.set_ssrc(ssrc_);
// Random start, 16 bits. Can't be 0.
sequence_number_rtx_ = random_.Rand(1, kMaxInitRtpSeqNumber);
sequence_number_ = random_.Rand(1, kMaxInitRtpSeqNumber);
@ -208,18 +165,11 @@ RTPSender::~RTPSender() {
}
}
void RTPSender::SetTargetBitrate(uint32_t bitrate) {
rtc::CritScope cs(&target_bitrate_critsect_);
target_bitrate_ = bitrate;
}
uint32_t RTPSender::GetTargetBitrate() {
rtc::CritScope cs(&target_bitrate_critsect_);
return target_bitrate_;
}
uint16_t RTPSender::ActualSendBitrateKbit() const {
return (uint16_t)(total_bitrate_sent_.BitrateNow() / 1000);
rtc::CritScope cs(&statistics_crit_);
return static_cast<uint16_t>(
total_bitrate_sent_.Rate(clock_->TimeInMilliseconds()).value_or(0) /
1000);
}
uint32_t RTPSender::VideoBitrateSent() const {
@ -237,7 +187,8 @@ uint32_t RTPSender::FecOverheadRate() const {
}
uint32_t RTPSender::NackOverheadRate() const {
return nack_bitrate_.BitrateLast();
rtc::CritScope cs(&statistics_crit_);
return nack_bitrate_sent_.Rate(clock_->TimeInMilliseconds()).value_or(0);
}
int32_t RTPSender::SetTransmissionTimeOffset(int32_t transmission_time_offset) {
@ -754,6 +705,12 @@ int32_t RTPSender::ReSendPacket(uint16_t packet_id, int64_t min_resend_time) {
return 0;
}
// Check if we're overusing retransmission bitrate.
// TODO(sprang): Add histograms for nack success or failure reasons.
RTC_DCHECK(retransmission_rate_limiter_);
if (!retransmission_rate_limiter_->TryUseRate(length))
return -1;
if (paced_sender_) {
RtpUtility::RtpHeaderParser rtp_parser(data_buffer, length);
RTPHeader header;
@ -824,44 +781,14 @@ void RTPSender::OnReceivedNACK(const std::list<uint16_t>& nack_sequence_numbers,
TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("webrtc_rtp"),
"RTPSender::OnReceivedNACK", "num_seqnum",
nack_sequence_numbers.size(), "avg_rtt", avg_rtt);
const int64_t now = clock_->TimeInMilliseconds();
uint32_t bytes_re_sent = 0;
uint32_t target_bitrate = GetTargetBitrate();
// Enough bandwidth to send NACK?
if (!ProcessNACKBitRate(now)) {
LOG(LS_INFO) << "NACK bitrate reached. Skip sending NACK response. Target "
<< target_bitrate;
return;
}
for (std::list<uint16_t>::const_iterator it = nack_sequence_numbers.begin();
it != nack_sequence_numbers.end(); ++it) {
const int32_t bytes_sent = ReSendPacket(*it, 5 + avg_rtt);
if (bytes_sent > 0) {
bytes_re_sent += bytes_sent;
} else if (bytes_sent == 0) {
// The packet has previously been resent.
// Try resending next packet in the list.
continue;
} else {
for (uint16_t seq_no : nack_sequence_numbers) {
const int32_t bytes_sent = ReSendPacket(seq_no, 5 + avg_rtt);
if (bytes_sent < 0) {
// Failed to send one Sequence number. Give up the rest in this nack.
LOG(LS_WARNING) << "Failed resending RTP packet " << *it
LOG(LS_WARNING) << "Failed resending RTP packet " << seq_no
<< ", Discard rest of packets";
break;
}
// Delay bandwidth estimate (RTT * BW).
if (target_bitrate != 0 && avg_rtt) {
// kbits/s * ms = bits => bits/8 = bytes
size_t target_bytes =
(static_cast<size_t>(target_bitrate / 1000) * avg_rtt) >> 3;
if (bytes_re_sent > target_bytes) {
break; // Ignore the rest of the packets in the list.
}
}
}
if (bytes_re_sent > 0) {
UpdateNACKBitRate(bytes_re_sent, now);
}
}
@ -870,51 +797,6 @@ void RTPSender::OnReceivedRtcpReportBlocks(
playout_delay_oracle_.OnReceivedRtcpReportBlocks(report_blocks);
}
bool RTPSender::ProcessNACKBitRate(uint32_t now) {
uint32_t num = 0;
size_t byte_count = 0;
const uint32_t kAvgIntervalMs = 1000;
uint32_t target_bitrate = GetTargetBitrate();
rtc::CritScope lock(&send_critsect_);
if (target_bitrate == 0) {
return true;
}
for (num = 0; num < NACK_BYTECOUNT_SIZE; ++num) {
if ((now - nack_byte_count_times_[num]) > kAvgIntervalMs) {
// Don't use data older than 1sec.
break;
} else {
byte_count += nack_byte_count_[num];
}
}
uint32_t time_interval = kAvgIntervalMs;
if (num == NACK_BYTECOUNT_SIZE) {
// More than NACK_BYTECOUNT_SIZE nack messages has been received
// during the last msg_interval.
if (nack_byte_count_times_[num - 1] <= now) {
time_interval = now - nack_byte_count_times_[num - 1];
}
}
return (byte_count * 8) < (target_bitrate / 1000 * time_interval);
}
void RTPSender::UpdateNACKBitRate(uint32_t bytes, int64_t now) {
rtc::CritScope lock(&send_critsect_);
if (bytes == 0)
return;
nack_bitrate_.Update(bytes);
// Save bitrate statistics.
// Shift all but first time.
for (int i = NACK_BYTECOUNT_SIZE - 2; i >= 0; i--) {
nack_byte_count_[i + 1] = nack_byte_count_[i];
nack_byte_count_times_[i + 1] = nack_byte_count_times_[i];
}
nack_byte_count_[0] = bytes;
nack_byte_count_times_[0] = now;
}
// Called from pacer when we can send the packet.
bool RTPSender::TimeToSendPacket(uint16_t sequence_number,
int64_t capture_time_ms,
@ -1009,6 +891,7 @@ void RTPSender::UpdateRtpStats(const uint8_t* buffer,
StreamDataCounters* counters;
// Get ssrc before taking statistics_crit_ to avoid possible deadlock.
uint32_t ssrc = is_rtx ? RtxSsrc() : SSRC();
int64_t now_ms = clock_->TimeInMilliseconds();
rtc::CritScope lock(&statistics_crit_);
if (is_rtx) {
@ -1017,22 +900,23 @@ void RTPSender::UpdateRtpStats(const uint8_t* buffer,
counters = &rtp_stats_;
}
total_bitrate_sent_.Update(packet_length);
total_bitrate_sent_.Update(packet_length, now_ms);
if (counters->first_packet_time_ms == -1) {
if (counters->first_packet_time_ms == -1)
counters->first_packet_time_ms = clock_->TimeInMilliseconds();
}
if (IsFecPacket(buffer, header)) {
if (IsFecPacket(buffer, header))
counters->fec.AddPacket(packet_length, header);
}
if (is_retransmit) {
counters->retransmitted.AddPacket(packet_length, header);
nack_bitrate_sent_.Update(packet_length, now_ms);
}
counters->transmitted.AddPacket(packet_length, header);
if (rtp_stats_callback_) {
if (rtp_stats_callback_)
rtp_stats_callback_->DataCountersUpdated(*counters, ssrc);
}
}
bool RTPSender::IsFecPacket(const uint8_t* buffer,
@ -1180,13 +1064,18 @@ void RTPSender::UpdateOnSendPacket(int packet_id,
}
void RTPSender::ProcessBitrate() {
rtc::CritScope lock(&send_critsect_);
total_bitrate_sent_.Process();
nack_bitrate_.Process();
if (audio_configured_) {
if (!bitrate_callback_)
return;
int64_t now_ms = clock_->TimeInMilliseconds();
uint32_t ssrc;
{
rtc::CritScope lock(&send_critsect_);
ssrc = ssrc_;
}
video_->ProcessBitrate();
rtc::CritScope lock(&statistics_crit_);
bitrate_callback_->Notify(total_bitrate_sent_.Rate(now_ms).value_or(0),
nack_bitrate_sent_.Rate(now_ms).value_or(0), ssrc);
}
size_t RTPSender::RtpHeaderLength() const {
@ -1746,7 +1635,6 @@ void RTPSender::SetSendingStatus(bool enabled) {
ssrc_db_->ReturnSSRC(ssrc_);
ssrc_ = ssrc_db_->CreateSSRC();
RTC_DCHECK(ssrc_ != 0);
bitrates_.set_ssrc(ssrc_);
}
// Don't initialize seq number if SSRC passed externally.
if (!sequence_number_forced_ && !ssrc_forced_) {
@ -1797,7 +1685,6 @@ uint32_t RTPSender::GenerateNewSSRC() {
}
ssrc_ = ssrc_db_->CreateSSRC();
RTC_DCHECK(ssrc_ != 0);
bitrates_.set_ssrc(ssrc_);
return ssrc_;
}
@ -1812,7 +1699,6 @@ void RTPSender::SetSSRC(uint32_t ssrc) {
ssrc_db_->ReturnSSRC(ssrc_);
ssrc_db_->RegisterSSRC(ssrc);
ssrc_ = ssrc;
bitrates_.set_ssrc(ssrc_);
if (!sequence_number_forced_) {
sequence_number_ = random_.Rand(1, kMaxInitRtpSeqNumber);
}
@ -1961,7 +1847,8 @@ StreamDataCountersCallback* RTPSender::GetRtpStatisticsCallback() const {
}
uint32_t RTPSender::BitrateSent() const {
return total_bitrate_sent_.BitrateLast();
rtc::CritScope cs(&statistics_crit_);
return total_bitrate_sent_.Rate(clock_->TimeInMilliseconds()).value_or(0);
}
void RTPSender::SetRtpState(const RtpState& rtp_state) {

View File

@ -20,10 +20,10 @@
#include "webrtc/base/constructormagic.h"
#include "webrtc/base/criticalsection.h"
#include "webrtc/base/random.h"
#include "webrtc/base/rate_statistics.h"
#include "webrtc/base/thread_annotations.h"
#include "webrtc/common_types.h"
#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "webrtc/modules/rtp_rtcp/source/bitrate.h"
#include "webrtc/modules/rtp_rtcp/source/playout_delay_oracle.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_header_extension.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_packet_history.h"
@ -34,6 +34,7 @@
namespace webrtc {
class RateLimiter;
class RTPSenderAudio;
class RTPSenderVideo;
class RtcEventLog;
@ -93,7 +94,8 @@ class RTPSender : public RTPSenderInterface {
FrameCountObserver* frame_count_observer,
SendSideDelayObserver* send_side_delay_observer,
RtcEventLog* event_log,
SendPacketObserver* send_packet_observer);
SendPacketObserver* send_packet_observer,
RateLimiter* nack_rate_limiter);
virtual ~RTPSender();
@ -105,9 +107,6 @@ class RTPSender : public RTPSenderInterface {
uint32_t FecOverheadRate() const;
uint32_t NackOverheadRate() const;
void SetTargetBitrate(uint32_t bitrate);
uint32_t GetTargetBitrate();
// Includes size of RTP and FEC headers.
size_t MaxDataPayloadLength() const override;
@ -227,8 +226,6 @@ class RTPSender : public RTPSenderInterface {
int32_t ReSendPacket(uint16_t packet_id, int64_t min_resend_time = 0);
bool ProcessNACKBitRate(uint32_t now);
// Feedback to decide when to stop sending playout delay.
void OnReceivedRtcpReportBlocks(const ReportBlockList& report_blocks);
@ -340,8 +337,6 @@ class RTPSender : public RTPSenderInterface {
uint16_t sequence_number,
const std::vector<uint32_t>& csrcs) const;
void UpdateNACKBitRate(uint32_t bytes, int64_t now);
bool PrepareAndSendPacket(uint8_t* buffer,
size_t length,
int64_t capture_time_ms,
@ -406,45 +401,10 @@ class RTPSender : public RTPSenderInterface {
bool is_retransmit);
bool IsFecPacket(const uint8_t* buffer, const RTPHeader& header) const;
class BitrateAggregator {
public:
explicit BitrateAggregator(BitrateStatisticsObserver* bitrate_callback);
void OnStatsUpdated() const;
Bitrate::Observer* total_bitrate_observer();
Bitrate::Observer* retransmit_bitrate_observer();
void set_ssrc(uint32_t ssrc);
private:
// We assume that these observers are called on the same thread, which is
// true for RtpSender as they are called on the Process thread.
class BitrateObserver : public Bitrate::Observer {
public:
explicit BitrateObserver(const BitrateAggregator& aggregator);
// Implements Bitrate::Observer.
void BitrateUpdated(const BitrateStatistics& stats) override;
const BitrateStatistics& statistics() const;
private:
BitrateStatistics statistics_;
const BitrateAggregator& aggregator_;
};
BitrateStatisticsObserver* const callback_;
BitrateObserver total_bitrate_observer_;
BitrateObserver retransmit_bitrate_observer_;
uint32_t ssrc_;
};
Clock* const clock_;
const int64_t clock_delta_ms_;
Random random_ GUARDED_BY(send_critsect_);
BitrateAggregator bitrates_;
Bitrate total_bitrate_sent_;
const bool audio_configured_;
const std::unique_ptr<RTPSenderAudio> audio_;
const std::unique_ptr<RTPSenderVideo> video_;
@ -470,11 +430,6 @@ class RTPSender : public RTPSenderInterface {
bool video_rotation_active_;
uint16_t transport_sequence_number_;
// NACK
uint32_t nack_byte_count_times_[NACK_BYTECOUNT_SIZE];
size_t nack_byte_count_[NACK_BYTECOUNT_SIZE];
Bitrate nack_bitrate_;
// Tracks the current request for playout delay limits from application
// and decides whether the current RTP frame should include the playout
// delay extension on header.
@ -490,10 +445,13 @@ class RTPSender : public RTPSenderInterface {
StreamDataCounters rtp_stats_ GUARDED_BY(statistics_crit_);
StreamDataCounters rtx_rtp_stats_ GUARDED_BY(statistics_crit_);
StreamDataCountersCallback* rtp_stats_callback_ GUARDED_BY(statistics_crit_);
RateStatistics total_bitrate_sent_ GUARDED_BY(statistics_crit_);
RateStatistics nack_bitrate_sent_ GUARDED_BY(statistics_crit_);
FrameCountObserver* const frame_count_observer_;
SendSideDelayObserver* const send_side_delay_observer_;
RtcEventLog* const event_log_;
SendPacketObserver* const send_packet_observer_;
BitrateStatisticsObserver* const bitrate_callback_;
// RTP variables
bool start_timestamp_forced_ GUARDED_BY(send_critsect_);
@ -516,12 +474,7 @@ class RTPSender : public RTPSenderInterface {
// Mapping rtx_payload_type_map_[associated] = rtx.
std::map<int8_t, int8_t> rtx_payload_type_map_ GUARDED_BY(send_critsect_);
// Note: Don't access this variable directly, always go through
// SetTargetBitrateKbps or GetTargetBitrateKbps. Also remember
// that by the time the function returns there is no guarantee
// that the target bitrate is still valid.
rtc::CriticalSection target_bitrate_critsect_;
uint32_t target_bitrate_ GUARDED_BY(target_bitrate_critsect_);
RateLimiter* const retransmission_rate_limiter_;
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RTPSender);
};

View File

@ -15,6 +15,7 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "webrtc/base/buffer.h"
#include "webrtc/base/rate_limiter.h"
#include "webrtc/call/mock/mock_rtc_event_log.h"
#include "webrtc/modules/rtp_rtcp/include/rtp_cvo.h"
#include "webrtc/modules/rtp_rtcp/include/rtp_header_parser.h"
@ -133,11 +134,11 @@ class RtpSenderTest : public ::testing::Test {
: fake_clock_(kStartTime),
mock_rtc_event_log_(),
mock_paced_sender_(),
retransmission_rate_limiter_(&fake_clock_, 1000),
rtp_sender_(),
payload_(kPayload),
transport_(),
kMarkerBit(true) {
}
kMarkerBit(true) {}
void SetUp() override { SetUpRtpSender(true); }
@ -145,7 +146,8 @@ class RtpSenderTest : public ::testing::Test {
rtp_sender_.reset(new RTPSender(
false, &fake_clock_, &transport_, pacer ? &mock_paced_sender_ : nullptr,
&seq_num_allocator_, nullptr, nullptr, nullptr, nullptr,
&mock_rtc_event_log_, &send_packet_observer_));
&mock_rtc_event_log_, &send_packet_observer_,
&retransmission_rate_limiter_));
rtp_sender_->SetSequenceNumber(kSeqNum);
}
@ -154,6 +156,7 @@ class RtpSenderTest : public ::testing::Test {
MockRtpPacketSender mock_paced_sender_;
MockTransportSequenceNumberAllocator seq_num_allocator_;
MockSendPacketObserver send_packet_observer_;
RateLimiter retransmission_rate_limiter_;
std::unique_ptr<RTPSender> rtp_sender_;
int payload_;
LoopbackTransportTest transport_;
@ -743,7 +746,6 @@ TEST_F(RtpSenderTest, TrafficSmoothingWithExtensions) {
EXPECT_EQ(
0, rtp_sender_->RegisterRtpHeaderExtension(kRtpExtensionAbsoluteSendTime,
kAbsoluteSendTimeExtensionId));
rtp_sender_->SetTargetBitrate(300000);
int64_t capture_time_ms = fake_clock_.TimeInMilliseconds();
int rtp_length_int = rtp_sender_->BuildRTPheader(
packet_, kPayload, kMarkerBit, kTimestamp, capture_time_ms);
@ -797,7 +799,6 @@ TEST_F(RtpSenderTest, TrafficSmoothingRetransmits) {
EXPECT_EQ(
0, rtp_sender_->RegisterRtpHeaderExtension(kRtpExtensionAbsoluteSendTime,
kAbsoluteSendTimeExtensionId));
rtp_sender_->SetTargetBitrate(300000);
int64_t capture_time_ms = fake_clock_.TimeInMilliseconds();
int rtp_length_int = rtp_sender_->BuildRTPheader(
packet_, kPayload, kMarkerBit, kTimestamp, capture_time_ms);
@ -879,7 +880,6 @@ TEST_F(RtpSenderTest, SendPadding) {
kAbsoluteSendTimeExtensionId);
webrtc::RTPHeader rtp_header;
rtp_sender_->SetTargetBitrate(300000);
int64_t capture_time_ms = fake_clock_.TimeInMilliseconds();
int rtp_length_int = rtp_sender_->BuildRTPheader(
packet_, kPayload, kMarkerBit, timestamp, capture_time_ms);
@ -1011,7 +1011,7 @@ TEST_F(RtpSenderTest, OnSendPacketNotUpdatedWithoutSeqNumAllocator) {
rtp_sender_.reset(new RTPSender(
false, &fake_clock_, &transport_, &mock_paced_sender_,
nullptr /* TransportSequenceNumberAllocator */, nullptr, nullptr, nullptr,
nullptr, nullptr, &send_packet_observer_));
nullptr, nullptr, &send_packet_observer_, nullptr));
rtp_sender_->SetSequenceNumber(kSeqNum);
rtp_sender_->SetStorePacketsStatus(true, 10);
@ -1029,7 +1029,7 @@ TEST_F(RtpSenderTest, SendRedundantPayloads) {
MockTransport transport;
rtp_sender_.reset(new RTPSender(
false, &fake_clock_, &transport, &mock_paced_sender_, nullptr, nullptr,
nullptr, nullptr, nullptr, &mock_rtc_event_log_, nullptr));
nullptr, nullptr, nullptr, &mock_rtc_event_log_, nullptr, nullptr));
rtp_sender_->SetSequenceNumber(kSeqNum);
rtp_sender_->SetRtxPayloadType(kRtxPayload, kPayload);
@ -1054,7 +1054,6 @@ TEST_F(RtpSenderTest, SendRedundantPayloads) {
kTransmissionTimeOffsetExtensionId);
rtp_parser->RegisterRtpHeaderExtension(kRtpExtensionAbsoluteSendTime,
kAbsoluteSendTimeExtensionId);
rtp_sender_->SetTargetBitrate(300000);
const size_t kNumPayloadSizes = 10;
const size_t kPayloadSizes[kNumPayloadSizes] = {500, 550, 600, 650, 700,
750, 800, 850, 900, 950};
@ -1176,7 +1175,7 @@ TEST_F(RtpSenderTest, FrameCountCallbacks) {
rtp_sender_.reset(new RTPSender(
false, &fake_clock_, &transport_, &mock_paced_sender_, nullptr, nullptr,
nullptr, &callback, nullptr, nullptr, nullptr));
nullptr, &callback, nullptr, nullptr, nullptr, nullptr));
char payload_name[RTP_PAYLOAD_NAME_SIZE] = "GENERIC";
const uint8_t payload_type = 127;
@ -1213,30 +1212,39 @@ TEST_F(RtpSenderTest, FrameCountCallbacks) {
TEST_F(RtpSenderTest, BitrateCallbacks) {
class TestCallback : public BitrateStatisticsObserver {
public:
TestCallback() : BitrateStatisticsObserver(), num_calls_(0), ssrc_(0) {}
TestCallback()
: BitrateStatisticsObserver(),
num_calls_(0),
ssrc_(0),
total_bitrate_(0),
retransmit_bitrate_(0) {}
virtual ~TestCallback() {}
void Notify(const BitrateStatistics& total_stats,
const BitrateStatistics& retransmit_stats,
void Notify(uint32_t total_bitrate,
uint32_t retransmit_bitrate,
uint32_t ssrc) override {
++num_calls_;
ssrc_ = ssrc;
total_stats_ = total_stats;
retransmit_stats_ = retransmit_stats;
total_bitrate_ = total_bitrate;
retransmit_bitrate_ = retransmit_bitrate;
}
uint32_t num_calls_;
uint32_t ssrc_;
BitrateStatistics total_stats_;
BitrateStatistics retransmit_stats_;
uint32_t total_bitrate_;
uint32_t retransmit_bitrate_;
} callback;
rtp_sender_.reset(new RTPSender(false, &fake_clock_, &transport_, nullptr,
nullptr, nullptr, &callback, nullptr, nullptr,
nullptr, nullptr));
nullptr, nullptr, nullptr));
// Simulate kNumPackets sent with kPacketInterval ms intervals.
const uint32_t kNumPackets = 15;
// Simulate kNumPackets sent with kPacketInterval ms intervals, with the
// number of packets selected so that we fill (but don't overflow) the one
// second averaging window.
const uint32_t kWindowSizeMs = 1000;
const uint32_t kPacketInterval = 20;
const uint32_t kNumPackets =
(kWindowSizeMs - kPacketInterval) / kPacketInterval;
// Overhead = 12 bytes RTP header + 1 byte generic header.
const uint32_t kPacketOverhead = 13;
@ -1250,7 +1258,6 @@ TEST_F(RtpSenderTest, BitrateCallbacks) {
// Initial process call so we get a new time window.
rtp_sender_->ProcessBitrate();
uint64_t start_time = fake_clock_.CurrentNtpInMilliseconds();
// Send a few frames.
for (uint32_t i = 0; i < kNumPackets; ++i) {
@ -1262,17 +1269,18 @@ TEST_F(RtpSenderTest, BitrateCallbacks) {
rtp_sender_->ProcessBitrate();
const uint32_t expected_packet_rate = 1000 / kPacketInterval;
// We get one call for every stats updated, thus two calls since both the
// stream stats and the retransmit stats are updated once.
EXPECT_EQ(2u, callback.num_calls_);
EXPECT_EQ(ssrc, callback.ssrc_);
EXPECT_EQ(start_time + (kNumPackets * kPacketInterval),
callback.total_stats_.timestamp_ms);
EXPECT_EQ(expected_packet_rate, callback.total_stats_.packet_rate);
EXPECT_EQ((kPacketOverhead + sizeof(payload)) * 8 * expected_packet_rate,
callback.total_stats_.bitrate_bps);
const uint32_t kTotalPacketSize = kPacketOverhead + sizeof(payload);
// Bitrate measured over delta between last and first timestamp, plus one.
const uint32_t kExpectedWindowMs = kNumPackets * kPacketInterval + 1;
const uint32_t kExpectedBitsAccumulated = kTotalPacketSize * kNumPackets * 8;
const uint32_t kExpectedRateBps =
(kExpectedBitsAccumulated * 1000 + (kExpectedWindowMs / 2)) /
kExpectedWindowMs;
EXPECT_EQ(kExpectedRateBps, callback.total_bitrate_);
rtp_sender_.reset();
}
@ -1285,7 +1293,7 @@ class RtpSenderAudioTest : public RtpSenderTest {
payload_ = kAudioPayload;
rtp_sender_.reset(new RTPSender(true, &fake_clock_, &transport_, nullptr,
nullptr, nullptr, nullptr, nullptr, nullptr,
nullptr, nullptr));
nullptr, nullptr, nullptr));
rtp_sender_->SetSequenceNumber(kSeqNum);
}
};
@ -1553,9 +1561,9 @@ TEST_F(RtpSenderTestWithoutPacer, RespectsNackBitrateLimit) {
const int32_t kPacketSize = 1400;
const int32_t kNumPackets = 30;
retransmission_rate_limiter_.SetMaxRate(kPacketSize * kNumPackets * 8);
rtp_sender_->SetStorePacketsStatus(true, kNumPackets);
// Set bitrate (in kbps) to fit kNumPackets á kPacketSize bytes in one second.
rtp_sender_->SetTargetBitrate(kNumPackets * kPacketSize * 8);
const uint16_t kStartSequenceNumber = rtp_sender_->SequenceNumber();
std::list<uint16_t> sequence_numbers;
for (int32_t i = 0; i < kNumPackets; ++i) {
@ -1573,6 +1581,9 @@ TEST_F(RtpSenderTestWithoutPacer, RespectsNackBitrateLimit) {
rtp_sender_->OnReceivedNACK(sequence_numbers, 0);
EXPECT_EQ(kNumPackets * 2, transport_.packets_sent_);
// Must be at least 5ms in between retransmission attempts.
fake_clock_.AdvanceTimeMilliseconds(5);
// Resending should not work, bandwidth exceeded.
rtp_sender_->OnReceivedNACK(sequence_numbers, 0);
EXPECT_EQ(kNumPackets * 2, transport_.packets_sent_);

View File

@ -31,6 +31,7 @@ enum { REDForFECHeaderLength = 1 };
RTPSenderVideo::RTPSenderVideo(Clock* clock, RTPSenderInterface* rtpSender)
: _rtpSender(*rtpSender),
clock_(clock),
_videoType(kRtpVideoGeneric),
_retransmissionSettings(kRetransmitBaseLayer),
// Generic FEC
@ -41,8 +42,8 @@ RTPSenderVideo::RTPSenderVideo(Clock* clock, RTPSenderInterface* rtpSender)
delta_fec_params_(),
key_fec_params_(),
producer_fec_(&fec_),
_fecOverheadRate(clock, NULL),
_videoBitrate(clock, NULL) {
fec_bitrate_(1000, RateStatistics::kBpsScale),
video_bitrate_(1000, RateStatistics::kBpsScale) {
memset(&delta_fec_params_, 0, sizeof(delta_fec_params_));
memset(&key_fec_params_, 0, sizeof(key_fec_params_));
delta_fec_params_.max_fec_frames = key_fec_params_.max_fec_frames = 1;
@ -95,7 +96,9 @@ void RTPSenderVideo::SendVideoPacket(uint8_t* data_buffer,
if (_rtpSender.SendToNetwork(data_buffer, payload_length, rtp_header_length,
capture_time_ms, storage,
RtpPacketSender::kLowPriority) == 0) {
_videoBitrate.Update(payload_length + rtp_header_length);
rtc::CritScope cs(&stats_crit_);
video_bitrate_.Update(payload_length + rtp_header_length,
clock_->TimeInMilliseconds());
TRACE_EVENT_INSTANT2(TRACE_DISABLED_BY_DEFAULT("webrtc_rtp"),
"Video::PacketNormal", "timestamp", capture_timestamp,
"seqnum", seq_num);
@ -141,7 +144,8 @@ void RTPSenderVideo::SendVideoPacketAsRed(uint8_t* data_buffer,
red_packet->data(), red_packet->length() - rtp_header_length,
rtp_header_length, capture_time_ms, media_packet_storage,
RtpPacketSender::kLowPriority) == 0) {
_videoBitrate.Update(red_packet->length());
rtc::CritScope cs(&stats_crit_);
video_bitrate_.Update(red_packet->length(), clock_->TimeInMilliseconds());
TRACE_EVENT_INSTANT2(TRACE_DISABLED_BY_DEFAULT("webrtc_rtp"),
"Video::PacketRed", "timestamp", capture_timestamp,
"seqnum", media_seq_num);
@ -153,7 +157,8 @@ void RTPSenderVideo::SendVideoPacketAsRed(uint8_t* data_buffer,
fec_packet->data(), fec_packet->length() - rtp_header_length,
rtp_header_length, capture_time_ms, fec_storage,
RtpPacketSender::kLowPriority) == 0) {
_fecOverheadRate.Update(fec_packet->length());
rtc::CritScope cs(&stats_crit_);
fec_bitrate_.Update(fec_packet->length(), clock_->TimeInMilliseconds());
TRACE_EVENT_INSTANT2(TRACE_DISABLED_BY_DEFAULT("webrtc_rtp"),
"Video::PacketFec", "timestamp", capture_timestamp,
"seqnum", next_fec_sequence_number);
@ -337,17 +342,14 @@ int32_t RTPSenderVideo::SendVideo(const RtpVideoCodecTypes videoType,
return 0;
}
void RTPSenderVideo::ProcessBitrate() {
_videoBitrate.Process();
_fecOverheadRate.Process();
}
uint32_t RTPSenderVideo::VideoBitrateSent() const {
return _videoBitrate.BitrateLast();
rtc::CritScope cs(&stats_crit_);
return video_bitrate_.Rate(clock_->TimeInMilliseconds()).value_or(0);
}
uint32_t RTPSenderVideo::FecOverheadRate() const {
return _fecOverheadRate.BitrateLast();
rtc::CritScope cs(&stats_crit_);
return fec_bitrate_.Rate(clock_->TimeInMilliseconds()).value_or(0);
}
int RTPSenderVideo::SelectiveRetransmissions() const {

View File

@ -15,10 +15,10 @@
#include "webrtc/base/criticalsection.h"
#include "webrtc/base/onetimeevent.h"
#include "webrtc/base/rate_statistics.h"
#include "webrtc/base/thread_annotations.h"
#include "webrtc/common_types.h"
#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h"
#include "webrtc/modules/rtp_rtcp/source/bitrate.h"
#include "webrtc/modules/rtp_rtcp/source/forward_error_correction.h"
#include "webrtc/modules/rtp_rtcp/source/producer_fec.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_rtcp_config.h"
@ -68,8 +68,6 @@ class RTPSenderVideo {
void SetFecParameters(const FecProtectionParams* delta_params,
const FecProtectionParams* key_params);
void ProcessBitrate();
uint32_t VideoBitrateSent() const;
uint32_t FecOverheadRate() const;
@ -95,9 +93,10 @@ class RTPSenderVideo {
bool protect);
RTPSenderInterface& _rtpSender;
Clock* const clock_;
// Should never be held when calling out of this class.
const rtc::CriticalSection crit_;
rtc::CriticalSection crit_;
RtpVideoCodecTypes _videoType;
int32_t _retransmissionSettings GUARDED_BY(crit_);
@ -111,11 +110,12 @@ class RTPSenderVideo {
FecProtectionParams key_fec_params_ GUARDED_BY(crit_);
ProducerFec producer_fec_ GUARDED_BY(crit_);
rtc::CriticalSection stats_crit_;
// Bitrate used for FEC payload, RED headers, RTP headers for FEC packets
// and any padding overhead.
Bitrate _fecOverheadRate;
// Bitrate used for video payload and RTP headers
Bitrate _videoBitrate;
RateStatistics fec_bitrate_ GUARDED_BY(stats_crit_);
// Bitrate used for video payload and RTP headers.
RateStatistics video_bitrate_ GUARDED_BY(stats_crit_);
OneTimeEvent first_frame_sent_;
};
} // namespace webrtc

View File

@ -26,6 +26,7 @@
#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp.h"
#include "webrtc/modules/rtp_rtcp/source/byte_io.h"
#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h"
#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
#include "webrtc/modules/video_coding/codecs/h264/include/h264.h"
#include "webrtc/modules/video_coding/codecs/vp8/include/vp8.h"
#include "webrtc/modules/video_coding/codecs/vp9/include/vp9.h"
@ -1527,7 +1528,8 @@ class TransportFeedbackTester : public test::EndToEndTest {
: EndToEndTest(::webrtc::EndToEndTest::kDefaultTimeoutMs),
feedback_enabled_(feedback_enabled),
num_video_streams_(num_video_streams),
num_audio_streams_(num_audio_streams) {
num_audio_streams_(num_audio_streams),
receiver_call_(nullptr) {
// Only one stream of each supported for now.
EXPECT_LE(num_video_streams, 1u);
EXPECT_LE(num_audio_streams, 1u);
@ -2539,6 +2541,16 @@ TEST_F(EndToEndTest, GetStats) {
private:
Action OnSendRtp(const uint8_t* packet, size_t length) override {
// Drop every 25th packet => 4% loss.
static const int kPacketLossFrac = 25;
RTPHeader header;
RtpUtility::RtpHeaderParser parser(packet, length);
if (parser.Parse(&header) &&
expected_send_ssrcs_.find(header.ssrc) !=
expected_send_ssrcs_.end() &&
header.sequenceNumber % kPacketLossFrac == 0) {
return DROP_PACKET;
}
check_stats_event_.Set();
return SEND_PACKET;
}
@ -2639,8 +2651,8 @@ TEST_F(EndToEndTest, GetStats) {
for (std::map<uint32_t, VideoSendStream::StreamStats>::const_iterator it =
stats.substreams.begin();
it != stats.substreams.end(); ++it) {
EXPECT_TRUE(expected_send_ssrcs_.find(it->first) !=
expected_send_ssrcs_.end());
if (expected_send_ssrcs_.find(it->first) == expected_send_ssrcs_.end())
continue; // Probably RTX.
send_stats_filled_[CompoundKey("CapturedFrameRate", it->first)] |=
stats.input_frame_rate != 0;
@ -2658,10 +2670,14 @@ TEST_F(EndToEndTest, GetStats) {
stream_stats.rtp_stats.retransmitted.packets != 0 ||
stream_stats.rtp_stats.transmitted.packets != 0;
send_stats_filled_[CompoundKey("BitrateStatisticsObserver",
send_stats_filled_[CompoundKey("BitrateStatisticsObserver.Total",
it->first)] |=
stream_stats.total_bitrate_bps != 0;
send_stats_filled_[CompoundKey("BitrateStatisticsObserver.Retransmit",
it->first)] |=
stream_stats.retransmit_bitrate_bps != 0;
send_stats_filled_[CompoundKey("FrameCountObserver", it->first)] |=
stream_stats.frame_counts.delta_frames != 0 ||
stream_stats.frame_counts.key_frames != 0;
@ -2692,10 +2708,8 @@ TEST_F(EndToEndTest, GetStats) {
}
bool AllStatsFilled(const std::map<std::string, bool>& stats_map) {
for (std::map<std::string, bool>::const_iterator it = stats_map.begin();
it != stats_map.end();
++it) {
if (!it->second)
for (const auto& stat : stats_map) {
if (!stat.second)
return false;
}
return true;
@ -2718,9 +2732,18 @@ TEST_F(EndToEndTest, GetStats) {
VideoSendStream::Config* send_config,
std::vector<VideoReceiveStream::Config>* receive_configs,
VideoEncoderConfig* encoder_config) override {
// Set low rates to avoid waiting for rampup.
for (size_t i = 0; i < encoder_config->streams.size(); ++i) {
encoder_config->streams[i].min_bitrate_bps = 10000;
encoder_config->streams[i].target_bitrate_bps = 15000;
encoder_config->streams[i].max_bitrate_bps = 20000;
}
send_config->pre_encode_callback = this; // Used to inject delay.
expected_cname_ = send_config->rtp.c_name = "SomeCName";
send_config->rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
send_config->rtp.rtx.payload_type = kSendRtxPayloadType;
const std::vector<uint32_t>& ssrcs = send_config->rtp.ssrcs;
for (size_t i = 0; i < ssrcs.size(); ++i) {
expected_send_ssrcs_.insert(ssrcs[i]);
@ -2728,7 +2751,17 @@ TEST_F(EndToEndTest, GetStats) {
(*receive_configs)[i].rtp.remote_ssrc);
(*receive_configs)[i].render_delay_ms = kExpectedRenderDelayMs;
(*receive_configs)[i].renderer = &receive_stream_renderer_;
(*receive_configs)[i].rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
(*receive_configs)[i].rtp.rtx[kFakeVideoSendPayloadType].ssrc =
kSendRtxSsrcs[i];
(*receive_configs)[i].rtp.rtx[kFakeVideoSendPayloadType].payload_type =
kSendRtxPayloadType;
}
for (size_t i = 0; i < kNumSsrcs; ++i)
send_config->rtp.rtx.ssrcs.push_back(kSendRtxSsrcs[i]);
// Use a delayed encoder to make sure we see CpuOveruseMetrics stats that
// are non-zero.
send_config->encoder_settings.encoder = &encoder_;

View File

@ -167,25 +167,6 @@ int32_t PayloadRouter::Encoded(const EncodedImage& encoded_image,
encoded_image._length, fragmentation, &rtp_video_header);
}
void PayloadRouter::SetTargetSendBitrate(uint32_t bitrate_bps) {
rtc::CritScope lock(&crit_);
RTC_DCHECK_LE(streams_.size(), rtp_modules_.size());
// TODO(sprang): Rebase https://codereview.webrtc.org/1913073002/ on top of
// this.
int bitrate_remainder = bitrate_bps;
for (size_t i = 0; i < streams_.size() && bitrate_remainder > 0; ++i) {
int stream_bitrate = 0;
if (streams_[i].max_bitrate_bps > bitrate_remainder) {
stream_bitrate = bitrate_remainder;
} else {
stream_bitrate = streams_[i].max_bitrate_bps;
}
bitrate_remainder -= stream_bitrate;
rtp_modules_[i]->SetTargetSendBitrate(stream_bitrate);
}
}
size_t PayloadRouter::MaxPayloadLength() const {
size_t min_payload_length = DefaultMaxPayloadLength();
rtc::CritScope lock(&crit_);

View File

@ -50,9 +50,6 @@ class PayloadRouter : public EncodedImageCallback {
const CodecSpecificInfo* codec_specific_info,
const RTPFragmentationHeader* fragmentation) override;
// Configures current target bitrate.
void SetTargetSendBitrate(uint32_t bitrate_bps);
// Returns the maximum allowed data payload length, given the configured MTU
// and RTP headers.
size_t MaxPayloadLength() const;

View File

@ -186,25 +186,4 @@ TEST(PayloadRouterTest, MaxPayloadLength) {
.WillOnce(Return(kTestMinPayloadLength));
EXPECT_EQ(kTestMinPayloadLength, payload_router.MaxPayloadLength());
}
TEST(PayloadRouterTest, SetTargetSendBitrates) {
NiceMock<MockRtpRtcp> rtp_1;
NiceMock<MockRtpRtcp> rtp_2;
std::vector<RtpRtcp*> modules;
modules.push_back(&rtp_1);
modules.push_back(&rtp_2);
PayloadRouter payload_router(modules, 42);
std::vector<VideoStream> streams(2);
streams[0].max_bitrate_bps = 10000;
streams[1].max_bitrate_bps = 100000;
payload_router.SetSendStreams(streams);
const uint32_t bitrate_1 = 10000;
const uint32_t bitrate_2 = 76543;
EXPECT_CALL(rtp_1, SetTargetSendBitrate(bitrate_1))
.Times(1);
EXPECT_CALL(rtp_2, SetTargetSendBitrate(bitrate_2))
.Times(1);
payload_router.SetTargetSendBitrate(bitrate_1 + bitrate_2);
}
} // namespace webrtc

View File

@ -58,6 +58,7 @@ std::unique_ptr<RtpRtcp> CreateRtpRtcpModule(
configuration.send_packet_observer = nullptr;
configuration.bandwidth_callback = nullptr;
configuration.transport_feedback_callback = nullptr;
configuration.retransmission_rate_limiter = nullptr;
std::unique_ptr<RtpRtcp> rtp_rtcp(RtpRtcp::CreateRtpRtcp(configuration));
rtp_rtcp->SetSendingStatus(false);
@ -185,12 +186,10 @@ RtpStreamReceiver::RtpStreamReceiver(
// Stats callback for CNAME changes.
rtp_rtcp_->RegisterRtcpStatisticsCallback(receive_stats_proxy);
process_thread_->RegisterModule(rtp_receive_statistics_.get());
process_thread_->RegisterModule(rtp_rtcp_.get());
}
RtpStreamReceiver::~RtpStreamReceiver() {
process_thread_->DeRegisterModule(rtp_receive_statistics_.get());
process_thread_->DeRegisterModule(rtp_rtcp_.get());
packet_router_->RemoveRtpModule(rtp_rtcp_.get());

View File

@ -564,16 +564,16 @@ void SendStatisticsProxy::DataCountersUpdated(
uma_container_->first_rtp_stats_time_ms_ = clock_->TimeInMilliseconds();
}
void SendStatisticsProxy::Notify(const BitrateStatistics& total_stats,
const BitrateStatistics& retransmit_stats,
void SendStatisticsProxy::Notify(uint32_t total_bitrate_bps,
uint32_t retransmit_bitrate_bps,
uint32_t ssrc) {
rtc::CritScope lock(&crit_);
VideoSendStream::StreamStats* stats = GetStatsEntry(ssrc);
if (!stats)
return;
stats->total_bitrate_bps = total_stats.bitrate_bps;
stats->retransmit_bitrate_bps = retransmit_stats.bitrate_bps;
stats->total_bitrate_bps = total_bitrate_bps;
stats->retransmit_bitrate_bps = retransmit_bitrate_bps;
}
void SendStatisticsProxy::FrameCountUpdated(const FrameCounts& frame_counts,

View File

@ -85,8 +85,8 @@ class SendStatisticsProxy : public CpuOveruseMetricsObserver,
uint32_t ssrc) override;
// From BitrateStatisticsObserver.
void Notify(const BitrateStatistics& total_stats,
const BitrateStatistics& retransmit_stats,
void Notify(uint32_t total_bitrate_bps,
uint32_t retransmit_bitrate_bps,
uint32_t ssrc) override;
// From FrameCountObserver.

View File

@ -226,24 +226,24 @@ TEST_F(SendStatisticsProxyTest, DataCounters) {
TEST_F(SendStatisticsProxyTest, Bitrate) {
BitrateStatisticsObserver* observer = statistics_proxy_.get();
for (const auto& ssrc : config_.rtp.ssrcs) {
BitrateStatistics total;
BitrateStatistics retransmit;
uint32_t total;
uint32_t retransmit;
// Use ssrc as bitrate_bps to get a unique value for each stream.
total.bitrate_bps = ssrc;
retransmit.bitrate_bps = ssrc + 1;
total = ssrc;
retransmit = ssrc + 1;
observer->Notify(total, retransmit, ssrc);
expected_.substreams[ssrc].total_bitrate_bps = total.bitrate_bps;
expected_.substreams[ssrc].retransmit_bitrate_bps = retransmit.bitrate_bps;
expected_.substreams[ssrc].total_bitrate_bps = total;
expected_.substreams[ssrc].retransmit_bitrate_bps = retransmit;
}
for (const auto& ssrc : config_.rtp.rtx.ssrcs) {
BitrateStatistics total;
BitrateStatistics retransmit;
uint32_t total;
uint32_t retransmit;
// Use ssrc as bitrate_bps to get a unique value for each stream.
total.bitrate_bps = ssrc;
retransmit.bitrate_bps = ssrc + 1;
total = ssrc;
retransmit = ssrc + 1;
observer->Notify(total, retransmit, ssrc);
expected_.substreams[ssrc].total_bitrate_bps = total.bitrate_bps;
expected_.substreams[ssrc].retransmit_bitrate_bps = retransmit.bitrate_bps;
expected_.substreams[ssrc].total_bitrate_bps = total;
expected_.substreams[ssrc].retransmit_bitrate_bps = retransmit;
}
VideoSendStream::Stats stats = statistics_proxy_->GetStats();
@ -397,8 +397,8 @@ TEST_F(SendStatisticsProxyTest, NoSubstreams) {
rtcp_callback->StatisticsUpdated(rtcp_stats, excluded_ssrc);
// From BitrateStatisticsObserver.
BitrateStatistics total;
BitrateStatistics retransmit;
uint32_t total = 0;
uint32_t retransmit = 0;
BitrateStatisticsObserver* bitrate_observer = statistics_proxy_.get();
bitrate_observer->Notify(total, retransmit, excluded_ssrc);
@ -484,8 +484,7 @@ TEST_F(SendStatisticsProxyTest, ClearsResolutionFromInactiveSsrcs) {
}
TEST_F(SendStatisticsProxyTest, ClearsBitratesFromInactiveSsrcs) {
BitrateStatistics bitrate;
bitrate.bitrate_bps = 42;
uint32_t bitrate = 42;
BitrateStatisticsObserver* observer = statistics_proxy_.get();
observer->Notify(bitrate, bitrate, config_.rtp.ssrcs[0]);
observer->Notify(bitrate, bitrate, config_.rtp.ssrcs[1]);
@ -493,9 +492,9 @@ TEST_F(SendStatisticsProxyTest, ClearsBitratesFromInactiveSsrcs) {
statistics_proxy_->OnInactiveSsrc(config_.rtp.ssrcs[1]);
VideoSendStream::Stats stats = statistics_proxy_->GetStats();
EXPECT_EQ(static_cast<int>(bitrate.bitrate_bps),
EXPECT_EQ(static_cast<int>(bitrate),
stats.substreams[config_.rtp.ssrcs[0]].total_bitrate_bps);
EXPECT_EQ(static_cast<int>(bitrate.bitrate_bps),
EXPECT_EQ(static_cast<int>(bitrate),
stats.substreams[config_.rtp.ssrcs[0]].retransmit_bitrate_bps);
EXPECT_EQ(0, stats.substreams[config_.rtp.ssrcs[1]].total_bitrate_bps);
EXPECT_EQ(0, stats.substreams[config_.rtp.ssrcs[1]].retransmit_bitrate_bps);

View File

@ -52,6 +52,7 @@ std::vector<RtpRtcp*> CreateRtpRtcpModules(
SendStatisticsProxy* stats_proxy,
SendDelayStats* send_delay_stats,
RtcEventLog* event_log,
RateLimiter* retransmission_rate_limiter,
size_t num_modules) {
RTC_DCHECK_GT(num_modules, 0u);
RtpRtcp::Configuration configuration;
@ -73,6 +74,7 @@ std::vector<RtpRtcp*> CreateRtpRtcpModules(
configuration.send_side_delay_observer = stats_proxy;
configuration.send_packet_observer = send_delay_stats;
configuration.event_log = event_log;
configuration.retransmission_rate_limiter = retransmission_rate_limiter;
std::vector<RtpRtcp*> modules;
for (size_t i = 0; i < num_modules; ++i) {
@ -428,6 +430,7 @@ VideoSendStream::VideoSendStream(
&stats_proxy_,
send_delay_stats,
event_log,
congestion_controller_->GetRetransmissionRateLimiter(),
config_.rtp.ssrcs.size())),
payload_router_(rtp_rtcp_modules_, config.encoder_settings.payload_type),
input_(&encoder_wakeup_event_,
@ -885,7 +888,6 @@ void VideoSendStream::SignalNetworkState(NetworkState state) {
uint32_t VideoSendStream::OnBitrateUpdated(uint32_t bitrate_bps,
uint8_t fraction_loss,
int64_t rtt) {
payload_router_.SetTargetSendBitrate(bitrate_bps);
// Get the encoder target rate. It is the estimated network rate -
// protection overhead.
uint32_t encoder_target_rate_bps =

View File

@ -63,6 +63,7 @@
'base/proxy_unittest.cc',
'base/proxydetect_unittest.cc',
'base/random_unittest.cc',
'base/rate_limiter_unittest.cc',
'base/rate_statistics_unittest.cc',
'base/ratelimiter_unittest.cc',
'base/ratetracker_unittest.cc',