| 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 uint8* map = active_map_.get(); |
| 533 // Mark active areas updated. |
| 534 for (int y = 0; y < active_map_height_; ++y) { |
| 535 int x1; |
| 536 for (int x0 = 0; x0 < active_map_width_; x0 = x1 + 1) { |
| 537 for (x1 = x0; 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 } |
| 547 } |
| 548 updated_region->IntersectWith( |
| 549 webrtc::DesktopRect::MakeWH(image_->w, image_->h)); |
| 550 } |
| 551 |
| 513 } // namespace remoting | 552 } // namespace remoting |
| OLD | NEW |