Chromium Code Reviews| 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 kVp9AqNone = 0; | |
| 44 const int kVp9AqCyclicRefresh = 3; | |
|
Wez
2015/04/18 00:17:54
nit: Suggest these start kVp9AqMode, for consisten
aconverse
2015/04/20 18:32:38
Done.
| |
| 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 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 127 | 131 |
| 128 // Use the lowest level of noise sensitivity so as to spend less time | 132 // Use the lowest level of noise sensitivity so as to spend less time |
| 129 // on motion estimation and inter-prediction mode. | 133 // on motion estimation and inter-prediction mode. |
| 130 ret = vpx_codec_control(codec, VP9E_SET_NOISE_SENSITIVITY, 0); | 134 ret = vpx_codec_control(codec, VP9E_SET_NOISE_SENSITIVITY, 0); |
| 131 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set noise sensitivity"; | 135 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set noise sensitivity"; |
| 132 | 136 |
| 133 // Configure the codec to tune it for screen media. | 137 // Configure the codec to tune it for screen media. |
| 134 ret = vpx_codec_control( | 138 ret = vpx_codec_control( |
| 135 codec, VP9E_SET_TUNE_CONTENT, VP9E_CONTENT_SCREEN); | 139 codec, VP9E_SET_TUNE_CONTENT, VP9E_CONTENT_SCREEN); |
| 136 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set screen content mode"; | 140 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set screen content mode"; |
| 141 | |
| 142 // Set cyclic refresh for lossy encode | |
|
Wez
2015/04/18 00:17:54
nit: missing .
Suggest "Enable cyclic refresh (ak
aconverse
2015/04/20 18:32:38
Done.
| |
| 143 int aq_mode = lossless_encode ? kVp9AqNone : kVp9AqCyclicRefresh; | |
| 144 ret = vpx_codec_control(codec, VP9E_SET_AQ_MODE, aq_mode); | |
| 145 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set aq mode"; | |
| 137 } | 146 } |
| 138 | 147 |
| 139 void FreeImageIfMismatched(bool use_i444, | 148 void FreeImageIfMismatched(bool use_i444, |
| 140 const webrtc::DesktopSize& size, | 149 const webrtc::DesktopSize& size, |
| 141 scoped_ptr<vpx_image_t>* out_image, | 150 scoped_ptr<vpx_image_t>* out_image, |
| 142 scoped_ptr<uint8[]>* out_image_buffer) { | 151 scoped_ptr<uint8[]>* out_image_buffer) { |
| 143 if (*out_image) { | 152 if (*out_image) { |
| 144 const vpx_img_fmt_t desired_fmt = | 153 const vpx_img_fmt_t desired_fmt = |
| 145 use_i444 ? VPX_IMG_FMT_I444 : VPX_IMG_FMT_I420; | 154 use_i444 ? VPX_IMG_FMT_I444 : VPX_IMG_FMT_I420; |
| 146 if (!size.equals(webrtc::DesktopSize((*out_image)->w, (*out_image)->h)) || | 155 if (!size.equals(webrtc::DesktopSize((*out_image)->w, (*out_image)->h)) || |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 283 | 292 |
| 284 // Do the actual encoding. | 293 // Do the actual encoding. |
| 285 int timestamp = (encode_start_time - timestamp_base_).InMilliseconds(); | 294 int timestamp = (encode_start_time - timestamp_base_).InMilliseconds(); |
| 286 vpx_codec_err_t ret = vpx_codec_encode( | 295 vpx_codec_err_t ret = vpx_codec_encode( |
| 287 codec_.get(), image_.get(), timestamp, 1, 0, VPX_DL_REALTIME); | 296 codec_.get(), image_.get(), timestamp, 1, 0, VPX_DL_REALTIME); |
| 288 DCHECK_EQ(ret, VPX_CODEC_OK) | 297 DCHECK_EQ(ret, VPX_CODEC_OK) |
| 289 << "Encoding error: " << vpx_codec_err_to_string(ret) << "\n" | 298 << "Encoding error: " << vpx_codec_err_to_string(ret) << "\n" |
| 290 << "Details: " << vpx_codec_error(codec_.get()) << "\n" | 299 << "Details: " << vpx_codec_error(codec_.get()) << "\n" |
| 291 << vpx_codec_error_detail(codec_.get()); | 300 << vpx_codec_error_detail(codec_.get()); |
| 292 | 301 |
| 302 if (use_vp9_) { | |
| 303 if (vpx_codec_control(codec_.get(), VP9E_GET_ACTIVEMAP, &act_map) == | |
| 304 VPX_CODEC_OK) { | |
|
Wez
2015/04/18 00:17:54
Is there any point calling this if we are already
aconverse
2015/04/20 18:32:39
Done.
| |
| 305 UpdateActiveMap(&updated_region); | |
| 306 } else { | |
| 307 LOG(ERROR) << "Failed to fetch active map"; | |
| 308 updated_region.Clear(); | |
| 309 updated_region.AddRect(webrtc::DesktopRect::MakeWH(image_->w, image_->h)); | |
|
Wez
2015/04/18 00:17:54
Under what circumstances can the get call fail? Sh
aconverse
2015/04/20 18:32:39
I was following the example of SET_ACTIVEMAP. Neit
Wez
2015/04/21 01:29:18
Ah, OK; we should probably have SET_ACTIVEMAP erro
| |
| 310 } | |
| 311 } | |
| 312 | |
| 293 // Read the encoded data. | 313 // Read the encoded data. |
| 294 vpx_codec_iter_t iter = NULL; | 314 vpx_codec_iter_t iter = NULL; |
| 295 bool got_data = false; | 315 bool got_data = false; |
| 296 | 316 |
| 297 // 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. |
| 298 // 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. |
| 299 scoped_ptr<VideoPacket> packet( | 319 scoped_ptr<VideoPacket> packet( |
| 300 helper_.CreateVideoPacketWithUpdatedRegion(frame, updated_region)); | 320 helper_.CreateVideoPacketWithUpdatedRegion(frame, updated_region)); |
| 301 packet->mutable_format()->set_encoding(VideoPacketFormat::ENCODING_VP8); | 321 packet->mutable_format()->set_encoding(VideoPacketFormat::ENCODING_VP8); |
| 302 | 322 |
| (...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 500 | 520 |
| 501 uint8* map = active_map_.get() + top * active_map_width_; | 521 uint8* map = active_map_.get() + top * active_map_width_; |
| 502 for (int y = top; y <= bottom; ++y) { | 522 for (int y = top; y <= bottom; ++y) { |
| 503 for (int x = left; x <= right; ++x) | 523 for (int x = left; x <= right; ++x) |
| 504 map[x] = 1; | 524 map[x] = 1; |
| 505 map += active_map_width_; | 525 map += active_map_width_; |
| 506 } | 526 } |
| 507 } | 527 } |
| 508 } | 528 } |
| 509 | 529 |
| 530 void VideoEncoderVpx::UpdateActiveMap(webrtc::DesktopRegion *updated_region) { | |
| 531 uint8* map = active_map_.get(); | |
| 532 // Mark active areas updated. | |
|
Wez
2015/04/18 00:17:54
nit: This comment doesn't fit this function; we're
aconverse
2015/04/20 18:32:38
We've already fetched the areas. We are marking th
Wez
2015/04/21 01:29:18
OK; suggest "Add areas marked active to |updated_r
| |
| 533 for (int r = 0; r < active_map_height_; ++r) { | |
|
Wez
2015/04/18 00:17:54
Suggest r -> y
aconverse
2015/04/20 18:32:39
Done.
| |
| 534 int c1; | |
|
Wez
2015/04/18 00:17:54
Move this inside the c0 loop?
aconverse
2015/04/20 18:32:38
This is used in the iteration condition so that we
Wez
2015/04/21 01:29:18
Gotcha; I think it might be more readable to decla
aconverse
2015/04/28 17:03:56
Done. lmk if I misunderstood
| |
| 535 for (int c0 = 0; c0 < active_map_height_; c0 = c1 + 1) { | |
|
Wez
2015/04/18 00:17:54
Suggest c0 -> x0
Wez
2015/04/18 00:17:54
Looks like this should be |active_map_width_|?
aconverse
2015/04/20 18:32:39
Done.
aconverse
2015/04/20 18:32:39
Done.
| |
| 536 for (c1 = c0; c1 < active_map_height_; ++c1) { | |
|
Wez
2015/04/18 00:17:54
Ditto.
aconverse
2015/04/20 18:32:38
Done.
| |
| 537 if (map[c1] == 0) | |
|
aconverse
2015/04/20 18:32:39
Big oops on this line as well. [not taking row int
Wez
2015/04/21 01:29:18
Yes and no; no, we can't remove this, because we d
aconverse
2015/04/28 17:03:56
I should give this a try before we move forward.
| |
| 538 break; | |
| 539 } | |
| 540 if (c1 > c0) { | |
| 541 updated_region->AddRect(webrtc::DesktopRect::MakeLTRB( | |
| 542 kMacroBlockSize * c0, | |
| 543 kMacroBlockSize * r, | |
| 544 kMacroBlockSize * c1, | |
| 545 kMacroBlockSize * (r + 1))); | |
| 546 } | |
| 547 } | |
| 548 } | |
| 549 updated_region->IntersectWith( | |
| 550 webrtc::DesktopRect::MakeWH(image_->w, image_->h)); | |
| 551 } | |
| 552 | |
| 510 } // namespace remoting | 553 } // namespace remoting |
| OLD | NEW |