blob: 7a58f7b2aeb9027534b1bf3e136989eb0b3fb5d4 [file] [log] [blame]
[email protected]cb3b1f9312010-06-07 19:58:231// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/logging.h"
6#include "media/base/callback.h"
7#include "media/base/data_buffer.h"
[email protected]df10bf12010-09-28 22:19:488#include "media/base/media.h"
9#include "remoting/base/capture_data.h"
10#include "remoting/base/encoder_vp8.h"
[email protected]cb3b1f9312010-06-07 19:58:2311
12extern "C" {
[email protected]df10bf12010-09-28 22:19:4813#define VPX_CODEC_DISABLE_COMPAT 1
14#include "third_party/libvpx/include/vpx/vpx_codec.h"
15#include "third_party/libvpx/include/vpx/vpx_encoder.h"
16#include "third_party/libvpx/include/vpx/vp8cx.h"
[email protected]cb3b1f9312010-06-07 19:58:2317}
18
19namespace remoting {
20
21EncoderVp8::EncoderVp8()
22 : initialized_(false),
[email protected]df10bf12010-09-28 22:19:4823 codec_(NULL),
24 image_(NULL),
[email protected]cb3b1f9312010-06-07 19:58:2325 last_timestamp_(0) {
26}
27
28EncoderVp8::~EncoderVp8() {
[email protected]df10bf12010-09-28 22:19:4829 if (initialized_) {
30 vpx_codec_err_t ret = vpx_codec_destroy(codec_.get());
31 DCHECK(ret == VPX_CODEC_OK) << "Failed to destroy codec";
32 }
[email protected]cb3b1f9312010-06-07 19:58:2333}
34
[email protected]df10bf12010-09-28 22:19:4835bool EncoderVp8::Init(int width, int height) {
36 codec_.reset(new vpx_codec_ctx_t());
37 image_.reset(new vpx_image_t());
38 memset(image_.get(), 0, sizeof(vpx_image_t));
[email protected]cb3b1f9312010-06-07 19:58:2339
[email protected]df10bf12010-09-28 22:19:4840 image_->fmt = VPX_IMG_FMT_YV12;
[email protected]cb3b1f9312010-06-07 19:58:2341
[email protected]df10bf12010-09-28 22:19:4842 // libvpx seems to require both to be assigned.
43 image_->d_w = width;
44 image_->w = width;
45 image_->d_h = height;
46 image_->h = height;
47
48 vpx_codec_enc_cfg_t config;
49 const vpx_codec_iface_t* algo =
50 (const vpx_codec_iface_t*)media::GetVp8CxAlgoAddress();
[email protected]04b36142010-11-02 01:08:1951 CHECK(algo);
[email protected]df10bf12010-09-28 22:19:4852 vpx_codec_err_t ret = vpx_codec_enc_config_default(algo, &config, 0);
53 if (ret != VPX_CODEC_OK)
54 return false;
55
56 // TODO(hclam): Tune the parameters to better suit the application.
57 config.rc_target_bitrate = width * height * config.rc_target_bitrate
58 / config.g_w / config.g_h;
59 config.g_w = width;
60 config.g_h = height;
61 config.g_pass = VPX_RC_ONE_PASS;
[email protected]cb3b1f9312010-06-07 19:58:2362 config.g_profile = 1;
63 config.g_threads = 2;
[email protected]cb3b1f9312010-06-07 19:58:2364 config.rc_min_quantizer = 0;
65 config.rc_max_quantizer = 15;
66 config.g_timebase.num = 1;
67 config.g_timebase.den = 30;
68
[email protected]df10bf12010-09-28 22:19:4869 if (vpx_codec_enc_init(codec_.get(), algo, &config, 0))
[email protected]cb3b1f9312010-06-07 19:58:2370 return false;
[email protected]cb3b1f9312010-06-07 19:58:2371 return true;
72}
73
[email protected]04b36142010-11-02 01:08:1974static int clip_byte(int x) {
75 if (x > 255)
76 return 255;
77 else if (x < 0)
78 return 0;
79 else
80 return x;
81}
82
[email protected]df10bf12010-09-28 22:19:4883bool EncoderVp8::PrepareImage(scoped_refptr<CaptureData> capture_data) {
[email protected]04b36142010-11-02 01:08:1984 const int plane_size = capture_data->width() * capture_data->height();
85
[email protected]df10bf12010-09-28 22:19:4886 if (!yuv_image_.get()) {
[email protected]cb3b1f9312010-06-07 19:58:2387
[email protected]df10bf12010-09-28 22:19:4888 // YUV image size is 1.5 times of a plane. Multiplication is performed first
89 // to avoid rounding error.
90 const int size = plane_size * 3 / 2;
91
92 yuv_image_.reset(new uint8[size]);
93
94 // Reset image value to 128 so we just need to fill in the y plane.
95 memset(yuv_image_.get(), 128, size);
96
97 // Fill in the information for |image_|.
98 unsigned char* image = reinterpret_cast<unsigned char*>(yuv_image_.get());
99 image_->planes[0] = image;
100 image_->planes[1] = image + plane_size;
101
102 // The V plane starts from 1.25 of the plane size.
103 image_->planes[2] = image + plane_size + plane_size / 4;
104
105 // In YV12 Y plane has full width, UV plane has half width because of
106 // subsampling.
107 image_->stride[0] = image_->w;
108 image_->stride[1] = image_->w / 2;
109 image_->stride[2] = image_->w / 2;
[email protected]cb3b1f9312010-06-07 19:58:23110 }
111
[email protected]df10bf12010-09-28 22:19:48112 // And then do RGB->YUV conversion.
113 // Currently we just produce the Y channel as the average of RGB. This will
[email protected]04b36142010-11-02 01:08:19114 // giv ae gray scale image after conversion.
115 // TODO(sergeyu): Move this code to a separate routine.
116 // TODO(sergeyu): Optimize this code.
117 DCHECK(capture_data->pixel_format() == PIXEL_FORMAT_RGB32)
[email protected]df10bf12010-09-28 22:19:48118 << "Only RGB32 is supported";
119 uint8* in = capture_data->data_planes().data[0];
120 const int in_stride = capture_data->data_planes().strides[0];
[email protected]04b36142010-11-02 01:08:19121 uint8* y_out = yuv_image_.get();
122 uint8* u_out = yuv_image_.get() + plane_size;
123 uint8* v_out = yuv_image_.get() + plane_size + plane_size / 4;
[email protected]df10bf12010-09-28 22:19:48124 const int out_stride = image_->stride[0];
125 for (int i = 0; i < capture_data->height(); ++i) {
126 for (int j = 0; j < capture_data->width(); ++j) {
127 // Since the input pixel format is RGB32, there are 4 bytes per pixel.
128 uint8* pixel = in + 4 * j;
[email protected]04b36142010-11-02 01:08:19129 y_out[j] = clip_byte(((pixel[0] * 66 + pixel[1] * 129 +
130 pixel[2] * 25 + 128) >> 8) + 16);
131 if (i % 2 == 0 && j % 2 == 0) {
132 u_out[j / 2] = clip_byte(((pixel[0] * -38 + pixel[1] * -74 +
133 pixel[2] * 112 + 128) >> 8) + 128);
134 v_out[j / 2] = clip_byte(((pixel[0] * 112 + pixel[1] * -94 +
135 pixel[2] * -18 + 128) >> 8) + 128);
136 }
[email protected]df10bf12010-09-28 22:19:48137 }
138 in += in_stride;
[email protected]04b36142010-11-02 01:08:19139 y_out += out_stride;
140 if (i % 2 == 0) {
141 u_out += out_stride / 2;
142 v_out += out_stride / 2;
143 }
[email protected]df10bf12010-09-28 22:19:48144 }
145 return true;
146}
[email protected]cb3b1f9312010-06-07 19:58:23147
[email protected]df10bf12010-09-28 22:19:48148void EncoderVp8::Encode(scoped_refptr<CaptureData> capture_data,
149 bool key_frame,
150 DataAvailableCallback* data_available_callback) {
151 if (!initialized_) {
152 bool ret = Init(capture_data->width(), capture_data->height());
153 // TODO(hclam): Handle error better.
154 DCHECK(ret) << "Initialization of encoder failed";
155 initialized_ = ret;
156 }
157
158 if (!PrepareImage(capture_data)) {
159 NOTREACHED() << "Can't image data for encoding";
160 }
[email protected]cb3b1f9312010-06-07 19:58:23161
162 // Do the actual encoding.
[email protected]df10bf12010-09-28 22:19:48163 vpx_codec_err_t ret = vpx_codec_encode(codec_.get(), image_.get(),
164 last_timestamp_,
165 1, 0, VPX_DL_REALTIME);
[email protected]04b36142010-11-02 01:08:19166 DCHECK_EQ(ret, VPX_CODEC_OK)
167 << "Encoding error: " << vpx_codec_err_to_string(ret) << "\n"
168 << "Details: " << vpx_codec_error(codec_.get()) << "\n"
169 << vpx_codec_error_detail(codec_.get());
[email protected]cb3b1f9312010-06-07 19:58:23170
171 // TODO(hclam): fix this.
172 last_timestamp_ += 100;
173
174 // Read the encoded data.
[email protected]df10bf12010-09-28 22:19:48175 vpx_codec_iter_t iter = NULL;
[email protected]cb3b1f9312010-06-07 19:58:23176 bool got_data = false;
177
[email protected]df10bf12010-09-28 22:19:48178 // TODO(hclam): Make sure we get exactly one frame from the packet.
179 // TODO(hclam): We should provide the output buffer to avoid one copy.
[email protected]04b36142010-11-02 01:08:19180 VideoPacket* message = new VideoPacket();
[email protected]df10bf12010-09-28 22:19:48181
[email protected]cb3b1f9312010-06-07 19:58:23182 while (!got_data) {
[email protected]df10bf12010-09-28 22:19:48183 const vpx_codec_cx_pkt_t* packet = vpx_codec_get_cx_data(codec_.get(),
184 &iter);
[email protected]cb3b1f9312010-06-07 19:58:23185 if (!packet)
186 continue;
187
188 switch (packet->kind) {
[email protected]df10bf12010-09-28 22:19:48189 case VPX_CODEC_CX_FRAME_PKT:
[email protected]cb3b1f9312010-06-07 19:58:23190 got_data = true;
[email protected]04b36142010-11-02 01:08:19191 message->set_data(
[email protected]df10bf12010-09-28 22:19:48192 packet->data.frame.buf, packet->data.frame.sz);
[email protected]cb3b1f9312010-06-07 19:58:23193 break;
194 default:
195 break;
196 }
197 }
[email protected]cb3b1f9312010-06-07 19:58:23198
[email protected]04b36142010-11-02 01:08:19199 message->mutable_format()->set_encoding(VideoPacketFormat::ENCODING_VP8);
200 message->set_flags(VideoPacket::FIRST_PACKET | VideoPacket::LAST_PACKET);
201 message->mutable_format()->set_pixel_format(PIXEL_FORMAT_RGB32);
202 message->mutable_format()->set_x(0);
203 message->mutable_format()->set_y(0);
204 message->mutable_format()->set_width(capture_data->width());
205 message->mutable_format()->set_height(capture_data->height());
206
207 data_available_callback->Run(message);
[email protected]df10bf12010-09-28 22:19:48208 delete data_available_callback;
[email protected]cb3b1f9312010-06-07 19:58:23209}
210
211} // namespace remoting