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 "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/sys_info.h" | 9 #include "base/sys_info.h" |
10 #include "remoting/base/util.h" | 10 #include "remoting/base/util.h" |
(...skipping 248 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
259 // codec_->config.enc->g_h)); | 259 // codec_->config.enc->g_h)); |
260 codec_.reset(); | 260 codec_.reset(); |
261 } | 261 } |
262 } | 262 } |
263 | 263 |
264 scoped_ptr<VideoPacket> VideoEncoderVpx::Encode( | 264 scoped_ptr<VideoPacket> VideoEncoderVpx::Encode( |
265 const webrtc::DesktopFrame& frame) { | 265 const webrtc::DesktopFrame& frame) { |
266 DCHECK_LE(32, frame.size().width()); | 266 DCHECK_LE(32, frame.size().width()); |
267 DCHECK_LE(32, frame.size().height()); | 267 DCHECK_LE(32, frame.size().height()); |
268 | 268 |
269 if (!use_vp9_ || lossless_encode_) { | 269 // If there is nothing to encode, and nothing to top-off, then return nothing. |
270 // Neither VP8 nor VP9-lossless support top-off, so ignore unchanged frames. | 270 if (frame.updated_region().is_empty() && !encode_unchanged_frame_) |
271 if (frame.updated_region().is_empty()) | 271 return nullptr; |
272 return nullptr; | |
273 } else { | |
274 // Let VP9-lossy mode top-off, by continuing to pass it unchanged frames | |
275 // for a short while. | |
276 if (frame.updated_region().is_empty()) { | |
277 if (topoff_frame_count_ == 0) | |
278 return nullptr; | |
279 topoff_frame_count_--; | |
280 } else { | |
281 topoff_frame_count_ = 2; | |
282 } | |
283 } | |
284 | 272 |
285 base::TimeTicks encode_start_time = base::TimeTicks::Now(); | 273 base::TimeTicks encode_start_time = base::TimeTicks::Now(); |
286 | 274 |
287 // Create or reconfigure the codec to match the size of |frame|. | 275 // Create or reconfigure the codec to match the size of |frame|. |
288 if (!codec_ || | 276 if (!codec_ || |
289 (image_ && | 277 (image_ && |
290 !frame.size().equals(webrtc::DesktopSize(image_->w, image_->h)))) { | 278 !frame.size().equals(webrtc::DesktopSize(image_->w, image_->h)))) { |
291 Configure(frame.size()); | 279 Configure(frame.size()); |
292 } | 280 } |
293 | 281 |
294 // Convert the updated capture data ready for encode. | 282 // Convert the updated capture data ready for encode. |
295 webrtc::DesktopRegion updated_region; | 283 webrtc::DesktopRegion updated_region; |
296 PrepareImage(frame, &updated_region); | 284 PrepareImage(frame, &updated_region); |
297 | 285 |
298 // Update active map based on updated region. | 286 // Update active map based on updated region. |
299 SetActiveMapFromRegion(updated_region); | 287 SetActiveMapFromRegion(updated_region); |
300 | 288 |
301 // Apply active map to the encoder. | 289 // Apply active map to the encoder. |
302 vpx_active_map_t act_map; | 290 vpx_active_map_t act_map; |
303 act_map.rows = active_map_height_; | 291 act_map.rows = active_map_size_.height(); |
304 act_map.cols = active_map_width_; | 292 act_map.cols = active_map_size_.width(); |
305 act_map.active_map = active_map_.get(); | 293 act_map.active_map = active_map_.get(); |
306 if (vpx_codec_control(codec_.get(), VP8E_SET_ACTIVEMAP, &act_map)) { | 294 if (vpx_codec_control(codec_.get(), VP8E_SET_ACTIVEMAP, &act_map)) { |
307 LOG(ERROR) << "Unable to apply active map"; | 295 LOG(ERROR) << "Unable to apply active map"; |
308 } | 296 } |
309 | 297 |
310 // Do the actual encoding. | 298 // Do the actual encoding. |
311 int timestamp = (encode_start_time - timestamp_base_).InMilliseconds(); | 299 int timestamp = (encode_start_time - timestamp_base_).InMilliseconds(); |
312 vpx_codec_err_t ret = vpx_codec_encode( | 300 vpx_codec_err_t ret = vpx_codec_encode( |
313 codec_.get(), image_.get(), timestamp, 1, 0, VPX_DL_REALTIME); | 301 codec_.get(), image_.get(), timestamp, 1, 0, VPX_DL_REALTIME); |
314 DCHECK_EQ(ret, VPX_CODEC_OK) | 302 DCHECK_EQ(ret, VPX_CODEC_OK) |
315 << "Encoding error: " << vpx_codec_err_to_string(ret) << "\n" | 303 << "Encoding error: " << vpx_codec_err_to_string(ret) << "\n" |
316 << "Details: " << vpx_codec_error(codec_.get()) << "\n" | 304 << "Details: " << vpx_codec_error(codec_.get()) << "\n" |
317 << vpx_codec_error_detail(codec_.get()); | 305 << vpx_codec_error_detail(codec_.get()); |
318 | 306 |
319 if (use_vp9_ && !lossless_encode_) { | 307 if (use_vp9_ && !lossless_encode_) { |
320 ret = vpx_codec_control(codec_.get(), VP9E_GET_ACTIVEMAP, &act_map); | 308 ret = vpx_codec_control(codec_.get(), VP9E_GET_ACTIVEMAP, &act_map); |
321 DCHECK_EQ(ret, VPX_CODEC_OK) | 309 DCHECK_EQ(ret, VPX_CODEC_OK) |
322 << "Failed to fetch active map: " | 310 << "Failed to fetch active map: " |
323 << vpx_codec_err_to_string(ret) << "\n"; | 311 << vpx_codec_err_to_string(ret) << "\n"; |
324 UpdateRegionFromActiveMap(&updated_region); | 312 UpdateRegionFromActiveMap(&updated_region); |
| 313 |
| 314 // If the encoder output no changes then there's nothing left to top-off. |
| 315 encode_unchanged_frame_ = !updated_region.is_empty(); |
325 } | 316 } |
326 | 317 |
327 // Read the encoded data. | 318 // Read the encoded data. |
328 vpx_codec_iter_t iter = NULL; | 319 vpx_codec_iter_t iter = NULL; |
329 bool got_data = false; | 320 bool got_data = false; |
330 | 321 |
331 // TODO(hclam): Make sure we get exactly one frame from the packet. | 322 // TODO(hclam): Make sure we get exactly one frame from the packet. |
332 // TODO(hclam): We should provide the output buffer to avoid one copy. | 323 // TODO(hclam): We should provide the output buffer to avoid one copy. |
333 scoped_ptr<VideoPacket> packet( | 324 scoped_ptr<VideoPacket> packet( |
334 helper_.CreateVideoPacketWithUpdatedRegion(frame, updated_region)); | 325 helper_.CreateVideoPacketWithUpdatedRegion(frame, updated_region)); |
(...skipping 15 matching lines...) Expand all Loading... |
350 } | 341 } |
351 } | 342 } |
352 | 343 |
353 // Note the time taken to encode the pixel data. | 344 // Note the time taken to encode the pixel data. |
354 packet->set_encode_time_ms( | 345 packet->set_encode_time_ms( |
355 (base::TimeTicks::Now() - encode_start_time).InMillisecondsRoundedUp()); | 346 (base::TimeTicks::Now() - encode_start_time).InMillisecondsRoundedUp()); |
356 | 347 |
357 return packet.Pass(); | 348 return packet.Pass(); |
358 } | 349 } |
359 | 350 |
360 VideoEncoderVpx::VideoEncoderVpx(bool use_vp9) : use_vp9_(use_vp9) { | 351 VideoEncoderVpx::VideoEncoderVpx(bool use_vp9) |
| 352 : use_vp9_(use_vp9), encode_unchanged_frame_(false) { |
361 } | 353 } |
362 | 354 |
363 void VideoEncoderVpx::Configure(const webrtc::DesktopSize& size) { | 355 void VideoEncoderVpx::Configure(const webrtc::DesktopSize& size) { |
364 DCHECK(use_vp9_ || !lossless_color_); | 356 DCHECK(use_vp9_ || !lossless_color_); |
365 DCHECK(use_vp9_ || !lossless_encode_); | 357 DCHECK(use_vp9_ || !lossless_encode_); |
366 | 358 |
367 // Tear down |image_| if it no longer matches the size and color settings. | 359 // Tear down |image_| if it no longer matches the size and color settings. |
368 // PrepareImage() will then create a new buffer of the required dimensions if | 360 // PrepareImage() will then create a new buffer of the required dimensions if |
369 // |image_| is not allocated. | 361 // |image_| is not allocated. |
370 FreeImageIfMismatched(lossless_color_, size, &image_, &image_buffer_); | 362 FreeImageIfMismatched(lossless_color_, size, &image_, &image_buffer_); |
371 | 363 |
372 // Initialize active map. | 364 // Initialize active map. |
373 active_map_width_ = (size.width() + kMacroBlockSize - 1) / kMacroBlockSize; | 365 active_map_size_ = webrtc::DesktopSize( |
374 active_map_height_ = (size.height() + kMacroBlockSize - 1) / kMacroBlockSize; | 366 (size.width() + kMacroBlockSize - 1) / kMacroBlockSize, |
375 active_map_.reset(new uint8[active_map_width_ * active_map_height_]); | 367 (size.height() + kMacroBlockSize - 1) / kMacroBlockSize); |
| 368 active_map_.reset( |
| 369 new uint8[active_map_size_.width() * active_map_size_.height()]); |
376 | 370 |
377 // TODO(wez): Remove this hack once VPX can handle frame size reconfiguration. | 371 // TODO(wez): Remove this hack once VPX can handle frame size reconfiguration. |
378 // See https://code.google.com/p/webm/issues/detail?id=912. | 372 // See https://code.google.com/p/webm/issues/detail?id=912. |
379 if (codec_) { | 373 if (codec_) { |
380 // If the frame size has changed then force re-creation of the codec. | 374 // If the frame size has changed then force re-creation of the codec. |
381 if (codec_->config.enc->g_w != static_cast<unsigned int>(size.width()) || | 375 if (codec_->config.enc->g_w != static_cast<unsigned int>(size.width()) || |
382 codec_->config.enc->g_h != static_cast<unsigned int>(size.height())) { | 376 codec_->config.enc->g_h != static_cast<unsigned int>(size.height())) { |
383 codec_.reset(); | 377 codec_.reset(); |
384 } | 378 } |
385 } | 379 } |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
500 break; | 494 break; |
501 default: | 495 default: |
502 NOTREACHED(); | 496 NOTREACHED(); |
503 break; | 497 break; |
504 } | 498 } |
505 } | 499 } |
506 | 500 |
507 void VideoEncoderVpx::SetActiveMapFromRegion( | 501 void VideoEncoderVpx::SetActiveMapFromRegion( |
508 const webrtc::DesktopRegion& updated_region) { | 502 const webrtc::DesktopRegion& updated_region) { |
509 // Clear active map first. | 503 // Clear active map first. |
510 memset(active_map_.get(), 0, active_map_width_ * active_map_height_); | 504 memset(active_map_.get(), 0, |
| 505 active_map_size_.width() * active_map_size_.height()); |
511 | 506 |
512 // Mark updated areas active. | 507 // Mark updated areas active. |
513 for (webrtc::DesktopRegion::Iterator r(updated_region); !r.IsAtEnd(); | 508 for (webrtc::DesktopRegion::Iterator r(updated_region); !r.IsAtEnd(); |
514 r.Advance()) { | 509 r.Advance()) { |
515 const webrtc::DesktopRect& rect = r.rect(); | 510 const webrtc::DesktopRect& rect = r.rect(); |
516 int left = rect.left() / kMacroBlockSize; | 511 int left = rect.left() / kMacroBlockSize; |
517 int right = (rect.right() - 1) / kMacroBlockSize; | 512 int right = (rect.right() - 1) / kMacroBlockSize; |
518 int top = rect.top() / kMacroBlockSize; | 513 int top = rect.top() / kMacroBlockSize; |
519 int bottom = (rect.bottom() - 1) / kMacroBlockSize; | 514 int bottom = (rect.bottom() - 1) / kMacroBlockSize; |
520 DCHECK_LT(right, active_map_width_); | 515 DCHECK_LT(right, active_map_size_.width()); |
521 DCHECK_LT(bottom, active_map_height_); | 516 DCHECK_LT(bottom, active_map_size_.height()); |
522 | 517 |
523 uint8* map = active_map_.get() + top * active_map_width_; | 518 uint8* map = active_map_.get() + top * active_map_size_.width(); |
524 for (int y = top; y <= bottom; ++y) { | 519 for (int y = top; y <= bottom; ++y) { |
525 for (int x = left; x <= right; ++x) | 520 for (int x = left; x <= right; ++x) |
526 map[x] = 1; | 521 map[x] = 1; |
527 map += active_map_width_; | 522 map += active_map_size_.width(); |
528 } | 523 } |
529 } | 524 } |
530 } | 525 } |
531 | 526 |
532 void VideoEncoderVpx::UpdateRegionFromActiveMap( | 527 void VideoEncoderVpx::UpdateRegionFromActiveMap( |
533 webrtc::DesktopRegion* updated_region) { | 528 webrtc::DesktopRegion* updated_region) { |
534 const uint8* map = active_map_.get(); | 529 const uint8* map = active_map_.get(); |
535 for (int y = 0; y < active_map_height_; ++y) { | 530 for (int y = 0; y < active_map_size_.height(); ++y) { |
536 for (int x0 = 0; x0 < active_map_width_;) { | 531 for (int x0 = 0; x0 < active_map_size_.width();) { |
537 int x1 = x0; | 532 int x1 = x0; |
538 for (; x1 < active_map_width_; ++x1) { | 533 for (; x1 < active_map_size_.width(); ++x1) { |
539 if (map[y * active_map_width_ + x1] == 0) | 534 if (map[y * active_map_size_.width() + x1] == 0) |
540 break; | 535 break; |
541 } | 536 } |
542 if (x1 > x0) { | 537 if (x1 > x0) { |
543 updated_region->AddRect(webrtc::DesktopRect::MakeLTRB( | 538 updated_region->AddRect(webrtc::DesktopRect::MakeLTRB( |
544 kMacroBlockSize * x0, kMacroBlockSize * y, kMacroBlockSize * x1, | 539 kMacroBlockSize * x0, kMacroBlockSize * y, kMacroBlockSize * x1, |
545 kMacroBlockSize * (y + 1))); | 540 kMacroBlockSize * (y + 1))); |
546 } | 541 } |
547 x0 = x1 + 1; | 542 x0 = x1 + 1; |
548 } | 543 } |
549 } | 544 } |
550 updated_region->IntersectWith( | 545 updated_region->IntersectWith( |
551 webrtc::DesktopRect::MakeWH(image_->w, image_->h)); | 546 webrtc::DesktopRect::MakeWH(image_->w, image_->h)); |
552 } | 547 } |
553 | 548 |
554 } // namespace remoting | 549 } // namespace remoting |
OLD | NEW |