OLD | NEW |
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 |
OLD | NEW |