| Index: remoting/base/encoder_vp8.cc
|
| diff --git a/remoting/base/encoder_vp8.cc b/remoting/base/encoder_vp8.cc
|
| index 231acd55ac88c8226fc35c48f17ed0a00410c4ae..eec6ed5d18a5883690437e468f77fdce72eb034f 100644
|
| --- a/remoting/base/encoder_vp8.cc
|
| +++ b/remoting/base/encoder_vp8.cc
|
| @@ -5,129 +5,190 @@
|
| #include "base/logging.h"
|
| #include "media/base/callback.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" {
|
| -// TODO(garykac): Rix with correct path to vp8 header.
|
| -#include "remoting/third_party/on2/include/vp8cx.h"
|
| +#define VPX_CODEC_DISABLE_COMPAT 1
|
| +#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 {
|
|
|
| EncoderVp8::EncoderVp8()
|
| : initialized_(false),
|
| + codec_(NULL),
|
| + image_(NULL),
|
| last_timestamp_(0) {
|
| }
|
|
|
| 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() {
|
| - // TODO(hclam): Now always assume we receive YV12. May need to extend this
|
| - // so we can do color space conversion manually.
|
| - image_.fmt = IMG_FMT_YV12;
|
| - image_.w = width_;
|
| - image_.h = height_;
|
| -
|
| - on2_codec_enc_cfg_t config;
|
| - on2_codec_err_t result = on2_codec_enc_config_default(&on2_codec_vp8_cx_algo,
|
| - &config, 0);
|
| -
|
| - // TODO(hclam): Adjust the parameters.
|
| - config.g_w = width_;
|
| - config.g_h = height_;
|
| - config.g_pass = ON2_RC_ONE_PASS;
|
| +bool EncoderVp8::Init(int width, int height) {
|
| + codec_.reset(new vpx_codec_ctx_t());
|
| + 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 = width;
|
| + image_->w = width;
|
| + 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_threads = 2;
|
| - config.rc_target_bitrate = 1000000;
|
| config.rc_min_quantizer = 0;
|
| config.rc_max_quantizer = 15;
|
| config.g_timebase.num = 1;
|
| 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 true;
|
| +}
|
| +
|
| +bool EncoderVp8::PrepareImage(scoped_refptr<CaptureData> capture_data) {
|
| + if (!yuv_image_.get()) {
|
| + const int plane_size = capture_data->width() * capture_data->height();
|
| +
|
| + // YUV image size is 1.5 times of a plane. Multiplication is performed first
|
| + // to avoid rounding error.
|
| + const int size = plane_size * 3 / 2;
|
| +
|
| + yuv_image_.reset(new uint8[size]);
|
| +
|
| + // Reset image value to 128 so we just need to fill in the y plane.
|
| + 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;
|
|
|
| - on2_codec_control_(&codec_, VP8E_SET_CPUUSED, -15);
|
| + // 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;
|
| + }
|
| +
|
| + // And then do RGB->YUV conversion.
|
| + // Currently we just produce the Y channel as the average of RGB. This will
|
| + // 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;
|
| }
|
|
|
| -void EncoderVp8::Encode(const DirtyRects& dirty_rects,
|
| - const uint8** input_data,
|
| - const int* strides,
|
| +void EncoderVp8::Encode(scoped_refptr<CaptureData> capture_data,
|
| 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
|
| - // allow encoder be initialized with difference sizes.
|
| + DataAvailableCallback* data_available_callback) {
|
| if (!initialized_) {
|
| - if (!Init()) {
|
| - LOG(ERROR) << "Can't initialize VP8 encoder";
|
| - return;
|
| - }
|
| - initialized_ = true;
|
| + bool ret = Init(capture_data->width(), capture_data->height());
|
| + // TODO(hclam): Handle error better.
|
| + DCHECK(ret) << "Initialization of encoder failed";
|
| + initialized_ = ret;
|
| }
|
|
|
| - // Assume the capturer has done the color space conversion.
|
| - if (!input_data || !strides)
|
| - return;
|
| -
|
| - image_.planes[0] = (unsigned char*)input_data[0];
|
| - image_.planes[1] = (unsigned char*)input_data[1];
|
| - image_.planes[2] = (unsigned char*)input_data[2];
|
| - image_.stride[0] = strides[0];
|
| - image_.stride[1] = strides[1];
|
| - image_.stride[2] = strides[2];
|
| + if (!PrepareImage(capture_data)) {
|
| + NOTREACHED() << "Can't image data for encoding";
|
| + }
|
|
|
| // Do the actual encoding.
|
| - if (on2_codec_encode(&codec_, &image_,
|
| - last_timestamp_, 1, 0, ON2_DL_REALTIME)) {
|
| - return;
|
| - }
|
| + vpx_codec_err_t ret = vpx_codec_encode(codec_.get(), image_.get(),
|
| + last_timestamp_,
|
| + 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.
|
| last_timestamp_ += 100;
|
|
|
| // Read the encoded data.
|
| - on2_codec_iter_t iter = NULL;
|
| + vpx_codec_iter_t iter = NULL;
|
| bool got_data = false;
|
|
|
| - // TODO(hclam: We assume one frame of input will get exactly one frame of
|
| - // output. This assumption may not be valid.
|
| + // TODO(hclam): Make sure we get exactly one frame from the packet.
|
| + // 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) {
|
| - 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)
|
| continue;
|
|
|
| switch (packet->kind) {
|
| - case ON2_CODEC_CX_FRAME_PKT:
|
| + case VPX_CODEC_CX_FRAME_PKT:
|
| got_data = true;
|
| - *encode_done = true;
|
| - *output_data = new media::DataBuffer(packet->data.frame.sz);
|
| - memcpy((*output_data)->GetWritableData(),
|
| - packet->data.frame.buf,
|
| - packet->data.frame.sz);
|
| + message->mutable_update_stream_packet()->mutable_rect_data()->set_data(
|
| + packet->data.frame.buf, packet->data.frame.sz);
|
| break;
|
| default:
|
| break;
|
| }
|
| }
|
| - return;
|
| -}
|
| -
|
| -void EncoderVp8::SetSize(int width, int height) {
|
| - width_ = width;
|
| - height_ = height;
|
| -}
|
|
|
| -void EncoderVp8::SetPixelFormat(PixelFormat pixel_format) {
|
| - pixel_format_ = pixel_format;
|
| + // Enter the end rect.
|
| + message->mutable_update_stream_packet()->mutable_end_rect();
|
| + data_available_callback->Run(
|
| + message,
|
| + EncodingStarting | EncodingInProgress | EncodingEnded);
|
| + delete data_available_callback;
|
| }
|
|
|
| } // namespace remoting
|
|
|