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 <cmath> | 8 #include <cmath> |
9 #include <cstring> | 9 #include <cstring> |
10 | 10 |
(...skipping 21 matching lines...) Expand all Loading... | |
32 // network quality (e.g., additional time that accounts for encode and decode | 32 // network quality (e.g., additional time that accounts for encode and decode |
33 // time). | 33 // time). |
34 const int kConstantTimeMs = 75; | 34 const int kConstantTimeMs = 75; |
35 | 35 |
36 // The target maximum utilization of the encoder and network resources. This is | 36 // The target maximum utilization of the encoder and network resources. This is |
37 // used to attenuate the actual measured utilization values in order to provide | 37 // used to attenuate the actual measured utilization values in order to provide |
38 // "breathing room" (i.e., to ensure there will be sufficient CPU and bandwidth | 38 // "breathing room" (i.e., to ensure there will be sufficient CPU and bandwidth |
39 // available to handle the occasional more-complex frames). | 39 // available to handle the occasional more-complex frames). |
40 const int kTargetUtilizationPercentage = 75; | 40 const int kTargetUtilizationPercentage = 75; |
41 | 41 |
42 // Minimum number of user interactions before we consider the user to be in | |
43 // interactive mode. The goal is to prevent user interactions to launch | |
44 // animated content from causing target playout time flip-flop. | |
45 const int kMinUserInteractionsForInteractiveMode = 5; | |
46 | |
42 // Extract capture begin/end timestamps from |video_frame|'s metadata and log | 47 // Extract capture begin/end timestamps from |video_frame|'s metadata and log |
43 // it. | 48 // it. |
44 void LogVideoCaptureTimestamps(CastEnvironment* cast_environment, | 49 void LogVideoCaptureTimestamps(CastEnvironment* cast_environment, |
45 const media::VideoFrame& video_frame, | 50 const media::VideoFrame& video_frame, |
46 RtpTimestamp rtp_timestamp) { | 51 RtpTimestamp rtp_timestamp) { |
47 scoped_ptr<FrameEvent> capture_begin_event(new FrameEvent()); | 52 scoped_ptr<FrameEvent> capture_begin_event(new FrameEvent()); |
48 capture_begin_event->type = FRAME_CAPTURE_BEGIN; | 53 capture_begin_event->type = FRAME_CAPTURE_BEGIN; |
49 capture_begin_event->media_type = VIDEO_EVENT; | 54 capture_begin_event->media_type = VIDEO_EVENT; |
50 capture_begin_event->rtp_timestamp = rtp_timestamp; | 55 capture_begin_event->rtp_timestamp = rtp_timestamp; |
51 | 56 |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
98 video_config.use_external_encoder | 103 video_config.use_external_encoder |
99 ? NewFixedCongestionControl( | 104 ? NewFixedCongestionControl( |
100 (video_config.min_bitrate + video_config.max_bitrate) / 2) | 105 (video_config.min_bitrate + video_config.max_bitrate) / 2) |
101 : NewAdaptiveCongestionControl(cast_environment->Clock(), | 106 : NewAdaptiveCongestionControl(cast_environment->Clock(), |
102 video_config.max_bitrate, | 107 video_config.max_bitrate, |
103 video_config.min_bitrate, | 108 video_config.min_bitrate, |
104 video_config.max_frame_rate)), | 109 video_config.max_frame_rate)), |
105 frames_in_encoder_(0), | 110 frames_in_encoder_(0), |
106 last_bitrate_(0), | 111 last_bitrate_(0), |
107 playout_delay_change_cb_(playout_delay_change_cb), | 112 playout_delay_change_cb_(playout_delay_change_cb), |
113 animation_content_(true), | |
114 user_interaction_(false), | |
115 user_interaction_count_(0), | |
116 interactive_mode_(false), | |
108 last_reported_deadline_utilization_(-1.0), | 117 last_reported_deadline_utilization_(-1.0), |
109 last_reported_lossy_utilization_(-1.0), | 118 last_reported_lossy_utilization_(-1.0), |
110 weak_factory_(this) { | 119 weak_factory_(this) { |
111 video_encoder_ = VideoEncoder::Create( | 120 video_encoder_ = VideoEncoder::Create( |
112 cast_environment_, | 121 cast_environment_, |
113 video_config, | 122 video_config, |
114 status_change_cb, | 123 status_change_cb, |
115 create_vea_cb, | 124 create_vea_cb, |
116 create_video_encode_mem_cb); | 125 create_video_encode_mem_cb); |
117 if (!video_encoder_) { | 126 if (!video_encoder_) { |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
154 LogVideoCaptureTimestamps(cast_environment_.get(), *video_frame, | 163 LogVideoCaptureTimestamps(cast_environment_.get(), *video_frame, |
155 rtp_timestamp); | 164 rtp_timestamp); |
156 | 165 |
157 // Used by chrome/browser/extension/api/cast_streaming/performance_test.cc | 166 // Used by chrome/browser/extension/api/cast_streaming/performance_test.cc |
158 TRACE_EVENT_INSTANT2( | 167 TRACE_EVENT_INSTANT2( |
159 "cast_perf_test", "InsertRawVideoFrame", | 168 "cast_perf_test", "InsertRawVideoFrame", |
160 TRACE_EVENT_SCOPE_THREAD, | 169 TRACE_EVENT_SCOPE_THREAD, |
161 "timestamp", reference_time.ToInternalValue(), | 170 "timestamp", reference_time.ToInternalValue(), |
162 "rtp_timestamp", rtp_timestamp); | 171 "rtp_timestamp", rtp_timestamp); |
163 | 172 |
173 bool prev_user_interaction = user_interaction_; | |
174 if (video_frame->metadata()->GetBoolean(VideoFrameMetadata::ANIMATION_CONTENT, | |
175 &animation_content_) && | |
176 video_frame->metadata()->GetBoolean(VideoFrameMetadata::USER_INTERACTION, | |
177 &user_interaction_)) { | |
178 if (user_interaction_ && !prev_user_interaction) { | |
179 user_interaction_count_++; | |
miu
2015/12/01 21:15:27
IIUC, this is counting the number of frames where
Irfan
2015/12/02 22:32:44
This has moved to capture side now.
| |
180 } | |
181 if (user_interaction_count_ > kMinUserInteractionsForInteractiveMode && | |
182 !animation_content_ && !interactive_mode_) { | |
miu
2015/12/01 21:15:27
Perhaps we should only consider the "user interact
Irfan
2015/12/02 22:37:55
As discussed f2f, I think we should be more conser
| |
183 interactive_mode_ = true; | |
184 VLOG(1) << "Interactive mode playout time " | |
185 << media::cast::kInteractiveModeStartPlayoutTimeMs; | |
186 playout_delay_change_cb_.Run(base::TimeDelta::FromMilliseconds( | |
187 media::cast::kInteractiveModeStartPlayoutTimeMs)); | |
188 } else if (animation_content_) { | |
189 // Reset user interactions when animating content is playing. | |
190 user_interaction_count_ = 0; | |
191 interactive_mode_ = false; | |
192 } | |
193 } | |
194 | |
miu
2015/12/01 21:15:27
BTW--What about audio? If we change video's playo
Irfan
2015/12/02 22:32:44
This already works as you describe :-)
| |
164 // Drop the frame if either its RTP or reference timestamp is not an increase | 195 // Drop the frame if either its RTP or reference timestamp is not an increase |
165 // over the last frame's. This protects: 1) the duration calculations that | 196 // over the last frame's. This protects: 1) the duration calculations that |
166 // assume timestamps are monotonically non-decreasing, and 2) assumptions made | 197 // assume timestamps are monotonically non-decreasing, and 2) assumptions made |
167 // deeper in the implementation where each frame's RTP timestamp needs to be | 198 // deeper in the implementation where each frame's RTP timestamp needs to be |
168 // unique. | 199 // unique. |
169 if (!last_enqueued_frame_reference_time_.is_null() && | 200 if (!last_enqueued_frame_reference_time_.is_null() && |
170 (!IsNewerRtpTimestamp(rtp_timestamp, | 201 (!IsNewerRtpTimestamp(rtp_timestamp, |
171 last_enqueued_frame_rtp_timestamp_) || | 202 last_enqueued_frame_rtp_timestamp_) || |
172 reference_time <= last_enqueued_frame_reference_time_)) { | 203 reference_time <= last_enqueued_frame_reference_time_)) { |
173 VLOG(1) << "Dropping video frame: RTP or reference time did not increase."; | 204 VLOG(1) << "Dropping video frame: RTP or reference time did not increase."; |
(...skipping 11 matching lines...) Expand all Loading... | |
185 // OnEncodedVideoFrame(). | 216 // OnEncodedVideoFrame(). |
186 const base::TimeDelta duration_added_by_next_frame = frames_in_encoder_ > 0 ? | 217 const base::TimeDelta duration_added_by_next_frame = frames_in_encoder_ > 0 ? |
187 reference_time - last_enqueued_frame_reference_time_ : | 218 reference_time - last_enqueued_frame_reference_time_ : |
188 base::TimeDelta::FromSecondsD(1.0 / max_frame_rate_); | 219 base::TimeDelta::FromSecondsD(1.0 / max_frame_rate_); |
189 | 220 |
190 if (ShouldDropNextFrame(duration_added_by_next_frame)) { | 221 if (ShouldDropNextFrame(duration_added_by_next_frame)) { |
191 base::TimeDelta new_target_delay = std::min( | 222 base::TimeDelta new_target_delay = std::min( |
192 current_round_trip_time_ * kRoundTripsNeeded + | 223 current_round_trip_time_ * kRoundTripsNeeded + |
193 base::TimeDelta::FromMilliseconds(kConstantTimeMs), | 224 base::TimeDelta::FromMilliseconds(kConstantTimeMs), |
194 max_playout_delay_); | 225 max_playout_delay_); |
195 if (new_target_delay > target_playout_delay_) { | 226 // In case of interactive mode, we prefer frame drops over increasing |
227 // playout time. | |
228 if (!interactive_mode_ && new_target_delay > target_playout_delay_) { | |
229 // In case we detect user is no more in an interactive mode and there is | |
230 // a need to drop a frame, we ensure the playout delay is at-least the | |
231 // the starting value that is known to work well. | |
232 // This is intentended to minimize freeze when moving from an interactive | |
233 // session to watching animating content while being limited by end-to-end | |
234 // delay. | |
235 VLOG(1) << "Ensure playout time is at least " | |
236 << media::cast::kNonInteractiveModeStartPlayoutTimeMs; | |
237 if (new_target_delay.InMilliseconds() < | |
238 media::cast::kNonInteractiveModeStartPlayoutTimeMs) | |
239 new_target_delay = base::TimeDelta::FromMilliseconds( | |
240 kNonInteractiveModeStartPlayoutTimeMs); | |
196 VLOG(1) << "New target delay: " << new_target_delay.InMilliseconds(); | 241 VLOG(1) << "New target delay: " << new_target_delay.InMilliseconds(); |
197 playout_delay_change_cb_.Run(new_target_delay); | 242 playout_delay_change_cb_.Run(new_target_delay); |
198 } | 243 } |
199 | 244 |
200 // Some encoder implementations have a frame window for analysis. Since we | 245 // Some encoder implementations have a frame window for analysis. Since we |
201 // are dropping this frame, unless we instruct the encoder to flush all the | 246 // are dropping this frame, unless we instruct the encoder to flush all the |
202 // frames that have been enqueued for encoding, frames_in_encoder_ and | 247 // frames that have been enqueued for encoding, frames_in_encoder_ and |
203 // last_enqueued_frame_reference_time_ will never be updated and we will | 248 // last_enqueued_frame_reference_time_ will never be updated and we will |
204 // drop every subsequent frame for the rest of the session. | 249 // drop every subsequent frame for the rest of the session. |
205 video_encoder_->EmitFrames(); | 250 video_encoder_->EmitFrames(); |
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
356 media::VideoFrameMetadata::RESOURCE_UTILIZATION, | 401 media::VideoFrameMetadata::RESOURCE_UTILIZATION, |
357 encoded_frame->dependency == EncodedFrame::KEY ? | 402 encoded_frame->dependency == EncodedFrame::KEY ? |
358 std::min(1.0, attenuated_utilization) : attenuated_utilization); | 403 std::min(1.0, attenuated_utilization) : attenuated_utilization); |
359 } | 404 } |
360 | 405 |
361 SendEncodedFrame(encoder_bitrate, encoded_frame.Pass()); | 406 SendEncodedFrame(encoder_bitrate, encoded_frame.Pass()); |
362 } | 407 } |
363 | 408 |
364 } // namespace cast | 409 } // namespace cast |
365 } // namespace media | 410 } // namespace media |
OLD | NEW |