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

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: Remove unnecessary code 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
« no previous file with comments | « remoting/codec/webrtc_video_encoder_vpx.h ('k') | remoting/proto/video.proto » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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(int new_bitrate_kbps) {
277 target_bitrate_kbps_ = new_bitrate_kbps;
278 // Configuration not initialized.
279 if (config_.g_timebase.den == 0)
280 return;
281
282 if (config_.rc_target_bitrate == static_cast<unsigned int>(new_bitrate_kbps))
283 return;
284 config_.rc_target_bitrate = new_bitrate_kbps;
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 << " 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 requirement. We specify a target rate of 1 / 15 fps here.
306 334 // TODO(isheriff): Investigate if it makes sense to increase the target FPS.
307 // Do the actual encoding.
308 int timestamp = (clock_->NowTicks() - timestamp_base_).InMilliseconds();
309 vpx_codec_err_t ret = vpx_codec_encode( 335 vpx_codec_err_t ret = vpx_codec_encode(
310 codec_.get(), image_.get(), timestamp, 1, 0, VPX_DL_REALTIME); 336 codec_.get(), image_.get(), 0, 66666,
337 (flags & REQUEST_KEY_FRAME) ? VPX_EFLAG_FORCE_KF : 0, VPX_DL_REALTIME);
311 DCHECK_EQ(ret, VPX_CODEC_OK) 338 DCHECK_EQ(ret, VPX_CODEC_OK)
312 << "Encoding error: " << vpx_codec_err_to_string(ret) << "\n" 339 << "Encoding error: " << vpx_codec_err_to_string(ret) << "\n"
313 << "Details: " << vpx_codec_error(codec_.get()) << "\n" 340 << "Details: " << vpx_codec_error(codec_.get()) << "\n"
314 << vpx_codec_error_detail(codec_.get()); 341 << vpx_codec_error_detail(codec_.get());
315 342
316 if (use_vp9_ && !lossless_encode_) { 343 if (use_vp9_ && !lossless_encode_) {
317 ret = vpx_codec_control(codec_.get(), VP9E_GET_ACTIVEMAP, &act_map); 344 ret = vpx_codec_control(codec_.get(), VP9E_GET_ACTIVEMAP, &act_map);
318 DCHECK_EQ(ret, VPX_CODEC_OK) 345 DCHECK_EQ(ret, VPX_CODEC_OK)
319 << "Failed to fetch active map: " 346 << "Failed to fetch active map: " << vpx_codec_err_to_string(ret)
320 << vpx_codec_err_to_string(ret) << "\n"; 347 << "\n";
321 UpdateRegionFromActiveMap(&updated_region); 348 UpdateRegionFromActiveMap(&updated_region);
322 349
323 // If the encoder output no changes then there's nothing left to top-off. 350 // If the encoder output no changes then there's nothing left to top-off.
324 encode_unchanged_frame_ = !updated_region.is_empty(); 351 encode_unchanged_frame_ = !updated_region.is_empty();
325 } 352 }
326 353
327 // Read the encoded data. 354 // Read the encoded data.
328 vpx_codec_iter_t iter = nullptr; 355 vpx_codec_iter_t iter = nullptr;
329 bool got_data = false; 356 bool got_data = false;
330 357
331 // TODO(hclam): Make sure we get exactly one frame from the packet. 358 // 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. 359 // TODO(hclam): We should provide the output buffer to avoid one copy.
333 std::unique_ptr<VideoPacket> packet( 360 std::unique_ptr<VideoPacket> packet(
334 helper_.CreateVideoPacketWithUpdatedRegion(frame, updated_region)); 361 helper_.CreateVideoPacketWithUpdatedRegion(frame, updated_region));
335 packet->mutable_format()->set_encoding(VideoPacketFormat::ENCODING_VP8); 362 packet->mutable_format()->set_encoding(VideoPacketFormat::ENCODING_VP8);
336 363
337 while (!got_data) { 364 while (!got_data) {
338 const vpx_codec_cx_pkt_t* vpx_packet = 365 const vpx_codec_cx_pkt_t* vpx_packet =
339 vpx_codec_get_cx_data(codec_.get(), &iter); 366 vpx_codec_get_cx_data(codec_.get(), &iter);
340 if (!vpx_packet) 367 if (!vpx_packet)
341 continue; 368 continue;
342 369
370 int quantizer = -1;
343 switch (vpx_packet->kind) { 371 switch (vpx_packet->kind) {
344 case VPX_CODEC_CX_FRAME_PKT: 372 case VPX_CODEC_CX_FRAME_PKT:
345 got_data = true; 373 got_data = true;
346 packet->set_data(vpx_packet->data.frame.buf, vpx_packet->data.frame.sz); 374 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); 375 packet->set_key_frame(vpx_packet->data.frame.flags & VPX_FRAME_IS_KEY);
376 CHECK_EQ(vpx_codec_control(codec_.get(), VP8E_GET_LAST_QUANTIZER_64,
377 &quantizer),
378 VPX_CODEC_OK);
379 packet->set_quantizer(quantizer);
348 break; 380 break;
349 default: 381 default:
350 break; 382 break;
351 } 383 }
352 } 384 }
353 385
354 return packet; 386 return packet;
355 } 387 }
356 388
357 VideoEncoderVpx::VideoEncoderVpx(bool use_vp9) 389 WebRtcVideoEncoderVpx::WebRtcVideoEncoderVpx(bool use_vp9)
358 : use_vp9_(use_vp9), 390 : use_vp9_(use_vp9),
391 target_bitrate_kbps_(kDefaultTargetBitrateKbps),
359 encode_unchanged_frame_(false), 392 encode_unchanged_frame_(false),
360 clock_(&default_tick_clock_) {} 393 clock_(&default_tick_clock_) {
394 // Indicates config is still uninitialized.
395 config_.g_timebase.den = 0;
396 }
361 397
362 void VideoEncoderVpx::Configure(const webrtc::DesktopSize& size) { 398 void WebRtcVideoEncoderVpx::Configure(const webrtc::DesktopSize& size) {
363 DCHECK(use_vp9_ || !lossless_color_); 399 DCHECK(use_vp9_ || !lossless_color_);
364 DCHECK(use_vp9_ || !lossless_encode_); 400 DCHECK(use_vp9_ || !lossless_encode_);
365 401
366 // Tear down |image_| if it no longer matches the size and color settings. 402 // 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 403 // PrepareImage() will then create a new buffer of the required dimensions if
368 // |image_| is not allocated. 404 // |image_| is not allocated.
369 FreeImageIfMismatched(lossless_color_, size, &image_, &image_buffer_); 405 FreeImageIfMismatched(lossless_color_, size, &image_, &image_buffer_);
370 406
371 // Initialize active map. 407 // Initialize active map.
372 active_map_size_ = webrtc::DesktopSize( 408 active_map_size_ = webrtc::DesktopSize(
373 (size.width() + kMacroBlockSize - 1) / kMacroBlockSize, 409 (size.width() + kMacroBlockSize - 1) / kMacroBlockSize,
374 (size.height() + kMacroBlockSize - 1) / kMacroBlockSize); 410 (size.height() + kMacroBlockSize - 1) / kMacroBlockSize);
375 active_map_.reset( 411 active_map_.reset(
376 new uint8_t[active_map_size_.width() * active_map_size_.height()]); 412 new uint8_t[active_map_size_.width() * active_map_size_.height()]);
377 413
378 // TODO(wez): Remove this hack once VPX can handle frame size reconfiguration. 414 // TODO(wez): Remove this hack once VPX can handle frame size reconfiguration.
379 // See https://code.google.com/p/webm/issues/detail?id=912. 415 // See https://code.google.com/p/webm/issues/detail?id=912.
380 if (codec_) { 416 if (codec_) {
381 // If the frame size has changed then force re-creation of the codec. 417 // 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()) || 418 if (codec_->config.enc->g_w != static_cast<unsigned int>(size.width()) ||
383 codec_->config.enc->g_h != static_cast<unsigned int>(size.height())) { 419 codec_->config.enc->g_h != static_cast<unsigned int>(size.height())) {
384 codec_.reset(); 420 codec_.reset();
385 } 421 }
386 } 422 }
387 423
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. 424 // Fetch a default configuration for the desired codec.
394 const vpx_codec_iface_t* interface = 425 const vpx_codec_iface_t* interface =
395 use_vp9_ ? vpx_codec_vp9_cx() : vpx_codec_vp8_cx(); 426 use_vp9_ ? vpx_codec_vp9_cx() : vpx_codec_vp8_cx();
396 vpx_codec_enc_cfg_t config; 427 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"; 428 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to fetch default configuration";
399 429
400 // Customize the default configuration to our needs. 430 // Customize the default configuration to our needs.
401 if (use_vp9_) { 431 if (use_vp9_) {
402 SetVp9CodecParameters(&config, size, lossless_color_, lossless_encode_); 432 SetVp9CodecParameters(&config_, size, lossless_color_, lossless_encode_);
403 } else { 433 } else {
404 SetVp8CodecParameters(&config, size); 434 SetVp8CodecParameters(&config_, size);
405 } 435 }
436 config_.rc_target_bitrate = target_bitrate_kbps_;
406 437
407 // Initialize or re-configure the codec with the custom configuration. 438 // Initialize or re-configure the codec with the custom configuration.
408 if (!codec_) { 439 if (!codec_) {
409 codec_.reset(new vpx_codec_ctx_t); 440 codec_.reset(new vpx_codec_ctx_t);
410 ret = vpx_codec_enc_init(codec_.get(), interface, &config, 0); 441 ret = vpx_codec_enc_init(codec_.get(), interface, &config_, 0);
411 CHECK_EQ(VPX_CODEC_OK, ret) << "Failed to initialize codec"; 442 CHECK_EQ(VPX_CODEC_OK, ret) << "Failed to initialize codec";
412 } else { 443 } else {
413 ret = vpx_codec_enc_config_set(codec_.get(), &config); 444 ret = vpx_codec_enc_config_set(codec_.get(), &config_);
414 CHECK_EQ(VPX_CODEC_OK, ret) << "Failed to reconfigure codec"; 445 CHECK_EQ(VPX_CODEC_OK, ret) << "Failed to reconfigure codec";
415 } 446 }
416 447
417 // Apply further customizations to the codec now it's initialized. 448 // Apply further customizations to the codec now it's initialized.
418 if (use_vp9_) { 449 if (use_vp9_) {
419 SetVp9CodecOptions(codec_.get(), lossless_encode_); 450 SetVp9CodecOptions(codec_.get(), lossless_encode_);
420 } else { 451 } else {
421 SetVp8CodecOptions(codec_.get()); 452 SetVp8CodecOptions(codec_.get());
422 } 453 }
423 } 454 }
424 455
425 void VideoEncoderVpx::PrepareImage(const webrtc::DesktopFrame& frame, 456 void WebRtcVideoEncoderVpx::PrepareImage(
426 webrtc::DesktopRegion* updated_region) { 457 const webrtc::DesktopFrame& frame,
458 webrtc::DesktopRegion* updated_region) {
427 if (frame.updated_region().is_empty()) { 459 if (frame.updated_region().is_empty()) {
428 updated_region->Clear(); 460 updated_region->Clear();
429 return; 461 return;
430 } 462 }
431 463
432 updated_region->Clear(); 464 updated_region->Clear();
433 if (image_) { 465 if (image_) {
434 // Pad each rectangle to avoid the block-artefact filters in libvpx from 466 // 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 467 // 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 468 // 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]; 499 const int uv_stride = image_->stride[1];
468 uint8_t* y_data = image_->planes[0]; 500 uint8_t* y_data = image_->planes[0];
469 uint8_t* u_data = image_->planes[1]; 501 uint8_t* u_data = image_->planes[1];
470 uint8_t* v_data = image_->planes[2]; 502 uint8_t* v_data = image_->planes[2];
471 503
472 switch (image_->fmt) { 504 switch (image_->fmt) {
473 case VPX_IMG_FMT_I444: 505 case VPX_IMG_FMT_I444:
474 for (webrtc::DesktopRegion::Iterator r(*updated_region); !r.IsAtEnd(); 506 for (webrtc::DesktopRegion::Iterator r(*updated_region); !r.IsAtEnd();
475 r.Advance()) { 507 r.Advance()) {
476 const webrtc::DesktopRect& rect = r.rect(); 508 const webrtc::DesktopRect& rect = r.rect();
477 int rgb_offset = rgb_stride * rect.top() + 509 int rgb_offset =
478 rect.left() * kBytesPerRgbPixel; 510 rgb_stride * rect.top() + rect.left() * kBytesPerRgbPixel;
479 int yuv_offset = uv_stride * rect.top() + rect.left(); 511 int yuv_offset = uv_stride * rect.top() + rect.left();
480 libyuv::ARGBToI444(rgb_data + rgb_offset, rgb_stride, 512 libyuv::ARGBToI444(rgb_data + rgb_offset, rgb_stride,
481 y_data + yuv_offset, y_stride, 513 y_data + yuv_offset, y_stride, u_data + yuv_offset,
482 u_data + yuv_offset, uv_stride, 514 uv_stride, v_data + yuv_offset, uv_stride,
483 v_data + yuv_offset, uv_stride,
484 rect.width(), rect.height()); 515 rect.width(), rect.height());
485 } 516 }
486 break; 517 break;
487 case VPX_IMG_FMT_YV12: 518 case VPX_IMG_FMT_YV12:
488 for (webrtc::DesktopRegion::Iterator r(*updated_region); !r.IsAtEnd(); 519 for (webrtc::DesktopRegion::Iterator r(*updated_region); !r.IsAtEnd();
489 r.Advance()) { 520 r.Advance()) {
490 const webrtc::DesktopRect& rect = r.rect(); 521 const webrtc::DesktopRect& rect = r.rect();
491 int rgb_offset = rgb_stride * rect.top() + 522 int rgb_offset =
492 rect.left() * kBytesPerRgbPixel; 523 rgb_stride * rect.top() + rect.left() * kBytesPerRgbPixel;
493 int y_offset = y_stride * rect.top() + rect.left(); 524 int y_offset = y_stride * rect.top() + rect.left();
494 int uv_offset = uv_stride * rect.top() / 2 + rect.left() / 2; 525 int uv_offset = uv_stride * rect.top() / 2 + rect.left() / 2;
495 libyuv::ARGBToI420(rgb_data + rgb_offset, rgb_stride, 526 libyuv::ARGBToI420(rgb_data + rgb_offset, rgb_stride, y_data + y_offset,
496 y_data + y_offset, y_stride, 527 y_stride, u_data + uv_offset, uv_stride,
497 u_data + uv_offset, uv_stride, 528 v_data + uv_offset, uv_stride, rect.width(),
498 v_data + uv_offset, uv_stride, 529 rect.height());
499 rect.width(), rect.height());
500 } 530 }
501 break; 531 break;
502 default: 532 default:
503 NOTREACHED(); 533 NOTREACHED();
504 break; 534 break;
505 } 535 }
506 } 536 }
507 537
508 void VideoEncoderVpx::SetActiveMapFromRegion( 538 void WebRtcVideoEncoderVpx::SetActiveMapFromRegion(
509 const webrtc::DesktopRegion& updated_region) { 539 const webrtc::DesktopRegion& updated_region) {
510 // Clear active map first. 540 // Clear active map first.
511 memset(active_map_.get(), 0, 541 memset(active_map_.get(), 0,
512 active_map_size_.width() * active_map_size_.height()); 542 active_map_size_.width() * active_map_size_.height());
513 543
514 // Mark updated areas active. 544 // Mark updated areas active.
515 for (webrtc::DesktopRegion::Iterator r(updated_region); !r.IsAtEnd(); 545 for (webrtc::DesktopRegion::Iterator r(updated_region); !r.IsAtEnd();
516 r.Advance()) { 546 r.Advance()) {
517 const webrtc::DesktopRect& rect = r.rect(); 547 const webrtc::DesktopRect& rect = r.rect();
518 int left = rect.left() / kMacroBlockSize; 548 int left = rect.left() / kMacroBlockSize;
519 int right = (rect.right() - 1) / kMacroBlockSize; 549 int right = (rect.right() - 1) / kMacroBlockSize;
520 int top = rect.top() / kMacroBlockSize; 550 int top = rect.top() / kMacroBlockSize;
521 int bottom = (rect.bottom() - 1) / kMacroBlockSize; 551 int bottom = (rect.bottom() - 1) / kMacroBlockSize;
522 DCHECK_LT(right, active_map_size_.width()); 552 DCHECK_LT(right, active_map_size_.width());
523 DCHECK_LT(bottom, active_map_size_.height()); 553 DCHECK_LT(bottom, active_map_size_.height());
524 554
525 uint8_t* map = active_map_.get() + top * active_map_size_.width(); 555 uint8_t* map = active_map_.get() + top * active_map_size_.width();
526 for (int y = top; y <= bottom; ++y) { 556 for (int y = top; y <= bottom; ++y) {
527 for (int x = left; x <= right; ++x) 557 for (int x = left; x <= right; ++x)
528 map[x] = 1; 558 map[x] = 1;
529 map += active_map_size_.width(); 559 map += active_map_size_.width();
530 } 560 }
531 } 561 }
532 } 562 }
533 563
534 void VideoEncoderVpx::UpdateRegionFromActiveMap( 564 void WebRtcVideoEncoderVpx::UpdateRegionFromActiveMap(
535 webrtc::DesktopRegion* updated_region) { 565 webrtc::DesktopRegion* updated_region) {
536 const uint8_t* map = active_map_.get(); 566 const uint8_t* map = active_map_.get();
537 for (int y = 0; y < active_map_size_.height(); ++y) { 567 for (int y = 0; y < active_map_size_.height(); ++y) {
538 for (int x0 = 0; x0 < active_map_size_.width();) { 568 for (int x0 = 0; x0 < active_map_size_.width();) {
539 int x1 = x0; 569 int x1 = x0;
540 for (; x1 < active_map_size_.width(); ++x1) { 570 for (; x1 < active_map_size_.width(); ++x1) {
541 if (map[y * active_map_size_.width() + x1] == 0) 571 if (map[y * active_map_size_.width() + x1] == 0)
542 break; 572 break;
543 } 573 }
544 if (x1 > x0) { 574 if (x1 > x0) {
545 updated_region->AddRect(webrtc::DesktopRect::MakeLTRB( 575 updated_region->AddRect(webrtc::DesktopRect::MakeLTRB(
546 kMacroBlockSize * x0, kMacroBlockSize * y, kMacroBlockSize * x1, 576 kMacroBlockSize * x0, kMacroBlockSize * y, kMacroBlockSize * x1,
547 kMacroBlockSize * (y + 1))); 577 kMacroBlockSize * (y + 1)));
548 } 578 }
549 x0 = x1 + 1; 579 x0 = x1 + 1;
550 } 580 }
551 } 581 }
552 updated_region->IntersectWith( 582 updated_region->IntersectWith(
553 webrtc::DesktopRect::MakeWH(image_->w, image_->h)); 583 webrtc::DesktopRect::MakeWH(image_->w, image_->h));
554 } 584 }
555 585
556 } // namespace remoting 586 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/codec/webrtc_video_encoder_vpx.h ('k') | remoting/proto/video.proto » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698