Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/cast/sender/frame_sender.h" | 5 #include "media/cast/sender/frame_sender.h" |
| 6 | 6 |
| 7 #include <algorithm> | |
| 8 | |
| 7 #include "base/debug/trace_event.h" | 9 #include "base/debug/trace_event.h" |
| 8 | 10 |
| 9 namespace media { | 11 namespace media { |
| 10 namespace cast { | 12 namespace cast { |
| 11 namespace { | 13 namespace { |
| 12 | 14 |
| 13 const int kMinSchedulingDelayMs = 1; | 15 const int kMinSchedulingDelayMs = 1; |
| 14 const int kNumAggressiveReportsSentAtStart = 100; | 16 const int kNumAggressiveReportsSentAtStart = 100; |
| 15 | 17 |
| 18 // These control the amount and granularity of history in the "max round trip | |
| 19 // time" estimation logic. 40 buckets * 15000 millis --> 10 minutes total | |
| 20 const int kNumRttBuckets = 40; | |
| 21 const int kRttBucketRotationMs = 15000; | |
| 22 | |
| 16 } // namespace | 23 } // namespace |
| 17 | 24 |
| 18 FrameSender::FrameSender(scoped_refptr<CastEnvironment> cast_environment, | 25 FrameSender::FrameSender(scoped_refptr<CastEnvironment> cast_environment, |
| 19 bool is_audio, | 26 bool is_audio, |
| 20 CastTransportSender* const transport_sender, | 27 CastTransportSender* const transport_sender, |
| 21 base::TimeDelta rtcp_interval, | 28 base::TimeDelta rtcp_interval, |
| 22 int rtp_timebase, | 29 int rtp_timebase, |
| 23 uint32 ssrc, | 30 uint32 ssrc, |
| 24 double max_frame_rate, | 31 double max_frame_rate, |
| 25 base::TimeDelta playout_delay, | 32 base::TimeDelta playout_delay, |
| 26 CongestionControl* congestion_control) | 33 CongestionControl* congestion_control) |
| 27 : cast_environment_(cast_environment), | 34 : cast_environment_(cast_environment), |
| 28 transport_sender_(transport_sender), | 35 transport_sender_(transport_sender), |
| 29 ssrc_(ssrc), | 36 ssrc_(ssrc), |
| 30 rtcp_interval_(rtcp_interval), | 37 rtcp_interval_(rtcp_interval), |
| 31 max_frame_rate_(max_frame_rate), | 38 max_frame_rate_(max_frame_rate), |
| 32 num_aggressive_rtcp_reports_sent_(0), | 39 num_aggressive_rtcp_reports_sent_(0), |
| 33 last_sent_frame_id_(0), | 40 last_sent_frame_id_(0), |
| 34 latest_acked_frame_id_(0), | 41 latest_acked_frame_id_(0), |
| 35 duplicate_ack_counter_(0), | 42 duplicate_ack_counter_(0), |
| 43 congestion_control_(congestion_control), | |
| 36 rtp_timebase_(rtp_timebase), | 44 rtp_timebase_(rtp_timebase), |
| 37 congestion_control_(congestion_control), | |
| 38 is_audio_(is_audio), | 45 is_audio_(is_audio), |
| 39 weak_factory_(this) { | 46 weak_factory_(this) { |
| 40 DCHECK(transport_sender_); | 47 DCHECK(transport_sender_); |
| 48 DCHECK_GE(max_frame_rate_, 0.001); | |
| 41 DCHECK_GT(rtp_timebase_, 0); | 49 DCHECK_GT(rtp_timebase_, 0); |
| 42 DCHECK(congestion_control_); | 50 DCHECK(congestion_control_); |
| 43 SetTargetPlayoutDelay(playout_delay); | 51 SetTargetPlayoutDelay(playout_delay); |
| 44 send_target_playout_delay_ = false; | 52 send_target_playout_delay_ = false; |
| 45 memset(frame_rtp_timestamps_, 0, sizeof(frame_rtp_timestamps_)); | 53 memset(frame_rtp_timestamps_, 0, sizeof(frame_rtp_timestamps_)); |
| 46 } | 54 } |
| 47 | 55 |
| 48 FrameSender::~FrameSender() { | 56 FrameSender::~FrameSender() { |
| 49 } | 57 } |
| 50 | 58 |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 84 static_cast<uint32>(rtp_delta); | 92 static_cast<uint32>(rtp_delta); |
| 85 transport_sender_->SendSenderReport(ssrc_, now, now_as_rtp_timestamp); | 93 transport_sender_->SendSenderReport(ssrc_, now, now_as_rtp_timestamp); |
| 86 | 94 |
| 87 if (schedule_future_reports) | 95 if (schedule_future_reports) |
| 88 ScheduleNextRtcpReport(); | 96 ScheduleNextRtcpReport(); |
| 89 } | 97 } |
| 90 | 98 |
| 91 void FrameSender::OnMeasuredRoundTripTime(base::TimeDelta rtt) { | 99 void FrameSender::OnMeasuredRoundTripTime(base::TimeDelta rtt) { |
| 92 DCHECK(rtt > base::TimeDelta()); | 100 DCHECK(rtt > base::TimeDelta()); |
| 93 current_round_trip_time_ = rtt; | 101 current_round_trip_time_ = rtt; |
| 102 | |
| 103 // Rotate the "max round trip time" buckets, if needed to prune-out old | |
|
hubbe
2014/09/11 23:05:06
This is a fun algorithm, but do we need it?
Would
miu
2014/09/18 01:10:52
Removed as discussed.
| |
| 104 // history, and then update the current bucket and overall expected value. | |
| 105 const base::TimeTicks now = cast_environment_->Clock()->NowTicks(); | |
| 106 if (last_max_rtt_bucket_rotation_.is_null() || | |
| 107 (now - last_max_rtt_bucket_rotation_) > | |
| 108 base::TimeDelta::FromMilliseconds(kRttBucketRotationMs)) { | |
| 109 if (max_rtt_buckets_.size() == kNumRttBuckets) | |
| 110 max_rtt_buckets_.pop_front(); | |
| 111 max_rtt_buckets_.push_back(base::TimeDelta()); | |
| 112 last_max_rtt_bucket_rotation_ = now; | |
| 113 } | |
| 114 if (current_round_trip_time_ > max_rtt_buckets_.back()) { | |
| 115 max_rtt_buckets_.back() = current_round_trip_time_; | |
| 116 const base::TimeDelta& max_expected_rtt = | |
| 117 *(std::max_element(max_rtt_buckets_.begin(), max_rtt_buckets_.end())); | |
| 118 expected_max_one_way_trip_time_ = max_expected_rtt / 2; | |
| 119 } | |
| 120 | |
| 121 VLOG(2) | |
| 122 << (is_audio_ ? "AUDIO[" : "VIDEO[") << ssrc_ << "] Last measured RTT is " | |
| 123 << current_round_trip_time_.InMicroseconds() | |
| 124 << " usec (expected max one way trip time is " | |
| 125 << expected_max_one_way_trip_time_.InMicroseconds() << " usec)"; | |
| 94 } | 126 } |
| 95 | 127 |
| 96 void FrameSender::SetTargetPlayoutDelay( | 128 void FrameSender::SetTargetPlayoutDelay( |
| 97 base::TimeDelta new_target_playout_delay) { | 129 base::TimeDelta new_target_playout_delay) { |
| 98 target_playout_delay_ = new_target_playout_delay; | 130 target_playout_delay_ = new_target_playout_delay; |
| 99 max_unacked_frames_ = | 131 max_unacked_frames_ = |
| 100 std::min(kMaxUnackedFrames, | 132 std::min(kMaxUnackedFrames, |
| 101 1 + static_cast<int>(target_playout_delay_ * | 133 1 + static_cast<int>(target_playout_delay_ * |
| 102 max_frame_rate_ / | 134 max_frame_rate_ / |
| 103 base::TimeDelta::FromSeconds(1))); | 135 base::TimeDelta::FromSeconds(1))); |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 155 } | 187 } |
| 156 | 188 |
| 157 base::TimeTicks FrameSender::GetRecordedReferenceTime(uint32 frame_id) const { | 189 base::TimeTicks FrameSender::GetRecordedReferenceTime(uint32 frame_id) const { |
| 158 return frame_reference_times_[frame_id % arraysize(frame_reference_times_)]; | 190 return frame_reference_times_[frame_id % arraysize(frame_reference_times_)]; |
| 159 } | 191 } |
| 160 | 192 |
| 161 RtpTimestamp FrameSender::GetRecordedRtpTimestamp(uint32 frame_id) const { | 193 RtpTimestamp FrameSender::GetRecordedRtpTimestamp(uint32 frame_id) const { |
| 162 return frame_rtp_timestamps_[frame_id % arraysize(frame_rtp_timestamps_)]; | 194 return frame_rtp_timestamps_[frame_id % arraysize(frame_rtp_timestamps_)]; |
| 163 } | 195 } |
| 164 | 196 |
| 197 int FrameSender::GetUnackedFrameCount() const { | |
| 198 const int count = | |
| 199 static_cast<int32>(last_sent_frame_id_ - latest_acked_frame_id_); | |
| 200 DCHECK_GE(count, 0); | |
| 201 return count; | |
| 202 } | |
| 203 | |
| 204 base::TimeDelta FrameSender::GetAllowedInFlightMediaDuration() const { | |
| 205 // The total amount allowed in-flight media should equal the amount that fits | |
| 206 // within the entire playout delay window, plus the maximum amount of time it | |
| 207 // could take to receive an ACK from the receiver. | |
| 208 return target_playout_delay_ + | |
| 209 std::min(expected_max_one_way_trip_time_, target_playout_delay_ / 2); | |
| 210 } | |
| 211 | |
| 165 void FrameSender::SendEncodedFrame( | 212 void FrameSender::SendEncodedFrame( |
| 166 int requested_bitrate_before_encode, | 213 int requested_bitrate_before_encode, |
| 167 scoped_ptr<EncodedFrame> encoded_frame) { | 214 scoped_ptr<EncodedFrame> encoded_frame) { |
| 168 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); | 215 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); |
| 169 | 216 |
| 217 VLOG(2) << (is_audio_ ? "AUDIO[" : "VIDEO[") << ssrc_ | |
| 218 << "] About to send another frame: last_sent=" | |
| 219 << last_sent_frame_id_ << ", latest_acked=" << latest_acked_frame_id_; | |
| 220 | |
| 170 const uint32 frame_id = encoded_frame->frame_id; | 221 const uint32 frame_id = encoded_frame->frame_id; |
| 171 | 222 |
| 172 const bool is_first_frame_to_be_sent = last_send_time_.is_null(); | 223 const bool is_first_frame_to_be_sent = last_send_time_.is_null(); |
| 173 last_send_time_ = cast_environment_->Clock()->NowTicks(); | 224 last_send_time_ = cast_environment_->Clock()->NowTicks(); |
| 174 last_sent_frame_id_ = frame_id; | 225 last_sent_frame_id_ = frame_id; |
| 175 // If this is the first frame about to be sent, fake the value of | 226 // If this is the first frame about to be sent, fake the value of |
| 176 // |latest_acked_frame_id_| to indicate the receiver starts out all caught up. | 227 // |latest_acked_frame_id_| to indicate the receiver starts out all caught up. |
| 177 // Also, schedule the periodic frame re-send checks. | 228 // Also, schedule the periodic frame re-send checks. |
| 178 if (is_first_frame_to_be_sent) { | 229 if (is_first_frame_to_be_sent) { |
| 179 latest_acked_frame_id_ = frame_id - 1; | 230 latest_acked_frame_id_ = frame_id - 1; |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 290 std::vector<uint32> cancel_sending_frames; | 341 std::vector<uint32> cancel_sending_frames; |
| 291 while (latest_acked_frame_id_ != cast_feedback.ack_frame_id) { | 342 while (latest_acked_frame_id_ != cast_feedback.ack_frame_id) { |
| 292 latest_acked_frame_id_++; | 343 latest_acked_frame_id_++; |
| 293 cancel_sending_frames.push_back(latest_acked_frame_id_); | 344 cancel_sending_frames.push_back(latest_acked_frame_id_); |
| 294 } | 345 } |
| 295 transport_sender_->CancelSendingFrames(ssrc_, cancel_sending_frames); | 346 transport_sender_->CancelSendingFrames(ssrc_, cancel_sending_frames); |
| 296 latest_acked_frame_id_ = cast_feedback.ack_frame_id; | 347 latest_acked_frame_id_ = cast_feedback.ack_frame_id; |
| 297 } | 348 } |
| 298 } | 349 } |
| 299 | 350 |
| 300 bool FrameSender::ShouldDropNextFrame(base::TimeTicks capture_time) const { | |
| 301 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); | |
| 302 int frames_in_flight = 0; | |
| 303 base::TimeDelta duration_in_flight; | |
| 304 if (!last_send_time_.is_null()) { | |
| 305 frames_in_flight = | |
| 306 static_cast<int32>(last_sent_frame_id_ - latest_acked_frame_id_); | |
| 307 if (frames_in_flight > 0) { | |
| 308 const uint32 oldest_unacked_frame_id = latest_acked_frame_id_ + 1; | |
| 309 duration_in_flight = | |
| 310 capture_time - GetRecordedReferenceTime(oldest_unacked_frame_id); | |
| 311 } | |
| 312 } | |
| 313 frames_in_flight += GetNumberOfFramesInEncoder(); | |
| 314 VLOG(2) << frames_in_flight | |
| 315 << " frames in flight; last sent: " << last_sent_frame_id_ | |
| 316 << "; latest acked: " << latest_acked_frame_id_ | |
| 317 << "; frames in encoder: " << GetNumberOfFramesInEncoder() | |
| 318 << "; duration in flight: " | |
| 319 << duration_in_flight.InMicroseconds() << " usec (" | |
| 320 << (target_playout_delay_ > base::TimeDelta() ? | |
| 321 100 * duration_in_flight / target_playout_delay_ : | |
| 322 kint64max) << "%)"; | |
| 323 return frames_in_flight >= max_unacked_frames_ || | |
| 324 duration_in_flight >= target_playout_delay_; | |
| 325 } | |
| 326 | |
| 327 } // namespace cast | 351 } // namespace cast |
| 328 } // namespace media | 352 } // namespace media |
| OLD | NEW |