Implement VP8 encoder for chromoting
Added EncoderVp8 with test for chromoting. TEST=remoting_unittests BUG=50235 Review URL: http://codereview.chromium.org/3005036 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@60854 0039d316-1c4b-4281-b951-d872f2087c98
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -74,6 +74,8 @@
|
|||||||
/third_party/hunspell_dictionaries
|
/third_party/hunspell_dictionaries
|
||||||
/third_party/icu
|
/third_party/icu
|
||||||
/third_party/libjingle/source
|
/third_party/libjingle/source
|
||||||
|
/third_party/libvpx/lib
|
||||||
|
/third_party/libvpx/include
|
||||||
/third_party/libsrtp/src
|
/third_party/libsrtp/src
|
||||||
/third_party/libvpx
|
/third_party/libvpx
|
||||||
/third_party/lighttpd
|
/third_party/lighttpd
|
||||||
|
@@ -25,6 +25,14 @@ bool InitializeMediaLibrary(const FilePath& module_dir);
|
|||||||
// Returns true if OpenMAX was successfully initialized and loaded.
|
// Returns true if OpenMAX was successfully initialized and loaded.
|
||||||
bool InitializeOpenMaxLibrary(const FilePath& module_dir);
|
bool InitializeOpenMaxLibrary(const FilePath& module_dir);
|
||||||
|
|
||||||
|
// This is temporary to get the address of vpx_codec_vp8_cx_algo in FFmpeg.
|
||||||
|
// This method should only be called after media library is loaded.
|
||||||
|
|
||||||
|
// TODO(hclam): Remove this after we have a getter function for the same
|
||||||
|
// purpose in libvpx.
|
||||||
|
// See bug: http://code.google.com/p/webm/issues/detail?id=169
|
||||||
|
void* GetVp8CxAlgoAddress();
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
|
|
||||||
#endif // MEDIA_BASE_MEDIA_H_
|
#endif // MEDIA_BASE_MEDIA_H_
|
||||||
|
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include "base/file_path.h"
|
#include "base/file_path.h"
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
|
#include "base/native_library.h"
|
||||||
#include "base/path_service.h"
|
#include "base/path_service.h"
|
||||||
#include "media/ffmpeg/ffmpeg_common.h"
|
#include "media/ffmpeg/ffmpeg_common.h"
|
||||||
#include "third_party/ffmpeg/ffmpeg_stubs.h"
|
#include "third_party/ffmpeg/ffmpeg_stubs.h"
|
||||||
@@ -69,6 +70,9 @@ std::string GetDSOName(tp_ffmpeg::StubModules stub_key) {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
// Address of vpx_codec_vp8_cx_algo.
|
||||||
|
static void* vp8_cx_algo_address = NULL;
|
||||||
|
|
||||||
// Attempts to initialize the media library (loading DSOs, etc.).
|
// Attempts to initialize the media library (loading DSOs, etc.).
|
||||||
// Returns true if everything was successfully initialized, false otherwise.
|
// Returns true if everything was successfully initialized, false otherwise.
|
||||||
bool InitializeMediaLibrary(const FilePath& module_dir) {
|
bool InitializeMediaLibrary(const FilePath& module_dir) {
|
||||||
@@ -85,7 +89,18 @@ bool InitializeMediaLibrary(const FilePath& module_dir) {
|
|||||||
paths[module].push_back(path.value());
|
paths[module].push_back(path.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
return tp_ffmpeg::InitializeStubs(paths);
|
bool ret = tp_ffmpeg::InitializeStubs(paths);
|
||||||
|
|
||||||
|
// TODO(hclam): This is temporary to obtain address of
|
||||||
|
// vpx_codec_vp8_cx_algo. This should be removed once libvpx has a
|
||||||
|
// getter method for it.
|
||||||
|
base::NativeLibrary sumo_lib =
|
||||||
|
base::LoadNativeLibrary(module_dir.Append(sumo_name));
|
||||||
|
if (sumo_lib) {
|
||||||
|
vp8_cx_algo_address = base::GetFunctionPointerFromNativeLibrary(
|
||||||
|
sumo_lib, "vpx_codec_vp8_cx_algo");
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(OS_LINUX)
|
#if defined(OS_LINUX)
|
||||||
@@ -114,4 +129,8 @@ bool InitializeOpenMaxLibrary(const FilePath& module_dir) {
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void* GetVp8CxAlgoAddress() {
|
||||||
|
return vp8_cx_algo_address;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "base/file_path.h"
|
#include "base/file_path.h"
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
|
#include "base/native_library.h"
|
||||||
#include "base/path_service.h"
|
#include "base/path_service.h"
|
||||||
#include "base/scoped_ptr.h"
|
#include "base/scoped_ptr.h"
|
||||||
|
|
||||||
@@ -47,6 +48,9 @@ FilePath::CharType* GetDLLName(FFmpegDLLKeys dll_key) {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
// Address of vpx_codec_vp8_cx_algo.
|
||||||
|
static void* vp8_cx_algo_address = NULL;
|
||||||
|
|
||||||
// Attempts to initialize the media library (loading DLLs, DSOs, etc.).
|
// Attempts to initialize the media library (loading DLLs, DSOs, etc.).
|
||||||
// Returns true if everything was successfully initialized, false otherwise.
|
// Returns true if everything was successfully initialized, false otherwise.
|
||||||
bool InitializeMediaLibrary(const FilePath& base_path) {
|
bool InitializeMediaLibrary(const FilePath& base_path) {
|
||||||
@@ -78,6 +82,16 @@ bool InitializeMediaLibrary(const FilePath& base_path) {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(hclam): This is temporary to obtain address of
|
||||||
|
// vpx_codec_vp8_cx_algo. This should be removed once libvpx has a
|
||||||
|
// getter method for it.
|
||||||
|
base::NativeLibrary avcodec_lib =
|
||||||
|
base::LoadNativeLibrary(FilePath(GetDLLName(media::FILE_LIBAVCODEC)));
|
||||||
|
if (avcodec_lib) {
|
||||||
|
vp8_cx_algo_address = base::GetFunctionPointerFromNativeLibrary(
|
||||||
|
avcodec_lib, "vpx_codec_vp8_cx_algo");
|
||||||
|
}
|
||||||
|
|
||||||
// Check that we loaded all libraries successfully. We only need to check the
|
// Check that we loaded all libraries successfully. We only need to check the
|
||||||
// last array element because the loop above will break without initializing
|
// last array element because the loop above will break without initializing
|
||||||
// it on any prior error.
|
// it on any prior error.
|
||||||
@@ -97,4 +111,8 @@ bool InitializeOpenMaxLibrary(const FilePath& module_dir) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* GetVp8CxAlgoAddress() {
|
||||||
|
return vp8_cx_algo_address;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
|
@@ -78,9 +78,10 @@ class EncoderMessageTester {
|
|||||||
|
|
||||||
~EncoderMessageTester() {
|
~EncoderMessageTester() {
|
||||||
EXPECT_EQ(begin_rect_, end_rect_);
|
EXPECT_EQ(begin_rect_, end_rect_);
|
||||||
|
EXPECT_GT(begin_rect_, 0);
|
||||||
EXPECT_EQ(kWaitingForBeginRect, state_);
|
EXPECT_EQ(kWaitingForBeginRect, state_);
|
||||||
if (strict_){
|
if (strict_) {
|
||||||
EXPECT_EQ(begin_rect_, added_rects_);
|
EXPECT_EQ(added_rects_, begin_rect_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -5,129 +5,190 @@
|
|||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "media/base/callback.h"
|
#include "media/base/callback.h"
|
||||||
#include "media/base/data_buffer.h"
|
#include "media/base/data_buffer.h"
|
||||||
#include "remoting/host/encoder_vp8.h"
|
#include "media/base/media.h"
|
||||||
|
#include "remoting/base/capture_data.h"
|
||||||
|
#include "remoting/base/encoder_vp8.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
// TODO(garykac): Rix with correct path to vp8 header.
|
#define VPX_CODEC_DISABLE_COMPAT 1
|
||||||
#include "remoting/third_party/on2/include/vp8cx.h"
|
#include "third_party/libvpx/include/vpx/vpx_codec.h"
|
||||||
|
#include "third_party/libvpx/include/vpx/vpx_encoder.h"
|
||||||
|
#include "third_party/libvpx/include/vpx/vp8cx.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace remoting {
|
namespace remoting {
|
||||||
|
|
||||||
EncoderVp8::EncoderVp8()
|
EncoderVp8::EncoderVp8()
|
||||||
: initialized_(false),
|
: initialized_(false),
|
||||||
|
codec_(NULL),
|
||||||
|
image_(NULL),
|
||||||
last_timestamp_(0) {
|
last_timestamp_(0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
EncoderVp8::~EncoderVp8() {
|
EncoderVp8::~EncoderVp8() {
|
||||||
|
if (initialized_) {
|
||||||
|
vpx_codec_err_t ret = vpx_codec_destroy(codec_.get());
|
||||||
|
DCHECK(ret == VPX_CODEC_OK) << "Failed to destroy codec";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EncoderVp8::Init() {
|
bool EncoderVp8::Init(int width, int height) {
|
||||||
// TODO(hclam): Now always assume we receive YV12. May need to extend this
|
codec_.reset(new vpx_codec_ctx_t());
|
||||||
// so we can do color space conversion manually.
|
image_.reset(new vpx_image_t());
|
||||||
image_.fmt = IMG_FMT_YV12;
|
memset(image_.get(), 0, sizeof(vpx_image_t));
|
||||||
image_.w = width_;
|
|
||||||
image_.h = height_;
|
|
||||||
|
|
||||||
on2_codec_enc_cfg_t config;
|
image_->fmt = VPX_IMG_FMT_YV12;
|
||||||
on2_codec_err_t result = on2_codec_enc_config_default(&on2_codec_vp8_cx_algo,
|
|
||||||
&config, 0);
|
|
||||||
|
|
||||||
// TODO(hclam): Adjust the parameters.
|
// libvpx seems to require both to be assigned.
|
||||||
config.g_w = width_;
|
image_->d_w = width;
|
||||||
config.g_h = height_;
|
image_->w = width;
|
||||||
config.g_pass = ON2_RC_ONE_PASS;
|
image_->d_h = height;
|
||||||
|
image_->h = height;
|
||||||
|
|
||||||
|
vpx_codec_enc_cfg_t config;
|
||||||
|
const vpx_codec_iface_t* algo =
|
||||||
|
(const vpx_codec_iface_t*)media::GetVp8CxAlgoAddress();
|
||||||
|
vpx_codec_err_t ret = vpx_codec_enc_config_default(algo, &config, 0);
|
||||||
|
if (ret != VPX_CODEC_OK)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// TODO(hclam): Tune the parameters to better suit the application.
|
||||||
|
config.rc_target_bitrate = width * height * config.rc_target_bitrate
|
||||||
|
/ config.g_w / config.g_h;
|
||||||
|
config.g_w = width;
|
||||||
|
config.g_h = height;
|
||||||
|
config.g_pass = VPX_RC_ONE_PASS;
|
||||||
config.g_profile = 1;
|
config.g_profile = 1;
|
||||||
config.g_threads = 2;
|
config.g_threads = 2;
|
||||||
config.rc_target_bitrate = 1000000;
|
|
||||||
config.rc_min_quantizer = 0;
|
config.rc_min_quantizer = 0;
|
||||||
config.rc_max_quantizer = 15;
|
config.rc_max_quantizer = 15;
|
||||||
config.g_timebase.num = 1;
|
config.g_timebase.num = 1;
|
||||||
config.g_timebase.den = 30;
|
config.g_timebase.den = 30;
|
||||||
|
|
||||||
if (on2_codec_enc_init(&codec_, &on2_codec_vp8_cx_algo, &config, 0))
|
if (vpx_codec_enc_init(codec_.get(), algo, &config, 0))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
on2_codec_control_(&codec_, VP8E_SET_CPUUSED, -15);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EncoderVp8::Encode(const DirtyRects& dirty_rects,
|
bool EncoderVp8::PrepareImage(scoped_refptr<CaptureData> capture_data) {
|
||||||
const uint8** input_data,
|
if (!yuv_image_.get()) {
|
||||||
const int* strides,
|
const int plane_size = capture_data->width() * capture_data->height();
|
||||||
bool key_frame,
|
|
||||||
UpdateStreamPacketHeader* header,
|
|
||||||
scoped_refptr<media::DataBuffer>* output_data,
|
|
||||||
bool* encode_done,
|
|
||||||
Task* data_available_task) {
|
|
||||||
// This will allow the task be called when this method exits.
|
|
||||||
media::AutoTaskRunner task(data_available_task);
|
|
||||||
*encode_done = false;
|
|
||||||
|
|
||||||
// TODO(hclam): We only initialize the encoder once. We may have to
|
// YUV image size is 1.5 times of a plane. Multiplication is performed first
|
||||||
// allow encoder be initialized with difference sizes.
|
// to avoid rounding error.
|
||||||
if (!initialized_) {
|
const int size = plane_size * 3 / 2;
|
||||||
if (!Init()) {
|
|
||||||
LOG(ERROR) << "Can't initialize VP8 encoder";
|
yuv_image_.reset(new uint8[size]);
|
||||||
return;
|
|
||||||
}
|
// Reset image value to 128 so we just need to fill in the y plane.
|
||||||
initialized_ = true;
|
memset(yuv_image_.get(), 128, 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 + plane_size;
|
||||||
|
|
||||||
|
// The V plane starts from 1.25 of the plane size.
|
||||||
|
image_->planes[2] = image + plane_size + plane_size / 4;
|
||||||
|
|
||||||
|
// In YV12 Y plane has full width, UV plane has half width because of
|
||||||
|
// subsampling.
|
||||||
|
image_->stride[0] = image_->w;
|
||||||
|
image_->stride[1] = image_->w / 2;
|
||||||
|
image_->stride[2] = image_->w / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assume the capturer has done the color space conversion.
|
// And then do RGB->YUV conversion.
|
||||||
if (!input_data || !strides)
|
// Currently we just produce the Y channel as the average of RGB. This will
|
||||||
return;
|
// give a gray scale image after conversion.
|
||||||
|
// TODO(hclam): Implement the actual color space conversion.
|
||||||
|
DCHECK(capture_data->pixel_format() == PixelFormatRgb32)
|
||||||
|
<< "Only RGB32 is supported";
|
||||||
|
uint8* in = capture_data->data_planes().data[0];
|
||||||
|
const int in_stride = capture_data->data_planes().strides[0];
|
||||||
|
uint8* out = yuv_image_.get();
|
||||||
|
const int out_stride = image_->stride[0];
|
||||||
|
for (int i = 0; i < capture_data->height(); ++i) {
|
||||||
|
for (int j = 0; j < capture_data->width(); ++j) {
|
||||||
|
// Since the input pixel format is RGB32, there are 4 bytes per pixel.
|
||||||
|
uint8* pixel = in + 4 * j;
|
||||||
|
out[j] = (pixel[0] + pixel[1] + pixel[2]) / 3;
|
||||||
|
}
|
||||||
|
in += in_stride;
|
||||||
|
out += out_stride;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
image_.planes[0] = (unsigned char*)input_data[0];
|
void EncoderVp8::Encode(scoped_refptr<CaptureData> capture_data,
|
||||||
image_.planes[1] = (unsigned char*)input_data[1];
|
bool key_frame,
|
||||||
image_.planes[2] = (unsigned char*)input_data[2];
|
DataAvailableCallback* data_available_callback) {
|
||||||
image_.stride[0] = strides[0];
|
if (!initialized_) {
|
||||||
image_.stride[1] = strides[1];
|
bool ret = Init(capture_data->width(), capture_data->height());
|
||||||
image_.stride[2] = strides[2];
|
// TODO(hclam): Handle error better.
|
||||||
|
DCHECK(ret) << "Initialization of encoder failed";
|
||||||
|
initialized_ = ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!PrepareImage(capture_data)) {
|
||||||
|
NOTREACHED() << "Can't image data for encoding";
|
||||||
|
}
|
||||||
|
|
||||||
// Do the actual encoding.
|
// Do the actual encoding.
|
||||||
if (on2_codec_encode(&codec_, &image_,
|
vpx_codec_err_t ret = vpx_codec_encode(codec_.get(), image_.get(),
|
||||||
last_timestamp_, 1, 0, ON2_DL_REALTIME)) {
|
last_timestamp_,
|
||||||
return;
|
1, 0, VPX_DL_REALTIME);
|
||||||
}
|
DCHECK(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): fix this.
|
// TODO(hclam): fix this.
|
||||||
last_timestamp_ += 100;
|
last_timestamp_ += 100;
|
||||||
|
|
||||||
// Read the encoded data.
|
// Read the encoded data.
|
||||||
on2_codec_iter_t iter = NULL;
|
vpx_codec_iter_t iter = NULL;
|
||||||
bool got_data = false;
|
bool got_data = false;
|
||||||
|
|
||||||
// TODO(hclam: We assume one frame of input will get exactly one frame of
|
// TODO(hclam): Make sure we get exactly one frame from the packet.
|
||||||
// output. This assumption may not be valid.
|
// TODO(hclam): We should provide the output buffer to avoid one copy.
|
||||||
|
ChromotingHostMessage* message = new ChromotingHostMessage();
|
||||||
|
UpdateStreamPacketMessage* packet = message->mutable_update_stream_packet();
|
||||||
|
|
||||||
|
// Prepare the begin rect.
|
||||||
|
packet->mutable_begin_rect()->set_x(0);
|
||||||
|
packet->mutable_begin_rect()->set_y(0);
|
||||||
|
packet->mutable_begin_rect()->set_width(capture_data->width());
|
||||||
|
packet->mutable_begin_rect()->set_height(capture_data->height());
|
||||||
|
packet->mutable_begin_rect()->set_encoding(EncodingVp8);
|
||||||
|
packet->mutable_begin_rect()->set_pixel_format(PixelFormatYv12);
|
||||||
|
|
||||||
while (!got_data) {
|
while (!got_data) {
|
||||||
on2_codec_cx_pkt_t* packet = on2_codec_get_cx_data(&codec_, &iter);
|
const vpx_codec_cx_pkt_t* packet = vpx_codec_get_cx_data(codec_.get(),
|
||||||
|
&iter);
|
||||||
if (!packet)
|
if (!packet)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
switch (packet->kind) {
|
switch (packet->kind) {
|
||||||
case ON2_CODEC_CX_FRAME_PKT:
|
case VPX_CODEC_CX_FRAME_PKT:
|
||||||
got_data = true;
|
got_data = true;
|
||||||
*encode_done = true;
|
message->mutable_update_stream_packet()->mutable_rect_data()->set_data(
|
||||||
*output_data = new media::DataBuffer(packet->data.frame.sz);
|
packet->data.frame.buf, packet->data.frame.sz);
|
||||||
memcpy((*output_data)->GetWritableData(),
|
|
||||||
packet->data.frame.buf,
|
|
||||||
packet->data.frame.sz);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EncoderVp8::SetSize(int width, int height) {
|
// Enter the end rect.
|
||||||
width_ = width;
|
message->mutable_update_stream_packet()->mutable_end_rect();
|
||||||
height_ = height;
|
data_available_callback->Run(
|
||||||
}
|
message,
|
||||||
|
EncodingStarting | EncodingInProgress | EncodingEnded);
|
||||||
void EncoderVp8::SetPixelFormat(PixelFormat pixel_format) {
|
delete data_available_callback;
|
||||||
pixel_format_ = pixel_format;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace remoting
|
} // namespace remoting
|
||||||
|
@@ -5,20 +5,11 @@
|
|||||||
#ifndef REMOTING_BASE_ENCODER_VP8_H_
|
#ifndef REMOTING_BASE_ENCODER_VP8_H_
|
||||||
#define REMOTING_BASE_ENCODER_VP8_H_
|
#define REMOTING_BASE_ENCODER_VP8_H_
|
||||||
|
|
||||||
#include "remoting/host/encoder.h"
|
#include "remoting/base/encoder.h"
|
||||||
|
|
||||||
#include "remoting/base/protocol/chromotocol.pb.h"
|
#include "remoting/base/protocol/chromotocol.pb.h"
|
||||||
|
|
||||||
extern "C" {
|
typedef struct vpx_codec_ctx vpx_codec_ctx_t;
|
||||||
// TODO(garykac): fix this link with the correct path to on2
|
typedef struct vpx_image vpx_image_t;
|
||||||
#include "remoting/third_party/on2/include/on2_encoder.h"
|
|
||||||
} // extern "C"
|
|
||||||
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
class DataBuffer;
|
|
||||||
|
|
||||||
} // namespace media
|
|
||||||
|
|
||||||
namespace remoting {
|
namespace remoting {
|
||||||
|
|
||||||
@@ -28,31 +19,27 @@ class EncoderVp8 : public Encoder {
|
|||||||
EncoderVp8();
|
EncoderVp8();
|
||||||
virtual ~EncoderVp8();
|
virtual ~EncoderVp8();
|
||||||
|
|
||||||
virtual void Encode(const DirtyRects& dirty_rects,
|
virtual void Encode(scoped_refptr<CaptureData> capture_data,
|
||||||
const uint8** input_data,
|
|
||||||
const int* strides,
|
|
||||||
bool key_frame,
|
bool key_frame,
|
||||||
UpdateStreamPacketHeader* header,
|
DataAvailableCallback* data_available_callback);
|
||||||
scoped_refptr<media::DataBuffer>* output_data,
|
|
||||||
bool* encode_done,
|
|
||||||
Task* data_available_task);
|
|
||||||
virtual void SetSize(int width, int height);
|
|
||||||
virtual void SetPixelFormat(PixelFormat pixel_format);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Setup the VP8 encoder.
|
// Initialize the encoder. Returns true if successful.
|
||||||
bool Init();
|
bool Init(int width, int height);
|
||||||
|
|
||||||
|
// Prepare |image_| for encoding. Returns true if successful.
|
||||||
|
bool PrepareImage(scoped_refptr<CaptureData> capture_data);
|
||||||
|
|
||||||
// True if the encoder is initialized.
|
// True if the encoder is initialized.
|
||||||
bool initialized_;
|
bool initialized_;
|
||||||
|
|
||||||
int width_;
|
scoped_ptr<vpx_codec_ctx_t> codec_;
|
||||||
int height_;
|
scoped_ptr<vpx_image_t> image_;
|
||||||
PixelFormat pixel_format_;
|
|
||||||
on2_codec_ctx_t codec_;
|
|
||||||
on2_image_t image_;
|
|
||||||
int last_timestamp_;
|
int last_timestamp_;
|
||||||
|
|
||||||
|
// Buffer for storing the yuv image.
|
||||||
|
scoped_array<uint8> yuv_image_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(EncoderVp8);
|
DISALLOW_COPY_AND_ASSIGN(EncoderVp8);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -2,67 +2,15 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
#include "media/base/data_buffer.h"
|
#include "remoting/base/codec_test.h"
|
||||||
#include "remoting/base/pixel_format.h"
|
#include "remoting/base/encoder_vp8.h"
|
||||||
#include "remoting/host/encoder_vp8.h"
|
#include "testing/gtest/include/gtest/gtest.h"
|
||||||
#include "testing/gmock/include/gmock/gmock.h"
|
|
||||||
|
|
||||||
namespace remoting {
|
namespace remoting {
|
||||||
|
|
||||||
static const int kWidth = 1024;
|
TEST(EncoderVp8Test, TestEncoder) {
|
||||||
static const int kHeight = 768;
|
|
||||||
static const PixelFormat kPixelFormat = kPixelFormat_YV12;
|
|
||||||
|
|
||||||
static void GenerateData(uint8* data, int size) {
|
|
||||||
for (int i = 0; i < size; ++i) {
|
|
||||||
data[i] = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class EncodeDoneHandler
|
|
||||||
: public base::RefCountedThreadSafe<EncodeDoneHandler> {
|
|
||||||
public:
|
|
||||||
MOCK_METHOD0(EncodeDone, void());
|
|
||||||
};
|
|
||||||
|
|
||||||
TEST(EncoderVp8Test, SimpleEncode) {
|
|
||||||
EncoderVp8 encoder;
|
EncoderVp8 encoder;
|
||||||
encoder.SetSize(kWidth, kHeight);
|
TestEncoder(&encoder, false);
|
||||||
encoder.SetPixelFormat(kPixelFormat);
|
|
||||||
|
|
||||||
DirtyRects rects;
|
|
||||||
rects.push_back(gfx::Rect(kWidth, kHeight));
|
|
||||||
|
|
||||||
// Prepare memory for encoding.
|
|
||||||
int strides[3];
|
|
||||||
strides[0] = kWidth;
|
|
||||||
strides[1] = strides[2] = kWidth / 2;
|
|
||||||
|
|
||||||
uint8* planes[3];
|
|
||||||
planes[0] = new uint8[kWidth * kHeight];
|
|
||||||
planes[1] = new uint8[kWidth * kHeight / 4];
|
|
||||||
planes[2] = new uint8[kWidth * kHeight / 4];
|
|
||||||
GenerateData(planes[0], kWidth * kHeight);
|
|
||||||
GenerateData(planes[1], kWidth * kHeight / 4);
|
|
||||||
GenerateData(planes[2], kWidth * kHeight / 4);
|
|
||||||
|
|
||||||
scoped_refptr<EncodeDoneHandler> handler = new EncodeDoneHandler();
|
|
||||||
UpdateStreamPacketHeader* header = new UpdateStreamPacketHeader();
|
|
||||||
scoped_refptr<media::DataBuffer> encoded_data;
|
|
||||||
bool encode_done = false;
|
|
||||||
EXPECT_CALL(*handler, EncodeDone());
|
|
||||||
encoder.Encode(rects, const_cast<const uint8**>(planes),
|
|
||||||
strides, true, header, &encoded_data, &encode_done,
|
|
||||||
NewRunnableMethod(handler.get(),
|
|
||||||
&EncodeDoneHandler::EncodeDone));
|
|
||||||
|
|
||||||
EXPECT_TRUE(encode_done);
|
|
||||||
ASSERT_TRUE(encoded_data.get());
|
|
||||||
EXPECT_NE(0u, encoded_data->GetBufferSize());
|
|
||||||
|
|
||||||
delete [] planes[0];
|
|
||||||
delete [] planes[1];
|
|
||||||
delete [] planes[2];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace remoting
|
} // namespace remoting
|
||||||
|
@@ -34,6 +34,7 @@ enum UpdateStreamEncoding {
|
|||||||
EncodingInvalid = -1;
|
EncodingInvalid = -1;
|
||||||
EncodingNone = 0;
|
EncodingNone = 0;
|
||||||
EncodingZlib = 1;
|
EncodingZlib = 1;
|
||||||
|
EncodingVp8 = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Identifies the pixel format.
|
// Identifies the pixel format.
|
||||||
|
@@ -114,18 +114,15 @@
|
|||||||
'../gfx/gfx.gyp:gfx',
|
'../gfx/gfx.gyp:gfx',
|
||||||
'../media/media.gyp:media',
|
'../media/media.gyp:media',
|
||||||
'../third_party/protobuf2/protobuf.gyp:protobuf_lite',
|
'../third_party/protobuf2/protobuf.gyp:protobuf_lite',
|
||||||
|
'../third_party/libvpx/libvpx.gyp:libvpx_include',
|
||||||
'../third_party/zlib/zlib.gyp:zlib',
|
'../third_party/zlib/zlib.gyp:zlib',
|
||||||
'base/protocol/chromotocol.gyp:chromotocol_proto_lib',
|
'base/protocol/chromotocol.gyp:chromotocol_proto_lib',
|
||||||
'base/protocol/chromotocol.gyp:trace_proto_lib',
|
'base/protocol/chromotocol.gyp:trace_proto_lib',
|
||||||
'chromoting_jingle_glue',
|
'chromoting_jingle_glue',
|
||||||
# TODO(hclam): Enable VP8 in the build.
|
|
||||||
#'third_party/on2/on2.gyp:vp8',
|
|
||||||
],
|
],
|
||||||
'export_dependent_settings': [
|
'export_dependent_settings': [
|
||||||
'../third_party/protobuf2/protobuf.gyp:protobuf_lite',
|
'../third_party/protobuf2/protobuf.gyp:protobuf_lite',
|
||||||
'base/protocol/chromotocol.gyp:chromotocol_proto_lib',
|
'base/protocol/chromotocol.gyp:chromotocol_proto_lib',
|
||||||
# TODO(hclam): Enable VP8 in the build.
|
|
||||||
#'third_party/on2/on2.gyp:vp8',
|
|
||||||
],
|
],
|
||||||
# This target needs a hard dependency because dependent targets
|
# This target needs a hard dependency because dependent targets
|
||||||
# depend on chromotocol_proto_lib for headers.
|
# depend on chromotocol_proto_lib for headers.
|
||||||
@@ -148,11 +145,10 @@
|
|||||||
'base/encoder.h',
|
'base/encoder.h',
|
||||||
'base/encoder_verbatim.cc',
|
'base/encoder_verbatim.cc',
|
||||||
'base/encoder_verbatim.h',
|
'base/encoder_verbatim.h',
|
||||||
|
'base/encoder_vp8.cc',
|
||||||
|
'base/encoder_vp8.h',
|
||||||
'base/encoder_zlib.cc',
|
'base/encoder_zlib.cc',
|
||||||
'base/encoder_zlib.h',
|
'base/encoder_zlib.h',
|
||||||
# TODO(hclam): Enable VP8 in the build.
|
|
||||||
#'base/encoder_vp8.cc',
|
|
||||||
#'base/encoder_vp8.h',
|
|
||||||
'base/multiple_array_input_stream.cc',
|
'base/multiple_array_input_stream.cc',
|
||||||
'base/multiple_array_input_stream.h',
|
'base/multiple_array_input_stream.h',
|
||||||
'base/protocol_decoder.cc',
|
'base/protocol_decoder.cc',
|
||||||
@@ -407,8 +403,7 @@
|
|||||||
'base/decoder_zlib_unittest.cc',
|
'base/decoder_zlib_unittest.cc',
|
||||||
'base/decompressor_zlib_unittest.cc',
|
'base/decompressor_zlib_unittest.cc',
|
||||||
'base/encoder_verbatim_unittest.cc',
|
'base/encoder_verbatim_unittest.cc',
|
||||||
# TODO(hclam): Enable VP8 in the build.
|
'base/encoder_vp8_unittest.cc',
|
||||||
#'base/encoder_vp8_unittest.cc',
|
|
||||||
'base/encoder_zlib_unittest.cc',
|
'base/encoder_zlib_unittest.cc',
|
||||||
'base/mock_objects.h',
|
'base/mock_objects.h',
|
||||||
'base/multiple_array_input_stream_unittest.cc',
|
'base/multiple_array_input_stream_unittest.cc',
|
||||||
|
@@ -2,8 +2,19 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "base/file_path.h"
|
||||||
|
#include "base/logging.h"
|
||||||
|
#include "base/path_service.h"
|
||||||
#include "base/test/test_suite.h"
|
#include "base/test/test_suite.h"
|
||||||
|
#include "media/base/media.h"
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
return base::TestSuite(argc, argv).Run();
|
base::TestSuite test_suite(argc, argv);
|
||||||
|
|
||||||
|
// Load the media library so we can use libvpx.
|
||||||
|
FilePath path;
|
||||||
|
PathService::Get(base::DIR_MODULE, &path);
|
||||||
|
CHECK(media::InitializeMediaLibrary(path))
|
||||||
|
<< "Cannot load media library";
|
||||||
|
return test_suite.Run();
|
||||||
}
|
}
|
||||||
|
90
third_party/libvpx/libvpx.gyp
vendored
Normal file
90
third_party/libvpx/libvpx.gyp
vendored
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
# Copyright (c) 2010 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.
|
||||||
|
|
||||||
|
{
|
||||||
|
'targets': [
|
||||||
|
# libvpx_lib is currently not being used since we use libvpx inside
|
||||||
|
# libavcodec. Keeping this just in case we need this later.
|
||||||
|
{
|
||||||
|
'target_name': 'libvpx_lib',
|
||||||
|
'type': 'none',
|
||||||
|
'variables': {
|
||||||
|
'libvpx_lib': 'libvpx.a',
|
||||||
|
},
|
||||||
|
'conditions': [
|
||||||
|
# This section specifies the folder for looking for libvpx.a.
|
||||||
|
#
|
||||||
|
['OS=="linux" and target_arch=="ia32"', {
|
||||||
|
'variables': {
|
||||||
|
'libvpx_path': 'lib/linux/ia32',
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
['OS=="linux" and target_arch=="x64"', {
|
||||||
|
'variables': {
|
||||||
|
'libvpx_path': 'lib/linux/x64',
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
['OS=="linux" and target_arch=="arm" and arm_neon==1', {
|
||||||
|
'variables': {
|
||||||
|
'libvpx_path': 'lib/linux/arm-neon',
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
['OS=="linux" and target_arch=="arm" and arm_neon==0', {
|
||||||
|
'variables': {
|
||||||
|
'libvpx_path': 'lib/linux/arm',
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
['OS=="win"', {
|
||||||
|
'variables': {
|
||||||
|
'libvpx_path': 'lib/win/ia32',
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
['OS=="mac"', {
|
||||||
|
'variables': {
|
||||||
|
'libvpx_path': 'lib/mac/ia32',
|
||||||
|
},
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
'actions': [
|
||||||
|
{
|
||||||
|
'action_name': 'copy_lib',
|
||||||
|
'inputs': [
|
||||||
|
'<(libvpx_path)/<(libvpx_lib)',
|
||||||
|
],
|
||||||
|
'outputs': [
|
||||||
|
'<(SHARED_INTERMEDIATE_DIR)/<(libvpx_lib)',
|
||||||
|
],
|
||||||
|
'action': [
|
||||||
|
'cp',
|
||||||
|
'<(libvpx_path)/<(libvpx_lib)',
|
||||||
|
'<(SHARED_INTERMEDIATE_DIR)/<(libvpx_lib)',
|
||||||
|
],
|
||||||
|
'message': 'Copying libvpx.a into <(SHARED_INTERMEDIATE_DIR)',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'all_dependent_settings': {
|
||||||
|
'link_settings': {
|
||||||
|
'libraries': [
|
||||||
|
'<(SHARED_INTERMEDIATE_DIR)/<(libvpx_lib)',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'target_name': 'libvpx_include',
|
||||||
|
'type': 'none',
|
||||||
|
'direct_dependent_settings': {
|
||||||
|
'include_dirs': [
|
||||||
|
'include',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
# Local Variables:
|
||||||
|
# tab-width:2
|
||||||
|
# indent-tabs-mode:nil
|
||||||
|
# End:
|
||||||
|
# vim: set expandtab tabstop=2 shiftwidth=2:
|
Reference in New Issue
Block a user