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 "base/debug/trace_event.h" |
| 8 |
7 namespace media { | 9 namespace media { |
8 namespace cast { | 10 namespace cast { |
9 namespace { | 11 namespace { |
| 12 |
10 const int kMinSchedulingDelayMs = 1; | 13 const int kMinSchedulingDelayMs = 1; |
| 14 const int kNumAggressiveReportsSentAtStart = 100; |
| 15 |
11 } // namespace | 16 } // namespace |
12 | 17 |
13 FrameSender::FrameSender(scoped_refptr<CastEnvironment> cast_environment, | 18 FrameSender::FrameSender(scoped_refptr<CastEnvironment> cast_environment, |
| 19 bool is_audio, |
14 CastTransportSender* const transport_sender, | 20 CastTransportSender* const transport_sender, |
15 base::TimeDelta rtcp_interval, | 21 base::TimeDelta rtcp_interval, |
16 int rtp_timebase, | 22 int rtp_timebase, |
17 uint32 ssrc, | 23 uint32 ssrc, |
18 double max_frame_rate, | 24 double max_frame_rate, |
19 base::TimeDelta playout_delay) | 25 base::TimeDelta playout_delay, |
| 26 CongestionControl* congestion_control) |
20 : cast_environment_(cast_environment), | 27 : cast_environment_(cast_environment), |
21 transport_sender_(transport_sender), | 28 transport_sender_(transport_sender), |
22 ssrc_(ssrc), | 29 ssrc_(ssrc), |
23 rtt_available_(false), | 30 rtt_available_(false), |
24 rtcp_interval_(rtcp_interval), | 31 rtcp_interval_(rtcp_interval), |
25 max_frame_rate_(max_frame_rate), | 32 max_frame_rate_(max_frame_rate), |
| 33 frames_in_encoder_(0), |
26 num_aggressive_rtcp_reports_sent_(0), | 34 num_aggressive_rtcp_reports_sent_(0), |
27 last_sent_frame_id_(0), | 35 last_sent_frame_id_(0), |
28 latest_acked_frame_id_(0), | 36 latest_acked_frame_id_(0), |
29 duplicate_ack_counter_(0), | 37 duplicate_ack_counter_(0), |
30 rtp_timebase_(rtp_timebase), | 38 rtp_timebase_(rtp_timebase), |
| 39 congestion_control_(congestion_control), |
| 40 is_audio_(is_audio), |
31 weak_factory_(this) { | 41 weak_factory_(this) { |
32 DCHECK_GT(rtp_timebase_, 0); | 42 DCHECK_GT(rtp_timebase_, 0); |
33 SetTargetPlayoutDelay(playout_delay); | 43 SetTargetPlayoutDelay(playout_delay); |
34 send_target_playout_delay_ = false; | 44 send_target_playout_delay_ = false; |
35 memset(frame_rtp_timestamps_, 0, sizeof(frame_rtp_timestamps_)); | 45 memset(frame_rtp_timestamps_, 0, sizeof(frame_rtp_timestamps_)); |
36 } | 46 } |
37 | 47 |
38 FrameSender::~FrameSender() { | 48 FrameSender::~FrameSender() { |
39 } | 49 } |
40 | 50 |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
151 } | 161 } |
152 | 162 |
153 base::TimeTicks FrameSender::GetRecordedReferenceTime(uint32 frame_id) const { | 163 base::TimeTicks FrameSender::GetRecordedReferenceTime(uint32 frame_id) const { |
154 return frame_reference_times_[frame_id % arraysize(frame_reference_times_)]; | 164 return frame_reference_times_[frame_id % arraysize(frame_reference_times_)]; |
155 } | 165 } |
156 | 166 |
157 RtpTimestamp FrameSender::GetRecordedRtpTimestamp(uint32 frame_id) const { | 167 RtpTimestamp FrameSender::GetRecordedRtpTimestamp(uint32 frame_id) const { |
158 return frame_rtp_timestamps_[frame_id % arraysize(frame_rtp_timestamps_)]; | 168 return frame_rtp_timestamps_[frame_id % arraysize(frame_rtp_timestamps_)]; |
159 } | 169 } |
160 | 170 |
| 171 |
| 172 void FrameSender::SendEncodedFrame( |
| 173 int requested_bitrate_before_encode, |
| 174 scoped_ptr<EncodedFrame> encoded_frame) { |
| 175 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); |
| 176 |
| 177 DCHECK_GT(frames_in_encoder_, 0) << " is_audio: " << is_audio_; |
| 178 frames_in_encoder_--; |
| 179 |
| 180 const uint32 frame_id = encoded_frame->frame_id; |
| 181 |
| 182 const bool is_first_frame_to_be_sent = last_send_time_.is_null(); |
| 183 last_send_time_ = cast_environment_->Clock()->NowTicks(); |
| 184 last_sent_frame_id_ = frame_id; |
| 185 // If this is the first frame about to be sent, fake the value of |
| 186 // |latest_acked_frame_id_| to indicate the receiver starts out all caught up. |
| 187 // Also, schedule the periodic frame re-send checks. |
| 188 if (is_first_frame_to_be_sent) { |
| 189 latest_acked_frame_id_ = frame_id - 1; |
| 190 ScheduleNextResendCheck(); |
| 191 } |
| 192 |
| 193 VLOG_IF(1, encoded_frame->dependency == EncodedFrame::KEY) |
| 194 << "Send encoded key frame; frame_id: " << frame_id; |
| 195 |
| 196 cast_environment_->Logging()->InsertEncodedFrameEvent( |
| 197 last_send_time_, FRAME_ENCODED, |
| 198 is_audio_ ? AUDIO_EVENT : VIDEO_EVENT, |
| 199 encoded_frame->rtp_timestamp, |
| 200 frame_id, static_cast<int>(encoded_frame->data.size()), |
| 201 encoded_frame->dependency == EncodedFrame::KEY, |
| 202 requested_bitrate_before_encode); |
| 203 |
| 204 RecordLatestFrameTimestamps(frame_id, |
| 205 encoded_frame->reference_time, |
| 206 encoded_frame->rtp_timestamp); |
| 207 |
| 208 if (!is_audio_) { |
| 209 // Used by chrome/browser/extension/api/cast_streaming/performance_test.cc |
| 210 TRACE_EVENT_INSTANT1( |
| 211 "cast_perf_test", "VideoFrameEncoded", |
| 212 TRACE_EVENT_SCOPE_THREAD, |
| 213 "rtp_timestamp", encoded_frame->rtp_timestamp); |
| 214 } |
| 215 |
| 216 // At the start of the session, it's important to send reports before each |
| 217 // frame so that the receiver can properly compute playout times. The reason |
| 218 // more than one report is sent is because transmission is not guaranteed, |
| 219 // only best effort, so send enough that one should almost certainly get |
| 220 // through. |
| 221 if (num_aggressive_rtcp_reports_sent_ < kNumAggressiveReportsSentAtStart) { |
| 222 // SendRtcpReport() will schedule future reports to be made if this is the |
| 223 // last "aggressive report." |
| 224 ++num_aggressive_rtcp_reports_sent_; |
| 225 const bool is_last_aggressive_report = |
| 226 (num_aggressive_rtcp_reports_sent_ == kNumAggressiveReportsSentAtStart); |
| 227 VLOG_IF(1, is_last_aggressive_report) << "Sending last aggressive report."; |
| 228 SendRtcpReport(is_last_aggressive_report); |
| 229 } |
| 230 |
| 231 congestion_control_->SendFrameToTransport( |
| 232 frame_id, encoded_frame->data.size() * 8, last_send_time_); |
| 233 |
| 234 if (send_target_playout_delay_) { |
| 235 encoded_frame->new_playout_delay_ms = |
| 236 target_playout_delay_.InMilliseconds(); |
| 237 } |
| 238 transport_sender_->InsertFrame(ssrc_, *encoded_frame); |
| 239 } |
| 240 |
| 241 void FrameSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) { |
| 242 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); |
| 243 |
| 244 base::TimeDelta rtt; |
| 245 base::TimeDelta avg_rtt; |
| 246 base::TimeDelta min_rtt; |
| 247 base::TimeDelta max_rtt; |
| 248 if (is_rtt_available()) { |
| 249 rtt = rtt_; |
| 250 avg_rtt = avg_rtt_; |
| 251 min_rtt = min_rtt_; |
| 252 max_rtt = max_rtt_; |
| 253 |
| 254 congestion_control_->UpdateRtt(rtt); |
| 255 |
| 256 // Don't use a RTT lower than our average. |
| 257 rtt = std::max(rtt, avg_rtt); |
| 258 |
| 259 // Having the RTT values implies the receiver sent back a receiver report |
| 260 // based on it having received a report from here. Therefore, ensure this |
| 261 // sender stops aggressively sending reports. |
| 262 if (num_aggressive_rtcp_reports_sent_ < kNumAggressiveReportsSentAtStart) { |
| 263 VLOG(1) << "No longer a need to send reports aggressively (sent " |
| 264 << num_aggressive_rtcp_reports_sent_ << ")."; |
| 265 num_aggressive_rtcp_reports_sent_ = kNumAggressiveReportsSentAtStart; |
| 266 ScheduleNextRtcpReport(); |
| 267 } |
| 268 } else { |
| 269 // We have no measured value use default. |
| 270 rtt = base::TimeDelta::FromMilliseconds(kStartRttMs); |
| 271 } |
| 272 |
| 273 if (last_send_time_.is_null()) |
| 274 return; // Cannot get an ACK without having first sent a frame. |
| 275 |
| 276 if (cast_feedback.missing_frames_and_packets.empty()) { |
| 277 OnAck(cast_feedback.ack_frame_id); |
| 278 |
| 279 // We only count duplicate ACKs when we have sent newer frames. |
| 280 if (latest_acked_frame_id_ == cast_feedback.ack_frame_id && |
| 281 latest_acked_frame_id_ != last_sent_frame_id_) { |
| 282 duplicate_ack_counter_++; |
| 283 } else { |
| 284 duplicate_ack_counter_ = 0; |
| 285 } |
| 286 // TODO(miu): The values "2" and "3" should be derived from configuration. |
| 287 if (duplicate_ack_counter_ >= 2 && duplicate_ack_counter_ % 3 == 2) { |
| 288 VLOG(1) << "Received duplicate ACK for frame " << latest_acked_frame_id_; |
| 289 ResendForKickstart(); |
| 290 } |
| 291 } else { |
| 292 // Only count duplicated ACKs if there is no NACK request in between. |
| 293 // This is to avoid aggresive resend. |
| 294 duplicate_ack_counter_ = 0; |
| 295 } |
| 296 |
| 297 base::TimeTicks now = cast_environment_->Clock()->NowTicks(); |
| 298 congestion_control_->AckFrame(cast_feedback.ack_frame_id, now); |
| 299 |
| 300 cast_environment_->Logging()->InsertFrameEvent( |
| 301 now, |
| 302 FRAME_ACK_RECEIVED, |
| 303 is_audio_ ? AUDIO_EVENT : VIDEO_EVENT, |
| 304 GetRecordedRtpTimestamp(cast_feedback.ack_frame_id), |
| 305 cast_feedback.ack_frame_id); |
| 306 |
| 307 const bool is_acked_out_of_order = |
| 308 static_cast<int32>(cast_feedback.ack_frame_id - |
| 309 latest_acked_frame_id_) < 0; |
| 310 VLOG(2) << "Received ACK" << (is_acked_out_of_order ? " out-of-order" : "") |
| 311 << " for frame " << cast_feedback.ack_frame_id; |
| 312 if (!is_acked_out_of_order) { |
| 313 // Cancel resends of acked frames. |
| 314 std::vector<uint32> cancel_sending_frames; |
| 315 while (latest_acked_frame_id_ != cast_feedback.ack_frame_id) { |
| 316 latest_acked_frame_id_++; |
| 317 cancel_sending_frames.push_back(latest_acked_frame_id_); |
| 318 } |
| 319 transport_sender_->CancelSendingFrames(ssrc_, cancel_sending_frames); |
| 320 latest_acked_frame_id_ = cast_feedback.ack_frame_id; |
| 321 } |
| 322 } |
| 323 |
| 324 bool FrameSender::ShouldDropNextFrame(base::TimeTicks capture_time) const { |
| 325 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); |
| 326 int frames_in_flight = 0; |
| 327 base::TimeDelta duration_in_flight; |
| 328 if (!last_send_time_.is_null()) { |
| 329 frames_in_flight = |
| 330 static_cast<int32>(last_sent_frame_id_ - latest_acked_frame_id_); |
| 331 if (frames_in_flight > 0) { |
| 332 const uint32 oldest_unacked_frame_id = latest_acked_frame_id_ + 1; |
| 333 duration_in_flight = |
| 334 capture_time - GetRecordedReferenceTime(oldest_unacked_frame_id); |
| 335 } |
| 336 } |
| 337 frames_in_flight += frames_in_encoder_; |
| 338 VLOG(2) << frames_in_flight |
| 339 << " frames in flight; last sent: " << last_sent_frame_id_ |
| 340 << "; latest acked: " << latest_acked_frame_id_ |
| 341 << "; frames in encoder: " << frames_in_encoder_ |
| 342 << "; duration in flight: " |
| 343 << duration_in_flight.InMicroseconds() << " usec (" |
| 344 << (target_playout_delay_ > base::TimeDelta() ? |
| 345 100 * duration_in_flight / target_playout_delay_ : |
| 346 kint64max) << "%)"; |
| 347 return frames_in_flight >= max_unacked_frames_ || |
| 348 duration_in_flight >= target_playout_delay_; |
| 349 } |
| 350 |
161 } // namespace cast | 351 } // namespace cast |
162 } // namespace media | 352 } // namespace media |
OLD | NEW |