| 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/video_sender.h" | 5 #include "media/cast/sender/video_sender.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <cstring> | 8 #include <cstring> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| 11 #include "base/debug/trace_event.h" | 11 #include "base/debug/trace_event.h" |
| 12 #include "base/logging.h" | 12 #include "base/logging.h" |
| 13 #include "base/message_loop/message_loop.h" | 13 #include "base/message_loop/message_loop.h" |
| 14 #include "media/cast/cast_defines.h" | 14 #include "media/cast/cast_defines.h" |
| 15 #include "media/cast/net/cast_transport_config.h" | 15 #include "media/cast/net/cast_transport_config.h" |
| 16 #include "media/cast/sender/external_video_encoder.h" | 16 #include "media/cast/sender/external_video_encoder.h" |
| 17 #include "media/cast/sender/video_encoder_impl.h" | 17 #include "media/cast/sender/video_encoder_impl.h" |
| 18 | 18 |
| 19 namespace media { | 19 namespace media { |
| 20 namespace cast { | 20 namespace cast { |
| 21 | 21 |
| 22 const int kNumAggressiveReportsSentAtStart = 100; | 22 // Note, we use a fixed bitrate value when external video encoder is used. |
| 23 | |
| 24 namespace { | |
| 25 | |
| 26 // Returns a fixed bitrate value when external video encoder is used. | |
| 27 // Some hardware encoder shows bad behavior if we set the bitrate too | 23 // Some hardware encoder shows bad behavior if we set the bitrate too |
| 28 // frequently, e.g. quality drop, not abiding by target bitrate, etc. | 24 // frequently, e.g. quality drop, not abiding by target bitrate, etc. |
| 29 // See details: crbug.com/392086. | 25 // See details: crbug.com/392086. |
| 30 size_t GetFixedBitrate(const VideoSenderConfig& video_config) { | |
| 31 if (!video_config.use_external_encoder) | |
| 32 return 0; | |
| 33 return (video_config.min_bitrate + video_config.max_bitrate) / 2; | |
| 34 } | |
| 35 | |
| 36 } // namespace | |
| 37 | |
| 38 VideoSender::VideoSender( | 26 VideoSender::VideoSender( |
| 39 scoped_refptr<CastEnvironment> cast_environment, | 27 scoped_refptr<CastEnvironment> cast_environment, |
| 40 const VideoSenderConfig& video_config, | 28 const VideoSenderConfig& video_config, |
| 41 const CreateVideoEncodeAcceleratorCallback& create_vea_cb, | 29 const CreateVideoEncodeAcceleratorCallback& create_vea_cb, |
| 42 const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb, | 30 const CreateVideoEncodeMemoryCallback& create_video_encode_mem_cb, |
| 43 CastTransportSender* const transport_sender) | 31 CastTransportSender* const transport_sender) |
| 44 : FrameSender( | 32 : FrameSender( |
| 45 cast_environment, | 33 cast_environment, |
| 34 false, |
| 46 transport_sender, | 35 transport_sender, |
| 47 base::TimeDelta::FromMilliseconds(video_config.rtcp_interval), | 36 base::TimeDelta::FromMilliseconds(video_config.rtcp_interval), |
| 48 kVideoFrequency, | 37 kVideoFrequency, |
| 49 video_config.ssrc, | 38 video_config.ssrc, |
| 50 video_config.max_frame_rate, | 39 video_config.max_frame_rate, |
| 51 video_config.target_playout_delay), | 40 video_config.target_playout_delay, |
| 52 fixed_bitrate_(GetFixedBitrate(video_config)), | 41 NewFixedCongestionControl( |
| 53 frames_in_encoder_(0), | 42 (video_config.min_bitrate + video_config.max_bitrate) / 2)), |
| 54 congestion_control_(cast_environment->Clock(), | 43 last_bitrate_(0), |
| 55 video_config.max_bitrate, | |
| 56 video_config.min_bitrate, | |
| 57 max_unacked_frames_), | |
| 58 weak_factory_(this) { | 44 weak_factory_(this) { |
| 59 cast_initialization_status_ = STATUS_VIDEO_UNINITIALIZED; | 45 cast_initialization_status_ = STATUS_VIDEO_UNINITIALIZED; |
| 60 VLOG(1) << "max_unacked_frames is " << max_unacked_frames_ | 46 VLOG(1) << "max_unacked_frames is " << max_unacked_frames_ |
| 61 << " for target_playout_delay=" | 47 << " for target_playout_delay=" |
| 62 << target_playout_delay_.InMilliseconds() << " ms" | 48 << target_playout_delay_.InMilliseconds() << " ms" |
| 63 << " and max_frame_rate=" << video_config.max_frame_rate; | 49 << " and max_frame_rate=" << video_config.max_frame_rate; |
| 64 DCHECK_GT(max_unacked_frames_, 0); | 50 DCHECK_GT(max_unacked_frames_, 0); |
| 65 | 51 |
| 66 if (video_config.use_external_encoder) { | 52 if (video_config.use_external_encoder) { |
| 67 video_encoder_.reset(new ExternalVideoEncoder(cast_environment, | 53 video_encoder_.reset(new ExternalVideoEncoder(cast_environment, |
| 68 video_config, | 54 video_config, |
| 69 create_vea_cb, | 55 create_vea_cb, |
| 70 create_video_encode_mem_cb)); | 56 create_video_encode_mem_cb)); |
| 71 } else { | 57 } else { |
| 58 congestion_control_.reset( |
| 59 NewAdaptiveCongestionControl(cast_environment->Clock(), |
| 60 video_config.max_bitrate, |
| 61 video_config.min_bitrate, |
| 62 max_unacked_frames_)); |
| 72 video_encoder_.reset(new VideoEncoderImpl( | 63 video_encoder_.reset(new VideoEncoderImpl( |
| 73 cast_environment, video_config, max_unacked_frames_)); | 64 cast_environment, video_config, max_unacked_frames_)); |
| 74 } | 65 } |
| 75 cast_initialization_status_ = STATUS_VIDEO_INITIALIZED; | 66 cast_initialization_status_ = STATUS_VIDEO_INITIALIZED; |
| 76 | 67 |
| 77 media::cast::CastTransportRtpConfig transport_config; | 68 media::cast::CastTransportRtpConfig transport_config; |
| 78 transport_config.ssrc = video_config.ssrc; | 69 transport_config.ssrc = video_config.ssrc; |
| 79 transport_config.feedback_ssrc = video_config.incoming_feedback_ssrc; | 70 transport_config.feedback_ssrc = video_config.incoming_feedback_ssrc; |
| 80 transport_config.rtp_payload_type = video_config.rtp_payload_type; | 71 transport_config.rtp_payload_type = video_config.rtp_payload_type; |
| 81 transport_config.stored_frames = max_unacked_frames_; | 72 transport_config.stored_frames = max_unacked_frames_; |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 117 "cast_perf_test", "InsertRawVideoFrame", | 108 "cast_perf_test", "InsertRawVideoFrame", |
| 118 TRACE_EVENT_SCOPE_THREAD, | 109 TRACE_EVENT_SCOPE_THREAD, |
| 119 "timestamp", capture_time.ToInternalValue(), | 110 "timestamp", capture_time.ToInternalValue(), |
| 120 "rtp_timestamp", rtp_timestamp); | 111 "rtp_timestamp", rtp_timestamp); |
| 121 | 112 |
| 122 if (ShouldDropNextFrame(capture_time)) { | 113 if (ShouldDropNextFrame(capture_time)) { |
| 123 VLOG(1) << "Dropping frame due to too many frames currently in-flight."; | 114 VLOG(1) << "Dropping frame due to too many frames currently in-flight."; |
| 124 return; | 115 return; |
| 125 } | 116 } |
| 126 | 117 |
| 127 uint32 bitrate = fixed_bitrate_; | 118 uint32 bitrate = congestion_control_->GetBitrate( |
| 128 if (!bitrate) { | |
| 129 bitrate = congestion_control_.GetBitrate( | |
| 130 capture_time + target_playout_delay_, target_playout_delay_); | 119 capture_time + target_playout_delay_, target_playout_delay_); |
| 131 DCHECK(bitrate); | 120 if (bitrate != last_bitrate_) { |
| 132 video_encoder_->SetBitRate(bitrate); | 121 video_encoder_->SetBitRate(bitrate); |
| 133 } else if (last_send_time_.is_null()) { | 122 last_bitrate_ = bitrate; |
| 134 // Set the fixed bitrate value to codec until a frame is sent. We might | |
| 135 // set this value a couple times at the very beginning of the stream but | |
| 136 // it is not harmful. | |
| 137 video_encoder_->SetBitRate(bitrate); | |
| 138 } | 123 } |
| 139 | 124 |
| 140 if (video_encoder_->EncodeVideoFrame( | 125 if (video_encoder_->EncodeVideoFrame( |
| 141 video_frame, | 126 video_frame, |
| 142 capture_time, | 127 capture_time, |
| 143 base::Bind(&VideoSender::SendEncodedVideoFrame, | 128 base::Bind(&FrameSender::SendEncodedFrame, |
| 144 weak_factory_.GetWeakPtr(), | 129 weak_factory_.GetWeakPtr(), |
| 145 bitrate))) { | 130 bitrate))) { |
| 146 frames_in_encoder_++; | 131 frames_in_encoder_++; |
| 147 } else { | 132 } else { |
| 148 VLOG(1) << "Encoder rejected a frame. Skipping..."; | 133 VLOG(1) << "Encoder rejected a frame. Skipping..."; |
| 149 } | 134 } |
| 150 } | 135 } |
| 151 | 136 |
| 152 void VideoSender::SendEncodedVideoFrame( | 137 void VideoSender::OnAck(uint32 frame_id) { |
| 153 int requested_bitrate_before_encode, | 138 video_encoder_->LatestFrameIdToReference(frame_id); |
| 154 scoped_ptr<EncodedFrame> encoded_frame) { | |
| 155 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); | |
| 156 | |
| 157 DCHECK_GT(frames_in_encoder_, 0); | |
| 158 frames_in_encoder_--; | |
| 159 | |
| 160 const uint32 frame_id = encoded_frame->frame_id; | |
| 161 | |
| 162 const bool is_first_frame_to_be_sent = last_send_time_.is_null(); | |
| 163 last_send_time_ = cast_environment_->Clock()->NowTicks(); | |
| 164 last_sent_frame_id_ = frame_id; | |
| 165 // If this is the first frame about to be sent, fake the value of | |
| 166 // |latest_acked_frame_id_| to indicate the receiver starts out all caught up. | |
| 167 // Also, schedule the periodic frame re-send checks. | |
| 168 if (is_first_frame_to_be_sent) { | |
| 169 latest_acked_frame_id_ = frame_id - 1; | |
| 170 ScheduleNextResendCheck(); | |
| 171 } | |
| 172 | |
| 173 VLOG_IF(1, encoded_frame->dependency == EncodedFrame::KEY) | |
| 174 << "Send encoded key frame; frame_id: " << frame_id; | |
| 175 | |
| 176 cast_environment_->Logging()->InsertEncodedFrameEvent( | |
| 177 last_send_time_, FRAME_ENCODED, VIDEO_EVENT, encoded_frame->rtp_timestamp, | |
| 178 frame_id, static_cast<int>(encoded_frame->data.size()), | |
| 179 encoded_frame->dependency == EncodedFrame::KEY, | |
| 180 requested_bitrate_before_encode); | |
| 181 | |
| 182 RecordLatestFrameTimestamps(frame_id, | |
| 183 encoded_frame->reference_time, | |
| 184 encoded_frame->rtp_timestamp); | |
| 185 | |
| 186 // Used by chrome/browser/extension/api/cast_streaming/performance_test.cc | |
| 187 TRACE_EVENT_INSTANT1( | |
| 188 "cast_perf_test", "VideoFrameEncoded", | |
| 189 TRACE_EVENT_SCOPE_THREAD, | |
| 190 "rtp_timestamp", encoded_frame->rtp_timestamp); | |
| 191 | |
| 192 // At the start of the session, it's important to send reports before each | |
| 193 // frame so that the receiver can properly compute playout times. The reason | |
| 194 // more than one report is sent is because transmission is not guaranteed, | |
| 195 // only best effort, so send enough that one should almost certainly get | |
| 196 // through. | |
| 197 if (num_aggressive_rtcp_reports_sent_ < kNumAggressiveReportsSentAtStart) { | |
| 198 // SendRtcpReport() will schedule future reports to be made if this is the | |
| 199 // last "aggressive report." | |
| 200 ++num_aggressive_rtcp_reports_sent_; | |
| 201 const bool is_last_aggressive_report = | |
| 202 (num_aggressive_rtcp_reports_sent_ == kNumAggressiveReportsSentAtStart); | |
| 203 VLOG_IF(1, is_last_aggressive_report) << "Sending last aggressive report."; | |
| 204 SendRtcpReport(is_last_aggressive_report); | |
| 205 } | |
| 206 | |
| 207 congestion_control_.SendFrameToTransport( | |
| 208 frame_id, encoded_frame->data.size() * 8, last_send_time_); | |
| 209 | |
| 210 if (send_target_playout_delay_) { | |
| 211 encoded_frame->new_playout_delay_ms = | |
| 212 target_playout_delay_.InMilliseconds(); | |
| 213 } | |
| 214 transport_sender_->InsertFrame(ssrc_, *encoded_frame); | |
| 215 } | |
| 216 | |
| 217 void VideoSender::OnReceivedCastFeedback(const RtcpCastMessage& cast_feedback) { | |
| 218 DCHECK(cast_environment_->CurrentlyOn(CastEnvironment::MAIN)); | |
| 219 | |
| 220 base::TimeDelta rtt; | |
| 221 base::TimeDelta avg_rtt; | |
| 222 base::TimeDelta min_rtt; | |
| 223 base::TimeDelta max_rtt; | |
| 224 if (is_rtt_available()) { | |
| 225 rtt = rtt_; | |
| 226 avg_rtt = avg_rtt_; | |
| 227 min_rtt = min_rtt_; | |
| 228 max_rtt = max_rtt_; | |
| 229 | |
| 230 congestion_control_.UpdateRtt(rtt); | |
| 231 | |
| 232 // Don't use a RTT lower than our average. | |
| 233 rtt = std::max(rtt, avg_rtt); | |
| 234 | |
| 235 // Having the RTT values implies the receiver sent back a receiver report | |
| 236 // based on it having received a report from here. Therefore, ensure this | |
| 237 // sender stops aggressively sending reports. | |
| 238 if (num_aggressive_rtcp_reports_sent_ < kNumAggressiveReportsSentAtStart) { | |
| 239 VLOG(1) << "No longer a need to send reports aggressively (sent " | |
| 240 << num_aggressive_rtcp_reports_sent_ << ")."; | |
| 241 num_aggressive_rtcp_reports_sent_ = kNumAggressiveReportsSentAtStart; | |
| 242 ScheduleNextRtcpReport(); | |
| 243 } | |
| 244 } else { | |
| 245 // We have no measured value use default. | |
| 246 rtt = base::TimeDelta::FromMilliseconds(kStartRttMs); | |
| 247 } | |
| 248 | |
| 249 if (last_send_time_.is_null()) | |
| 250 return; // Cannot get an ACK without having first sent a frame. | |
| 251 | |
| 252 if (cast_feedback.missing_frames_and_packets.empty()) { | |
| 253 video_encoder_->LatestFrameIdToReference(cast_feedback.ack_frame_id); | |
| 254 | |
| 255 // We only count duplicate ACKs when we have sent newer frames. | |
| 256 if (latest_acked_frame_id_ == cast_feedback.ack_frame_id && | |
| 257 latest_acked_frame_id_ != last_sent_frame_id_) { | |
| 258 duplicate_ack_counter_++; | |
| 259 } else { | |
| 260 duplicate_ack_counter_ = 0; | |
| 261 } | |
| 262 // TODO(miu): The values "2" and "3" should be derived from configuration. | |
| 263 if (duplicate_ack_counter_ >= 2 && duplicate_ack_counter_ % 3 == 2) { | |
| 264 VLOG(1) << "Received duplicate ACK for frame " << latest_acked_frame_id_; | |
| 265 ResendForKickstart(); | |
| 266 } | |
| 267 } else { | |
| 268 // Only count duplicated ACKs if there is no NACK request in between. | |
| 269 // This is to avoid aggresive resend. | |
| 270 duplicate_ack_counter_ = 0; | |
| 271 } | |
| 272 | |
| 273 base::TimeTicks now = cast_environment_->Clock()->NowTicks(); | |
| 274 congestion_control_.AckFrame(cast_feedback.ack_frame_id, now); | |
| 275 | |
| 276 cast_environment_->Logging()->InsertFrameEvent( | |
| 277 now, | |
| 278 FRAME_ACK_RECEIVED, | |
| 279 VIDEO_EVENT, | |
| 280 GetRecordedRtpTimestamp(cast_feedback.ack_frame_id), | |
| 281 cast_feedback.ack_frame_id); | |
| 282 | |
| 283 const bool is_acked_out_of_order = | |
| 284 static_cast<int32>(cast_feedback.ack_frame_id - | |
| 285 latest_acked_frame_id_) < 0; | |
| 286 VLOG(2) << "Received ACK" << (is_acked_out_of_order ? " out-of-order" : "") | |
| 287 << " for frame " << cast_feedback.ack_frame_id; | |
| 288 if (!is_acked_out_of_order) { | |
| 289 // Cancel resends of acked frames. | |
| 290 std::vector<uint32> cancel_sending_frames; | |
| 291 while (latest_acked_frame_id_ != cast_feedback.ack_frame_id) { | |
| 292 latest_acked_frame_id_++; | |
| 293 cancel_sending_frames.push_back(latest_acked_frame_id_); | |
| 294 } | |
| 295 transport_sender_->CancelSendingFrames(ssrc_, cancel_sending_frames); | |
| 296 latest_acked_frame_id_ = cast_feedback.ack_frame_id; | |
| 297 } | |
| 298 } | |
| 299 | |
| 300 bool VideoSender::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 += frames_in_encoder_; | |
| 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: " << frames_in_encoder_ | |
| 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 } | 139 } |
| 326 | 140 |
| 327 } // namespace cast | 141 } // namespace cast |
| 328 } // namespace media | 142 } // namespace media |
| OLD | NEW |