Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(75)

Side by Side Diff: remoting/base/encoder_vp8.cc

Issue 3005036: Implement VP8 encoder for chromoting (Closed) Base URL: http://src.chromium.org/git/chromium.git
Patch Set: just uploading Created 10 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « remoting/base/encoder_vp8.h ('k') | remoting/base/encoder_vp8_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 // 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/logging.h" 5 #include "base/logging.h"
6 #include "media/base/callback.h" 6 #include "media/base/callback.h"
7 #include "media/base/data_buffer.h" 7 #include "media/base/data_buffer.h"
8 #include "remoting/host/encoder_vp8.h" 8 #include "media/base/media.h"
9 #include "remoting/base/capture_data.h"
10 #include "remoting/base/encoder_vp8.h"
9 11
10 extern "C" { 12 extern "C" {
11 // TODO(garykac): Rix with correct path to vp8 header. 13 #define VPX_CODEC_DISABLE_COMPAT 1
12 #include "remoting/third_party/on2/include/vp8cx.h" 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"
13 } 17 }
14 18
15 namespace remoting { 19 namespace remoting {
16 20
17 EncoderVp8::EncoderVp8() 21 EncoderVp8::EncoderVp8()
18 : initialized_(false), 22 : initialized_(false),
23 codec_(NULL),
24 image_(NULL),
19 last_timestamp_(0) { 25 last_timestamp_(0) {
20 } 26 }
21 27
22 EncoderVp8::~EncoderVp8() { 28 EncoderVp8::~EncoderVp8() {
29 if (initialized_) {
30 vpx_codec_err_t ret = vpx_codec_destroy(codec_.get());
31 DCHECK(ret == VPX_CODEC_OK) << "Failed to destroy codec";
32 }
23 } 33 }
24 34
25 bool EncoderVp8::Init() { 35 bool EncoderVp8::Init(int width, int height) {
26 // TODO(hclam): Now always assume we receive YV12. May need to extend this 36 codec_.reset(new vpx_codec_ctx_t());
27 // so we can do color space conversion manually. 37 image_.reset(new vpx_image_t());
28 image_.fmt = IMG_FMT_YV12; 38 memset(image_.get(), 0, sizeof(vpx_image_t));
29 image_.w = width_;
30 image_.h = height_;
31 39
32 on2_codec_enc_cfg_t config; 40 image_->fmt = VPX_IMG_FMT_YV12;
33 on2_codec_err_t result = on2_codec_enc_config_default(&on2_codec_vp8_cx_algo,
34 &config, 0);
35 41
36 // TODO(hclam): Adjust the parameters. 42 // libvpx seems to require both to be assigned.
37 config.g_w = width_; 43 image_->d_w = width;
38 config.g_h = height_; 44 image_->w = width;
39 config.g_pass = ON2_RC_ONE_PASS; 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();
51 vpx_codec_err_t ret = vpx_codec_enc_config_default(algo, &config, 0);
52 if (ret != VPX_CODEC_OK)
53 return false;
54
55 // TODO(hclam): Tune the parameters to better suit the application.
56 config.rc_target_bitrate = width * height * config.rc_target_bitrate
57 / config.g_w / config.g_h;
58 config.g_w = width;
59 config.g_h = height;
60 config.g_pass = VPX_RC_ONE_PASS;
40 config.g_profile = 1; 61 config.g_profile = 1;
41 config.g_threads = 2; 62 config.g_threads = 2;
42 config.rc_target_bitrate = 1000000;
43 config.rc_min_quantizer = 0; 63 config.rc_min_quantizer = 0;
44 config.rc_max_quantizer = 15; 64 config.rc_max_quantizer = 15;
45 config.g_timebase.num = 1; 65 config.g_timebase.num = 1;
46 config.g_timebase.den = 30; 66 config.g_timebase.den = 30;
47 67
48 if (on2_codec_enc_init(&codec_, &on2_codec_vp8_cx_algo, &config, 0)) 68 if (vpx_codec_enc_init(codec_.get(), algo, &config, 0))
49 return false; 69 return false;
50
51 on2_codec_control_(&codec_, VP8E_SET_CPUUSED, -15);
52 return true; 70 return true;
53 } 71 }
54 72
55 void EncoderVp8::Encode(const DirtyRects& dirty_rects, 73 bool EncoderVp8::PrepareImage(scoped_refptr<CaptureData> capture_data) {
56 const uint8** input_data, 74 if (!yuv_image_.get()) {
57 const int* strides, 75 const int plane_size = capture_data->width() * capture_data->height();
58 bool key_frame,
59 UpdateStreamPacketHeader* header,
60 scoped_refptr<media::DataBuffer>* output_data,
61 bool* encode_done,
62 Task* data_available_task) {
63 // This will allow the task be called when this method exits.
64 media::AutoTaskRunner task(data_available_task);
65 *encode_done = false;
66 76
67 // TODO(hclam): We only initialize the encoder once. We may have to 77 // YUV image size is 1.5 times of a plane. Multiplication is performed first
68 // allow encoder be initialized with difference sizes. 78 // to avoid rounding error.
69 if (!initialized_) { 79 const int size = plane_size * 3 / 2;
70 if (!Init()) { 80
71 LOG(ERROR) << "Can't initialize VP8 encoder"; 81 yuv_image_.reset(new uint8[size]);
72 return; 82
73 } 83 // Reset image value to 128 so we just need to fill in the y plane.
74 initialized_ = true; 84 memset(yuv_image_.get(), 128, size);
85
86 // Fill in the information for |image_|.
87 unsigned char* image = reinterpret_cast<unsigned char*>(yuv_image_.get());
88 image_->planes[0] = image;
89 image_->planes[1] = image + plane_size;
90
91 // The V plane starts from 1.25 of the plane size.
92 image_->planes[2] = image + plane_size + plane_size / 4;
93
94 // In YV12 Y plane has full width, UV plane has half width because of
95 // subsampling.
96 image_->stride[0] = image_->w;
97 image_->stride[1] = image_->w / 2;
98 image_->stride[2] = image_->w / 2;
75 } 99 }
76 100
77 // Assume the capturer has done the color space conversion. 101 // And then do RGB->YUV conversion.
78 if (!input_data || !strides) 102 // Currently we just produce the Y channel as the average of RGB. This will
79 return; 103 // give a gray scale image after conversion.
104 // TODO(hclam): Implement the actual color space conversion.
105 DCHECK(capture_data->pixel_format() == PixelFormatRgb32)
106 << "Only RGB32 is supported";
107 uint8* in = capture_data->data_planes().data[0];
108 const int in_stride = capture_data->data_planes().strides[0];
109 uint8* out = yuv_image_.get();
110 const int out_stride = image_->stride[0];
111 for (int i = 0; i < capture_data->height(); ++i) {
112 for (int j = 0; j < capture_data->width(); ++j) {
113 // Since the input pixel format is RGB32, there are 4 bytes per pixel.
114 uint8* pixel = in + 4 * j;
115 out[j] = (pixel[0] + pixel[1] + pixel[2]) / 3;
116 }
117 in += in_stride;
118 out += out_stride;
119 }
120 return true;
121 }
80 122
81 image_.planes[0] = (unsigned char*)input_data[0]; 123 void EncoderVp8::Encode(scoped_refptr<CaptureData> capture_data,
82 image_.planes[1] = (unsigned char*)input_data[1]; 124 bool key_frame,
83 image_.planes[2] = (unsigned char*)input_data[2]; 125 DataAvailableCallback* data_available_callback) {
84 image_.stride[0] = strides[0]; 126 if (!initialized_) {
85 image_.stride[1] = strides[1]; 127 bool ret = Init(capture_data->width(), capture_data->height());
86 image_.stride[2] = strides[2]; 128 // TODO(hclam): Handle error better.
129 DCHECK(ret) << "Initialization of encoder failed";
130 initialized_ = ret;
131 }
132
133 if (!PrepareImage(capture_data)) {
134 NOTREACHED() << "Can't image data for encoding";
135 }
87 136
88 // Do the actual encoding. 137 // Do the actual encoding.
89 if (on2_codec_encode(&codec_, &image_, 138 vpx_codec_err_t ret = vpx_codec_encode(codec_.get(), image_.get(),
90 last_timestamp_, 1, 0, ON2_DL_REALTIME)) { 139 last_timestamp_,
91 return; 140 1, 0, VPX_DL_REALTIME);
92 } 141 DCHECK(ret == VPX_CODEC_OK) << "Encoding error: "
142 << vpx_codec_err_to_string(ret)
143 << "\n"
144 << "Details: "
145 << vpx_codec_error(codec_.get())
146 << "\n"
147 << vpx_codec_error_detail(codec_.get());
93 148
94 // TODO(hclam): fix this. 149 // TODO(hclam): fix this.
95 last_timestamp_ += 100; 150 last_timestamp_ += 100;
96 151
97 // Read the encoded data. 152 // Read the encoded data.
98 on2_codec_iter_t iter = NULL; 153 vpx_codec_iter_t iter = NULL;
99 bool got_data = false; 154 bool got_data = false;
100 155
101 // TODO(hclam: We assume one frame of input will get exactly one frame of 156 // TODO(hclam): Make sure we get exactly one frame from the packet.
102 // output. This assumption may not be valid. 157 // TODO(hclam): We should provide the output buffer to avoid one copy.
158 ChromotingHostMessage* message = new ChromotingHostMessage();
159 UpdateStreamPacketMessage* packet = message->mutable_update_stream_packet();
160
161 // Prepare the begin rect.
162 packet->mutable_begin_rect()->set_x(0);
163 packet->mutable_begin_rect()->set_y(0);
164 packet->mutable_begin_rect()->set_width(capture_data->width());
165 packet->mutable_begin_rect()->set_height(capture_data->height());
166 packet->mutable_begin_rect()->set_encoding(EncodingVp8);
167 packet->mutable_begin_rect()->set_pixel_format(PixelFormatYv12);
168
103 while (!got_data) { 169 while (!got_data) {
104 on2_codec_cx_pkt_t* packet = on2_codec_get_cx_data(&codec_, &iter); 170 const vpx_codec_cx_pkt_t* packet = vpx_codec_get_cx_data(codec_.get(),
171 &iter);
105 if (!packet) 172 if (!packet)
106 continue; 173 continue;
107 174
108 switch (packet->kind) { 175 switch (packet->kind) {
109 case ON2_CODEC_CX_FRAME_PKT: 176 case VPX_CODEC_CX_FRAME_PKT:
110 got_data = true; 177 got_data = true;
111 *encode_done = true; 178 message->mutable_update_stream_packet()->mutable_rect_data()->set_data(
112 *output_data = new media::DataBuffer(packet->data.frame.sz); 179 packet->data.frame.buf, packet->data.frame.sz);
113 memcpy((*output_data)->GetWritableData(),
114 packet->data.frame.buf,
115 packet->data.frame.sz);
116 break; 180 break;
117 default: 181 default:
118 break; 182 break;
119 } 183 }
120 } 184 }
121 return;
122 }
123 185
124 void EncoderVp8::SetSize(int width, int height) { 186 // Enter the end rect.
125 width_ = width; 187 message->mutable_update_stream_packet()->mutable_end_rect();
126 height_ = height; 188 data_available_callback->Run(
127 } 189 message,
128 190 EncodingStarting | EncodingInProgress | EncodingEnded);
129 void EncoderVp8::SetPixelFormat(PixelFormat pixel_format) { 191 delete data_available_callback;
130 pixel_format_ = pixel_format;
131 } 192 }
132 193
133 } // namespace remoting 194 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/base/encoder_vp8.h ('k') | remoting/base/encoder_vp8_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698