| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 "remoting/codec/video_encoder_vpx.h" | 5 #include "remoting/codec/video_encoder_vpx.h" |
| 6 | 6 |
| 7 #include <utility> | 7 #include <utility> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/memory/ptr_util.h" |
| 11 #include "base/sys_info.h" | 12 #include "base/sys_info.h" |
| 12 #include "remoting/base/util.h" | 13 #include "remoting/base/util.h" |
| 13 #include "remoting/proto/video.pb.h" | 14 #include "remoting/proto/video.pb.h" |
| 14 #include "third_party/libyuv/include/libyuv/convert_from_argb.h" | 15 #include "third_party/libyuv/include/libyuv/convert_from_argb.h" |
| 15 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" | 16 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" |
| 16 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" | 17 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" |
| 17 #include "third_party/webrtc/modules/desktop_capture/desktop_region.h" | 18 #include "third_party/webrtc/modules/desktop_capture/desktop_region.h" |
| 18 | 19 |
| 19 extern "C" { | 20 extern "C" { |
| 20 #define VPX_CODEC_DISABLE_COMPAT 1 | 21 #define VPX_CODEC_DISABLE_COMPAT 1 |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 143 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set screen content mode"; | 144 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set screen content mode"; |
| 144 | 145 |
| 145 // Set cyclic refresh (aka "top-off") only for lossy encoding. | 146 // Set cyclic refresh (aka "top-off") only for lossy encoding. |
| 146 int aq_mode = lossless_encode ? kVp9AqModeNone : kVp9AqModeCyclicRefresh; | 147 int aq_mode = lossless_encode ? kVp9AqModeNone : kVp9AqModeCyclicRefresh; |
| 147 ret = vpx_codec_control(codec, VP9E_SET_AQ_MODE, aq_mode); | 148 ret = vpx_codec_control(codec, VP9E_SET_AQ_MODE, aq_mode); |
| 148 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set aq mode"; | 149 DCHECK_EQ(VPX_CODEC_OK, ret) << "Failed to set aq mode"; |
| 149 } | 150 } |
| 150 | 151 |
| 151 void FreeImageIfMismatched(bool use_i444, | 152 void FreeImageIfMismatched(bool use_i444, |
| 152 const webrtc::DesktopSize& size, | 153 const webrtc::DesktopSize& size, |
| 153 scoped_ptr<vpx_image_t>* out_image, | 154 std::unique_ptr<vpx_image_t>* out_image, |
| 154 scoped_ptr<uint8_t[]>* out_image_buffer) { | 155 std::unique_ptr<uint8_t[]>* out_image_buffer) { |
| 155 if (*out_image) { | 156 if (*out_image) { |
| 156 const vpx_img_fmt_t desired_fmt = | 157 const vpx_img_fmt_t desired_fmt = |
| 157 use_i444 ? VPX_IMG_FMT_I444 : VPX_IMG_FMT_I420; | 158 use_i444 ? VPX_IMG_FMT_I444 : VPX_IMG_FMT_I420; |
| 158 if (!size.equals(webrtc::DesktopSize((*out_image)->w, (*out_image)->h)) || | 159 if (!size.equals(webrtc::DesktopSize((*out_image)->w, (*out_image)->h)) || |
| 159 (*out_image)->fmt != desired_fmt) { | 160 (*out_image)->fmt != desired_fmt) { |
| 160 out_image_buffer->reset(); | 161 out_image_buffer->reset(); |
| 161 out_image->reset(); | 162 out_image->reset(); |
| 162 } | 163 } |
| 163 } | 164 } |
| 164 } | 165 } |
| 165 | 166 |
| 166 void CreateImage(bool use_i444, | 167 void CreateImage(bool use_i444, |
| 167 const webrtc::DesktopSize& size, | 168 const webrtc::DesktopSize& size, |
| 168 scoped_ptr<vpx_image_t>* out_image, | 169 std::unique_ptr<vpx_image_t>* out_image, |
| 169 scoped_ptr<uint8_t[]>* out_image_buffer) { | 170 std::unique_ptr<uint8_t[]>* out_image_buffer) { |
| 170 DCHECK(!size.is_empty()); | 171 DCHECK(!size.is_empty()); |
| 171 DCHECK(!*out_image_buffer); | 172 DCHECK(!*out_image_buffer); |
| 172 DCHECK(!*out_image); | 173 DCHECK(!*out_image); |
| 173 | 174 |
| 174 scoped_ptr<vpx_image_t> image(new vpx_image_t()); | 175 std::unique_ptr<vpx_image_t> image(new vpx_image_t()); |
| 175 memset(image.get(), 0, sizeof(vpx_image_t)); | 176 memset(image.get(), 0, sizeof(vpx_image_t)); |
| 176 | 177 |
| 177 // libvpx seems to require both to be assigned. | 178 // libvpx seems to require both to be assigned. |
| 178 image->d_w = size.width(); | 179 image->d_w = size.width(); |
| 179 image->w = size.width(); | 180 image->w = size.width(); |
| 180 image->d_h = size.height(); | 181 image->d_h = size.height(); |
| 181 image->h = size.height(); | 182 image->h = size.height(); |
| 182 | 183 |
| 183 // libvpx should derive chroma shifts from|fmt| but currently has a bug: | 184 // libvpx should derive chroma shifts from|fmt| but currently has a bug: |
| 184 // https://code.google.com/p/webm/issues/detail?id=627 | 185 // https://code.google.com/p/webm/issues/detail?id=627 |
| (...skipping 17 matching lines...) Expand all Loading... |
| 202 // if the image is not padded out to the next macroblock: crbug.com/119633. | 203 // if the image is not padded out to the next macroblock: crbug.com/119633. |
| 203 // Pad the Y, U and V planes' height out to compensate. | 204 // Pad the Y, U and V planes' height out to compensate. |
| 204 // Assuming macroblocks are 16x16, aligning the planes' strides above also | 205 // Assuming macroblocks are 16x16, aligning the planes' strides above also |
| 205 // macroblock aligned them. | 206 // macroblock aligned them. |
| 206 static_assert(kMacroBlockSize == 16, "macroblock_size_not_16"); | 207 static_assert(kMacroBlockSize == 16, "macroblock_size_not_16"); |
| 207 const int y_rows = ((image->h - 1) & ~(kMacroBlockSize-1)) + kMacroBlockSize; | 208 const int y_rows = ((image->h - 1) & ~(kMacroBlockSize-1)) + kMacroBlockSize; |
| 208 const int uv_rows = y_rows >> image->y_chroma_shift; | 209 const int uv_rows = y_rows >> image->y_chroma_shift; |
| 209 | 210 |
| 210 // Allocate a YUV buffer large enough for the aligned data & padding. | 211 // Allocate a YUV buffer large enough for the aligned data & padding. |
| 211 const int buffer_size = y_stride * y_rows + 2*uv_stride * uv_rows; | 212 const int buffer_size = y_stride * y_rows + 2*uv_stride * uv_rows; |
| 212 scoped_ptr<uint8_t[]> image_buffer(new uint8_t[buffer_size]); | 213 std::unique_ptr<uint8_t[]> image_buffer(new uint8_t[buffer_size]); |
| 213 | 214 |
| 214 // Reset image value to 128 so we just need to fill in the y plane. | 215 // Reset image value to 128 so we just need to fill in the y plane. |
| 215 memset(image_buffer.get(), 128, buffer_size); | 216 memset(image_buffer.get(), 128, buffer_size); |
| 216 | 217 |
| 217 // Fill in the information for |image_|. | 218 // Fill in the information for |image_|. |
| 218 unsigned char* uchar_buffer = | 219 unsigned char* uchar_buffer = |
| 219 reinterpret_cast<unsigned char*>(image_buffer.get()); | 220 reinterpret_cast<unsigned char*>(image_buffer.get()); |
| 220 image->planes[0] = uchar_buffer; | 221 image->planes[0] = uchar_buffer; |
| 221 image->planes[1] = image->planes[0] + y_stride * y_rows; | 222 image->planes[1] = image->planes[0] + y_stride * y_rows; |
| 222 image->planes[2] = image->planes[1] + uv_stride * uv_rows; | 223 image->planes[2] = image->planes[1] + uv_stride * uv_rows; |
| 223 image->stride[0] = y_stride; | 224 image->stride[0] = y_stride; |
| 224 image->stride[1] = uv_stride; | 225 image->stride[1] = uv_stride; |
| 225 image->stride[2] = uv_stride; | 226 image->stride[2] = uv_stride; |
| 226 | 227 |
| 227 *out_image = std::move(image); | 228 *out_image = std::move(image); |
| 228 *out_image_buffer = std::move(image_buffer); | 229 *out_image_buffer = std::move(image_buffer); |
| 229 } | 230 } |
| 230 | 231 |
| 231 } // namespace | 232 } // namespace |
| 232 | 233 |
| 233 // static | 234 // static |
| 234 scoped_ptr<VideoEncoderVpx> VideoEncoderVpx::CreateForVP8() { | 235 std::unique_ptr<VideoEncoderVpx> VideoEncoderVpx::CreateForVP8() { |
| 235 return make_scoped_ptr(new VideoEncoderVpx(false)); | 236 return base::WrapUnique(new VideoEncoderVpx(false)); |
| 236 } | 237 } |
| 237 | 238 |
| 238 // static | 239 // static |
| 239 scoped_ptr<VideoEncoderVpx> VideoEncoderVpx::CreateForVP9() { | 240 std::unique_ptr<VideoEncoderVpx> VideoEncoderVpx::CreateForVP9() { |
| 240 return make_scoped_ptr(new VideoEncoderVpx(true)); | 241 return base::WrapUnique(new VideoEncoderVpx(true)); |
| 241 } | 242 } |
| 242 | 243 |
| 243 VideoEncoderVpx::~VideoEncoderVpx() {} | 244 VideoEncoderVpx::~VideoEncoderVpx() {} |
| 244 | 245 |
| 245 void VideoEncoderVpx::SetTickClockForTests(base::TickClock* tick_clock) { | 246 void VideoEncoderVpx::SetTickClockForTests(base::TickClock* tick_clock) { |
| 246 clock_ = tick_clock; | 247 clock_ = tick_clock; |
| 247 } | 248 } |
| 248 | 249 |
| 249 void VideoEncoderVpx::SetLosslessEncode(bool want_lossless) { | 250 void VideoEncoderVpx::SetLosslessEncode(bool want_lossless) { |
| 250 if (use_vp9_ && (want_lossless != lossless_encode_)) { | 251 if (use_vp9_ && (want_lossless != lossless_encode_)) { |
| 251 lossless_encode_ = want_lossless; | 252 lossless_encode_ = want_lossless; |
| 252 if (codec_) | 253 if (codec_) |
| 253 Configure(webrtc::DesktopSize(codec_->config.enc->g_w, | 254 Configure(webrtc::DesktopSize(codec_->config.enc->g_w, |
| 254 codec_->config.enc->g_h)); | 255 codec_->config.enc->g_h)); |
| 255 } | 256 } |
| 256 } | 257 } |
| 257 | 258 |
| 258 void VideoEncoderVpx::SetLosslessColor(bool want_lossless) { | 259 void VideoEncoderVpx::SetLosslessColor(bool want_lossless) { |
| 259 if (use_vp9_ && (want_lossless != lossless_color_)) { | 260 if (use_vp9_ && (want_lossless != lossless_color_)) { |
| 260 lossless_color_ = want_lossless; | 261 lossless_color_ = want_lossless; |
| 261 // TODO(wez): Switch to ConfigureCodec() path once libvpx supports it. | 262 // TODO(wez): Switch to ConfigureCodec() path once libvpx supports it. |
| 262 // See https://code.google.com/p/webm/issues/detail?id=913. | 263 // See https://code.google.com/p/webm/issues/detail?id=913. |
| 263 //if (codec_) | 264 //if (codec_) |
| 264 // Configure(webrtc::DesktopSize(codec_->config.enc->g_w, | 265 // Configure(webrtc::DesktopSize(codec_->config.enc->g_w, |
| 265 // codec_->config.enc->g_h)); | 266 // codec_->config.enc->g_h)); |
| 266 codec_.reset(); | 267 codec_.reset(); |
| 267 } | 268 } |
| 268 } | 269 } |
| 269 | 270 |
| 270 scoped_ptr<VideoPacket> VideoEncoderVpx::Encode( | 271 std::unique_ptr<VideoPacket> VideoEncoderVpx::Encode( |
| 271 const webrtc::DesktopFrame& frame) { | 272 const webrtc::DesktopFrame& frame) { |
| 272 DCHECK_LE(32, frame.size().width()); | 273 DCHECK_LE(32, frame.size().width()); |
| 273 DCHECK_LE(32, frame.size().height()); | 274 DCHECK_LE(32, frame.size().height()); |
| 274 | 275 |
| 275 // If there is nothing to encode, and nothing to top-off, then return nothing. | 276 // If there is nothing to encode, and nothing to top-off, then return nothing. |
| 276 if (frame.updated_region().is_empty() && !encode_unchanged_frame_) | 277 if (frame.updated_region().is_empty() && !encode_unchanged_frame_) |
| 277 return nullptr; | 278 return nullptr; |
| 278 | 279 |
| 279 // Create or reconfigure the codec to match the size of |frame|. | 280 // Create or reconfigure the codec to match the size of |frame|. |
| 280 if (!codec_ || | 281 if (!codec_ || |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 318 // If the encoder output no changes then there's nothing left to top-off. | 319 // If the encoder output no changes then there's nothing left to top-off. |
| 319 encode_unchanged_frame_ = !updated_region.is_empty(); | 320 encode_unchanged_frame_ = !updated_region.is_empty(); |
| 320 } | 321 } |
| 321 | 322 |
| 322 // Read the encoded data. | 323 // Read the encoded data. |
| 323 vpx_codec_iter_t iter = nullptr; | 324 vpx_codec_iter_t iter = nullptr; |
| 324 bool got_data = false; | 325 bool got_data = false; |
| 325 | 326 |
| 326 // TODO(hclam): Make sure we get exactly one frame from the packet. | 327 // TODO(hclam): Make sure we get exactly one frame from the packet. |
| 327 // TODO(hclam): We should provide the output buffer to avoid one copy. | 328 // TODO(hclam): We should provide the output buffer to avoid one copy. |
| 328 scoped_ptr<VideoPacket> packet( | 329 std::unique_ptr<VideoPacket> packet( |
| 329 helper_.CreateVideoPacketWithUpdatedRegion(frame, updated_region)); | 330 helper_.CreateVideoPacketWithUpdatedRegion(frame, updated_region)); |
| 330 packet->mutable_format()->set_encoding(VideoPacketFormat::ENCODING_VP8); | 331 packet->mutable_format()->set_encoding(VideoPacketFormat::ENCODING_VP8); |
| 331 | 332 |
| 332 while (!got_data) { | 333 while (!got_data) { |
| 333 const vpx_codec_cx_pkt_t* vpx_packet = | 334 const vpx_codec_cx_pkt_t* vpx_packet = |
| 334 vpx_codec_get_cx_data(codec_.get(), &iter); | 335 vpx_codec_get_cx_data(codec_.get(), &iter); |
| 335 if (!vpx_packet) | 336 if (!vpx_packet) |
| 336 continue; | 337 continue; |
| 337 | 338 |
| 338 switch (vpx_packet->kind) { | 339 switch (vpx_packet->kind) { |
| (...skipping 202 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 541 kMacroBlockSize * (y + 1))); | 542 kMacroBlockSize * (y + 1))); |
| 542 } | 543 } |
| 543 x0 = x1 + 1; | 544 x0 = x1 + 1; |
| 544 } | 545 } |
| 545 } | 546 } |
| 546 updated_region->IntersectWith( | 547 updated_region->IntersectWith( |
| 547 webrtc::DesktopRect::MakeWH(image_->w, image_->h)); | 548 webrtc::DesktopRect::MakeWH(image_->w, image_->h)); |
| 548 } | 549 } |
| 549 | 550 |
| 550 } // namespace remoting | 551 } // namespace remoting |
| OLD | NEW |