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/vp8_encoder.h" | 5 #include "media/cast/sender/vp8_encoder.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
8 #include "media/base/video_frame.h" | 8 #include "media/base/video_frame.h" |
9 #include "media/cast/cast_defines.h" | 9 #include "media/cast/cast_defines.h" |
10 #include "media/cast/net/cast_transport_config.h" | 10 #include "media/cast/net/cast_transport_config.h" |
(...skipping 10 matching lines...) Expand all Loading... |
21 // pause in the video stream. | 21 // pause in the video stream. |
22 const int kRestartFramePeriods = 3; | 22 const int kRestartFramePeriods = 3; |
23 | 23 |
24 } // namespace | 24 } // namespace |
25 | 25 |
26 Vp8Encoder::Vp8Encoder(const VideoSenderConfig& video_config) | 26 Vp8Encoder::Vp8Encoder(const VideoSenderConfig& video_config) |
27 : cast_config_(video_config), | 27 : cast_config_(video_config), |
28 use_multiple_video_buffers_( | 28 use_multiple_video_buffers_( |
29 cast_config_.max_number_of_video_buffers_used == | 29 cast_config_.max_number_of_video_buffers_used == |
30 kNumberOfVp8VideoBuffers), | 30 kNumberOfVp8VideoBuffers), |
31 raw_image_(nullptr), | |
32 key_frame_requested_(true), | 31 key_frame_requested_(true), |
| 32 bitrate_kbit_(cast_config_.start_bitrate / 1000), |
33 last_encoded_frame_id_(kStartFrameId), | 33 last_encoded_frame_id_(kStartFrameId), |
34 last_acked_frame_id_(kStartFrameId), | 34 last_acked_frame_id_(kStartFrameId), |
35 undroppable_frames_(0) { | 35 undroppable_frames_(0) { |
36 config_.g_timebase.den = 0; // Not initialized. | 36 config_.g_timebase.den = 0; // Not initialized. |
37 | 37 |
38 // VP8 have 3 buffers available for prediction, with | 38 // VP8 have 3 buffers available for prediction, with |
39 // max_number_of_video_buffers_used set to 1 we maximize the coding efficiency | 39 // max_number_of_video_buffers_used set to 1 we maximize the coding efficiency |
40 // however in this mode we can not skip frames in the receiver to catch up | 40 // however in this mode we can not skip frames in the receiver to catch up |
41 // after a temporary network outage; with max_number_of_video_buffers_used | 41 // after a temporary network outage; with max_number_of_video_buffers_used |
42 // set to 3 we allow 2 frames to be skipped by the receiver without error | 42 // set to 3 we allow 2 frames to be skipped by the receiver without error |
43 // propagation. | 43 // propagation. |
44 DCHECK(cast_config_.max_number_of_video_buffers_used == 1 || | 44 DCHECK(cast_config_.max_number_of_video_buffers_used == 1 || |
45 cast_config_.max_number_of_video_buffers_used == | 45 cast_config_.max_number_of_video_buffers_used == |
46 kNumberOfVp8VideoBuffers) | 46 kNumberOfVp8VideoBuffers) |
47 << "Invalid argument"; | 47 << "Invalid argument"; |
48 | 48 |
49 thread_checker_.DetachFromThread(); | 49 thread_checker_.DetachFromThread(); |
50 } | 50 } |
51 | 51 |
52 Vp8Encoder::~Vp8Encoder() { | 52 Vp8Encoder::~Vp8Encoder() { |
53 DCHECK(thread_checker_.CalledOnValidThread()); | 53 DCHECK(thread_checker_.CalledOnValidThread()); |
54 if (is_initialized()) | 54 if (is_initialized()) |
55 vpx_codec_destroy(&encoder_); | 55 vpx_codec_destroy(&encoder_); |
56 vpx_img_free(raw_image_); | |
57 } | 56 } |
58 | 57 |
59 void Vp8Encoder::Initialize() { | 58 void Vp8Encoder::Initialize() { |
60 DCHECK(thread_checker_.CalledOnValidThread()); | 59 DCHECK(thread_checker_.CalledOnValidThread()); |
61 DCHECK(!is_initialized()); | 60 DCHECK(!is_initialized()); |
| 61 // The encoder will be created/configured when the first frame encode is |
| 62 // requested. |
| 63 } |
62 | 64 |
63 // Creating a wrapper to the image - setting image data to NULL. Actual | 65 void Vp8Encoder::ConfigureForNewFrameSize(const gfx::Size& frame_size) { |
64 // pointer will be set during encode. Setting align to 1, as it is | 66 if (is_initialized()) { |
65 // meaningless (actual memory is not allocated). | 67 // Workaround for VP8 bug: If the new size is strictly less-than-or-equal to |
66 raw_image_ = vpx_img_wrap( | 68 // the old size, in terms of area, the existing encoder instance can |
67 NULL, VPX_IMG_FMT_I420, cast_config_.width, cast_config_.height, 1, NULL); | 69 // continue. Otherwise, completely tear-down and re-create a new encoder to |
| 70 // avoid a shutdown crash. |
| 71 if (frame_size.GetArea() <= gfx::Size(config_.g_w, config_.g_h).GetArea()) { |
| 72 DVLOG(1) << "Continuing to use existing encoder at smaller frame size: " |
| 73 << gfx::Size(config_.g_w, config_.g_h).ToString() << " --> " |
| 74 << frame_size.ToString(); |
| 75 config_.g_w = frame_size.width(); |
| 76 config_.g_h = frame_size.height(); |
| 77 CHECK_EQ(vpx_codec_enc_config_set(&encoder_, &config_), VPX_CODEC_OK) |
| 78 << "Failed to update frame size in encoder config."; |
| 79 return; |
| 80 } |
| 81 |
| 82 DVLOG(1) << "Destroying/Re-Creating encoder for larger frame size: " |
| 83 << gfx::Size(config_.g_w, config_.g_h).ToString() << " --> " |
| 84 << frame_size.ToString(); |
| 85 vpx_codec_destroy(&encoder_); |
| 86 } else { |
| 87 DVLOG(1) << "Creating encoder for the first frame; size: " |
| 88 << frame_size.ToString(); |
| 89 } |
68 | 90 |
69 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) { | 91 for (int i = 0; i < kNumberOfVp8VideoBuffers; ++i) { |
70 buffer_state_[i].frame_id = kStartFrameId; | 92 buffer_state_[i].frame_id = kStartFrameId; |
71 buffer_state_[i].state = kBufferStartState; | 93 buffer_state_[i].state = kBufferStartState; |
72 } | 94 } |
73 | 95 |
74 // Populate encoder configuration with default values. | 96 // Populate encoder configuration with default values. |
75 if (vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &config_, 0)) { | 97 CHECK_EQ(vpx_codec_enc_config_default(vpx_codec_vp8_cx(), &config_, 0), |
76 NOTREACHED() << "Invalid return value"; | 98 VPX_CODEC_OK); |
77 config_.g_timebase.den = 0; // Do not call vpx_codec_destroy() in dtor. | |
78 return; | |
79 } | |
80 | 99 |
81 config_.g_threads = cast_config_.number_of_encode_threads; | 100 config_.g_threads = cast_config_.number_of_encode_threads; |
82 config_.g_w = cast_config_.width; | 101 config_.g_w = frame_size.width(); |
83 config_.g_h = cast_config_.height; | 102 config_.g_h = frame_size.height(); |
84 // Set the timebase to match that of base::TimeDelta. | 103 // Set the timebase to match that of base::TimeDelta. |
85 config_.g_timebase.num = 1; | 104 config_.g_timebase.num = 1; |
86 config_.g_timebase.den = base::Time::kMicrosecondsPerSecond; | 105 config_.g_timebase.den = base::Time::kMicrosecondsPerSecond; |
87 if (use_multiple_video_buffers_) { | 106 if (use_multiple_video_buffers_) { |
88 // We must enable error resilience when we use multiple buffers, due to | 107 // We must enable error resilience when we use multiple buffers, due to |
89 // codec requirements. | 108 // codec requirements. |
90 config_.g_error_resilient = 1; | 109 config_.g_error_resilient = 1; |
91 } | 110 } |
| 111 // |g_pass| and |g_lag_in_frames| must be "one pass" and zero, respectively, |
| 112 // in order for VP8 to support changing frame sizes during encoding: |
92 config_.g_pass = VPX_RC_ONE_PASS; | 113 config_.g_pass = VPX_RC_ONE_PASS; |
93 config_.g_lag_in_frames = 0; // Immediate data output for each frame. | 114 config_.g_lag_in_frames = 0; // Immediate data output for each frame. |
94 | 115 |
95 // Rate control settings. | 116 // Rate control settings. |
96 config_.rc_dropframe_thresh = 0; // The encoder may not drop any frames. | 117 config_.rc_dropframe_thresh = 0; // The encoder may not drop any frames. |
97 config_.rc_resize_allowed = 0; // TODO(miu): Why not? Investigate this. | 118 config_.rc_resize_allowed = 0; // TODO(miu): Why not? Investigate this. |
98 config_.rc_end_usage = VPX_CBR; | 119 config_.rc_end_usage = VPX_CBR; |
99 config_.rc_target_bitrate = cast_config_.start_bitrate / 1000; // In kbit/s. | 120 config_.rc_target_bitrate = bitrate_kbit_; |
100 config_.rc_min_quantizer = cast_config_.min_qp; | 121 config_.rc_min_quantizer = cast_config_.min_qp; |
101 config_.rc_max_quantizer = cast_config_.max_qp; | 122 config_.rc_max_quantizer = cast_config_.max_qp; |
102 // TODO(miu): Revisit these now that the encoder is being successfully | 123 // TODO(miu): Revisit these now that the encoder is being successfully |
103 // micro-managed. | 124 // micro-managed. |
104 config_.rc_undershoot_pct = 100; | 125 config_.rc_undershoot_pct = 100; |
105 config_.rc_overshoot_pct = 15; | 126 config_.rc_overshoot_pct = 15; |
106 // TODO(miu): Document why these rc_buf_*_sz values were chosen and/or | 127 // TODO(miu): Document why these rc_buf_*_sz values were chosen and/or |
107 // research for better values. Should they be computed from the target | 128 // research for better values. Should they be computed from the target |
108 // playout delay? | 129 // playout delay? |
109 config_.rc_buf_initial_sz = 500; | 130 config_.rc_buf_initial_sz = 500; |
110 config_.rc_buf_optimal_sz = 600; | 131 config_.rc_buf_optimal_sz = 600; |
111 config_.rc_buf_sz = 1000; | 132 config_.rc_buf_sz = 1000; |
112 | 133 |
113 config_.kf_mode = VPX_KF_DISABLED; | 134 config_.kf_mode = VPX_KF_DISABLED; |
114 | 135 |
115 vpx_codec_flags_t flags = 0; | 136 vpx_codec_flags_t flags = 0; |
116 if (vpx_codec_enc_init(&encoder_, vpx_codec_vp8_cx(), &config_, flags)) { | 137 CHECK_EQ(vpx_codec_enc_init(&encoder_, vpx_codec_vp8_cx(), &config_, flags), |
117 NOTREACHED() << "vpx_codec_enc_init() failed."; | 138 VPX_CODEC_OK); |
118 config_.g_timebase.den = 0; // Do not call vpx_codec_destroy() in dtor. | |
119 return; | |
120 } | |
121 | 139 |
122 // Raise the threshold for considering macroblocks as static. The default is | 140 // Raise the threshold for considering macroblocks as static. The default is |
123 // zero, so this setting makes the encoder less sensitive to motion. This | 141 // zero, so this setting makes the encoder less sensitive to motion. This |
124 // lowers the probability of needing to utilize more CPU to search for motion | 142 // lowers the probability of needing to utilize more CPU to search for motion |
125 // vectors. | 143 // vectors. |
126 vpx_codec_control(&encoder_, VP8E_SET_STATIC_THRESHOLD, 1); | 144 CHECK_EQ(vpx_codec_control(&encoder_, VP8E_SET_STATIC_THRESHOLD, 1), |
| 145 VPX_CODEC_OK); |
127 | 146 |
128 // Improve quality by enabling sets of codec features that utilize more CPU. | 147 // Improve quality by enabling sets of codec features that utilize more CPU. |
129 // The default is zero, with increasingly more CPU to be used as the value is | 148 // The default is zero, with increasingly more CPU to be used as the value is |
130 // more negative. | 149 // more negative. |
131 // TODO(miu): Document why this value was chosen and expected behaviors. | 150 // TODO(miu): Document why this value was chosen and expected behaviors. |
132 // Should this be dynamic w.r.t. hardware performance? | 151 // Should this be dynamic w.r.t. hardware performance? |
133 vpx_codec_control(&encoder_, VP8E_SET_CPUUSED, -6); | 152 CHECK_EQ(vpx_codec_control(&encoder_, VP8E_SET_CPUUSED, -6), VPX_CODEC_OK); |
134 } | 153 } |
135 | 154 |
136 void Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame, | 155 void Vp8Encoder::Encode(const scoped_refptr<media::VideoFrame>& video_frame, |
137 const base::TimeTicks& reference_time, | 156 const base::TimeTicks& reference_time, |
138 EncodedFrame* encoded_frame) { | 157 EncodedFrame* encoded_frame) { |
139 DCHECK(thread_checker_.CalledOnValidThread()); | 158 DCHECK(thread_checker_.CalledOnValidThread()); |
140 DCHECK(encoded_frame); | 159 DCHECK(encoded_frame); |
141 | 160 |
142 CHECK(is_initialized()); // No illegal reference to |config_| or |encoder_|. | 161 // Initialize on-demand. Later, if the video frame size has changed, update |
143 | 162 // the encoder configuration. |
144 // Image in vpx_image_t format. | 163 const gfx::Size frame_size = video_frame->visible_rect().size(); |
145 // Input image is const. VP8's raw image is not defined as const. | 164 if (!is_initialized() || gfx::Size(config_.g_w, config_.g_h) != frame_size) |
146 raw_image_->planes[VPX_PLANE_Y] = | 165 ConfigureForNewFrameSize(frame_size); |
147 const_cast<uint8*>(video_frame->data(VideoFrame::kYPlane)); | |
148 raw_image_->planes[VPX_PLANE_U] = | |
149 const_cast<uint8*>(video_frame->data(VideoFrame::kUPlane)); | |
150 raw_image_->planes[VPX_PLANE_V] = | |
151 const_cast<uint8*>(video_frame->data(VideoFrame::kVPlane)); | |
152 | |
153 raw_image_->stride[VPX_PLANE_Y] = video_frame->stride(VideoFrame::kYPlane); | |
154 raw_image_->stride[VPX_PLANE_U] = video_frame->stride(VideoFrame::kUPlane); | |
155 raw_image_->stride[VPX_PLANE_V] = video_frame->stride(VideoFrame::kVPlane); | |
156 | 166 |
157 uint32 latest_frame_id_to_reference; | 167 uint32 latest_frame_id_to_reference; |
158 Vp8Buffers buffer_to_update; | 168 Vp8Buffers buffer_to_update; |
159 vpx_codec_flags_t flags = 0; | 169 vpx_codec_flags_t flags = 0; |
160 if (key_frame_requested_) { | 170 if (key_frame_requested_) { |
161 flags = VPX_EFLAG_FORCE_KF; | 171 flags = VPX_EFLAG_FORCE_KF; |
162 // Self reference. | 172 // Self reference. |
163 latest_frame_id_to_reference = last_encoded_frame_id_ + 1; | 173 latest_frame_id_to_reference = last_encoded_frame_id_ + 1; |
164 // We can pick any buffer as buffer_to_update since we update | 174 // We can pick any buffer as buffer_to_update since we update |
165 // them all. | 175 // them all. |
166 buffer_to_update = kLastBuffer; | 176 buffer_to_update = kLastBuffer; |
167 } else { | 177 } else { |
168 // Reference all acked frames (buffers). | 178 // Reference all acked frames (buffers). |
169 latest_frame_id_to_reference = GetCodecReferenceFlags(&flags); | 179 latest_frame_id_to_reference = GetCodecReferenceFlags(&flags); |
170 buffer_to_update = GetNextBufferToUpdate(); | 180 buffer_to_update = GetNextBufferToUpdate(); |
171 GetCodecUpdateFlags(buffer_to_update, &flags); | 181 GetCodecUpdateFlags(buffer_to_update, &flags); |
172 } | 182 } |
173 | 183 |
| 184 // Wrapper for vpx_codec_encode() to access the YUV data in the |video_frame|. |
| 185 // Only the VISIBLE rectangle within |video_frame| is exposed to the codec. |
| 186 vpx_image_t vpx_image; |
| 187 vpx_image_t* const result = vpx_img_wrap( |
| 188 &vpx_image, |
| 189 VPX_IMG_FMT_I420, |
| 190 frame_size.width(), |
| 191 frame_size.height(), |
| 192 1, |
| 193 video_frame->data(VideoFrame::kYPlane)); |
| 194 DCHECK_EQ(result, &vpx_image); |
| 195 vpx_image.planes[VPX_PLANE_Y] = |
| 196 video_frame->visible_data(VideoFrame::kYPlane); |
| 197 vpx_image.planes[VPX_PLANE_U] = |
| 198 video_frame->visible_data(VideoFrame::kUPlane); |
| 199 vpx_image.planes[VPX_PLANE_V] = |
| 200 video_frame->visible_data(VideoFrame::kVPlane); |
| 201 vpx_image.stride[VPX_PLANE_Y] = video_frame->stride(VideoFrame::kYPlane); |
| 202 vpx_image.stride[VPX_PLANE_U] = video_frame->stride(VideoFrame::kUPlane); |
| 203 vpx_image.stride[VPX_PLANE_V] = video_frame->stride(VideoFrame::kVPlane); |
| 204 |
174 // The frame duration given to the VP8 codec affects a number of important | 205 // The frame duration given to the VP8 codec affects a number of important |
175 // behaviors, including: per-frame bandwidth, CPU time spent encoding, | 206 // behaviors, including: per-frame bandwidth, CPU time spent encoding, |
176 // temporal quality trade-offs, and key/golden/alt-ref frame generation | 207 // temporal quality trade-offs, and key/golden/alt-ref frame generation |
177 // intervals. Use the actual amount of time between the current and previous | 208 // intervals. Use the actual amount of time between the current and previous |
178 // frames as a prediction for the next frame's duration, but bound the | 209 // frames as a prediction for the next frame's duration, but bound the |
179 // prediction to account for the fact that the frame rate can be highly | 210 // prediction to account for the fact that the frame rate can be highly |
180 // variable, including long pauses in the video stream. | 211 // variable, including long pauses in the video stream. |
181 const base::TimeDelta minimum_frame_duration = | 212 const base::TimeDelta minimum_frame_duration = |
182 base::TimeDelta::FromSecondsD(1.0 / cast_config_.max_frame_rate); | 213 base::TimeDelta::FromSecondsD(1.0 / cast_config_.max_frame_rate); |
183 const base::TimeDelta maximum_frame_duration = | 214 const base::TimeDelta maximum_frame_duration = |
184 base::TimeDelta::FromSecondsD(static_cast<double>(kRestartFramePeriods) / | 215 base::TimeDelta::FromSecondsD(static_cast<double>(kRestartFramePeriods) / |
185 cast_config_.max_frame_rate); | 216 cast_config_.max_frame_rate); |
186 const base::TimeDelta last_frame_duration = | 217 const base::TimeDelta last_frame_duration = |
187 video_frame->timestamp() - last_frame_timestamp_; | 218 video_frame->timestamp() - last_frame_timestamp_; |
188 const base::TimeDelta predicted_frame_duration = | 219 const base::TimeDelta predicted_frame_duration = |
189 std::max(minimum_frame_duration, | 220 std::max(minimum_frame_duration, |
190 std::min(maximum_frame_duration, last_frame_duration)); | 221 std::min(maximum_frame_duration, last_frame_duration)); |
191 last_frame_timestamp_ = video_frame->timestamp(); | 222 last_frame_timestamp_ = video_frame->timestamp(); |
192 | 223 |
193 // Encode the frame. The presentation time stamp argument here is fixed to | 224 // Encode the frame. The presentation time stamp argument here is fixed to |
194 // zero to force the encoder to base its single-frame bandwidth calculations | 225 // zero to force the encoder to base its single-frame bandwidth calculations |
195 // entirely on |predicted_frame_duration| and the target bitrate setting being | 226 // entirely on |predicted_frame_duration| and the target bitrate setting being |
196 // micro-managed via calls to UpdateRates(). | 227 // micro-managed via calls to UpdateRates(). |
197 CHECK_EQ(vpx_codec_encode(&encoder_, | 228 CHECK_EQ(vpx_codec_encode(&encoder_, |
198 raw_image_, | 229 &vpx_image, |
199 0, | 230 0, |
200 predicted_frame_duration.InMicroseconds(), | 231 predicted_frame_duration.InMicroseconds(), |
201 flags, | 232 flags, |
202 VPX_DL_REALTIME), | 233 VPX_DL_REALTIME), |
203 VPX_CODEC_OK) | 234 VPX_CODEC_OK) |
204 << "BUG: Invalid arguments passed to vpx_codec_encode()."; | 235 << "BUG: Invalid arguments passed to vpx_codec_encode()."; |
205 | 236 |
206 // Pull data from the encoder, populating a new EncodedFrame. | 237 // Pull data from the encoder, populating a new EncodedFrame. |
207 encoded_frame->frame_id = ++last_encoded_frame_id_; | 238 encoded_frame->frame_id = ++last_encoded_frame_id_; |
208 const vpx_codec_cx_pkt_t* pkt = NULL; | 239 const vpx_codec_cx_pkt_t* pkt = NULL; |
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
395 void Vp8Encoder::UpdateRates(uint32 new_bitrate) { | 426 void Vp8Encoder::UpdateRates(uint32 new_bitrate) { |
396 DCHECK(thread_checker_.CalledOnValidThread()); | 427 DCHECK(thread_checker_.CalledOnValidThread()); |
397 | 428 |
398 if (!is_initialized()) | 429 if (!is_initialized()) |
399 return; | 430 return; |
400 | 431 |
401 uint32 new_bitrate_kbit = new_bitrate / 1000; | 432 uint32 new_bitrate_kbit = new_bitrate / 1000; |
402 if (config_.rc_target_bitrate == new_bitrate_kbit) | 433 if (config_.rc_target_bitrate == new_bitrate_kbit) |
403 return; | 434 return; |
404 | 435 |
405 config_.rc_target_bitrate = new_bitrate_kbit; | 436 config_.rc_target_bitrate = bitrate_kbit_ = new_bitrate_kbit; |
406 | 437 |
407 // Update encoder context. | 438 // Update encoder context. |
408 if (vpx_codec_enc_config_set(&encoder_, &config_)) { | 439 if (vpx_codec_enc_config_set(&encoder_, &config_)) { |
409 NOTREACHED() << "Invalid return value"; | 440 NOTREACHED() << "Invalid return value"; |
410 } | 441 } |
411 | 442 |
412 VLOG(1) << "VP8 new rc_target_bitrate: " << new_bitrate_kbit << " kbps"; | 443 VLOG(1) << "VP8 new rc_target_bitrate: " << new_bitrate_kbit << " kbps"; |
413 } | 444 } |
414 | 445 |
415 void Vp8Encoder::LatestFrameIdToReference(uint32 frame_id) { | 446 void Vp8Encoder::LatestFrameIdToReference(uint32 frame_id) { |
(...skipping 13 matching lines...) Expand all Loading... |
429 } | 460 } |
430 } | 461 } |
431 | 462 |
432 void Vp8Encoder::GenerateKeyFrame() { | 463 void Vp8Encoder::GenerateKeyFrame() { |
433 DCHECK(thread_checker_.CalledOnValidThread()); | 464 DCHECK(thread_checker_.CalledOnValidThread()); |
434 key_frame_requested_ = true; | 465 key_frame_requested_ = true; |
435 } | 466 } |
436 | 467 |
437 } // namespace cast | 468 } // namespace cast |
438 } // namespace media | 469 } // namespace media |
OLD | NEW |