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 |