| Index: remoting/codec/video_encoder_vpx.cc
|
| diff --git a/remoting/codec/video_encoder_vpx.cc b/remoting/codec/video_encoder_vpx.cc
|
| index 59bb5dc0b21d6f7ec55c3c48f200e279556c3965..3b27172f35dafe9c9973c2080f0ed72413739851 100644
|
| --- a/remoting/codec/video_encoder_vpx.cc
|
| +++ b/remoting/codec/video_encoder_vpx.cc
|
| @@ -7,9 +7,9 @@
|
| #include "base/bind.h"
|
| #include "base/logging.h"
|
| #include "base/sys_info.h"
|
| -#include "media/base/yuv_convert.h"
|
| #include "remoting/base/util.h"
|
| #include "remoting/proto/video.pb.h"
|
| +#include "third_party/libyuv/include/libyuv/convert_from_argb.h"
|
| #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
|
| #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
|
| #include "third_party/webrtc/modules/desktop_capture/desktop_region.h"
|
| @@ -24,10 +24,17 @@ namespace remoting {
|
|
|
| namespace {
|
|
|
| +// Number of bytes in an RGBx pixel.
|
| +const int kBytesPerRgbPixel = 4;
|
| +
|
| // Defines the dimension of a macro block. This is used to compute the active
|
| // map for the encoder.
|
| const int kMacroBlockSize = 16;
|
|
|
| +// Magic encoder profile numbers for I420 and I444 input formats.
|
| +const int kVp9I420ProfileNumber = 0;
|
| +const int kVp9I444ProfileNumber = 1;
|
| +
|
| void SetCommonCodecParameters(const webrtc::DesktopSize& size,
|
| vpx_codec_enc_cfg_t* config) {
|
| // Use millisecond granularity time base.
|
| @@ -90,7 +97,7 @@ ScopedVpxCodec CreateVP8Codec(const webrtc::DesktopSize& size) {
|
| return codec.Pass();
|
| }
|
|
|
| -ScopedVpxCodec CreateVP9Codec(const webrtc::DesktopSize& size) {
|
| +ScopedVpxCodec CreateVP9Codec(bool use_i444, const webrtc::DesktopSize& size) {
|
| ScopedVpxCodec codec(new vpx_codec_ctx_t);
|
|
|
| // Configure the encoder.
|
| @@ -103,8 +110,8 @@ ScopedVpxCodec CreateVP9Codec(const webrtc::DesktopSize& size) {
|
|
|
| SetCommonCodecParameters(size, &config);
|
|
|
| - // Configure VP9 for I420 source frames.
|
| - config.g_profile = 0;
|
| + // Configure VP9 for I420 or I444 source frames.
|
| + config.g_profile = use_i444 ? kVp9I444ProfileNumber : kVp9I420ProfileNumber;
|
|
|
| // Disable quantization entirely, putting the encoder in "lossless" mode.
|
| config.rc_min_quantizer = 0;
|
| @@ -128,18 +135,90 @@ ScopedVpxCodec CreateVP9Codec(const webrtc::DesktopSize& size) {
|
| return codec.Pass();
|
| }
|
|
|
| -} // namespace
|
| +void CreateImage(bool use_i444,
|
| + const webrtc::DesktopSize& size,
|
| + scoped_ptr<vpx_image_t>* out_image,
|
| + scoped_ptr<uint8[]>* out_image_buffer) {
|
| + DCHECK(!size.is_empty());
|
| +
|
| + scoped_ptr<vpx_image_t> image(new vpx_image_t());
|
| + memset(image.get(), 0, sizeof(vpx_image_t));
|
| +
|
| + // libvpx seems to require both to be assigned.
|
| + image->d_w = size.width();
|
| + image->w = size.width();
|
| + image->d_h = size.height();
|
| + image->h = size.height();
|
| +
|
| + // libvpx should derive chroma shifts from|fmt| but currently has a bug:
|
| + // https://code.google.com/p/webm/issues/detail?id=627
|
| + if (use_i444) {
|
| + image->fmt = VPX_IMG_FMT_I444;
|
| + image->x_chroma_shift = 0;
|
| + image->y_chroma_shift = 0;
|
| + } else { // I420
|
| + image->fmt = VPX_IMG_FMT_YV12;
|
| + image->x_chroma_shift = 1;
|
| + image->y_chroma_shift = 1;
|
| + }
|
| +
|
| + // libyuv's fast-path requires 16-byte aligned pointers and strides, so pad
|
| + // the Y, U and V planes' strides to multiples of 16 bytes.
|
| + const int y_stride = ((image->w - 1) & ~15) + 16;
|
| + const int uv_unaligned_stride = y_stride >> image->x_chroma_shift;
|
| + const int uv_stride = ((uv_unaligned_stride - 1) & ~15) + 16;
|
| +
|
| + // libvpx accesses the source image in macro blocks, and will over-read
|
| + // if the image is not padded out to the next macroblock: crbug.com/119633.
|
| + // Pad the Y, U and V planes' height out to compensate.
|
| + // Assuming macroblocks are 16x16, aligning the planes' strides above also
|
| + // macroblock aligned them.
|
| + DCHECK_EQ(16, kMacroBlockSize);
|
| + const int y_rows = ((image->h - 1) & ~(kMacroBlockSize-1)) + kMacroBlockSize;
|
| + const int uv_rows = y_rows >> image->y_chroma_shift;
|
| +
|
| + // Allocate a YUV buffer large enough for the aligned data & padding.
|
| + const int buffer_size = y_stride * y_rows + 2*uv_stride * uv_rows;
|
| + scoped_ptr<uint8[]> image_buffer(new uint8[buffer_size]);
|
| +
|
| + // Reset image value to 128 so we just need to fill in the y plane.
|
| + memset(image_buffer.get(), 128, buffer_size);
|
| +
|
| + // Fill in the information for |image_|.
|
| + unsigned char* uchar_buffer =
|
| + reinterpret_cast<unsigned char*>(image_buffer.get());
|
| + image->planes[0] = uchar_buffer;
|
| + image->planes[1] = image->planes[0] + y_stride * y_rows;
|
| + image->planes[2] = image->planes[1] + uv_stride * uv_rows;
|
| + image->stride[0] = y_stride;
|
| + image->stride[1] = uv_stride;
|
| + image->stride[2] = uv_stride;
|
| +
|
| + *out_image = image.Pass();
|
| + *out_image_buffer = image_buffer.Pass();
|
| +}
|
| +
|
| +} // namespace
|
|
|
| // static
|
| scoped_ptr<VideoEncoderVpx> VideoEncoderVpx::CreateForVP8() {
|
| return scoped_ptr<VideoEncoderVpx>(
|
| - new VideoEncoderVpx(base::Bind(&CreateVP8Codec)));
|
| + new VideoEncoderVpx(base::Bind(&CreateVP8Codec),
|
| + base::Bind(&CreateImage, false)));
|
| +}
|
| +
|
| +// static
|
| +scoped_ptr<VideoEncoderVpx> VideoEncoderVpx::CreateForVP9I420() {
|
| + return scoped_ptr<VideoEncoderVpx>(
|
| + new VideoEncoderVpx(base::Bind(&CreateVP9Codec, false),
|
| + base::Bind(&CreateImage, false)));
|
| }
|
|
|
| // static
|
| -scoped_ptr<VideoEncoderVpx> VideoEncoderVpx::CreateForVP9() {
|
| +scoped_ptr<VideoEncoderVpx> VideoEncoderVpx::CreateForVP9I444() {
|
| return scoped_ptr<VideoEncoderVpx>(
|
| - new VideoEncoderVpx(base::Bind(&CreateVP9Codec)));
|
| + new VideoEncoderVpx(base::Bind(&CreateVP9Codec, true),
|
| + base::Bind(&CreateImage, true)));
|
| }
|
|
|
| VideoEncoderVpx::~VideoEncoderVpx() {}
|
| @@ -233,8 +312,10 @@ scoped_ptr<VideoPacket> VideoEncoderVpx::Encode(
|
| return packet.Pass();
|
| }
|
|
|
| -VideoEncoderVpx::VideoEncoderVpx(const InitializeCodecCallback& init_codec)
|
| - : init_codec_(init_codec),
|
| +VideoEncoderVpx::VideoEncoderVpx(const CreateCodecCallback& create_codec,
|
| + const CreateImageCallback& create_image)
|
| + : create_codec_(create_codec),
|
| + create_image_(create_image),
|
| active_map_width_(0),
|
| active_map_height_(0) {
|
| }
|
| @@ -242,60 +323,16 @@ VideoEncoderVpx::VideoEncoderVpx(const InitializeCodecCallback& init_codec)
|
| bool VideoEncoderVpx::Initialize(const webrtc::DesktopSize& size) {
|
| codec_.reset();
|
|
|
| - image_.reset(new vpx_image_t());
|
| - memset(image_.get(), 0, sizeof(vpx_image_t));
|
| -
|
| - image_->fmt = VPX_IMG_FMT_YV12;
|
| -
|
| - // libvpx seems to require both to be assigned.
|
| - image_->d_w = size.width();
|
| - image_->w = size.width();
|
| - image_->d_h = size.height();
|
| - image_->h = size.height();
|
| -
|
| - // libvpx should derive this from|fmt| but currently has a bug:
|
| - // https://code.google.com/p/webm/issues/detail?id=627
|
| - image_->x_chroma_shift = 1;
|
| - image_->y_chroma_shift = 1;
|
| + // (Re)Create the VPX image structure and pixel buffer.
|
| + create_image_.Run(size, &image_, &image_buffer_);
|
|
|
| // Initialize active map.
|
| active_map_width_ = (image_->w + kMacroBlockSize - 1) / kMacroBlockSize;
|
| active_map_height_ = (image_->h + kMacroBlockSize - 1) / kMacroBlockSize;
|
| active_map_.reset(new uint8[active_map_width_ * active_map_height_]);
|
|
|
| - // libyuv's fast-path requires 16-byte aligned pointers and strides, so pad
|
| - // the Y, U and V planes' strides to multiples of 16 bytes.
|
| - const int y_stride = ((image_->w - 1) & ~15) + 16;
|
| - const int uv_unaligned_stride = y_stride / 2;
|
| - const int uv_stride = ((uv_unaligned_stride - 1) & ~15) + 16;
|
| -
|
| - // libvpx accesses the source image in macro blocks, and will over-read
|
| - // if the image is not padded out to the next macroblock: crbug.com/119633.
|
| - // Pad the Y, U and V planes' height out to compensate.
|
| - // Assuming macroblocks are 16x16, aligning the planes' strides above also
|
| - // macroblock aligned them.
|
| - DCHECK_EQ(16, kMacroBlockSize);
|
| - const int y_rows = active_map_height_ * kMacroBlockSize;
|
| - const int uv_rows = y_rows / 2;
|
| -
|
| - // Allocate a YUV buffer large enough for the aligned data & padding.
|
| - const int buffer_size = y_stride * y_rows + 2 * uv_stride * uv_rows;
|
| - yuv_image_.reset(new uint8[buffer_size]);
|
| -
|
| - // Reset image value to 128 so we just need to fill in the y plane.
|
| - memset(yuv_image_.get(), 128, buffer_size);
|
| -
|
| - // Fill in the information for |image_|.
|
| - unsigned char* image = reinterpret_cast<unsigned char*>(yuv_image_.get());
|
| - image_->planes[0] = image;
|
| - image_->planes[1] = image_->planes[0] + y_stride * y_rows;
|
| - image_->planes[2] = image_->planes[1] + uv_stride * uv_rows;
|
| - image_->stride[0] = y_stride;
|
| - image_->stride[1] = uv_stride;
|
| - image_->stride[2] = uv_stride;
|
| -
|
| - // Initialize the codec.
|
| - codec_ = init_codec_.Run(size);
|
| + // (Re)Initialize the codec.
|
| + codec_ = create_codec_.Run(size);
|
|
|
| return codec_;
|
| }
|
| @@ -336,13 +373,40 @@ void VideoEncoderVpx::PrepareImage(const webrtc::DesktopFrame& frame,
|
| uint8* y_data = image_->planes[0];
|
| uint8* u_data = image_->planes[1];
|
| uint8* v_data = image_->planes[2];
|
| - for (webrtc::DesktopRegion::Iterator r(*updated_region); !r.IsAtEnd();
|
| - r.Advance()) {
|
| - const webrtc::DesktopRect& rect = r.rect();
|
| - ConvertRGB32ToYUVWithRect(
|
| - rgb_data, y_data, u_data, v_data,
|
| - rect.left(), rect.top(), rect.width(), rect.height(),
|
| - rgb_stride, y_stride, uv_stride);
|
| +
|
| + switch (image_->fmt) {
|
| + case VPX_IMG_FMT_I444:
|
| + for (webrtc::DesktopRegion::Iterator r(*updated_region); !r.IsAtEnd();
|
| + r.Advance()) {
|
| + const webrtc::DesktopRect& rect = r.rect();
|
| + int rgb_offset = rgb_stride * rect.top() +
|
| + rect.left() * kBytesPerRgbPixel;
|
| + int yuv_offset = uv_stride * rect.top() + rect.left();
|
| + libyuv::ARGBToI444(rgb_data + rgb_offset, rgb_stride,
|
| + y_data + yuv_offset, y_stride,
|
| + u_data + yuv_offset, uv_stride,
|
| + v_data + yuv_offset, uv_stride,
|
| + rect.width(), rect.height());
|
| + }
|
| + break;
|
| + case VPX_IMG_FMT_YV12:
|
| + for (webrtc::DesktopRegion::Iterator r(*updated_region); !r.IsAtEnd();
|
| + r.Advance()) {
|
| + const webrtc::DesktopRect& rect = r.rect();
|
| + int rgb_offset = rgb_stride * rect.top() +
|
| + rect.left() * kBytesPerRgbPixel;
|
| + int y_offset = y_stride * rect.top() + rect.left();
|
| + int uv_offset = uv_stride * rect.top() / 2 + rect.left() / 2;
|
| + libyuv::ARGBToI420(rgb_data + rgb_offset, rgb_stride,
|
| + y_data + y_offset, y_stride,
|
| + u_data + uv_offset, uv_stride,
|
| + v_data + uv_offset, uv_stride,
|
| + rect.width(), rect.height());
|
| + }
|
| + break;
|
| + default:
|
| + NOTREACHED();
|
| + break;
|
| }
|
| }
|
|
|
|
|