diff options
author | wez@chromium.org <wez@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-19 22:14:54 +0000 |
---|---|---|
committer | wez@chromium.org <wez@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-19 22:14:54 +0000 |
commit | a43cfae9c2ead7b9b14882101f2264a785f17f41 (patch) | |
tree | 0c715d27bef0304b2a7122ca622238673a34cb1e /remoting/codec | |
parent | ffd8afd8391366b1a0bdcc849d973dc1e5fee6d9 (diff) | |
download | chromium_src-a43cfae9c2ead7b9b14882101f2264a785f17f41.zip chromium_src-a43cfae9c2ead7b9b14882101f2264a785f17f41.tar.gz chromium_src-a43cfae9c2ead7b9b14882101f2264a785f17f41.tar.bz2 |
Move VideoDecoderVp8 to VideoDecoderVpx base that will support VP8/9.
BUG=260879
Review URL: https://codereview.chromium.org/26888009
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@229624 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/codec')
-rw-r--r-- | remoting/codec/scoped_vpx_codec.cc | 23 | ||||
-rw-r--r-- | remoting/codec/scoped_vpx_codec.h | 24 | ||||
-rw-r--r-- | remoting/codec/video_decoder_vpx.cc (renamed from remoting/codec/video_decoder_vp8.cc) | 114 | ||||
-rw-r--r-- | remoting/codec/video_decoder_vpx.h (renamed from remoting/codec/video_decoder_vp8.h) | 37 | ||||
-rw-r--r-- | remoting/codec/video_decoder_vpx_unittest.cc (renamed from remoting/codec/video_decoder_vp8_unittest.cc) | 40 | ||||
-rw-r--r-- | remoting/codec/video_encoder_vpx.cc (renamed from remoting/codec/video_encoder_vp8.cc) | 312 | ||||
-rw-r--r-- | remoting/codec/video_encoder_vpx.h (renamed from remoting/codec/video_encoder_vp8.h) | 41 | ||||
-rw-r--r-- | remoting/codec/video_encoder_vpx_unittest.cc (renamed from remoting/codec/video_encoder_vp8_unittest.cc) | 36 |
8 files changed, 335 insertions, 292 deletions
diff --git a/remoting/codec/scoped_vpx_codec.cc b/remoting/codec/scoped_vpx_codec.cc new file mode 100644 index 0000000..dda986e --- /dev/null +++ b/remoting/codec/scoped_vpx_codec.cc @@ -0,0 +1,23 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "remoting/codec/scoped_vpx_codec.h" + +#include "base/logging.h" + +extern "C" { +#define VPX_CODEC_DISABLE_COMPAT 1 +#include "third_party/libvpx/source/libvpx/vpx/vpx_codec.h" +} +namespace remoting { + +void VpxCodecDeleter::operator()(vpx_codec_ctx_t* codec) { + if (codec) { + vpx_codec_err_t ret = vpx_codec_destroy(codec); + CHECK_EQ(ret, VPX_CODEC_OK) << "Failed to destroy codec"; + delete codec; + } +} + +} // namespace remoting diff --git a/remoting/codec/scoped_vpx_codec.h b/remoting/codec/scoped_vpx_codec.h new file mode 100644 index 0000000..bf03c64 --- /dev/null +++ b/remoting/codec/scoped_vpx_codec.h @@ -0,0 +1,24 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef REMOTING_CODEC_SCOPED_VPX_CODEC_H_ +#define REMOTING_CODEC_SCOPED_VPX_CODEC_H_ + +#include "base/memory/scoped_ptr.h" + +extern "C" { +typedef struct vpx_codec_ctx vpx_codec_ctx_t; +} + +namespace remoting { + +struct VpxCodecDeleter { + void operator()(vpx_codec_ctx_t* codec); +}; + +typedef scoped_ptr<vpx_codec_ctx_t, VpxCodecDeleter> ScopedVpxCodec; + +} // namespace remoting + +#endif // REMOTING_CODEC_SCOPED_VPX_CODEC_H_ diff --git a/remoting/codec/video_decoder_vp8.cc b/remoting/codec/video_decoder_vpx.cc index 0d43f15..ba65e88 100644 --- a/remoting/codec/video_decoder_vp8.cc +++ b/remoting/codec/video_decoder_vpx.cc @@ -1,8 +1,8 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "remoting/codec/video_decoder_vp8.h" +#include "remoting/codec/video_decoder_vpx.h" #include <math.h> @@ -21,69 +21,73 @@ extern "C" { namespace remoting { -const uint32 kTransparent = 0; +namespace { -VideoDecoderVp8::VideoDecoderVp8() - : state_(kUninitialized), - codec_(NULL), - last_image_(NULL) { +const uint32 kTransparentColor = 0; + +// Fills the rectangle |rect| with the given ARGB color |color| in |buffer|. +void FillRect(uint8* buffer, + int stride, + const webrtc::DesktopRect& rect, + uint32 color) { + uint32* ptr = reinterpret_cast<uint32*>(buffer + (rect.top() * stride) + + (rect.left() * VideoDecoder::kBytesPerPixel)); + int width = rect.width(); + for (int height = rect.height(); height > 0; --height) { + std::fill(ptr, ptr + width, color); + ptr += stride / VideoDecoder::kBytesPerPixel; + } } -VideoDecoderVp8::~VideoDecoderVp8() { - if (codec_) { - vpx_codec_err_t ret = vpx_codec_destroy(codec_); - CHECK(ret == VPX_CODEC_OK) << "Failed to destroy codec"; +} // namespace + +// static +scoped_ptr<VideoDecoderVpx> VideoDecoderVpx::CreateForVP8() { + ScopedVpxCodec codec(new vpx_codec_ctx_t); + + // TODO(hclam): Scale the number of threads with number of cores of the + // machine. + vpx_codec_dec_cfg config; + config.w = 0; + config.h = 0; + config.threads = 2; + vpx_codec_err_t ret = + vpx_codec_dec_init(codec.get(), vpx_codec_vp8_dx(), &config, 0); + if (ret != VPX_CODEC_OK) { + LOG(INFO) << "Cannot initialize codec."; + return scoped_ptr<VideoDecoderVpx>(); } - delete codec_; + + return scoped_ptr<VideoDecoderVpx>(new VideoDecoderVpx(codec.Pass())); } -void VideoDecoderVp8::Initialize(const webrtc::DesktopSize& screen_size) { +VideoDecoderVpx::~VideoDecoderVpx() {} + +void VideoDecoderVpx::Initialize(const webrtc::DesktopSize& screen_size) { DCHECK(!screen_size.is_empty()); screen_size_ = screen_size; - state_ = kReady; transparent_region_.SetRect(webrtc::DesktopRect::MakeSize(screen_size_)); } -bool VideoDecoderVp8::DecodePacket(const VideoPacket& packet) { - DCHECK_EQ(kReady, state_); - - // Initialize the codec as needed. - if (!codec_) { - codec_ = new vpx_codec_ctx_t(); - - // TODO(hclam): Scale the number of threads with number of cores of the - // machine. - vpx_codec_dec_cfg config; - config.w = 0; - config.h = 0; - config.threads = 2; - vpx_codec_err_t ret = - vpx_codec_dec_init(codec_, vpx_codec_vp8_dx(), &config, 0); - if (ret != VPX_CODEC_OK) { - LOG(INFO) << "Cannot initialize codec."; - delete codec_; - codec_ = NULL; - state_ = kError; - return false; - } - } +bool VideoDecoderVpx::DecodePacket(const VideoPacket& packet) { + DCHECK(!screen_size_.is_empty()); // Do the actual decoding. vpx_codec_err_t ret = vpx_codec_decode( - codec_, reinterpret_cast<const uint8*>(packet.data().data()), + codec_.get(), reinterpret_cast<const uint8*>(packet.data().data()), packet.data().size(), NULL, 0); if (ret != VPX_CODEC_OK) { LOG(INFO) << "Decoding failed:" << vpx_codec_err_to_string(ret) << "\n" - << "Details: " << vpx_codec_error(codec_) << "\n" - << vpx_codec_error_detail(codec_); + << "Details: " << vpx_codec_error(codec_.get()) << "\n" + << vpx_codec_error_detail(codec_.get()); return false; } // Gets the decoded data. vpx_codec_iter_t iter = NULL; - vpx_image_t* image = vpx_codec_get_frame(codec_, &iter); + vpx_image_t* image = vpx_codec_get_frame(codec_.get(), &iter); if (!image) { LOG(INFO) << "No video frame decoded"; return false; @@ -121,9 +125,8 @@ bool VideoDecoderVp8::DecodePacket(const VideoPacket& packet) { return true; } -void VideoDecoderVp8::Invalidate(const webrtc::DesktopSize& view_size, +void VideoDecoderVpx::Invalidate(const webrtc::DesktopSize& view_size, const webrtc::DesktopRegion& region) { - DCHECK_EQ(kReady, state_); DCHECK(!view_size.is_empty()); for (webrtc::DesktopRegion::Iterator i(region); !i.IsAtEnd(); i.Advance()) { @@ -138,12 +141,12 @@ void VideoDecoderVp8::Invalidate(const webrtc::DesktopSize& view_size, transparent_region_.AddRegion(difference); } -void VideoDecoderVp8::RenderFrame(const webrtc::DesktopSize& view_size, +void VideoDecoderVpx::RenderFrame(const webrtc::DesktopSize& view_size, const webrtc::DesktopRect& clip_area, uint8* image_buffer, int image_stride, webrtc::DesktopRegion* output_region) { - DCHECK_EQ(kReady, state_); + DCHECK(!screen_size_.is_empty()); DCHECK(!view_size.is_empty()); // Early-return and do nothing if we haven't yet decoded any frames. @@ -244,7 +247,7 @@ void VideoDecoderVp8::RenderFrame(const webrtc::DesktopSize& view_size, continue; // Fill the rectange with transparent pixels. - FillRect(image_buffer, image_stride, rect, kTransparent); + FillRect(image_buffer, image_stride, rect, kTransparentColor); output_region->AddRect(rect); } @@ -254,24 +257,17 @@ void VideoDecoderVp8::RenderFrame(const webrtc::DesktopSize& view_size, transparent_region_.Subtract(scaled_clip_area); } -const webrtc::DesktopRegion* VideoDecoderVp8::GetImageShape() { +const webrtc::DesktopRegion* VideoDecoderVpx::GetImageShape() { return &desktop_shape_; } -void VideoDecoderVp8::FillRect(uint8* buffer, - int stride, - const webrtc::DesktopRect& rect, - uint32 color) { - uint32* ptr = reinterpret_cast<uint32*>(buffer + (rect.top() * stride) + - (rect.left() * kBytesPerPixel)); - int width = rect.width(); - for (int height = rect.height(); height > 0; --height) { - std::fill(ptr, ptr + width, color); - ptr += stride / kBytesPerPixel; - } +VideoDecoderVpx::VideoDecoderVpx(ScopedVpxCodec codec) + : codec_(codec.Pass()), + last_image_(NULL) { + DCHECK(codec_); } -void VideoDecoderVp8::UpdateImageShapeRegion( +void VideoDecoderVpx::UpdateImageShapeRegion( webrtc::DesktopRegion* new_desktop_shape) { // Add all areas that have been updated or become transparent to the // transparent region. Exclude anything within the new desktop shape. diff --git a/remoting/codec/video_decoder_vp8.h b/remoting/codec/video_decoder_vpx.h index 42a3176..800c873 100644 --- a/remoting/codec/video_decoder_vp8.h +++ b/remoting/codec/video_decoder_vpx.h @@ -1,26 +1,29 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef REMOTING_CODEC_VIDEO_DECODER_VP8_H_ -#define REMOTING_CODEC_VIDEO_DECODER_VP8_H_ +#ifndef REMOTING_CODEC_VIDEO_DECODER_VPX_H_ +#define REMOTING_CODEC_VIDEO_DECODER_VPX_H_ #include "base/compiler_specific.h" +#include "base/memory/scoped_ptr.h" +#include "remoting/codec/scoped_vpx_codec.h" #include "remoting/codec/video_decoder.h" #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" #include "third_party/webrtc/modules/desktop_capture/desktop_region.h" -typedef struct vpx_codec_ctx vpx_codec_ctx_t; typedef struct vpx_image vpx_image_t; namespace remoting { -class VideoDecoderVp8 : public VideoDecoder { +class VideoDecoderVpx : public VideoDecoder { public: - VideoDecoderVp8(); - virtual ~VideoDecoderVp8(); + // Creates a decoder for VP8. + static scoped_ptr<VideoDecoderVpx> CreateForVP8(); - // VideoDecoder implementations. + virtual ~VideoDecoderVpx(); + + // VideoDecoder interface. virtual void Initialize(const webrtc::DesktopSize& screen_size) OVERRIDE; virtual bool DecodePacket(const VideoPacket& packet) OVERRIDE; virtual void Invalidate(const webrtc::DesktopSize& view_size, @@ -33,26 +36,14 @@ class VideoDecoderVp8 : public VideoDecoder { virtual const webrtc::DesktopRegion* GetImageShape() OVERRIDE; private: - enum State { - kUninitialized, - kReady, - kError, - }; - - // Fills the rectangle |rect| with the given ARGB color |color| in |buffer|. - void FillRect(uint8* buffer, int stride, - const webrtc::DesktopRect& rect, - uint32 color); + explicit VideoDecoderVpx(ScopedVpxCodec codec); // Calculates the difference between the desktop shape regions in two // consecutive frames and updates |updated_region_| and |transparent_region_| // accordingly. void UpdateImageShapeRegion(webrtc::DesktopRegion* new_desktop_shape); - // The internal state of the decoder. - State state_; - - vpx_codec_ctx_t* codec_; + ScopedVpxCodec codec_; // Pointer to the last decoded image. vpx_image_t* last_image_; @@ -69,7 +60,7 @@ class VideoDecoderVp8 : public VideoDecoder { // The region that should be make transparent. webrtc::DesktopRegion transparent_region_; - DISALLOW_COPY_AND_ASSIGN(VideoDecoderVp8); + DISALLOW_COPY_AND_ASSIGN(VideoDecoderVpx); }; } // namespace remoting diff --git a/remoting/codec/video_decoder_vp8_unittest.cc b/remoting/codec/video_decoder_vpx_unittest.cc index 64d30df..eab0274 100644 --- a/remoting/codec/video_decoder_vp8_unittest.cc +++ b/remoting/codec/video_decoder_vpx_unittest.cc @@ -1,76 +1,80 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "remoting/codec/video_decoder_vp8.h" +#include "remoting/codec/video_decoder_vpx.h" #include "media/base/video_frame.h" #include "remoting/codec/codec_test.h" -#include "remoting/codec/video_encoder_vp8.h" +#include "remoting/codec/video_encoder_vpx.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" namespace remoting { -class VideoDecoderVp8Test : public testing::Test { +class VideoDecoderVpxTest : public testing::Test { protected: - VideoEncoderVp8 encoder_; - VideoDecoderVp8 decoder_; + scoped_ptr<VideoEncoderVpx> encoder_; + scoped_ptr<VideoDecoderVpx> decoder_; + + VideoDecoderVpxTest() : encoder_(VideoEncoderVpx::CreateForVP8()), + decoder_(VideoDecoderVpx::CreateForVP8()) { + } void TestGradient(int screen_width, int screen_height, int view_width, int view_height, double max_error_limit, double mean_error_limit) { TestVideoEncoderDecoderGradient( - &encoder_, &decoder_, + encoder_.get(), decoder_.get(), webrtc::DesktopSize(screen_width, screen_height), webrtc::DesktopSize(view_width, view_height), max_error_limit, mean_error_limit); } }; -TEST_F(VideoDecoderVp8Test, VideoEncodeAndDecode) { - TestVideoEncoderDecoder(&encoder_, &decoder_, false); +TEST_F(VideoDecoderVpxTest, VideoEncodeAndDecode) { + TestVideoEncoderDecoder(encoder_.get(), decoder_.get(), false); } // Check that encoding and decoding a particular frame doesn't change the // frame too much. The frame used is a gradient, which does not contain sharp // transitions, so encoding lossiness should not be too high. -TEST_F(VideoDecoderVp8Test, Gradient) { +TEST_F(VideoDecoderVpxTest, Gradient) { TestGradient(320, 240, 320, 240, 0.04, 0.02); } -TEST_F(VideoDecoderVp8Test, GradientScaleUpEvenToEven) { +TEST_F(VideoDecoderVpxTest, GradientScaleUpEvenToEven) { TestGradient(320, 240, 640, 480, 0.04, 0.02); } -TEST_F(VideoDecoderVp8Test, GradientScaleUpEvenToOdd) { +TEST_F(VideoDecoderVpxTest, GradientScaleUpEvenToOdd) { TestGradient(320, 240, 641, 481, 0.04, 0.02); } -TEST_F(VideoDecoderVp8Test, GradientScaleUpOddToEven) { +TEST_F(VideoDecoderVpxTest, GradientScaleUpOddToEven) { TestGradient(321, 241, 640, 480, 0.04, 0.02); } -TEST_F(VideoDecoderVp8Test, GradientScaleUpOddToOdd) { +TEST_F(VideoDecoderVpxTest, GradientScaleUpOddToOdd) { TestGradient(321, 241, 641, 481, 0.04, 0.02); } -TEST_F(VideoDecoderVp8Test, GradientScaleDownEvenToEven) { +TEST_F(VideoDecoderVpxTest, GradientScaleDownEvenToEven) { TestGradient(320, 240, 160, 120, 0.04, 0.02); } -TEST_F(VideoDecoderVp8Test, GradientScaleDownEvenToOdd) { +TEST_F(VideoDecoderVpxTest, GradientScaleDownEvenToOdd) { // The maximum error is non-deterministic. The mean error is not too high, // which suggests that the problem is restricted to a small area of the output // image. See crbug.com/139437 and crbug.com/139633. TestGradient(320, 240, 161, 121, 1.0, 0.02); } -TEST_F(VideoDecoderVp8Test, GradientScaleDownOddToEven) { +TEST_F(VideoDecoderVpxTest, GradientScaleDownOddToEven) { TestGradient(321, 241, 160, 120, 0.04, 0.02); } -TEST_F(VideoDecoderVp8Test, GradientScaleDownOddToOdd) { +TEST_F(VideoDecoderVpxTest, GradientScaleDownOddToOdd) { TestGradient(321, 241, 161, 121, 0.04, 0.02); } diff --git a/remoting/codec/video_encoder_vp8.cc b/remoting/codec/video_encoder_vpx.cc index 077bdd1..6c39d47 100644 --- a/remoting/codec/video_encoder_vp8.cc +++ b/remoting/codec/video_encoder_vpx.cc @@ -1,9 +1,10 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "remoting/codec/video_encoder_vp8.h" +#include "remoting/codec/video_encoder_vpx.h" +#include "base/bind.h" #include "base/logging.h" #include "base/sys_info.h" #include "base/time/time.h" @@ -20,37 +21,171 @@ extern "C" { #include "third_party/libvpx/source/libvpx/vpx/vp8cx.h" } +namespace remoting { + namespace { // Defines the dimension of a macro block. This is used to compute the active // map for the encoder. const int kMacroBlockSize = 16; -} // namespace remoting +ScopedVpxCodec CreateVP8Codec(const webrtc::DesktopSize& size) { + ScopedVpxCodec codec(new vpx_codec_ctx_t); -namespace remoting { + // Configure the encoder. + vpx_codec_enc_cfg_t config; + const vpx_codec_iface_t* algo = vpx_codec_vp8_cx(); + CHECK(algo); + vpx_codec_err_t ret = vpx_codec_enc_config_default(algo, &config, 0); + if (ret != VPX_CODEC_OK) + return ScopedVpxCodec(); -VideoEncoderVp8::VideoEncoderVp8() - : initialized_(false), - active_map_width_(0), - active_map_height_(0), - last_timestamp_(0) {} + config.rc_target_bitrate = size.width() * size.height() * + config.rc_target_bitrate / config.g_w / config.g_h; + config.g_w = size.width(); + config.g_h = size.height(); + config.g_pass = VPX_RC_ONE_PASS; -VideoEncoderVp8::~VideoEncoderVp8() { - Destroy(); + // Value of 2 means using the real time profile. This is basically a + // redundant option since we explicitly select real time mode when doing + // encoding. + config.g_profile = 2; + + // Using 2 threads gives a great boost in performance for most systems with + // adequate processing power. NB: Going to multiple threads on low end + // windows systems can really hurt performance. + // http://crbug.com/99179 + config.g_threads = (base::SysInfo::NumberOfProcessors() > 2) ? 2 : 1; + config.rc_min_quantizer = 20; + config.rc_max_quantizer = 30; + config.g_timebase.num = 1; + config.g_timebase.den = 20; + + if (vpx_codec_enc_init(codec.get(), algo, &config, 0)) + return ScopedVpxCodec(); + + // Value of 16 will have the smallest CPU load. This turns off subpixel + // motion search. + if (vpx_codec_control(codec.get(), VP8E_SET_CPUUSED, 16)) + return ScopedVpxCodec(); + + // Use the lowest level of noise sensitivity so as to spend less time + // on motion estimation and inter-prediction mode. + if (vpx_codec_control(codec.get(), VP8E_SET_NOISE_SENSITIVITY, 0)) + return ScopedVpxCodec(); + + return codec.Pass(); +} + +} // namespace + +// static +scoped_ptr<VideoEncoderVpx> VideoEncoderVpx::CreateForVP8() { + return scoped_ptr<VideoEncoderVpx>( + new VideoEncoderVpx(base::Bind(&CreateVP8Codec))); } -void VideoEncoderVp8::Destroy() { - if (initialized_) { - vpx_codec_err_t ret = vpx_codec_destroy(codec_.get()); - DCHECK_EQ(ret, VPX_CODEC_OK) << "Failed to destroy codec"; - initialized_ = false; +VideoEncoderVpx::~VideoEncoderVpx() {} + +scoped_ptr<VideoPacket> VideoEncoderVpx::Encode( + const webrtc::DesktopFrame& frame) { + DCHECK_LE(32, frame.size().width()); + DCHECK_LE(32, frame.size().height()); + + base::Time encode_start_time = base::Time::Now(); + + if (!codec_ || + !frame.size().equals(webrtc::DesktopSize(image_->w, image_->h))) { + bool ret = Initialize(frame.size()); + // TODO(hclam): Handle error better. + CHECK(ret) << "Initialization of encoder failed"; + } + + // Convert the updated capture data ready for encode. + webrtc::DesktopRegion updated_region; + PrepareImage(frame, &updated_region); + + // Update active map based on updated region. + PrepareActiveMap(updated_region); + + // Apply active map to the encoder. + vpx_active_map_t act_map; + act_map.rows = active_map_height_; + act_map.cols = active_map_width_; + act_map.active_map = active_map_.get(); + if (vpx_codec_control(codec_.get(), VP8E_SET_ACTIVEMAP, &act_map)) { + LOG(ERROR) << "Unable to apply active map"; + } + + // Do the actual encoding. + vpx_codec_err_t ret = vpx_codec_encode(codec_.get(), image_.get(), + last_timestamp_, + 1, 0, VPX_DL_REALTIME); + DCHECK_EQ(ret, VPX_CODEC_OK) + << "Encoding error: " << vpx_codec_err_to_string(ret) << "\n" + << "Details: " << vpx_codec_error(codec_.get()) << "\n" + << vpx_codec_error_detail(codec_.get()); + + // TODO(hclam): Apply the proper timestamp here. + last_timestamp_ += 50; + + // Read the encoded data. + vpx_codec_iter_t iter = NULL; + bool got_data = false; + + // TODO(hclam): Make sure we get exactly one frame from the packet. + // TODO(hclam): We should provide the output buffer to avoid one copy. + scoped_ptr<VideoPacket> packet(new VideoPacket()); + + while (!got_data) { + const vpx_codec_cx_pkt_t* vpx_packet = + vpx_codec_get_cx_data(codec_.get(), &iter); + if (!vpx_packet) + continue; + + switch (vpx_packet->kind) { + case VPX_CODEC_CX_FRAME_PKT: + got_data = true; + packet->set_data(vpx_packet->data.frame.buf, vpx_packet->data.frame.sz); + break; + default: + break; + } + } + + // Construct the VideoPacket message. + packet->mutable_format()->set_encoding(VideoPacketFormat::ENCODING_VP8); + packet->mutable_format()->set_screen_width(frame.size().width()); + packet->mutable_format()->set_screen_height(frame.size().height()); + packet->set_capture_time_ms(frame.capture_time_ms()); + packet->set_encode_time_ms( + (base::Time::Now() - encode_start_time).InMillisecondsRoundedUp()); + if (!frame.dpi().is_zero()) { + packet->mutable_format()->set_x_dpi(frame.dpi().x()); + packet->mutable_format()->set_y_dpi(frame.dpi().y()); } + for (webrtc::DesktopRegion::Iterator r(updated_region); !r.IsAtEnd(); + r.Advance()) { + Rect* rect = packet->add_dirty_rects(); + rect->set_x(r.rect().left()); + rect->set_y(r.rect().top()); + rect->set_width(r.rect().width()); + rect->set_height(r.rect().height()); + } + + return packet.Pass(); +} + +VideoEncoderVpx::VideoEncoderVpx(const InitializeCodecCallback& init_codec) + : init_codec_(init_codec), + active_map_width_(0), + active_map_height_(0), + last_timestamp_(0) { } -bool VideoEncoderVp8::Init(const webrtc::DesktopSize& size) { - Destroy(); - codec_.reset(new vpx_codec_ctx_t()); +bool VideoEncoderVpx::Initialize(const webrtc::DesktopSize& size) { + codec_.reset(); + image_.reset(new vpx_image_t()); memset(image_.get(), 0, sizeof(vpx_image_t)); @@ -101,51 +236,13 @@ bool VideoEncoderVp8::Init(const webrtc::DesktopSize& size) { image_->stride[1] = uv_width; image_->stride[2] = uv_width; - // Configure the encoder. - vpx_codec_enc_cfg_t config; - const vpx_codec_iface_t* algo = vpx_codec_vp8_cx(); - CHECK(algo); - vpx_codec_err_t ret = vpx_codec_enc_config_default(algo, &config, 0); - if (ret != VPX_CODEC_OK) - return false; - - config.rc_target_bitrate = image_->w * image_->h * - config.rc_target_bitrate / config.g_w / config.g_h; - config.g_w = image_->w; - config.g_h = image_->h; - config.g_pass = VPX_RC_ONE_PASS; - - // Value of 2 means using the real time profile. This is basically a - // redundant option since we explicitly select real time mode when doing - // encoding. - config.g_profile = 2; - - // Using 2 threads gives a great boost in performance for most systems with - // adequate processing power. NB: Going to multiple threads on low end - // windows systems can really hurt performance. - // http://crbug.com/99179 - config.g_threads = (base::SysInfo::NumberOfProcessors() > 2) ? 2 : 1; - config.rc_min_quantizer = 20; - config.rc_max_quantizer = 30; - config.g_timebase.num = 1; - config.g_timebase.den = 20; - - if (vpx_codec_enc_init(codec_.get(), algo, &config, 0)) - return false; + // Initialize the codec. + codec_ = init_codec_.Run(size); - // Value of 16 will have the smallest CPU load. This turns off subpixel - // motion search. - if (vpx_codec_control(codec_.get(), VP8E_SET_CPUUSED, 16)) - return false; - - // Use the lowest level of noise sensitivity so as to spend less time - // on motion estimation and inter-prediction mode. - if (vpx_codec_control(codec_.get(), VP8E_SET_NOISE_SENSITIVITY, 0)) - return false; - return true; + return codec_; } -void VideoEncoderVp8::PrepareImage(const webrtc::DesktopFrame& frame, +void VideoEncoderVpx::PrepareImage(const webrtc::DesktopFrame& frame, webrtc::DesktopRegion* updated_region) { if (frame.updated_region().is_empty()) { updated_region->Clear(); @@ -191,7 +288,7 @@ void VideoEncoderVp8::PrepareImage(const webrtc::DesktopFrame& frame, } } -void VideoEncoderVp8::PrepareActiveMap( +void VideoEncoderVpx::PrepareActiveMap( const webrtc::DesktopRegion& updated_region) { // Clear active map first. memset(active_map_.get(), 0, active_map_width_ * active_map_height_); @@ -216,95 +313,4 @@ void VideoEncoderVp8::PrepareActiveMap( } } -scoped_ptr<VideoPacket> VideoEncoderVp8::Encode( - const webrtc::DesktopFrame& frame) { - DCHECK_LE(32, frame.size().width()); - DCHECK_LE(32, frame.size().height()); - - base::Time encode_start_time = base::Time::Now(); - - if (!initialized_ || - !frame.size().equals(webrtc::DesktopSize(image_->w, image_->h))) { - bool ret = Init(frame.size()); - // TODO(hclam): Handle error better. - CHECK(ret) << "Initialization of encoder failed"; - initialized_ = ret; - } - - // Convert the updated capture data ready for encode. - webrtc::DesktopRegion updated_region; - PrepareImage(frame, &updated_region); - - // Update active map based on updated region. - PrepareActiveMap(updated_region); - - // Apply active map to the encoder. - vpx_active_map_t act_map; - act_map.rows = active_map_height_; - act_map.cols = active_map_width_; - act_map.active_map = active_map_.get(); - if (vpx_codec_control(codec_.get(), VP8E_SET_ACTIVEMAP, &act_map)) { - LOG(ERROR) << "Unable to apply active map"; - } - - // Do the actual encoding. - vpx_codec_err_t ret = vpx_codec_encode(codec_.get(), image_.get(), - last_timestamp_, - 1, 0, VPX_DL_REALTIME); - DCHECK_EQ(ret, VPX_CODEC_OK) - << "Encoding error: " << vpx_codec_err_to_string(ret) << "\n" - << "Details: " << vpx_codec_error(codec_.get()) << "\n" - << vpx_codec_error_detail(codec_.get()); - - // TODO(hclam): Apply the proper timestamp here. - last_timestamp_ += 50; - - // Read the encoded data. - vpx_codec_iter_t iter = NULL; - bool got_data = false; - - // TODO(hclam): Make sure we get exactly one frame from the packet. - // TODO(hclam): We should provide the output buffer to avoid one copy. - scoped_ptr<VideoPacket> packet(new VideoPacket()); - - while (!got_data) { - const vpx_codec_cx_pkt_t* vpx_packet = - vpx_codec_get_cx_data(codec_.get(), &iter); - if (!vpx_packet) - continue; - - switch (vpx_packet->kind) { - case VPX_CODEC_CX_FRAME_PKT: - got_data = true; - // TODO(sergeyu): Split each frame into multiple partitions. - packet->set_data(vpx_packet->data.frame.buf, vpx_packet->data.frame.sz); - break; - default: - break; - } - } - - // Construct the VideoPacket message. - packet->mutable_format()->set_encoding(VideoPacketFormat::ENCODING_VP8); - packet->mutable_format()->set_screen_width(frame.size().width()); - packet->mutable_format()->set_screen_height(frame.size().height()); - packet->set_capture_time_ms(frame.capture_time_ms()); - packet->set_encode_time_ms( - (base::Time::Now() - encode_start_time).InMillisecondsRoundedUp()); - if (!frame.dpi().is_zero()) { - packet->mutable_format()->set_x_dpi(frame.dpi().x()); - packet->mutable_format()->set_y_dpi(frame.dpi().y()); - } - for (webrtc::DesktopRegion::Iterator r(updated_region); !r.IsAtEnd(); - r.Advance()) { - Rect* rect = packet->add_dirty_rects(); - rect->set_x(r.rect().left()); - rect->set_y(r.rect().top()); - rect->set_width(r.rect().width()); - rect->set_height(r.rect().height()); - } - - return packet.Pass(); -} - } // namespace remoting diff --git a/remoting/codec/video_encoder_vp8.h b/remoting/codec/video_encoder_vpx.h index 25b00b3..2e76532 100644 --- a/remoting/codec/video_encoder_vp8.h +++ b/remoting/codec/video_encoder_vpx.h @@ -1,14 +1,14 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef REMOTING_CODEC_VIDEO_ENCODER_VP8_H_ -#define REMOTING_CODEC_VIDEO_ENCODER_VP8_H_ +#ifndef REMOTING_CODEC_VIDEO_ENCODER_VPX_H_ +#define REMOTING_CODEC_VIDEO_ENCODER_VPX_H_ -#include "base/gtest_prod_util.h" +#include "base/callback.h" +#include "remoting/codec/scoped_vpx_codec.h" #include "remoting/codec/video_encoder.h" -typedef struct vpx_codec_ctx vpx_codec_ctx_t; typedef struct vpx_image vpx_image_t; namespace webrtc { @@ -19,39 +19,38 @@ class DesktopSize; namespace remoting { // A class that uses VP8 to perform encoding. -class VideoEncoderVp8 : public VideoEncoder { +class VideoEncoderVpx : public VideoEncoder { public: - VideoEncoderVp8(); - virtual ~VideoEncoderVp8(); + // Creates a encoder for VP8. + static scoped_ptr<VideoEncoderVpx> CreateForVP8(); + + virtual ~VideoEncoderVpx(); // VideoEncoder interface. virtual scoped_ptr<VideoPacket> Encode( const webrtc::DesktopFrame& frame) OVERRIDE; private: - FRIEND_TEST_ALL_PREFIXES(VideoEncoderVp8Test, AlignAndClipRect); + typedef base::Callback<ScopedVpxCodec(const webrtc::DesktopSize&)> + InitializeCodecCallback; - // Initialize the encoder. Returns true if successful. - bool Init(const webrtc::DesktopSize& size); + VideoEncoderVpx(const InitializeCodecCallback& init_codec); - // Destroy the encoder. - void Destroy(); + // Initializes the codec for frames of |size|. Returns true if successful. + bool Initialize(const webrtc::DesktopSize& size); - // Prepare |image_| for encoding. Write updated rectangles into + // Prepares |image_| for encoding. Writes updated rectangles into // |updated_region|. - // - // TODO(sergeyu): Update this code to use webrtc::DesktopRegion. void PrepareImage(const webrtc::DesktopFrame& frame, webrtc::DesktopRegion* updated_region); - // Update the active map according to |updated_region|. Active map is then + // Updates the active map according to |updated_region|. Active map is then // given to the encoder to speed up encoding. void PrepareActiveMap(const webrtc::DesktopRegion& updated_region); - // True if the encoder is initialized. - bool initialized_; + InitializeCodecCallback init_codec_; - scoped_ptr<vpx_codec_ctx_t> codec_; + ScopedVpxCodec codec_; scoped_ptr<vpx_image_t> image_; scoped_ptr<uint8[]> active_map_; int active_map_width_; @@ -61,7 +60,7 @@ class VideoEncoderVp8 : public VideoEncoder { // Buffer for storing the yuv image. scoped_ptr<uint8[]> yuv_image_; - DISALLOW_COPY_AND_ASSIGN(VideoEncoderVp8); + DISALLOW_COPY_AND_ASSIGN(VideoEncoderVpx); }; } // namespace remoting diff --git a/remoting/codec/video_encoder_vp8_unittest.cc b/remoting/codec/video_encoder_vpx_unittest.cc index a8871ba..f5a0c8d 100644 --- a/remoting/codec/video_encoder_vp8_unittest.cc +++ b/remoting/codec/video_encoder_vpx_unittest.cc @@ -1,8 +1,8 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include "remoting/codec/video_encoder_vp8.h" +#include "remoting/codec/video_encoder_vpx.h" #include <limits> #include <vector> @@ -21,44 +21,44 @@ const int kIntMax = std::numeric_limits<int>::max(); namespace remoting { -TEST(VideoEncoderVp8Test, TestVideoEncoder) { - VideoEncoderVp8 encoder; - TestVideoEncoder(&encoder, false); +TEST(VideoEncoderVpxTest, TestVideoEncoder) { + scoped_ptr<VideoEncoderVpx> encoder(VideoEncoderVpx::CreateForVP8()); + TestVideoEncoder(encoder.get(), false); } // Test that calling Encode with a differently-sized media::ScreenCaptureData // does not leak memory. -TEST(VideoEncoderVp8Test, TestSizeChangeNoLeak) { +TEST(VideoEncoderVpxTest, TestSizeChangeNoLeak) { int height = 1000; int width = 1000; - VideoEncoderVp8 encoder; + scoped_ptr<VideoEncoderVpx> encoder(VideoEncoderVpx::CreateForVP8()); - scoped_ptr<webrtc::DesktopFrame> frame(new webrtc::BasicDesktopFrame( - webrtc::DesktopSize(width, height))); + scoped_ptr<webrtc::DesktopFrame> frame( + new webrtc::BasicDesktopFrame(webrtc::DesktopSize(width, height))); - scoped_ptr<VideoPacket> packet = encoder.Encode(*frame); + scoped_ptr<VideoPacket> packet = encoder->Encode(*frame); EXPECT_TRUE(packet); height /= 2; - frame.reset(new webrtc::BasicDesktopFrame( - webrtc::DesktopSize(width, height))); - packet = encoder.Encode(*frame); + frame.reset( + new webrtc::BasicDesktopFrame(webrtc::DesktopSize(width, height))); + packet = encoder->Encode(*frame); EXPECT_TRUE(packet); } // Test that the DPI information is correctly propagated from the // media::ScreenCaptureData to the VideoPacket. -TEST(VideoEncoderVp8Test, TestDpiPropagation) { +TEST(VideoEncoderVpxTest, TestDpiPropagation) { int height = 32; int width = 32; - VideoEncoderVp8 encoder; + scoped_ptr<VideoEncoderVpx> encoder(VideoEncoderVpx::CreateForVP8()); - scoped_ptr<webrtc::DesktopFrame> frame(new webrtc::BasicDesktopFrame( - webrtc::DesktopSize(width, height))); + scoped_ptr<webrtc::DesktopFrame> frame( + new webrtc::BasicDesktopFrame(webrtc::DesktopSize(width, height))); frame->set_dpi(webrtc::DesktopVector(96, 97)); - scoped_ptr<VideoPacket> packet = encoder.Encode(*frame); + scoped_ptr<VideoPacket> packet = encoder->Encode(*frame); EXPECT_EQ(packet->format().x_dpi(), 96); EXPECT_EQ(packet->format().y_dpi(), 97); } |