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