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 |