Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(102)

Side by Side Diff: remoting/codec/webrtc_video_encoder_vpx.cc

Issue 1908203002: Adapt encoder behavior to target bitrate (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Moved webrtc encoder to its own file Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2016 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 "remoting/codec/video_encoder_vpx.h" 5 #include "remoting/codec/webrtc_video_encoder_vpx.h"
6 6
7 #include <utility> 7 #include <utility>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/logging.h" 10 #include "base/logging.h"
11 #include "base/memory/ptr_util.h" 11 #include "base/memory/ptr_util.h"
12 #include "base/sys_info.h" 12 #include "base/sys_info.h"
13 #include "remoting/base/util.h" 13 #include "remoting/base/util.h"
14 #include "remoting/proto/video.pb.h" 14 #include "remoting/proto/video.pb.h"
15 #include "third_party/libyuv/include/libyuv/convert_from_argb.h" 15 #include "third_party/libyuv/include/libyuv/convert_from_argb.h"
(...skipping 19 matching lines...) Expand all
35 const int kMacroBlockSize = 16; 35 const int kMacroBlockSize = 16;
36 36
37 // Magic encoder profile numbers for I420 and I444 input formats. 37 // Magic encoder profile numbers for I420 and I444 input formats.
38 const int kVp9I420ProfileNumber = 0; 38 const int kVp9I420ProfileNumber = 0;
39 const int kVp9I444ProfileNumber = 1; 39 const int kVp9I444ProfileNumber = 1;
40 40
41 // Magic encoder constants for adaptive quantization strategy. 41 // Magic encoder constants for adaptive quantization strategy.
42 const int kVp9AqModeNone = 0; 42 const int kVp9AqModeNone = 0;
43 const int kVp9AqModeCyclicRefresh = 3; 43 const int kVp9AqModeCyclicRefresh = 3;
44 44
45 const int kDefaultTargetBitrateKbps = 1000;
46
45 void SetCommonCodecParameters(vpx_codec_enc_cfg_t* config, 47 void SetCommonCodecParameters(vpx_codec_enc_cfg_t* config,
46 const webrtc::DesktopSize& size) { 48 const webrtc::DesktopSize& size) {
47 // Use millisecond granularity time base. 49 // Use millisecond granularity time base.
48 config->g_timebase.num = 1; 50 config->g_timebase.num = 1;
49 config->g_timebase.den = 1000; 51 config->g_timebase.den = base::Time::kMicrosecondsPerSecond;
50 52
51 config->g_w = size.width(); 53 config->g_w = size.width();
52 config->g_h = size.height(); 54 config->g_h = size.height();
53 config->g_pass = VPX_RC_ONE_PASS; 55 config->g_pass = VPX_RC_ONE_PASS;
54 56
55 // Start emitting packets immediately. 57 // Start emitting packets immediately.
56 config->g_lag_in_frames = 0; 58 config->g_lag_in_frames = 0;
57 59
58 // Since the transport layer is reliable, keyframes should not be necessary. 60 // Since the transport layer is reliable, keyframes should not be necessary.
59 // However, due to crbug.com/440223, decoding fails after 30,000 non-key 61 // However, due to crbug.com/440223, decoding fails after 30,000 non-key
60 // frames, so take the hit of an "unnecessary" key-frame every 10,000 frames. 62 // frames, so take the hit of an "unnecessary" key-frame every 10,000 frames.
61 config->kf_min_dist = 10000; 63 config->kf_min_dist = 10000;
62 config->kf_max_dist = 10000; 64 config->kf_max_dist = 10000;
63 65
64 // Using 2 threads gives a great boost in performance for most systems with 66 // Allow multiple cores on a system to be used for encoding for
65 // adequate processing power. NB: Going to multiple threads on low end 67 // performance while at the same time ensuring we do not saturate.
66 // windows systems can really hurt performance. 68 config->g_threads = (base::SysInfo::NumberOfProcessors() + 1) / 2;
67 // http://crbug.com/99179 69
68 config->g_threads = (base::SysInfo::NumberOfProcessors() > 2) ? 2 : 1; 70 // Do not drop any frames at encoder.
71 config->rc_dropframe_thresh = 0;
72 // We do not want variations in bandwidth.
73 config->rc_end_usage = VPX_CBR;
74 config->rc_undershoot_pct = 100;
75 config->rc_overshoot_pct = 15;
69 } 76 }
70 77
71 void SetVp8CodecParameters(vpx_codec_enc_cfg_t* config, 78 void SetVp8CodecParameters(vpx_codec_enc_cfg_t* config,
72 const webrtc::DesktopSize& size) { 79 const webrtc::DesktopSize& size) {
73 // Adjust default target bit-rate to account for actual desktop size.
74 config->rc_target_bitrate = size.width() * size.height() *
75 config->rc_target_bitrate / config->g_w / config->g_h;
76
77 SetCommonCodecParameters(config, size); 80 SetCommonCodecParameters(config, size);
78 81
79 // Value of 2 means using the real time profile. This is basically a 82 // Value of 2 means using the real time profile. This is basically a
80 // redundant option since we explicitly select real time mode when doing 83 // redundant option since we explicitly select real time mode when doing
81 // encoding. 84 // encoding.
82 config->g_profile = 2; 85 config->g_profile = 2;
83 86
84 // Clamping the quantizer constrains the worst-case quality and CPU usage. 87 // To enable remoting to be highly interactive and allow the target bitrate
88 // to be met, we relax the max quantizer. The quality will get topped-off
89 // in subsequent frames.
85 config->rc_min_quantizer = 20; 90 config->rc_min_quantizer = 20;
86 config->rc_max_quantizer = 30; 91 config->rc_max_quantizer = 63;
87 } 92 }
88 93
89 void SetVp9CodecParameters(vpx_codec_enc_cfg_t* config, 94 void SetVp9CodecParameters(vpx_codec_enc_cfg_t* config,
90 const webrtc::DesktopSize& size, 95 const webrtc::DesktopSize& size,
91 bool lossless_color, 96 bool lossless_color,
92 bool lossless_encode) { 97 bool lossless_encode) {
93 SetCommonCodecParameters(config, size); 98 SetCommonCodecParameters(config, size);
94 99
95 // Configure VP9 for I420 or I444 source frames. 100 // Configure VP9 for I420 or I444 source frames.
96 config->g_profile = 101 config->g_profile =
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
132 int cpu_used = lossless_encode ? 5 : 6; 137 int cpu_used = lossless_encode ? 5 : 6;
133 vpx_codec_err_t ret = vpx_codec_control(codec, VP8E_SET_CPUUSED, cpu_used); 138 vpx_codec_err_t ret = vpx_codec_control(codec, VP8E_SET_CPUUSED, cpu_used);
134 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set CPUUSED"; 139 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set CPUUSED";
135 140
136 // Use the lowest level of noise sensitivity so as to spend less time 141 // Use the lowest level of noise sensitivity so as to spend less time
137 // on motion estimation and inter-prediction mode. 142 // on motion estimation and inter-prediction mode.
138 ret = vpx_codec_control(codec, VP9E_SET_NOISE_SENSITIVITY, 0); 143 ret = vpx_codec_control(codec, VP9E_SET_NOISE_SENSITIVITY, 0);
139 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set noise sensitivity"; 144 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set noise sensitivity";
140 145
141 // Configure the codec to tune it for screen media. 146 // Configure the codec to tune it for screen media.
142 ret = vpx_codec_control( 147 ret = vpx_codec_control(codec, VP9E_SET_TUNE_CONTENT, VP9E_CONTENT_SCREEN);
143 codec, VP9E_SET_TUNE_CONTENT, VP9E_CONTENT_SCREEN);
144 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set screen content mode"; 148 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set screen content mode";
145 149
146 // Set cyclic refresh (aka "top-off") only for lossy encoding. 150 // Set cyclic refresh (aka "top-off") only for lossy encoding.
147 int aq_mode = lossless_encode ? kVp9AqModeNone : kVp9AqModeCyclicRefresh; 151 int aq_mode = lossless_encode ? kVp9AqModeNone : kVp9AqModeCyclicRefresh;
148 ret = vpx_codec_control(codec, VP9E_SET_AQ_MODE, aq_mode); 152 ret = vpx_codec_control(codec, VP9E_SET_AQ_MODE, aq_mode);
149 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set aq mode"; 153 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set aq mode";
150 } 154 }
151 155
152 void FreeImageIfMismatched(bool use_i444, 156 void FreeImageIfMismatched(bool use_i444,
153 const webrtc::DesktopSize& size, 157 const webrtc::DesktopSize& size,
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after
198 const int y_stride = ((image->w - 1) & ~15) + 16; 202 const int y_stride = ((image->w - 1) & ~15) + 16;
199 const int uv_unaligned_stride = y_stride >> image->x_chroma_shift; 203 const int uv_unaligned_stride = y_stride >> image->x_chroma_shift;
200 const int uv_stride = ((uv_unaligned_stride - 1) & ~15) + 16; 204 const int uv_stride = ((uv_unaligned_stride - 1) & ~15) + 16;
201 205
202 // libvpx accesses the source image in macro blocks, and will over-read 206 // libvpx accesses the source image in macro blocks, and will over-read
203 // if the image is not padded out to the next macroblock: crbug.com/119633. 207 // if the image is not padded out to the next macroblock: crbug.com/119633.
204 // Pad the Y, U and V planes' height out to compensate. 208 // Pad the Y, U and V planes' height out to compensate.
205 // Assuming macroblocks are 16x16, aligning the planes' strides above also 209 // Assuming macroblocks are 16x16, aligning the planes' strides above also
206 // macroblock aligned them. 210 // macroblock aligned them.
207 static_assert(kMacroBlockSize == 16, "macroblock_size_not_16"); 211 static_assert(kMacroBlockSize == 16, "macroblock_size_not_16");
208 const int y_rows = ((image->h - 1) & ~(kMacroBlockSize-1)) + kMacroBlockSize; 212 const int y_rows =
213 ((image->h - 1) & ~(kMacroBlockSize - 1)) + kMacroBlockSize;
209 const int uv_rows = y_rows >> image->y_chroma_shift; 214 const int uv_rows = y_rows >> image->y_chroma_shift;
210 215
211 // Allocate a YUV buffer large enough for the aligned data & padding. 216 // Allocate a YUV buffer large enough for the aligned data & padding.
212 const int buffer_size = y_stride * y_rows + 2*uv_stride * uv_rows; 217 const int buffer_size = y_stride * y_rows + 2 * uv_stride * uv_rows;
213 std::unique_ptr<uint8_t[]> image_buffer(new uint8_t[buffer_size]); 218 std::unique_ptr<uint8_t[]> image_buffer(new uint8_t[buffer_size]);
214 219
215 // Reset image value to 128 so we just need to fill in the y plane. 220 // Reset image value to 128 so we just need to fill in the y plane.
216 memset(image_buffer.get(), 128, buffer_size); 221 memset(image_buffer.get(), 128, buffer_size);
217 222
218 // Fill in the information for |image_|. 223 // Fill in the information for |image_|.
219 unsigned char* uchar_buffer = 224 unsigned char* uchar_buffer =
220 reinterpret_cast<unsigned char*>(image_buffer.get()); 225 reinterpret_cast<unsigned char*>(image_buffer.get());
221 image->planes[0] = uchar_buffer; 226 image->planes[0] = uchar_buffer;
222 image->planes[1] = image->planes[0] + y_stride * y_rows; 227 image->planes[1] = image->planes[0] + y_stride * y_rows;
223 image->planes[2] = image->planes[1] + uv_stride * uv_rows; 228 image->planes[2] = image->planes[1] + uv_stride * uv_rows;
224 image->stride[0] = y_stride; 229 image->stride[0] = y_stride;
225 image->stride[1] = uv_stride; 230 image->stride[1] = uv_stride;
226 image->stride[2] = uv_stride; 231 image->stride[2] = uv_stride;
227 232
228 *out_image = std::move(image); 233 *out_image = std::move(image);
229 *out_image_buffer = std::move(image_buffer); 234 *out_image_buffer = std::move(image_buffer);
230 } 235 }
231 236
232 } // namespace 237 } // namespace
233 238
234 // static 239 // static
235 std::unique_ptr<VideoEncoderVpx> VideoEncoderVpx::CreateForVP8() { 240 std::unique_ptr<WebRtcVideoEncoderVpx> WebRtcVideoEncoderVpx::CreateForVP8() {
236 return base::WrapUnique(new VideoEncoderVpx(false)); 241 return base::WrapUnique(new WebRtcVideoEncoderVpx(false));
237 } 242 }
238 243
239 // static 244 // static
240 std::unique_ptr<VideoEncoderVpx> VideoEncoderVpx::CreateForVP9() { 245 std::unique_ptr<WebRtcVideoEncoderVpx> WebRtcVideoEncoderVpx::CreateForVP9() {
241 return base::WrapUnique(new VideoEncoderVpx(true)); 246 return base::WrapUnique(new WebRtcVideoEncoderVpx(true));
242 } 247 }
243 248
244 VideoEncoderVpx::~VideoEncoderVpx() {} 249 WebRtcVideoEncoderVpx::~WebRtcVideoEncoderVpx() {}
245 250
246 void VideoEncoderVpx::SetTickClockForTests(base::TickClock* tick_clock) { 251 void WebRtcVideoEncoderVpx::SetTickClockForTests(base::TickClock* tick_clock) {
247 clock_ = tick_clock; 252 clock_ = tick_clock;
248 } 253 }
249 254
250 void VideoEncoderVpx::SetLosslessEncode(bool want_lossless) { 255 void WebRtcVideoEncoderVpx::SetLosslessEncode(bool want_lossless) {
251 if (use_vp9_ && (want_lossless != lossless_encode_)) { 256 if (use_vp9_ && (want_lossless != lossless_encode_)) {
252 lossless_encode_ = want_lossless; 257 lossless_encode_ = want_lossless;
253 if (codec_) 258 if (codec_)
254 Configure(webrtc::DesktopSize(codec_->config.enc->g_w, 259 Configure(webrtc::DesktopSize(codec_->config.enc->g_w,
255 codec_->config.enc->g_h)); 260 codec_->config.enc->g_h));
256 } 261 }
257 } 262 }
258 263
259 void VideoEncoderVpx::SetLosslessColor(bool want_lossless) { 264 void WebRtcVideoEncoderVpx::SetLosslessColor(bool want_lossless) {
260 if (use_vp9_ && (want_lossless != lossless_color_)) { 265 if (use_vp9_ && (want_lossless != lossless_color_)) {
261 lossless_color_ = want_lossless; 266 lossless_color_ = want_lossless;
262 // TODO(wez): Switch to ConfigureCodec() path once libvpx supports it. 267 // TODO(wez): Switch to ConfigureCodec() path once libvpx supports it.
263 // See https://code.google.com/p/webm/issues/detail?id=913. 268 // See https://code.google.com/p/webm/issues/detail?id=913.
264 // if (codec_) 269 // if (codec_)
265 // Configure(webrtc::DesktopSize(codec_->config.enc->g_w, 270 // Configure(webrtc::DesktopSize(codec_->config.enc->g_w,
266 // codec_->config.enc->g_h)); 271 // codec_->config.enc->g_h));
267 codec_.reset(); 272 codec_.reset();
268 } 273 }
269 } 274 }
270 275
271 std::unique_ptr<VideoPacket> VideoEncoderVpx::Encode( 276 void WebRtcVideoEncoderVpx::UpdateTargetBitrate(uint32_t new_bitrate) {
277 target_bitrate_kbps_ = new_bitrate;
278 // Configuration not initialized.
279 if (config_.g_timebase.den == 0)
280 return;
281
282 if (config_.rc_target_bitrate == new_bitrate)
283 return;
284 config_.rc_target_bitrate = new_bitrate;
285
286 // Update encoder context.
287 if (vpx_codec_enc_config_set(codec_.get(), &config_))
288 NOTREACHED() << "Unable to set encoder config";
289
290 VLOG(1) << "New rc_target_bitrate: " << new_bitrate << " kbps";
291 }
292
293 std::unique_ptr<VideoPacket> WebRtcVideoEncoderVpx::Encode(
272 const webrtc::DesktopFrame& frame, 294 const webrtc::DesktopFrame& frame,
273 uint32_t flags) { 295 uint32_t flags) {
274 DCHECK_LE(32, frame.size().width()); 296 DCHECK_LE(32, frame.size().width());
275 DCHECK_LE(32, frame.size().height()); 297 DCHECK_LE(32, frame.size().height());
276 298
277 // If there is nothing to encode, and nothing to top-off, then return nothing. 299 // VP8: Encode top-off is controlled at the call site.
278 if (frame.updated_region().is_empty() && !encode_unchanged_frame_) 300 // VP9: Based on information fetching active map, we return here if there is
301 // nothing to top-off.
302 if (use_vp9_ && frame.updated_region().is_empty() && !encode_unchanged_frame_)
279 return nullptr; 303 return nullptr;
280 304
281 // Create or reconfigure the codec to match the size of |frame|. 305 // Create or reconfigure the codec to match the size of |frame|.
282 if (!codec_ || 306 if (!codec_ ||
283 (image_ && 307 (image_ &&
284 !frame.size().equals(webrtc::DesktopSize(image_->w, image_->h)))) { 308 !frame.size().equals(webrtc::DesktopSize(image_->w, image_->h)))) {
285 Configure(frame.size()); 309 Configure(frame.size());
286 } 310 }
287 311
288 // Convert the updated capture data ready for encode.
289 webrtc::DesktopRegion updated_region;
290 PrepareImage(frame, &updated_region);
291
292 // Update active map based on updated region.
293 SetActiveMapFromRegion(updated_region);
294
295 // Apply active map to the encoder.
296 vpx_active_map_t act_map; 312 vpx_active_map_t act_map;
297 act_map.rows = active_map_size_.height(); 313 act_map.rows = active_map_size_.height();
298 act_map.cols = active_map_size_.width(); 314 act_map.cols = active_map_size_.width();
299 act_map.active_map = active_map_.get(); 315 act_map.active_map = active_map_.get();
300 if (vpx_codec_control(codec_.get(), VP8E_SET_ACTIVEMAP, &act_map)) { 316
301 LOG(ERROR) << "Unable to apply active map"; 317 webrtc::DesktopRegion updated_region;
318 if (!frame.updated_region().is_empty()) {
319 // Convert the updated capture data ready for encode.
320 PrepareImage(frame, &updated_region);
321
322 // Update active map based on updated region.
323 SetActiveMapFromRegion(updated_region);
324
325 // Apply active map to the encoder.
326
327 if (vpx_codec_control(codec_.get(), VP8E_SET_ACTIVEMAP, &act_map)) {
328 LOG(ERROR) << "Unable to apply active map";
329 }
302 } 330 }
303 331
304 if (flags & REQUEST_KEY_FRAME) 332 // Frame rate is adapted based on how well the encoder meets the target
305 vpx_codec_control(codec_.get(), VP8E_SET_FRAME_FLAGS, VPX_EFLAG_FORCE_KF); 333 // bandwidth requiremetn. We specify a target rate of 1 / 15 fps here.
Sergey Ulanov 2016/05/10 00:00:58 typo: requiremetn
Sergey Ulanov 2016/05/10 00:00:58 why is it 1/15 fps instead of 30 fps?
Irfan 2016/05/10 16:30:45 I added a TODO here. Basically, we need to further
306
307 // Do the actual encoding.
308 int timestamp = (clock_->NowTicks() - timestamp_base_).InMilliseconds();
309 vpx_codec_err_t ret = vpx_codec_encode( 334 vpx_codec_err_t ret = vpx_codec_encode(
310 codec_.get(), image_.get(), timestamp, 1, 0, VPX_DL_REALTIME); 335 codec_.get(), image_.get(), 0, 66666,
336 (flags & REQUEST_KEY_FRAME) ? VPX_EFLAG_FORCE_KF : 0, VPX_DL_REALTIME);
311 DCHECK_EQ(ret, VPX_CODEC_OK) 337 DCHECK_EQ(ret, VPX_CODEC_OK)
312 << "Encoding error: " << vpx_codec_err_to_string(ret) << "\n" 338 << "Encoding error: " << vpx_codec_err_to_string(ret) << "\n"
313 << "Details: " << vpx_codec_error(codec_.get()) << "\n" 339 << "Details: " << vpx_codec_error(codec_.get()) << "\n"
314 << vpx_codec_error_detail(codec_.get()); 340 << vpx_codec_error_detail(codec_.get());
315 341
316 if (use_vp9_ && !lossless_encode_) { 342 if (use_vp9_ && !lossless_encode_) {
317 ret = vpx_codec_control(codec_.get(), VP9E_GET_ACTIVEMAP, &act_map); 343 ret = vpx_codec_control(codec_.get(), VP9E_GET_ACTIVEMAP, &act_map);
318 DCHECK_EQ(ret, VPX_CODEC_OK) 344 DCHECK_EQ(ret, VPX_CODEC_OK)
319 << "Failed to fetch active map: " 345 << "Failed to fetch active map: " << vpx_codec_err_to_string(ret)
320 << vpx_codec_err_to_string(ret) << "\n"; 346 << "\n";
321 UpdateRegionFromActiveMap(&updated_region); 347 UpdateRegionFromActiveMap(&updated_region);
322 348
323 // If the encoder output no changes then there's nothing left to top-off. 349 // If the encoder output no changes then there's nothing left to top-off.
324 encode_unchanged_frame_ = !updated_region.is_empty(); 350 encode_unchanged_frame_ = !updated_region.is_empty();
325 } 351 }
326 352
327 // Read the encoded data. 353 // Read the encoded data.
328 vpx_codec_iter_t iter = nullptr; 354 vpx_codec_iter_t iter = nullptr;
329 bool got_data = false; 355 bool got_data = false;
330 356
331 // TODO(hclam): Make sure we get exactly one frame from the packet. 357 // TODO(hclam): Make sure we get exactly one frame from the packet.
332 // TODO(hclam): We should provide the output buffer to avoid one copy. 358 // TODO(hclam): We should provide the output buffer to avoid one copy.
333 std::unique_ptr<VideoPacket> packet( 359 std::unique_ptr<VideoPacket> packet(
334 helper_.CreateVideoPacketWithUpdatedRegion(frame, updated_region)); 360 helper_.CreateVideoPacketWithUpdatedRegion(frame, updated_region));
335 packet->mutable_format()->set_encoding(VideoPacketFormat::ENCODING_VP8); 361 packet->mutable_format()->set_encoding(VideoPacketFormat::ENCODING_VP8);
336 362
337 while (!got_data) { 363 while (!got_data) {
338 const vpx_codec_cx_pkt_t* vpx_packet = 364 const vpx_codec_cx_pkt_t* vpx_packet =
339 vpx_codec_get_cx_data(codec_.get(), &iter); 365 vpx_codec_get_cx_data(codec_.get(), &iter);
340 if (!vpx_packet) 366 if (!vpx_packet)
341 continue; 367 continue;
342 368
369 int quantizer = -1;
343 switch (vpx_packet->kind) { 370 switch (vpx_packet->kind) {
344 case VPX_CODEC_CX_FRAME_PKT: 371 case VPX_CODEC_CX_FRAME_PKT:
345 got_data = true; 372 got_data = true;
346 packet->set_data(vpx_packet->data.frame.buf, vpx_packet->data.frame.sz); 373 packet->set_data(vpx_packet->data.frame.buf, vpx_packet->data.frame.sz);
347 packet->set_key_frame(vpx_packet->data.frame.flags & VPX_FRAME_IS_KEY); 374 packet->set_key_frame(vpx_packet->data.frame.flags & VPX_FRAME_IS_KEY);
375 CHECK_EQ(vpx_codec_control(codec_.get(), VP8E_GET_LAST_QUANTIZER_64,
376 &quantizer),
377 VPX_CODEC_OK);
378 packet->set_quantizer(quantizer);
348 break; 379 break;
349 default: 380 default:
350 break; 381 break;
351 } 382 }
352 } 383 }
353 384
354 return packet; 385 return packet;
355 } 386 }
356 387
357 VideoEncoderVpx::VideoEncoderVpx(bool use_vp9) 388 WebRtcVideoEncoderVpx::WebRtcVideoEncoderVpx(bool use_vp9)
358 : use_vp9_(use_vp9), 389 : use_vp9_(use_vp9),
390 target_bitrate_kbps_(kDefaultTargetBitrateKbps),
359 encode_unchanged_frame_(false), 391 encode_unchanged_frame_(false),
360 clock_(&default_tick_clock_) {} 392 clock_(&default_tick_clock_) {
393 // Indicates config is still uninitialized.
394 config_.g_timebase.den = 0;
395 }
361 396
362 void VideoEncoderVpx::Configure(const webrtc::DesktopSize& size) { 397 void WebRtcVideoEncoderVpx::Configure(const webrtc::DesktopSize& size) {
363 DCHECK(use_vp9_ || !lossless_color_); 398 DCHECK(use_vp9_ || !lossless_color_);
364 DCHECK(use_vp9_ || !lossless_encode_); 399 DCHECK(use_vp9_ || !lossless_encode_);
365 400
366 // Tear down |image_| if it no longer matches the size and color settings. 401 // Tear down |image_| if it no longer matches the size and color settings.
367 // PrepareImage() will then create a new buffer of the required dimensions if 402 // PrepareImage() will then create a new buffer of the required dimensions if
368 // |image_| is not allocated. 403 // |image_| is not allocated.
369 FreeImageIfMismatched(lossless_color_, size, &image_, &image_buffer_); 404 FreeImageIfMismatched(lossless_color_, size, &image_, &image_buffer_);
370 405
371 // Initialize active map. 406 // Initialize active map.
372 active_map_size_ = webrtc::DesktopSize( 407 active_map_size_ = webrtc::DesktopSize(
373 (size.width() + kMacroBlockSize - 1) / kMacroBlockSize, 408 (size.width() + kMacroBlockSize - 1) / kMacroBlockSize,
374 (size.height() + kMacroBlockSize - 1) / kMacroBlockSize); 409 (size.height() + kMacroBlockSize - 1) / kMacroBlockSize);
375 active_map_.reset( 410 active_map_.reset(
376 new uint8_t[active_map_size_.width() * active_map_size_.height()]); 411 new uint8_t[active_map_size_.width() * active_map_size_.height()]);
377 412
378 // TODO(wez): Remove this hack once VPX can handle frame size reconfiguration. 413 // TODO(wez): Remove this hack once VPX can handle frame size reconfiguration.
379 // See https://code.google.com/p/webm/issues/detail?id=912. 414 // See https://code.google.com/p/webm/issues/detail?id=912.
380 if (codec_) { 415 if (codec_) {
381 // If the frame size has changed then force re-creation of the codec. 416 // If the frame size has changed then force re-creation of the codec.
382 if (codec_->config.enc->g_w != static_cast<unsigned int>(size.width()) || 417 if (codec_->config.enc->g_w != static_cast<unsigned int>(size.width()) ||
383 codec_->config.enc->g_h != static_cast<unsigned int>(size.height())) { 418 codec_->config.enc->g_h != static_cast<unsigned int>(size.height())) {
384 codec_.reset(); 419 codec_.reset();
385 } 420 }
386 } 421 }
387 422
388 // (Re)Set the base for frame timestamps if the codec is being (re)created.
389 if (!codec_) {
390 timestamp_base_ = clock_->NowTicks();
391 }
392
393 // Fetch a default configuration for the desired codec. 423 // Fetch a default configuration for the desired codec.
394 const vpx_codec_iface_t* interface = 424 const vpx_codec_iface_t* interface =
395 use_vp9_ ? vpx_codec_vp9_cx() : vpx_codec_vp8_cx(); 425 use_vp9_ ? vpx_codec_vp9_cx() : vpx_codec_vp8_cx();
396 vpx_codec_enc_cfg_t config; 426 vpx_codec_err_t ret = vpx_codec_enc_config_default(interface, &config_, 0);
397 vpx_codec_err_t ret = vpx_codec_enc_config_default(interface, &config, 0);
398 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to fetch default configuration"; 427 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to fetch default configuration";
399 428
400 // Customize the default configuration to our needs. 429 // Customize the default configuration to our needs.
401 if (use_vp9_) { 430 if (use_vp9_) {
402 SetVp9CodecParameters(&config, size, lossless_color_, lossless_encode_); 431 SetVp9CodecParameters(&config_, size, lossless_color_, lossless_encode_);
403 } else { 432 } else {
404 SetVp8CodecParameters(&config, size); 433 SetVp8CodecParameters(&config_, size);
405 } 434 }
435 config_.rc_target_bitrate = target_bitrate_kbps_;
406 436
407 // Initialize or re-configure the codec with the custom configuration. 437 // Initialize or re-configure the codec with the custom configuration.
408 if (!codec_) { 438 if (!codec_) {
409 codec_.reset(new vpx_codec_ctx_t); 439 codec_.reset(new vpx_codec_ctx_t);
410 ret = vpx_codec_enc_init(codec_.get(), interface, &config, 0); 440 ret = vpx_codec_enc_init(codec_.get(), interface, &config_, 0);
411 CHECK_EQ(VPX_CODEC_OK, ret) << "Failed to initialize codec"; 441 CHECK_EQ(VPX_CODEC_OK, ret) << "Failed to initialize codec";
412 } else { 442 } else {
413 ret = vpx_codec_enc_config_set(codec_.get(), &config); 443 ret = vpx_codec_enc_config_set(codec_.get(), &config_);
414 CHECK_EQ(VPX_CODEC_OK, ret) << "Failed to reconfigure codec"; 444 CHECK_EQ(VPX_CODEC_OK, ret) << "Failed to reconfigure codec";
415 } 445 }
416 446
417 // Apply further customizations to the codec now it's initialized. 447 // Apply further customizations to the codec now it's initialized.
418 if (use_vp9_) { 448 if (use_vp9_) {
419 SetVp9CodecOptions(codec_.get(), lossless_encode_); 449 SetVp9CodecOptions(codec_.get(), lossless_encode_);
420 } else { 450 } else {
421 SetVp8CodecOptions(codec_.get()); 451 SetVp8CodecOptions(codec_.get());
422 } 452 }
423 } 453 }
424 454
425 void VideoEncoderVpx::PrepareImage(const webrtc::DesktopFrame& frame, 455 void WebRtcVideoEncoderVpx::PrepareImage(
426 webrtc::DesktopRegion* updated_region) { 456 const webrtc::DesktopFrame& frame,
457 webrtc::DesktopRegion* updated_region) {
427 if (frame.updated_region().is_empty()) { 458 if (frame.updated_region().is_empty()) {
428 updated_region->Clear(); 459 updated_region->Clear();
429 return; 460 return;
430 } 461 }
431 462
432 updated_region->Clear(); 463 updated_region->Clear();
433 if (image_) { 464 if (image_) {
434 // Pad each rectangle to avoid the block-artefact filters in libvpx from 465 // Pad each rectangle to avoid the block-artefact filters in libvpx from
435 // introducing artefacts; VP9 includes up to 8px either side, and VP8 up to 466 // introducing artefacts; VP9 includes up to 8px either side, and VP8 up to
436 // 3px, so unchanged pixels up to that far out may still be affected by the 467 // 3px, so unchanged pixels up to that far out may still be affected by the
(...skipping 30 matching lines...) Expand all
467 const int uv_stride = image_->stride[1]; 498 const int uv_stride = image_->stride[1];
468 uint8_t* y_data = image_->planes[0]; 499 uint8_t* y_data = image_->planes[0];
469 uint8_t* u_data = image_->planes[1]; 500 uint8_t* u_data = image_->planes[1];
470 uint8_t* v_data = image_->planes[2]; 501 uint8_t* v_data = image_->planes[2];
471 502
472 switch (image_->fmt) { 503 switch (image_->fmt) {
473 case VPX_IMG_FMT_I444: 504 case VPX_IMG_FMT_I444:
474 for (webrtc::DesktopRegion::Iterator r(*updated_region); !r.IsAtEnd(); 505 for (webrtc::DesktopRegion::Iterator r(*updated_region); !r.IsAtEnd();
475 r.Advance()) { 506 r.Advance()) {
476 const webrtc::DesktopRect& rect = r.rect(); 507 const webrtc::DesktopRect& rect = r.rect();
477 int rgb_offset = rgb_stride * rect.top() + 508 int rgb_offset =
478 rect.left() * kBytesPerRgbPixel; 509 rgb_stride * rect.top() + rect.left() * kBytesPerRgbPixel;
479 int yuv_offset = uv_stride * rect.top() + rect.left(); 510 int yuv_offset = uv_stride * rect.top() + rect.left();
480 libyuv::ARGBToI444(rgb_data + rgb_offset, rgb_stride, 511 libyuv::ARGBToI444(rgb_data + rgb_offset, rgb_stride,
481 y_data + yuv_offset, y_stride, 512 y_data + yuv_offset, y_stride, u_data + yuv_offset,
482 u_data + yuv_offset, uv_stride, 513 uv_stride, v_data + yuv_offset, uv_stride,
483 v_data + yuv_offset, uv_stride,
484 rect.width(), rect.height()); 514 rect.width(), rect.height());
485 } 515 }
486 break; 516 break;
487 case VPX_IMG_FMT_YV12: 517 case VPX_IMG_FMT_YV12:
488 for (webrtc::DesktopRegion::Iterator r(*updated_region); !r.IsAtEnd(); 518 for (webrtc::DesktopRegion::Iterator r(*updated_region); !r.IsAtEnd();
489 r.Advance()) { 519 r.Advance()) {
490 const webrtc::DesktopRect& rect = r.rect(); 520 const webrtc::DesktopRect& rect = r.rect();
491 int rgb_offset = rgb_stride * rect.top() + 521 int rgb_offset =
492 rect.left() * kBytesPerRgbPixel; 522 rgb_stride * rect.top() + rect.left() * kBytesPerRgbPixel;
493 int y_offset = y_stride * rect.top() + rect.left(); 523 int y_offset = y_stride * rect.top() + rect.left();
494 int uv_offset = uv_stride * rect.top() / 2 + rect.left() / 2; 524 int uv_offset = uv_stride * rect.top() / 2 + rect.left() / 2;
495 libyuv::ARGBToI420(rgb_data + rgb_offset, rgb_stride, 525 libyuv::ARGBToI420(rgb_data + rgb_offset, rgb_stride, y_data + y_offset,
496 y_data + y_offset, y_stride, 526 y_stride, u_data + uv_offset, uv_stride,
497 u_data + uv_offset, uv_stride, 527 v_data + uv_offset, uv_stride, rect.width(),
498 v_data + uv_offset, uv_stride, 528 rect.height());
499 rect.width(), rect.height());
500 } 529 }
501 break; 530 break;
502 default: 531 default:
503 NOTREACHED(); 532 NOTREACHED();
504 break; 533 break;
505 } 534 }
506 } 535 }
507 536
508 void VideoEncoderVpx::SetActiveMapFromRegion( 537 void WebRtcVideoEncoderVpx::SetActiveMapFromRegion(
509 const webrtc::DesktopRegion& updated_region) { 538 const webrtc::DesktopRegion& updated_region) {
510 // Clear active map first. 539 // Clear active map first.
511 memset(active_map_.get(), 0, 540 memset(active_map_.get(), 0,
512 active_map_size_.width() * active_map_size_.height()); 541 active_map_size_.width() * active_map_size_.height());
513 542
514 // Mark updated areas active. 543 // Mark updated areas active.
515 for (webrtc::DesktopRegion::Iterator r(updated_region); !r.IsAtEnd(); 544 for (webrtc::DesktopRegion::Iterator r(updated_region); !r.IsAtEnd();
516 r.Advance()) { 545 r.Advance()) {
517 const webrtc::DesktopRect& rect = r.rect(); 546 const webrtc::DesktopRect& rect = r.rect();
518 int left = rect.left() / kMacroBlockSize; 547 int left = rect.left() / kMacroBlockSize;
519 int right = (rect.right() - 1) / kMacroBlockSize; 548 int right = (rect.right() - 1) / kMacroBlockSize;
520 int top = rect.top() / kMacroBlockSize; 549 int top = rect.top() / kMacroBlockSize;
521 int bottom = (rect.bottom() - 1) / kMacroBlockSize; 550 int bottom = (rect.bottom() - 1) / kMacroBlockSize;
522 DCHECK_LT(right, active_map_size_.width()); 551 DCHECK_LT(right, active_map_size_.width());
523 DCHECK_LT(bottom, active_map_size_.height()); 552 DCHECK_LT(bottom, active_map_size_.height());
524 553
525 uint8_t* map = active_map_.get() + top * active_map_size_.width(); 554 uint8_t* map = active_map_.get() + top * active_map_size_.width();
526 for (int y = top; y <= bottom; ++y) { 555 for (int y = top; y <= bottom; ++y) {
527 for (int x = left; x <= right; ++x) 556 for (int x = left; x <= right; ++x)
528 map[x] = 1; 557 map[x] = 1;
529 map += active_map_size_.width(); 558 map += active_map_size_.width();
530 } 559 }
531 } 560 }
532 } 561 }
533 562
534 void VideoEncoderVpx::UpdateRegionFromActiveMap( 563 void WebRtcVideoEncoderVpx::UpdateRegionFromActiveMap(
535 webrtc::DesktopRegion* updated_region) { 564 webrtc::DesktopRegion* updated_region) {
536 const uint8_t* map = active_map_.get(); 565 const uint8_t* map = active_map_.get();
537 for (int y = 0; y < active_map_size_.height(); ++y) { 566 for (int y = 0; y < active_map_size_.height(); ++y) {
538 for (int x0 = 0; x0 < active_map_size_.width();) { 567 for (int x0 = 0; x0 < active_map_size_.width();) {
539 int x1 = x0; 568 int x1 = x0;
540 for (; x1 < active_map_size_.width(); ++x1) { 569 for (; x1 < active_map_size_.width(); ++x1) {
541 if (map[y * active_map_size_.width() + x1] == 0) 570 if (map[y * active_map_size_.width() + x1] == 0)
542 break; 571 break;
543 } 572 }
544 if (x1 > x0) { 573 if (x1 > x0) {
545 updated_region->AddRect(webrtc::DesktopRect::MakeLTRB( 574 updated_region->AddRect(webrtc::DesktopRect::MakeLTRB(
546 kMacroBlockSize * x0, kMacroBlockSize * y, kMacroBlockSize * x1, 575 kMacroBlockSize * x0, kMacroBlockSize * y, kMacroBlockSize * x1,
547 kMacroBlockSize * (y + 1))); 576 kMacroBlockSize * (y + 1)));
548 } 577 }
549 x0 = x1 + 1; 578 x0 = x1 + 1;
550 } 579 }
551 } 580 }
552 updated_region->IntersectWith( 581 updated_region->IntersectWith(
553 webrtc::DesktopRect::MakeWH(image_->w, image_->h)); 582 webrtc::DesktopRect::MakeWH(image_->w, image_->h));
554 } 583 }
555 584
556 } // namespace remoting 585 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698