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 |