OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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/video_encoder_vpx.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/command_line.h" | 8 #include "base/command_line.h" |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "base/sys_info.h" | 10 #include "base/sys_info.h" |
(...skipping 21 matching lines...) Expand all Loading... | |
32 const int kBytesPerRgbPixel = 4; | 32 const int kBytesPerRgbPixel = 4; |
33 | 33 |
34 // Defines the dimension of a macro block. This is used to compute the active | 34 // Defines the dimension of a macro block. This is used to compute the active |
35 // map for the encoder. | 35 // map for the encoder. |
36 const int kMacroBlockSize = 16; | 36 const int kMacroBlockSize = 16; |
37 | 37 |
38 // Magic encoder profile numbers for I420 and I444 input formats. | 38 // Magic encoder profile numbers for I420 and I444 input formats. |
39 const int kVp9I420ProfileNumber = 0; | 39 const int kVp9I420ProfileNumber = 0; |
40 const int kVp9I444ProfileNumber = 1; | 40 const int kVp9I444ProfileNumber = 1; |
41 | 41 |
42 // Magic encoder constants for adaptive quantization strategy. | |
43 const int kVp9AqModeNone = 0; | |
44 const int kVp9AqModeCyclicRefresh = 3; | |
45 | |
42 void SetCommonCodecParameters(vpx_codec_enc_cfg_t* config, | 46 void SetCommonCodecParameters(vpx_codec_enc_cfg_t* config, |
43 const webrtc::DesktopSize& size) { | 47 const webrtc::DesktopSize& size) { |
44 // Use millisecond granularity time base. | 48 // Use millisecond granularity time base. |
45 config->g_timebase.num = 1; | 49 config->g_timebase.num = 1; |
46 config->g_timebase.den = 1000; | 50 config->g_timebase.den = 1000; |
47 | 51 |
48 config->g_w = size.width(); | 52 config->g_w = size.width(); |
49 config->g_h = size.height(); | 53 config->g_h = size.height(); |
50 config->g_pass = VPX_RC_ONE_PASS; | 54 config->g_pass = VPX_RC_ONE_PASS; |
51 | 55 |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
130 | 134 |
131 // Use the lowest level of noise sensitivity so as to spend less time | 135 // Use the lowest level of noise sensitivity so as to spend less time |
132 // on motion estimation and inter-prediction mode. | 136 // on motion estimation and inter-prediction mode. |
133 ret = vpx_codec_control(codec, VP9E_SET_NOISE_SENSITIVITY, 0); | 137 ret = vpx_codec_control(codec, VP9E_SET_NOISE_SENSITIVITY, 0); |
134 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set noise sensitivity"; | 138 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set noise sensitivity"; |
135 | 139 |
136 // Configure the codec to tune it for screen media. | 140 // Configure the codec to tune it for screen media. |
137 ret = vpx_codec_control( | 141 ret = vpx_codec_control( |
138 codec, VP9E_SET_TUNE_CONTENT, VP9E_CONTENT_SCREEN); | 142 codec, VP9E_SET_TUNE_CONTENT, VP9E_CONTENT_SCREEN); |
139 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set screen content mode"; | 143 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set screen content mode"; |
144 | |
145 // Set cyclic refresh (aka "top-off") only for lossy encoding. | |
146 int aq_mode = lossless_encode ? kVp9AqModeNone : kVp9AqModeCyclicRefresh; | |
147 ret = vpx_codec_control(codec, VP9E_SET_AQ_MODE, aq_mode); | |
148 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set aq mode"; | |
140 } | 149 } |
141 | 150 |
142 void FreeImageIfMismatched(bool use_i444, | 151 void FreeImageIfMismatched(bool use_i444, |
143 const webrtc::DesktopSize& size, | 152 const webrtc::DesktopSize& size, |
144 scoped_ptr<vpx_image_t>* out_image, | 153 scoped_ptr<vpx_image_t>* out_image, |
145 scoped_ptr<uint8[]>* out_image_buffer) { | 154 scoped_ptr<uint8[]>* out_image_buffer) { |
146 if (*out_image) { | 155 if (*out_image) { |
147 const vpx_img_fmt_t desired_fmt = | 156 const vpx_img_fmt_t desired_fmt = |
148 use_i444 ? VPX_IMG_FMT_I444 : VPX_IMG_FMT_I420; | 157 use_i444 ? VPX_IMG_FMT_I444 : VPX_IMG_FMT_I420; |
149 if (!size.equals(webrtc::DesktopSize((*out_image)->w, (*out_image)->h)) || | 158 if (!size.equals(webrtc::DesktopSize((*out_image)->w, (*out_image)->h)) || |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
266 (image_ && | 275 (image_ && |
267 !frame.size().equals(webrtc::DesktopSize(image_->w, image_->h)))) { | 276 !frame.size().equals(webrtc::DesktopSize(image_->w, image_->h)))) { |
268 Configure(frame.size()); | 277 Configure(frame.size()); |
269 } | 278 } |
270 | 279 |
271 // Convert the updated capture data ready for encode. | 280 // Convert the updated capture data ready for encode. |
272 webrtc::DesktopRegion updated_region; | 281 webrtc::DesktopRegion updated_region; |
273 PrepareImage(frame, &updated_region); | 282 PrepareImage(frame, &updated_region); |
274 | 283 |
275 // Update active map based on updated region. | 284 // Update active map based on updated region. |
276 PrepareActiveMap(updated_region); | 285 SetActiveMapFromRegion(updated_region); |
277 | 286 |
278 // Apply active map to the encoder. | 287 // Apply active map to the encoder. |
279 vpx_active_map_t act_map; | 288 vpx_active_map_t act_map; |
280 act_map.rows = active_map_height_; | 289 act_map.rows = active_map_height_; |
281 act_map.cols = active_map_width_; | 290 act_map.cols = active_map_width_; |
282 act_map.active_map = active_map_.get(); | 291 act_map.active_map = active_map_.get(); |
283 if (vpx_codec_control(codec_.get(), VP8E_SET_ACTIVEMAP, &act_map)) { | 292 if (vpx_codec_control(codec_.get(), VP8E_SET_ACTIVEMAP, &act_map)) { |
284 LOG(ERROR) << "Unable to apply active map"; | 293 LOG(ERROR) << "Unable to apply active map"; |
285 } | 294 } |
286 | 295 |
287 // Do the actual encoding. | 296 // Do the actual encoding. |
288 int timestamp = (encode_start_time - timestamp_base_).InMilliseconds(); | 297 int timestamp = (encode_start_time - timestamp_base_).InMilliseconds(); |
289 vpx_codec_err_t ret = vpx_codec_encode( | 298 vpx_codec_err_t ret = vpx_codec_encode( |
290 codec_.get(), image_.get(), timestamp, 1, 0, VPX_DL_REALTIME); | 299 codec_.get(), image_.get(), timestamp, 1, 0, VPX_DL_REALTIME); |
291 DCHECK_EQ(ret, VPX_CODEC_OK) | 300 DCHECK_EQ(ret, VPX_CODEC_OK) |
292 << "Encoding error: " << vpx_codec_err_to_string(ret) << "\n" | 301 << "Encoding error: " << vpx_codec_err_to_string(ret) << "\n" |
293 << "Details: " << vpx_codec_error(codec_.get()) << "\n" | 302 << "Details: " << vpx_codec_error(codec_.get()) << "\n" |
294 << vpx_codec_error_detail(codec_.get()); | 303 << vpx_codec_error_detail(codec_.get()); |
295 | 304 |
305 if (use_vp9_ && !lossless_encode_) { | |
306 ret = vpx_codec_control(codec_.get(), VP9E_GET_ACTIVEMAP, &act_map); | |
307 DCHECK_EQ(ret, VPX_CODEC_OK) | |
308 << "Failed to fetch active map: " | |
309 << vpx_codec_err_to_string(ret) << "\n"; | |
310 UpdateRegionFromActiveMap(&updated_region); | |
311 } | |
312 | |
296 // Read the encoded data. | 313 // Read the encoded data. |
297 vpx_codec_iter_t iter = NULL; | 314 vpx_codec_iter_t iter = NULL; |
298 bool got_data = false; | 315 bool got_data = false; |
299 | 316 |
300 // TODO(hclam): Make sure we get exactly one frame from the packet. | 317 // TODO(hclam): Make sure we get exactly one frame from the packet. |
301 // TODO(hclam): We should provide the output buffer to avoid one copy. | 318 // TODO(hclam): We should provide the output buffer to avoid one copy. |
302 scoped_ptr<VideoPacket> packet( | 319 scoped_ptr<VideoPacket> packet( |
303 helper_.CreateVideoPacketWithUpdatedRegion(frame, updated_region)); | 320 helper_.CreateVideoPacketWithUpdatedRegion(frame, updated_region)); |
304 packet->mutable_format()->set_encoding(VideoPacketFormat::ENCODING_VP8); | 321 packet->mutable_format()->set_encoding(VideoPacketFormat::ENCODING_VP8); |
305 | 322 |
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
478 v_data + uv_offset, uv_stride, | 495 v_data + uv_offset, uv_stride, |
479 rect.width(), rect.height()); | 496 rect.width(), rect.height()); |
480 } | 497 } |
481 break; | 498 break; |
482 default: | 499 default: |
483 NOTREACHED(); | 500 NOTREACHED(); |
484 break; | 501 break; |
485 } | 502 } |
486 } | 503 } |
487 | 504 |
488 void VideoEncoderVpx::PrepareActiveMap( | 505 void VideoEncoderVpx::SetActiveMapFromRegion( |
489 const webrtc::DesktopRegion& updated_region) { | 506 const webrtc::DesktopRegion& updated_region) { |
490 // Clear active map first. | 507 // Clear active map first. |
491 memset(active_map_.get(), 0, active_map_width_ * active_map_height_); | 508 memset(active_map_.get(), 0, active_map_width_ * active_map_height_); |
492 | 509 |
493 // Mark updated areas active. | 510 // Mark updated areas active. |
494 for (webrtc::DesktopRegion::Iterator r(updated_region); !r.IsAtEnd(); | 511 for (webrtc::DesktopRegion::Iterator r(updated_region); !r.IsAtEnd(); |
495 r.Advance()) { | 512 r.Advance()) { |
496 const webrtc::DesktopRect& rect = r.rect(); | 513 const webrtc::DesktopRect& rect = r.rect(); |
497 int left = rect.left() / kMacroBlockSize; | 514 int left = rect.left() / kMacroBlockSize; |
498 int right = (rect.right() - 1) / kMacroBlockSize; | 515 int right = (rect.right() - 1) / kMacroBlockSize; |
499 int top = rect.top() / kMacroBlockSize; | 516 int top = rect.top() / kMacroBlockSize; |
500 int bottom = (rect.bottom() - 1) / kMacroBlockSize; | 517 int bottom = (rect.bottom() - 1) / kMacroBlockSize; |
501 DCHECK_LT(right, active_map_width_); | 518 DCHECK_LT(right, active_map_width_); |
502 DCHECK_LT(bottom, active_map_height_); | 519 DCHECK_LT(bottom, active_map_height_); |
503 | 520 |
504 uint8* map = active_map_.get() + top * active_map_width_; | 521 uint8* map = active_map_.get() + top * active_map_width_; |
505 for (int y = top; y <= bottom; ++y) { | 522 for (int y = top; y <= bottom; ++y) { |
506 for (int x = left; x <= right; ++x) | 523 for (int x = left; x <= right; ++x) |
507 map[x] = 1; | 524 map[x] = 1; |
508 map += active_map_width_; | 525 map += active_map_width_; |
509 } | 526 } |
510 } | 527 } |
511 } | 528 } |
512 | 529 |
530 void VideoEncoderVpx::UpdateRegionFromActiveMap( | |
531 webrtc::DesktopRegion* updated_region) { | |
532 const uint8* map = active_map_.get(); | |
533 // Mark active areas updated. | |
Wez
2015/05/04 23:57:43
nit: This comment still seems redundant!
| |
534 for (int y = 0; y < active_map_height_; ++y) { | |
535 for (int x0 = 0; x0 < active_map_width_;) { | |
536 int x1 = x0; | |
537 for (; x1 < active_map_width_; ++x1) { | |
538 if (map[y * active_map_width_ + x1] == 0) | |
539 break; | |
540 } | |
541 if (x1 > x0) { | |
542 updated_region->AddRect(webrtc::DesktopRect::MakeLTRB( | |
543 kMacroBlockSize * x0, kMacroBlockSize * y, kMacroBlockSize * x1, | |
544 kMacroBlockSize * (y + 1))); | |
545 } | |
546 x0 = x1 + 1; | |
547 } | |
548 } | |
549 updated_region->IntersectWith( | |
550 webrtc::DesktopRect::MakeWH(image_->w, image_->h)); | |
551 } | |
552 | |
513 } // namespace remoting | 553 } // namespace remoting |
OLD | NEW |