Commit 3d5b93cb authored by hubbe@chromium.org's avatar hubbe@chromium.org

Cast: Updated congestion control

This tries to update the bandwidth used to what is available.
The algorithm is essentially that we estimate how much bandwidth we've been sending recently.
Then we check how full our buffers are. We set a target for having our buffers be 90% empty.
If the buffer is more than 90% empty, we use more bandwidth than the resently sent, if
the buffer is less than 90% empty, we use less.

This change should be relatively safe since we normally set our min and max bandwidth to a
very limited range, and this change respects those limits.

Review URL: https://codereview.chromium.org/326783002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@276611 0039d316-1c4b-4281-b951-d872f2087c98
parent 9de1347a
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
#ifndef MEDIA_CAST_CONGESTION_CONTROL_CONGESTION_CONTROL_H_ #ifndef MEDIA_CAST_CONGESTION_CONTROL_CONGESTION_CONTROL_H_
#define MEDIA_CAST_CONGESTION_CONTROL_CONGESTION_CONTROL_H_ #define MEDIA_CAST_CONGESTION_CONTROL_CONGESTION_CONTROL_H_
#include <deque>
#include "base/basictypes.h" #include "base/basictypes.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/time/tick_clock.h" #include "base/time/tick_clock.h"
...@@ -16,28 +18,65 @@ namespace cast { ...@@ -16,28 +18,65 @@ namespace cast {
class CongestionControl { class CongestionControl {
public: public:
CongestionControl(base::TickClock* clock, CongestionControl(base::TickClock* clock,
float congestion_control_back_off,
uint32 max_bitrate_configured, uint32 max_bitrate_configured,
uint32 min_bitrate_configured, uint32 min_bitrate_configured,
uint32 start_bitrate); size_t max_unacked_frames);
virtual ~CongestionControl(); virtual ~CongestionControl();
// Don't call OnAck if the same message contain a NACK. void UpdateRtt(base::TimeDelta rtt);
// Returns true if the bitrate have changed.
bool OnAck(base::TimeDelta rtt_ms, uint32* new_bitrate); // Called when an encoded frame is sent to the transport.
void SendFrameToTransport(uint32 frame_id,
size_t frame_size,
base::TimeTicks when);
// Called when we receive an ACK for a frame.
void AckFrame(uint32 frame_id, base::TimeTicks when);
// Returns true if the bitrate have changed. // Returns the bitrate we should use for the next frame.
bool OnNack(base::TimeDelta rtt_ms, uint32* new_bitrate); uint32 GetBitrate(base::TimeTicks playout_time,
base::TimeDelta playout_delay);
private: private:
struct FrameStats {
FrameStats();
// Time this frame was sent to the transport.
base::TimeTicks sent_time;
// Time this frame was acked.
base::TimeTicks ack_time;
// Size of encoded frame in bits.
size_t frame_size;
};
// Calculate how much "dead air" (idle time) there is between two frames.
static base::TimeDelta DeadTime(const FrameStats& a, const FrameStats& b);
// Get the FrameStats for a given |frame_id|.
// Note: Older FrameStats will be removed automatically.
FrameStats* GetFrameStats(uint32 frame_id);
// Calculata safe bitrate. This is based on how much we've been
// sending in the past.
double CalculateSafeBitrate();
// For a given frame, calculate when it might be acked.
// (Or return the time it was acked, if it was.)
base::TimeTicks EstimatedAckTime(uint32 frame_id, double bitrate);
// Calculate when we start sending the data for a given frame.
// This is done by calculating when we were done sending the previous
// frame, but obvoiusly can't be less than |sent_time| (if known).
base::TimeTicks EstimatedSendingTime(uint32 frame_id, double bitrate);
base::TickClock* const clock_; // Not owned by this class. base::TickClock* const clock_; // Not owned by this class.
const float congestion_control_back_off_;
const uint32 max_bitrate_configured_; const uint32 max_bitrate_configured_;
const uint32 min_bitrate_configured_; const uint32 min_bitrate_configured_;
uint32 bitrate_; std::deque<FrameStats> frame_stats_;
base::TimeTicks time_last_increase_; uint32 last_frame_stats_;
base::TimeTicks time_last_decrease_; uint32 last_acked_frame_;
uint32 last_encoded_frame_;
base::TimeDelta rtt_;
size_t history_size_;
size_t acked_bits_in_history_;
base::TimeDelta dead_time_in_history_;
DISALLOW_COPY_AND_ASSIGN(CongestionControl); DISALLOW_COPY_AND_ASSIGN(CongestionControl);
}; };
......
...@@ -33,11 +33,11 @@ VideoSender::VideoSender( ...@@ -33,11 +33,11 @@ VideoSender::VideoSender(
target_playout_delay_(base::TimeDelta::FromMilliseconds( target_playout_delay_(base::TimeDelta::FromMilliseconds(
video_config.rtp_config.max_delay_ms)), video_config.rtp_config.max_delay_ms)),
transport_sender_(transport_sender), transport_sender_(transport_sender),
max_unacked_frames_(std::min( max_unacked_frames_(
kMaxUnackedFrames, std::min(kMaxUnackedFrames,
1 + static_cast<int>( 1 + static_cast<int>(target_playout_delay_ *
target_playout_delay_ * video_config.max_frame_rate / video_config.max_frame_rate /
base::TimeDelta::FromSeconds(1)))), base::TimeDelta::FromSeconds(1)))),
rtcp_(cast_environment_, rtcp_(cast_environment_,
this, this,
transport_sender_, transport_sender_,
...@@ -55,12 +55,10 @@ VideoSender::VideoSender( ...@@ -55,12 +55,10 @@ VideoSender::VideoSender(
last_sent_frame_id_(0), last_sent_frame_id_(0),
latest_acked_frame_id_(0), latest_acked_frame_id_(0),
duplicate_ack_counter_(0), duplicate_ack_counter_(0),
current_requested_bitrate_(video_config.start_bitrate),
congestion_control_(cast_environment->Clock(), congestion_control_(cast_environment->Clock(),
video_config.congestion_control_back_off,
video_config.max_bitrate, video_config.max_bitrate,
video_config.min_bitrate, video_config.min_bitrate,
video_config.start_bitrate), max_unacked_frames_),
cast_initialization_status_(STATUS_VIDEO_UNINITIALIZED), cast_initialization_status_(STATUS_VIDEO_UNINITIALIZED),
weak_factory_(this) { weak_factory_(this) {
VLOG(1) << "max_unacked_frames " << max_unacked_frames_; VLOG(1) << "max_unacked_frames " << max_unacked_frames_;
...@@ -123,12 +121,17 @@ void VideoSender::InsertRawVideoFrame( ...@@ -123,12 +121,17 @@ void VideoSender::InsertRawVideoFrame(
return; return;
} }
uint32 bitrate = congestion_control_.GetBitrate(
capture_time + target_playout_delay_, target_playout_delay_);
video_encoder_->SetBitRate(bitrate);
if (video_encoder_->EncodeVideoFrame( if (video_encoder_->EncodeVideoFrame(
video_frame, video_frame,
capture_time, capture_time,
base::Bind(&VideoSender::SendEncodedVideoFrame, base::Bind(&VideoSender::SendEncodedVideoFrame,
weak_factory_.GetWeakPtr(), weak_factory_.GetWeakPtr(),
current_requested_bitrate_))) { bitrate))) {
frames_in_encoder_++; frames_in_encoder_++;
} else { } else {
VLOG(1) << "Encoder rejected a frame. Skipping..."; VLOG(1) << "Encoder rejected a frame. Skipping...";
...@@ -192,6 +195,9 @@ void VideoSender::SendEncodedVideoFrame( ...@@ -192,6 +195,9 @@ void VideoSender::SendEncodedVideoFrame(
SendRtcpReport(is_last_aggressive_report); SendRtcpReport(is_last_aggressive_report);
} }
congestion_control_.SendFrameToTransport(
frame_id, encoded_frame->data.size() * 8, last_send_time_);
transport_sender_->InsertCodedVideoFrame(*encoded_frame); transport_sender_->InsertCodedVideoFrame(*encoded_frame);
} }
...@@ -271,6 +277,8 @@ void VideoSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) { ...@@ -271,6 +277,8 @@ void VideoSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) {
base::TimeDelta min_rtt; base::TimeDelta min_rtt;
base::TimeDelta max_rtt; base::TimeDelta max_rtt;
if (rtcp_.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt)) { if (rtcp_.Rtt(&rtt, &avg_rtt, &min_rtt, &max_rtt)) {
congestion_control_.UpdateRtt(rtt);
// Don't use a RTT lower than our average. // Don't use a RTT lower than our average.
rtt = std::max(rtt, avg_rtt); rtt = std::max(rtt, avg_rtt);
...@@ -294,12 +302,6 @@ void VideoSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) { ...@@ -294,12 +302,6 @@ void VideoSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) {
if (cast_feedback.missing_frames_and_packets_.empty()) { if (cast_feedback.missing_frames_and_packets_.empty()) {
video_encoder_->LatestFrameIdToReference(cast_feedback.ack_frame_id_); video_encoder_->LatestFrameIdToReference(cast_feedback.ack_frame_id_);
if ((latest_acked_frame_id_ + 1) == cast_feedback.ack_frame_id_) {
uint32 new_bitrate = 0;
if (congestion_control_.OnAck(rtt, &new_bitrate)) {
UpdateBitrate(new_bitrate);
}
}
// We only count duplicate ACKs when we have sent newer frames. // We only count duplicate ACKs when we have sent newer frames.
if (latest_acked_frame_id_ == cast_feedback.ack_frame_id_ && if (latest_acked_frame_id_ == cast_feedback.ack_frame_id_ &&
latest_acked_frame_id_ != last_sent_frame_id_) { latest_acked_frame_id_ != last_sent_frame_id_) {
...@@ -318,17 +320,18 @@ void VideoSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) { ...@@ -318,17 +320,18 @@ void VideoSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) {
duplicate_ack_counter_ = 0; duplicate_ack_counter_ = 0;
transport_sender_->ResendPackets( transport_sender_->ResendPackets(
false, cast_feedback.missing_frames_and_packets_); false, cast_feedback.missing_frames_and_packets_);
uint32 new_bitrate = 0;
if (congestion_control_.OnNack(rtt, &new_bitrate)) {
UpdateBitrate(new_bitrate);
}
} }
base::TimeTicks now = cast_environment_->Clock()->NowTicks();
congestion_control_.AckFrame(cast_feedback.ack_frame_id_, now);
RtpTimestamp rtp_timestamp = RtpTimestamp rtp_timestamp =
frame_id_to_rtp_timestamp_[cast_feedback.ack_frame_id_ & 0xff]; frame_id_to_rtp_timestamp_[cast_feedback.ack_frame_id_ & 0xff];
cast_environment_->Logging()->InsertFrameEvent( cast_environment_->Logging()->InsertFrameEvent(now,
cast_environment_->Clock()->NowTicks(), FRAME_ACK_RECEIVED, VIDEO_EVENT, FRAME_ACK_RECEIVED,
rtp_timestamp, cast_feedback.ack_frame_id_); VIDEO_EVENT,
rtp_timestamp,
cast_feedback.ack_frame_id_);
const bool is_acked_out_of_order = const bool is_acked_out_of_order =
static_cast<int32>(cast_feedback.ack_frame_id_ - static_cast<int32>(cast_feedback.ack_frame_id_ -
...@@ -370,12 +373,5 @@ void VideoSender::ResendForKickstart() { ...@@ -370,12 +373,5 @@ void VideoSender::ResendForKickstart() {
transport_sender_->ResendPackets(false, missing_frames_and_packets); transport_sender_->ResendPackets(false, missing_frames_and_packets);
} }
void VideoSender::UpdateBitrate(int new_bitrate) {
// Make sure we don't set the bitrate too insanely low.
DCHECK_GT(new_bitrate, 1000);
video_encoder_->SetBitRate(new_bitrate);
current_requested_bitrate_ = new_bitrate;
}
} // namespace cast } // namespace cast
} // namespace media } // namespace media
...@@ -97,8 +97,6 @@ class VideoSender : public RtcpSenderFeedback, ...@@ -97,8 +97,6 @@ class VideoSender : public RtcpSenderFeedback,
void SendEncodedVideoFrame(int requested_bitrate_before_encode, void SendEncodedVideoFrame(int requested_bitrate_before_encode,
scoped_ptr<transport::EncodedFrame> encoded_frame); scoped_ptr<transport::EncodedFrame> encoded_frame);
void UpdateBitrate(int32 new_bitrate);
const scoped_refptr<CastEnvironment> cast_environment_; const scoped_refptr<CastEnvironment> cast_environment_;
// The total amount of time between a frame's capture/recording on the sender // The total amount of time between a frame's capture/recording on the sender
...@@ -161,10 +159,6 @@ class VideoSender : public RtcpSenderFeedback, ...@@ -161,10 +159,6 @@ class VideoSender : public RtcpSenderFeedback,
// case, VideoSender will trigger a re-send of the next frame. // case, VideoSender will trigger a re-send of the next frame.
int duplicate_ack_counter_; int duplicate_ack_counter_;
// Desired encoder bitrate (in bits per second). This is updated by querying
// |congestion_control_| as each ACK is received.
int current_requested_bitrate_;
// When we get close to the max number of un-acked frames, we set lower // When we get close to the max number of un-acked frames, we set lower
// the bitrate drastically to ensure that we catch up. Without this we // the bitrate drastically to ensure that we catch up. Without this we
// risk getting stuck in a catch-up state forever. // risk getting stuck in a catch-up state forever.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment