| 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" |
| (...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 153 // Drop the frame if either its RTP or reference timestamp is not an increase | 153 // Drop the frame if either its RTP or reference timestamp is not an increase |
| 154 // over the last frame's. This protects: 1) the duration calculations that | 154 // over the last frame's. This protects: 1) the duration calculations that |
| 155 // assume timestamps are monotonically non-decreasing, and 2) assumptions made | 155 // assume timestamps are monotonically non-decreasing, and 2) assumptions made |
| 156 // deeper in the implementation where each frame's RTP timestamp needs to be | 156 // deeper in the implementation where each frame's RTP timestamp needs to be |
| 157 // unique. | 157 // unique. |
| 158 if (!last_enqueued_frame_reference_time_.is_null() && | 158 if (!last_enqueued_frame_reference_time_.is_null() && |
| 159 (!IsNewerRtpTimestamp(rtp_timestamp, | 159 (!IsNewerRtpTimestamp(rtp_timestamp, |
| 160 last_enqueued_frame_rtp_timestamp_) || | 160 last_enqueued_frame_rtp_timestamp_) || |
| 161 reference_time <= last_enqueued_frame_reference_time_)) { | 161 reference_time <= last_enqueued_frame_reference_time_)) { |
| 162 VLOG(1) << "Dropping video frame: RTP or reference time did not increase."; | 162 VLOG(1) << "Dropping video frame: RTP or reference time did not increase."; |
| 163 TRACE_EVENT_INSTANT2("cast.stream", "Video Frame Drop", |
| 164 TRACE_EVENT_SCOPE_THREAD, |
| 165 "timestamp", reference_time.ToInternalValue(), |
| 166 "rtp_timestamp", rtp_timestamp); |
| 163 return; | 167 return; |
| 164 } | 168 } |
| 165 | 169 |
| 166 // Two video frames are needed to compute the exact media duration added by | 170 // Two video frames are needed to compute the exact media duration added by |
| 167 // the next frame. If there are no frames in the encoder, compute a guess | 171 // the next frame. If there are no frames in the encoder, compute a guess |
| 168 // based on the configured |max_frame_rate_|. Any error introduced by this | 172 // based on the configured |max_frame_rate_|. Any error introduced by this |
| 169 // guess will be eliminated when |duration_in_encoder_| is updated in | 173 // guess will be eliminated when |duration_in_encoder_| is updated in |
| 170 // OnEncodedVideoFrame(). | 174 // OnEncodedVideoFrame(). |
| 171 const base::TimeDelta duration_added_by_next_frame = frames_in_encoder_ > 0 ? | 175 const base::TimeDelta duration_added_by_next_frame = frames_in_encoder_ > 0 ? |
| 172 reference_time - last_enqueued_frame_reference_time_ : | 176 reference_time - last_enqueued_frame_reference_time_ : |
| 173 base::TimeDelta::FromSecondsD(1.0 / max_frame_rate_); | 177 base::TimeDelta::FromSecondsD(1.0 / max_frame_rate_); |
| 174 | 178 |
| 175 if (ShouldDropNextFrame(duration_added_by_next_frame)) { | 179 if (ShouldDropNextFrame(duration_added_by_next_frame)) { |
| 176 base::TimeDelta new_target_delay = std::min( | 180 base::TimeDelta new_target_delay = std::min( |
| 177 current_round_trip_time_ * kRoundTripsNeeded + | 181 current_round_trip_time_ * kRoundTripsNeeded + |
| 178 base::TimeDelta::FromMilliseconds(kConstantTimeMs), | 182 base::TimeDelta::FromMilliseconds(kConstantTimeMs), |
| 179 max_playout_delay_); | 183 max_playout_delay_); |
| 180 if (new_target_delay > target_playout_delay_) { | 184 if (new_target_delay > target_playout_delay_) { |
| 181 VLOG(1) << "New target delay: " << new_target_delay.InMilliseconds(); | 185 VLOG(1) << "New target delay: " << new_target_delay.InMilliseconds(); |
| 182 playout_delay_change_cb_.Run(new_target_delay); | 186 playout_delay_change_cb_.Run(new_target_delay); |
| 183 } | 187 } |
| 184 | 188 |
| 185 // Some encoder implementations have a frame window for analysis. Since we | 189 // Some encoder implementations have a frame window for analysis. Since we |
| 186 // are dropping this frame, unless we instruct the encoder to flush all the | 190 // are dropping this frame, unless we instruct the encoder to flush all the |
| 187 // frames that have been enqueued for encoding, frames_in_encoder_ and | 191 // frames that have been enqueued for encoding, frames_in_encoder_ and |
| 188 // last_enqueued_frame_reference_time_ will never be updated and we will | 192 // last_enqueued_frame_reference_time_ will never be updated and we will |
| 189 // drop every subsequent frame for the rest of the session. | 193 // drop every subsequent frame for the rest of the session. |
| 190 video_encoder_->EmitFrames(); | 194 video_encoder_->EmitFrames(); |
| 191 | 195 |
| 196 TRACE_EVENT_INSTANT2("cast.stream", "Video Frame Drop", |
| 197 TRACE_EVENT_SCOPE_THREAD, |
| 198 "timestamp", reference_time.ToInternalValue(), |
| 199 "rtp_timestamp", rtp_timestamp); |
| 192 return; | 200 return; |
| 193 } | 201 } |
| 194 | 202 |
| 195 uint32 bitrate = congestion_control_->GetBitrate( | 203 uint32 bitrate = congestion_control_->GetBitrate( |
| 196 reference_time + target_playout_delay_, target_playout_delay_); | 204 reference_time + target_playout_delay_, target_playout_delay_); |
| 205 |
| 206 TRACE_COUNTER_ID1("cast.stream", "Video Target Bitrate", this, bitrate); |
| 207 |
| 197 if (bitrate != last_bitrate_) { | 208 if (bitrate != last_bitrate_) { |
| 198 video_encoder_->SetBitRate(bitrate); | 209 video_encoder_->SetBitRate(bitrate); |
| 199 last_bitrate_ = bitrate; | 210 last_bitrate_ = bitrate; |
| 200 } | 211 } |
| 201 | 212 |
| 202 if (video_frame->visible_rect().IsEmpty()) { | 213 if (video_frame->visible_rect().IsEmpty()) { |
| 203 VLOG(1) << "Rejecting empty video frame."; | 214 VLOG(1) << "Rejecting empty video frame."; |
| 204 return; | 215 return; |
| 205 } | 216 } |
| 206 | 217 |
| 207 MaybeRenderPerformanceMetricsOverlay(bitrate, | 218 MaybeRenderPerformanceMetricsOverlay(bitrate, |
| 208 frames_in_encoder_ + 1, | 219 frames_in_encoder_ + 1, |
| 209 last_reported_deadline_utilization_, | 220 last_reported_deadline_utilization_, |
| 210 last_reported_lossy_utilization_, | 221 last_reported_lossy_utilization_, |
| 211 video_frame.get()); | 222 video_frame.get()); |
| 212 | 223 |
| 224 TRACE_EVENT_ASYNC_BEGIN2("cast.stream", "Video Encode", video_frame.get(), |
| 225 "timestamp", reference_time.ToInternalValue(), |
| 226 "rtp_timestamp", rtp_timestamp); |
| 213 if (video_encoder_->EncodeVideoFrame( | 227 if (video_encoder_->EncodeVideoFrame( |
| 214 video_frame, | 228 video_frame, |
| 215 reference_time, | 229 reference_time, |
| 216 base::Bind(&VideoSender::OnEncodedVideoFrame, | 230 base::Bind(&VideoSender::OnEncodedVideoFrame, |
| 217 weak_factory_.GetWeakPtr(), | 231 weak_factory_.GetWeakPtr(), |
| 218 video_frame, | 232 video_frame, |
| 219 bitrate))) { | 233 bitrate))) { |
| 220 frames_in_encoder_++; | 234 frames_in_encoder_++; |
| 221 duration_in_encoder_ += duration_added_by_next_frame; | 235 duration_in_encoder_ += duration_added_by_next_frame; |
| 222 last_enqueued_frame_rtp_timestamp_ = rtp_timestamp; | 236 last_enqueued_frame_rtp_timestamp_ = rtp_timestamp; |
| 223 last_enqueued_frame_reference_time_ = reference_time; | 237 last_enqueued_frame_reference_time_ = reference_time; |
| 224 } else { | 238 } else { |
| 225 VLOG(1) << "Encoder rejected a frame. Skipping..."; | 239 VLOG(1) << "Encoder rejected a frame. Skipping..."; |
| 240 TRACE_EVENT_INSTANT2("cast.stream", "Video Encode Reject", |
| 241 TRACE_EVENT_SCOPE_THREAD, |
| 242 "timestamp", reference_time.ToInternalValue(), |
| 243 "rtp_timestamp", rtp_timestamp); |
| 226 } | 244 } |
| 227 } | 245 } |
| 228 | 246 |
| 229 scoped_ptr<VideoFrameFactory> VideoSender::CreateVideoFrameFactory() { | 247 scoped_ptr<VideoFrameFactory> VideoSender::CreateVideoFrameFactory() { |
| 230 return video_encoder_ ? video_encoder_->CreateVideoFrameFactory() : nullptr; | 248 return video_encoder_ ? video_encoder_->CreateVideoFrameFactory() : nullptr; |
| 231 } | 249 } |
| 232 | 250 |
| 233 int VideoSender::GetNumberOfFramesInEncoder() const { | 251 int VideoSender::GetNumberOfFramesInEncoder() const { |
| 234 return frames_in_encoder_; | 252 return frames_in_encoder_; |
| 235 } | 253 } |
| (...skipping 20 matching lines...) Expand all Loading... |
| 256 | 274 |
| 257 frames_in_encoder_--; | 275 frames_in_encoder_--; |
| 258 DCHECK_GE(frames_in_encoder_, 0); | 276 DCHECK_GE(frames_in_encoder_, 0); |
| 259 | 277 |
| 260 duration_in_encoder_ = | 278 duration_in_encoder_ = |
| 261 last_enqueued_frame_reference_time_ - encoded_frame->reference_time; | 279 last_enqueued_frame_reference_time_ - encoded_frame->reference_time; |
| 262 | 280 |
| 263 last_reported_deadline_utilization_ = encoded_frame->deadline_utilization; | 281 last_reported_deadline_utilization_ = encoded_frame->deadline_utilization; |
| 264 last_reported_lossy_utilization_ = encoded_frame->lossy_utilization; | 282 last_reported_lossy_utilization_ = encoded_frame->lossy_utilization; |
| 265 | 283 |
| 284 TRACE_EVENT_ASYNC_END2("cast.stream", "Video Encode", video_frame.get(), |
| 285 "duration", duration_in_encoder_.ToInternalValue(), |
| 286 "deadline_utilization", |
| 287 last_reported_lossy_utilization_); |
| 288 |
| 266 // Report the resource utilization for processing this frame. Take the | 289 // Report the resource utilization for processing this frame. Take the |
| 267 // greater of the two utilization values and attenuate them such that the | 290 // greater of the two utilization values and attenuate them such that the |
| 268 // target utilization is reported as the maximum sustainable amount. | 291 // target utilization is reported as the maximum sustainable amount. |
| 269 const double attenuated_utilization = | 292 const double attenuated_utilization = |
| 270 std::max(last_reported_deadline_utilization_, | 293 std::max(last_reported_deadline_utilization_, |
| 271 last_reported_lossy_utilization_) / | 294 last_reported_lossy_utilization_) / |
| 272 (kTargetUtilizationPercentage / 100.0); | 295 (kTargetUtilizationPercentage / 100.0); |
| 273 if (attenuated_utilization >= 0.0) { | 296 if (attenuated_utilization >= 0.0) { |
| 274 // Key frames are artificially capped to 1.0 because their actual | 297 // Key frames are artificially capped to 1.0 because their actual |
| 275 // utilization is atypical compared to the other frames in the stream, and | 298 // utilization is atypical compared to the other frames in the stream, and |
| 276 // this can misguide the producer of the input video frames. | 299 // this can misguide the producer of the input video frames. |
| 277 video_frame->metadata()->SetDouble( | 300 video_frame->metadata()->SetDouble( |
| 278 media::VideoFrameMetadata::RESOURCE_UTILIZATION, | 301 media::VideoFrameMetadata::RESOURCE_UTILIZATION, |
| 279 encoded_frame->dependency == EncodedFrame::KEY ? | 302 encoded_frame->dependency == EncodedFrame::KEY ? |
| 280 std::min(1.0, attenuated_utilization) : attenuated_utilization); | 303 std::min(1.0, attenuated_utilization) : attenuated_utilization); |
| 281 } | 304 } |
| 282 | 305 |
| 283 SendEncodedFrame(encoder_bitrate, encoded_frame.Pass()); | 306 SendEncodedFrame(encoder_bitrate, encoded_frame.Pass()); |
| 284 } | 307 } |
| 285 | 308 |
| 286 } // namespace cast | 309 } // namespace cast |
| 287 } // namespace media | 310 } // namespace media |
| OLD | NEW |