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/logging.h" | 8 #include "base/logging.h" |
9 #include "base/sys_info.h" | 9 #include "base/sys_info.h" |
10 #include "remoting/base/util.h" | 10 #include "remoting/base/util.h" |
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
142 | 142 |
143 // Set cyclic refresh (aka "top-off") only for lossy encoding. | 143 // Set cyclic refresh (aka "top-off") only for lossy encoding. |
144 int aq_mode = lossless_encode ? kVp9AqModeNone : kVp9AqModeCyclicRefresh; | 144 int aq_mode = lossless_encode ? kVp9AqModeNone : kVp9AqModeCyclicRefresh; |
145 ret = vpx_codec_control(codec, VP9E_SET_AQ_MODE, aq_mode); | 145 ret = vpx_codec_control(codec, VP9E_SET_AQ_MODE, aq_mode); |
146 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set aq mode"; | 146 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set aq mode"; |
147 } | 147 } |
148 | 148 |
149 void FreeImageIfMismatched(bool use_i444, | 149 void FreeImageIfMismatched(bool use_i444, |
150 const webrtc::DesktopSize& size, | 150 const webrtc::DesktopSize& size, |
151 scoped_ptr<vpx_image_t>* out_image, | 151 scoped_ptr<vpx_image_t>* out_image, |
152 scoped_ptr<uint8[]>* out_image_buffer) { | 152 scoped_ptr<uint8_t[]>* out_image_buffer) { |
153 if (*out_image) { | 153 if (*out_image) { |
154 const vpx_img_fmt_t desired_fmt = | 154 const vpx_img_fmt_t desired_fmt = |
155 use_i444 ? VPX_IMG_FMT_I444 : VPX_IMG_FMT_I420; | 155 use_i444 ? VPX_IMG_FMT_I444 : VPX_IMG_FMT_I420; |
156 if (!size.equals(webrtc::DesktopSize((*out_image)->w, (*out_image)->h)) || | 156 if (!size.equals(webrtc::DesktopSize((*out_image)->w, (*out_image)->h)) || |
157 (*out_image)->fmt != desired_fmt) { | 157 (*out_image)->fmt != desired_fmt) { |
158 out_image_buffer->reset(); | 158 out_image_buffer->reset(); |
159 out_image->reset(); | 159 out_image->reset(); |
160 } | 160 } |
161 } | 161 } |
162 } | 162 } |
163 | 163 |
164 void CreateImage(bool use_i444, | 164 void CreateImage(bool use_i444, |
165 const webrtc::DesktopSize& size, | 165 const webrtc::DesktopSize& size, |
166 scoped_ptr<vpx_image_t>* out_image, | 166 scoped_ptr<vpx_image_t>* out_image, |
167 scoped_ptr<uint8[]>* out_image_buffer) { | 167 scoped_ptr<uint8_t[]>* out_image_buffer) { |
168 DCHECK(!size.is_empty()); | 168 DCHECK(!size.is_empty()); |
169 DCHECK(!*out_image_buffer); | 169 DCHECK(!*out_image_buffer); |
170 DCHECK(!*out_image); | 170 DCHECK(!*out_image); |
171 | 171 |
172 scoped_ptr<vpx_image_t> image(new vpx_image_t()); | 172 scoped_ptr<vpx_image_t> image(new vpx_image_t()); |
173 memset(image.get(), 0, sizeof(vpx_image_t)); | 173 memset(image.get(), 0, sizeof(vpx_image_t)); |
174 | 174 |
175 // libvpx seems to require both to be assigned. | 175 // libvpx seems to require both to be assigned. |
176 image->d_w = size.width(); | 176 image->d_w = size.width(); |
177 image->w = size.width(); | 177 image->w = size.width(); |
(...skipping 22 matching lines...) Expand all Loading... |
200 // if the image is not padded out to the next macroblock: crbug.com/119633. | 200 // if the image is not padded out to the next macroblock: crbug.com/119633. |
201 // Pad the Y, U and V planes' height out to compensate. | 201 // Pad the Y, U and V planes' height out to compensate. |
202 // Assuming macroblocks are 16x16, aligning the planes' strides above also | 202 // Assuming macroblocks are 16x16, aligning the planes' strides above also |
203 // macroblock aligned them. | 203 // macroblock aligned them. |
204 static_assert(kMacroBlockSize == 16, "macroblock_size_not_16"); | 204 static_assert(kMacroBlockSize == 16, "macroblock_size_not_16"); |
205 const int y_rows = ((image->h - 1) & ~(kMacroBlockSize-1)) + kMacroBlockSize; | 205 const int y_rows = ((image->h - 1) & ~(kMacroBlockSize-1)) + kMacroBlockSize; |
206 const int uv_rows = y_rows >> image->y_chroma_shift; | 206 const int uv_rows = y_rows >> image->y_chroma_shift; |
207 | 207 |
208 // Allocate a YUV buffer large enough for the aligned data & padding. | 208 // Allocate a YUV buffer large enough for the aligned data & padding. |
209 const int buffer_size = y_stride * y_rows + 2*uv_stride * uv_rows; | 209 const int buffer_size = y_stride * y_rows + 2*uv_stride * uv_rows; |
210 scoped_ptr<uint8[]> image_buffer(new uint8[buffer_size]); | 210 scoped_ptr<uint8_t[]> image_buffer(new uint8_t[buffer_size]); |
211 | 211 |
212 // Reset image value to 128 so we just need to fill in the y plane. | 212 // Reset image value to 128 so we just need to fill in the y plane. |
213 memset(image_buffer.get(), 128, buffer_size); | 213 memset(image_buffer.get(), 128, buffer_size); |
214 | 214 |
215 // Fill in the information for |image_|. | 215 // Fill in the information for |image_|. |
216 unsigned char* uchar_buffer = | 216 unsigned char* uchar_buffer = |
217 reinterpret_cast<unsigned char*>(image_buffer.get()); | 217 reinterpret_cast<unsigned char*>(image_buffer.get()); |
218 image->planes[0] = uchar_buffer; | 218 image->planes[0] = uchar_buffer; |
219 image->planes[1] = image->planes[0] + y_stride * y_rows; | 219 image->planes[1] = image->planes[0] + y_stride * y_rows; |
220 image->planes[2] = image->planes[1] + uv_stride * uv_rows; | 220 image->planes[2] = image->planes[1] + uv_stride * uv_rows; |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
353 // Tear down |image_| if it no longer matches the size and color settings. | 353 // Tear down |image_| if it no longer matches the size and color settings. |
354 // PrepareImage() will then create a new buffer of the required dimensions if | 354 // PrepareImage() will then create a new buffer of the required dimensions if |
355 // |image_| is not allocated. | 355 // |image_| is not allocated. |
356 FreeImageIfMismatched(lossless_color_, size, &image_, &image_buffer_); | 356 FreeImageIfMismatched(lossless_color_, size, &image_, &image_buffer_); |
357 | 357 |
358 // Initialize active map. | 358 // Initialize active map. |
359 active_map_size_ = webrtc::DesktopSize( | 359 active_map_size_ = webrtc::DesktopSize( |
360 (size.width() + kMacroBlockSize - 1) / kMacroBlockSize, | 360 (size.width() + kMacroBlockSize - 1) / kMacroBlockSize, |
361 (size.height() + kMacroBlockSize - 1) / kMacroBlockSize); | 361 (size.height() + kMacroBlockSize - 1) / kMacroBlockSize); |
362 active_map_.reset( | 362 active_map_.reset( |
363 new uint8[active_map_size_.width() * active_map_size_.height()]); | 363 new uint8_t[active_map_size_.width() * active_map_size_.height()]); |
364 | 364 |
365 // TODO(wez): Remove this hack once VPX can handle frame size reconfiguration. | 365 // TODO(wez): Remove this hack once VPX can handle frame size reconfiguration. |
366 // See https://code.google.com/p/webm/issues/detail?id=912. | 366 // See https://code.google.com/p/webm/issues/detail?id=912. |
367 if (codec_) { | 367 if (codec_) { |
368 // If the frame size has changed then force re-creation of the codec. | 368 // If the frame size has changed then force re-creation of the codec. |
369 if (codec_->config.enc->g_w != static_cast<unsigned int>(size.width()) || | 369 if (codec_->config.enc->g_w != static_cast<unsigned int>(size.width()) || |
370 codec_->config.enc->g_h != static_cast<unsigned int>(size.height())) { | 370 codec_->config.enc->g_h != static_cast<unsigned int>(size.height())) { |
371 codec_.reset(); | 371 codec_.reset(); |
372 } | 372 } |
373 } | 373 } |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
440 // aligned. The conversion routines don't require even width & height, | 440 // aligned. The conversion routines don't require even width & height, |
441 // so this is safe even if the source dimensions are not even. | 441 // so this is safe even if the source dimensions are not even. |
442 updated_region->IntersectWith( | 442 updated_region->IntersectWith( |
443 webrtc::DesktopRect::MakeWH(image_->w, image_->h)); | 443 webrtc::DesktopRect::MakeWH(image_->w, image_->h)); |
444 } else { | 444 } else { |
445 CreateImage(lossless_color_, frame.size(), &image_, &image_buffer_); | 445 CreateImage(lossless_color_, frame.size(), &image_, &image_buffer_); |
446 updated_region->AddRect(webrtc::DesktopRect::MakeWH(image_->w, image_->h)); | 446 updated_region->AddRect(webrtc::DesktopRect::MakeWH(image_->w, image_->h)); |
447 } | 447 } |
448 | 448 |
449 // Convert the updated region to YUV ready for encoding. | 449 // Convert the updated region to YUV ready for encoding. |
450 const uint8* rgb_data = frame.data(); | 450 const uint8_t* rgb_data = frame.data(); |
451 const int rgb_stride = frame.stride(); | 451 const int rgb_stride = frame.stride(); |
452 const int y_stride = image_->stride[0]; | 452 const int y_stride = image_->stride[0]; |
453 DCHECK_EQ(image_->stride[1], image_->stride[2]); | 453 DCHECK_EQ(image_->stride[1], image_->stride[2]); |
454 const int uv_stride = image_->stride[1]; | 454 const int uv_stride = image_->stride[1]; |
455 uint8* y_data = image_->planes[0]; | 455 uint8_t* y_data = image_->planes[0]; |
456 uint8* u_data = image_->planes[1]; | 456 uint8_t* u_data = image_->planes[1]; |
457 uint8* v_data = image_->planes[2]; | 457 uint8_t* v_data = image_->planes[2]; |
458 | 458 |
459 switch (image_->fmt) { | 459 switch (image_->fmt) { |
460 case VPX_IMG_FMT_I444: | 460 case VPX_IMG_FMT_I444: |
461 for (webrtc::DesktopRegion::Iterator r(*updated_region); !r.IsAtEnd(); | 461 for (webrtc::DesktopRegion::Iterator r(*updated_region); !r.IsAtEnd(); |
462 r.Advance()) { | 462 r.Advance()) { |
463 const webrtc::DesktopRect& rect = r.rect(); | 463 const webrtc::DesktopRect& rect = r.rect(); |
464 int rgb_offset = rgb_stride * rect.top() + | 464 int rgb_offset = rgb_stride * rect.top() + |
465 rect.left() * kBytesPerRgbPixel; | 465 rect.left() * kBytesPerRgbPixel; |
466 int yuv_offset = uv_stride * rect.top() + rect.left(); | 466 int yuv_offset = uv_stride * rect.top() + rect.left(); |
467 libyuv::ARGBToI444(rgb_data + rgb_offset, rgb_stride, | 467 libyuv::ARGBToI444(rgb_data + rgb_offset, rgb_stride, |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
502 for (webrtc::DesktopRegion::Iterator r(updated_region); !r.IsAtEnd(); | 502 for (webrtc::DesktopRegion::Iterator r(updated_region); !r.IsAtEnd(); |
503 r.Advance()) { | 503 r.Advance()) { |
504 const webrtc::DesktopRect& rect = r.rect(); | 504 const webrtc::DesktopRect& rect = r.rect(); |
505 int left = rect.left() / kMacroBlockSize; | 505 int left = rect.left() / kMacroBlockSize; |
506 int right = (rect.right() - 1) / kMacroBlockSize; | 506 int right = (rect.right() - 1) / kMacroBlockSize; |
507 int top = rect.top() / kMacroBlockSize; | 507 int top = rect.top() / kMacroBlockSize; |
508 int bottom = (rect.bottom() - 1) / kMacroBlockSize; | 508 int bottom = (rect.bottom() - 1) / kMacroBlockSize; |
509 DCHECK_LT(right, active_map_size_.width()); | 509 DCHECK_LT(right, active_map_size_.width()); |
510 DCHECK_LT(bottom, active_map_size_.height()); | 510 DCHECK_LT(bottom, active_map_size_.height()); |
511 | 511 |
512 uint8* map = active_map_.get() + top * active_map_size_.width(); | 512 uint8_t* map = active_map_.get() + top * active_map_size_.width(); |
513 for (int y = top; y <= bottom; ++y) { | 513 for (int y = top; y <= bottom; ++y) { |
514 for (int x = left; x <= right; ++x) | 514 for (int x = left; x <= right; ++x) |
515 map[x] = 1; | 515 map[x] = 1; |
516 map += active_map_size_.width(); | 516 map += active_map_size_.width(); |
517 } | 517 } |
518 } | 518 } |
519 } | 519 } |
520 | 520 |
521 void VideoEncoderVpx::UpdateRegionFromActiveMap( | 521 void VideoEncoderVpx::UpdateRegionFromActiveMap( |
522 webrtc::DesktopRegion* updated_region) { | 522 webrtc::DesktopRegion* updated_region) { |
523 const uint8* map = active_map_.get(); | 523 const uint8_t* map = active_map_.get(); |
524 for (int y = 0; y < active_map_size_.height(); ++y) { | 524 for (int y = 0; y < active_map_size_.height(); ++y) { |
525 for (int x0 = 0; x0 < active_map_size_.width();) { | 525 for (int x0 = 0; x0 < active_map_size_.width();) { |
526 int x1 = x0; | 526 int x1 = x0; |
527 for (; x1 < active_map_size_.width(); ++x1) { | 527 for (; x1 < active_map_size_.width(); ++x1) { |
528 if (map[y * active_map_size_.width() + x1] == 0) | 528 if (map[y * active_map_size_.width() + x1] == 0) |
529 break; | 529 break; |
530 } | 530 } |
531 if (x1 > x0) { | 531 if (x1 > x0) { |
532 updated_region->AddRect(webrtc::DesktopRect::MakeLTRB( | 532 updated_region->AddRect(webrtc::DesktopRect::MakeLTRB( |
533 kMacroBlockSize * x0, kMacroBlockSize * y, kMacroBlockSize * x1, | 533 kMacroBlockSize * x0, kMacroBlockSize * y, kMacroBlockSize * x1, |
534 kMacroBlockSize * (y + 1))); | 534 kMacroBlockSize * (y + 1))); |
535 } | 535 } |
536 x0 = x1 + 1; | 536 x0 = x1 + 1; |
537 } | 537 } |
538 } | 538 } |
539 updated_region->IntersectWith( | 539 updated_region->IntersectWith( |
540 webrtc::DesktopRect::MakeWH(image_->w, image_->h)); | 540 webrtc::DesktopRect::MakeWH(image_->w, image_->h)); |
541 } | 541 } |
542 | 542 |
543 } // namespace remoting | 543 } // namespace remoting |
OLD | NEW |