| 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 "media/base/media.h" | 8 #include "media/base/media.h" |
| 9 #include "remoting/base/capture_data.h" | 9 #include "remoting/base/capture_data.h" |
| 10 #include "remoting/base/encoder_vp8.h" | 10 #include "remoting/base/encoder_vp8.h" |
| (...skipping 30 matching lines...) Expand all Loading... |
| 41 | 41 |
| 42 // libvpx seems to require both to be assigned. | 42 // libvpx seems to require both to be assigned. |
| 43 image_->d_w = width; | 43 image_->d_w = width; |
| 44 image_->w = width; | 44 image_->w = width; |
| 45 image_->d_h = height; | 45 image_->d_h = height; |
| 46 image_->h = height; | 46 image_->h = height; |
| 47 | 47 |
| 48 vpx_codec_enc_cfg_t config; | 48 vpx_codec_enc_cfg_t config; |
| 49 const vpx_codec_iface_t* algo = | 49 const vpx_codec_iface_t* algo = |
| 50 (const vpx_codec_iface_t*)media::GetVp8CxAlgoAddress(); | 50 (const vpx_codec_iface_t*)media::GetVp8CxAlgoAddress(); |
| 51 CHECK(algo); | |
| 52 vpx_codec_err_t ret = vpx_codec_enc_config_default(algo, &config, 0); | 51 vpx_codec_err_t ret = vpx_codec_enc_config_default(algo, &config, 0); |
| 53 if (ret != VPX_CODEC_OK) | 52 if (ret != VPX_CODEC_OK) |
| 54 return false; | 53 return false; |
| 55 | 54 |
| 56 // TODO(hclam): Tune the parameters to better suit the application. | 55 // TODO(hclam): Tune the parameters to better suit the application. |
| 57 config.rc_target_bitrate = width * height * config.rc_target_bitrate | 56 config.rc_target_bitrate = width * height * config.rc_target_bitrate |
| 58 / config.g_w / config.g_h; | 57 / config.g_w / config.g_h; |
| 59 config.g_w = width; | 58 config.g_w = width; |
| 60 config.g_h = height; | 59 config.g_h = height; |
| 61 config.g_pass = VPX_RC_ONE_PASS; | 60 config.g_pass = VPX_RC_ONE_PASS; |
| 62 config.g_profile = 1; | 61 config.g_profile = 1; |
| 63 config.g_threads = 2; | 62 config.g_threads = 2; |
| 64 config.rc_min_quantizer = 0; | 63 config.rc_min_quantizer = 0; |
| 65 config.rc_max_quantizer = 15; | 64 config.rc_max_quantizer = 15; |
| 66 config.g_timebase.num = 1; | 65 config.g_timebase.num = 1; |
| 67 config.g_timebase.den = 30; | 66 config.g_timebase.den = 30; |
| 68 | 67 |
| 69 if (vpx_codec_enc_init(codec_.get(), algo, &config, 0)) | 68 if (vpx_codec_enc_init(codec_.get(), algo, &config, 0)) |
| 70 return false; | 69 return false; |
| 71 return true; | 70 return true; |
| 72 } | 71 } |
| 73 | 72 |
| 74 static 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 | |
| 83 bool EncoderVp8::PrepareImage(scoped_refptr<CaptureData> capture_data) { | 73 bool EncoderVp8::PrepareImage(scoped_refptr<CaptureData> capture_data) { |
| 84 const int plane_size = capture_data->width() * capture_data->height(); | |
| 85 | |
| 86 if (!yuv_image_.get()) { | 74 if (!yuv_image_.get()) { |
| 75 const int plane_size = capture_data->width() * capture_data->height(); |
| 87 | 76 |
| 88 // YUV image size is 1.5 times of a plane. Multiplication is performed first | 77 // YUV image size is 1.5 times of a plane. Multiplication is performed first |
| 89 // to avoid rounding error. | 78 // to avoid rounding error. |
| 90 const int size = plane_size * 3 / 2; | 79 const int size = plane_size * 3 / 2; |
| 91 | 80 |
| 92 yuv_image_.reset(new uint8[size]); | 81 yuv_image_.reset(new uint8[size]); |
| 93 | 82 |
| 94 // Reset image value to 128 so we just need to fill in the y plane. | 83 // Reset image value to 128 so we just need to fill in the y plane. |
| 95 memset(yuv_image_.get(), 128, size); | 84 memset(yuv_image_.get(), 128, size); |
| 96 | 85 |
| 97 // Fill in the information for |image_|. | 86 // Fill in the information for |image_|. |
| 98 unsigned char* image = reinterpret_cast<unsigned char*>(yuv_image_.get()); | 87 unsigned char* image = reinterpret_cast<unsigned char*>(yuv_image_.get()); |
| 99 image_->planes[0] = image; | 88 image_->planes[0] = image; |
| 100 image_->planes[1] = image + plane_size; | 89 image_->planes[1] = image + plane_size; |
| 101 | 90 |
| 102 // The V plane starts from 1.25 of the plane size. | 91 // The V plane starts from 1.25 of the plane size. |
| 103 image_->planes[2] = image + plane_size + plane_size / 4; | 92 image_->planes[2] = image + plane_size + plane_size / 4; |
| 104 | 93 |
| 105 // In YV12 Y plane has full width, UV plane has half width because of | 94 // In YV12 Y plane has full width, UV plane has half width because of |
| 106 // subsampling. | 95 // subsampling. |
| 107 image_->stride[0] = image_->w; | 96 image_->stride[0] = image_->w; |
| 108 image_->stride[1] = image_->w / 2; | 97 image_->stride[1] = image_->w / 2; |
| 109 image_->stride[2] = image_->w / 2; | 98 image_->stride[2] = image_->w / 2; |
| 110 } | 99 } |
| 111 | 100 |
| 112 // And then do RGB->YUV conversion. | 101 // And then do RGB->YUV conversion. |
| 113 // Currently we just produce the Y channel as the average of RGB. This will | 102 // Currently we just produce the Y channel as the average of RGB. This will |
| 114 // giv ae gray scale image after conversion. | 103 // give a gray scale image after conversion. |
| 115 // TODO(sergeyu): Move this code to a separate routine. | 104 // TODO(hclam): Implement the actual color space conversion. |
| 116 // TODO(sergeyu): Optimize this code. | 105 DCHECK(capture_data->pixel_format() == PixelFormatRgb32) |
| 117 DCHECK(capture_data->pixel_format() == PIXEL_FORMAT_RGB32) | |
| 118 << "Only RGB32 is supported"; | 106 << "Only RGB32 is supported"; |
| 119 uint8* in = capture_data->data_planes().data[0]; | 107 uint8* in = capture_data->data_planes().data[0]; |
| 120 const int in_stride = capture_data->data_planes().strides[0]; | 108 const int in_stride = capture_data->data_planes().strides[0]; |
| 121 uint8* y_out = yuv_image_.get(); | 109 uint8* 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; | |
| 124 const int out_stride = image_->stride[0]; | 110 const int out_stride = image_->stride[0]; |
| 125 for (int i = 0; i < capture_data->height(); ++i) { | 111 for (int i = 0; i < capture_data->height(); ++i) { |
| 126 for (int j = 0; j < capture_data->width(); ++j) { | 112 for (int j = 0; j < capture_data->width(); ++j) { |
| 127 // Since the input pixel format is RGB32, there are 4 bytes per pixel. | 113 // Since the input pixel format is RGB32, there are 4 bytes per pixel. |
| 128 uint8* pixel = in + 4 * j; | 114 uint8* pixel = in + 4 * j; |
| 129 y_out[j] = clip_byte(((pixel[0] * 66 + pixel[1] * 129 + | 115 out[j] = (pixel[0] + pixel[1] + pixel[2]) / 3; |
| 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 } | |
| 137 } | 116 } |
| 138 in += in_stride; | 117 in += in_stride; |
| 139 y_out += out_stride; | 118 out += out_stride; |
| 140 if (i % 2 == 0) { | |
| 141 u_out += out_stride / 2; | |
| 142 v_out += out_stride / 2; | |
| 143 } | |
| 144 } | 119 } |
| 145 return true; | 120 return true; |
| 146 } | 121 } |
| 147 | 122 |
| 148 void EncoderVp8::Encode(scoped_refptr<CaptureData> capture_data, | 123 void EncoderVp8::Encode(scoped_refptr<CaptureData> capture_data, |
| 149 bool key_frame, | 124 bool key_frame, |
| 150 DataAvailableCallback* data_available_callback) { | 125 DataAvailableCallback* data_available_callback) { |
| 151 if (!initialized_) { | 126 if (!initialized_) { |
| 152 bool ret = Init(capture_data->width(), capture_data->height()); | 127 bool ret = Init(capture_data->width(), capture_data->height()); |
| 153 // TODO(hclam): Handle error better. | 128 // TODO(hclam): Handle error better. |
| 154 DCHECK(ret) << "Initialization of encoder failed"; | 129 DCHECK(ret) << "Initialization of encoder failed"; |
| 155 initialized_ = ret; | 130 initialized_ = ret; |
| 156 } | 131 } |
| 157 | 132 |
| 158 if (!PrepareImage(capture_data)) { | 133 if (!PrepareImage(capture_data)) { |
| 159 NOTREACHED() << "Can't image data for encoding"; | 134 NOTREACHED() << "Can't image data for encoding"; |
| 160 } | 135 } |
| 161 | 136 |
| 162 // Do the actual encoding. | 137 // Do the actual encoding. |
| 163 vpx_codec_err_t ret = vpx_codec_encode(codec_.get(), image_.get(), | 138 vpx_codec_err_t ret = vpx_codec_encode(codec_.get(), image_.get(), |
| 164 last_timestamp_, | 139 last_timestamp_, |
| 165 1, 0, VPX_DL_REALTIME); | 140 1, 0, VPX_DL_REALTIME); |
| 166 DCHECK_EQ(ret, VPX_CODEC_OK) | 141 DCHECK(ret == VPX_CODEC_OK) << "Encoding error: " |
| 167 << "Encoding error: " << vpx_codec_err_to_string(ret) << "\n" | 142 << vpx_codec_err_to_string(ret) |
| 168 << "Details: " << vpx_codec_error(codec_.get()) << "\n" | 143 << "\n" |
| 169 << vpx_codec_error_detail(codec_.get()); | 144 << "Details: " |
| 145 << vpx_codec_error(codec_.get()) |
| 146 << "\n" |
| 147 << vpx_codec_error_detail(codec_.get()); |
| 170 | 148 |
| 171 // TODO(hclam): fix this. | 149 // TODO(hclam): fix this. |
| 172 last_timestamp_ += 100; | 150 last_timestamp_ += 100; |
| 173 | 151 |
| 174 // Read the encoded data. | 152 // Read the encoded data. |
| 175 vpx_codec_iter_t iter = NULL; | 153 vpx_codec_iter_t iter = NULL; |
| 176 bool got_data = false; | 154 bool got_data = false; |
| 177 | 155 |
| 178 // TODO(hclam): Make sure we get exactly one frame from the packet. | 156 // 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. | 157 // TODO(hclam): We should provide the output buffer to avoid one copy. |
| 180 VideoPacket* message = new VideoPacket(); | 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); |
| 181 | 168 |
| 182 while (!got_data) { | 169 while (!got_data) { |
| 183 const vpx_codec_cx_pkt_t* packet = vpx_codec_get_cx_data(codec_.get(), | 170 const vpx_codec_cx_pkt_t* packet = vpx_codec_get_cx_data(codec_.get(), |
| 184 &iter); | 171 &iter); |
| 185 if (!packet) | 172 if (!packet) |
| 186 continue; | 173 continue; |
| 187 | 174 |
| 188 switch (packet->kind) { | 175 switch (packet->kind) { |
| 189 case VPX_CODEC_CX_FRAME_PKT: | 176 case VPX_CODEC_CX_FRAME_PKT: |
| 190 got_data = true; | 177 got_data = true; |
| 191 message->set_data( | 178 message->mutable_update_stream_packet()->mutable_rect_data()->set_data( |
| 192 packet->data.frame.buf, packet->data.frame.sz); | 179 packet->data.frame.buf, packet->data.frame.sz); |
| 193 break; | 180 break; |
| 194 default: | 181 default: |
| 195 break; | 182 break; |
| 196 } | 183 } |
| 197 } | 184 } |
| 198 | 185 |
| 199 message->mutable_format()->set_encoding(VideoPacketFormat::ENCODING_VP8); | 186 // Enter the end rect. |
| 200 message->set_flags(VideoPacket::FIRST_PACKET | VideoPacket::LAST_PACKET); | 187 message->mutable_update_stream_packet()->mutable_end_rect(); |
| 201 message->mutable_format()->set_pixel_format(PIXEL_FORMAT_RGB32); | 188 data_available_callback->Run( |
| 202 message->mutable_format()->set_x(0); | 189 message, |
| 203 message->mutable_format()->set_y(0); | 190 EncodingStarting | EncodingInProgress | EncodingEnded); |
| 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); | |
| 208 delete data_available_callback; | 191 delete data_available_callback; |
| 209 } | 192 } |
| 210 | 193 |
| 211 } // namespace remoting | 194 } // namespace remoting |
| OLD | NEW |