| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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/webrtc_video_encoder_vpx.h" | 5 #include "remoting/codec/webrtc_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" |
| (...skipping 258 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 269 lossless_color_ = want_lossless; | 269 lossless_color_ = want_lossless; |
| 270 // TODO(wez): Switch to ConfigureCodec() path once libvpx supports it. | 270 // TODO(wez): Switch to ConfigureCodec() path once libvpx supports it. |
| 271 // See https://code.google.com/p/webm/issues/detail?id=913. | 271 // See https://code.google.com/p/webm/issues/detail?id=913. |
| 272 // if (codec_) | 272 // if (codec_) |
| 273 // Configure(webrtc::DesktopSize(codec_->config.enc->g_w, | 273 // Configure(webrtc::DesktopSize(codec_->config.enc->g_w, |
| 274 // codec_->config.enc->g_h)); | 274 // codec_->config.enc->g_h)); |
| 275 codec_.reset(); | 275 codec_.reset(); |
| 276 } | 276 } |
| 277 } | 277 } |
| 278 | 278 |
| 279 void WebrtcVideoEncoderVpx::UpdateTargetBitrate(int new_bitrate_kbps) { | 279 std::unique_ptr<WebrtcVideoEncoder::EncodedFrame> WebrtcVideoEncoderVpx::Encode( |
| 280 target_bitrate_kbps_ = new_bitrate_kbps; | |
| 281 // Configuration not initialized. | |
| 282 if (config_.g_timebase.den == 0) | |
| 283 return; | |
| 284 | |
| 285 if (config_.rc_target_bitrate == static_cast<unsigned int>(new_bitrate_kbps)) | |
| 286 return; | |
| 287 config_.rc_target_bitrate = new_bitrate_kbps; | |
| 288 | |
| 289 // Update encoder context. | |
| 290 if (vpx_codec_enc_config_set(codec_.get(), &config_)) | |
| 291 NOTREACHED() << "Unable to set encoder config"; | |
| 292 | |
| 293 VLOG(1) << "New rc_target_bitrate: " << new_bitrate_kbps << " kbps"; | |
| 294 } | |
| 295 | |
| 296 std::unique_ptr<VideoPacket> WebrtcVideoEncoderVpx::Encode( | |
| 297 const webrtc::DesktopFrame& frame, | 280 const webrtc::DesktopFrame& frame, |
| 298 uint32_t flags) { | 281 const FrameParams& params) { |
| 299 DCHECK_LE(32, frame.size().width()); | 282 DCHECK_LE(32, frame.size().width()); |
| 300 DCHECK_LE(32, frame.size().height()); | 283 DCHECK_LE(32, frame.size().height()); |
| 301 | 284 |
| 302 // Based on information fetching active map, we return here if there is | 285 // Based on information fetching active map, we return here if there is |
| 303 // nothing to top-off. | 286 // nothing to top-off. |
| 304 if (frame.updated_region().is_empty() && !encode_unchanged_frame_) | 287 if (frame.updated_region().is_empty() && !encode_unchanged_frame_ && |
| 288 !params.key_frame) { |
| 305 return nullptr; | 289 return nullptr; |
| 290 } |
| 306 | 291 |
| 307 // Create or reconfigure the codec to match the size of |frame|. | 292 // Create or reconfigure the codec to match the size of |frame|. |
| 308 if (!codec_ || | 293 if (!codec_ || |
| 309 (image_ && | 294 (image_ && |
| 310 !frame.size().equals(webrtc::DesktopSize(image_->w, image_->h)))) { | 295 !frame.size().equals(webrtc::DesktopSize(image_->w, image_->h)))) { |
| 311 Configure(frame.size()); | 296 Configure(frame.size()); |
| 312 } | 297 } |
| 313 | 298 |
| 299 UpdateTargetBitrate(params.bitrate_kbps); |
| 300 |
| 314 vpx_active_map_t act_map; | 301 vpx_active_map_t act_map; |
| 315 act_map.rows = active_map_size_.height(); | 302 act_map.rows = active_map_size_.height(); |
| 316 act_map.cols = active_map_size_.width(); | 303 act_map.cols = active_map_size_.width(); |
| 317 act_map.active_map = active_map_.get(); | 304 act_map.active_map = active_map_.get(); |
| 318 | 305 |
| 319 webrtc::DesktopRegion updated_region; | 306 webrtc::DesktopRegion updated_region; |
| 320 if (!frame.updated_region().is_empty()) { | 307 if (!frame.updated_region().is_empty()) { |
| 321 // Convert the updated capture data ready for encode. | 308 // Convert the updated capture data ready for encode. |
| 322 PrepareImage(frame, &updated_region); | 309 PrepareImage(frame, &updated_region); |
| 323 | 310 |
| 324 // Update active map based on updated region. | 311 // Update active map based on updated region. |
| 325 SetActiveMapFromRegion(updated_region); | 312 SetActiveMapFromRegion(updated_region); |
| 326 | 313 |
| 327 // Apply active map to the encoder. | 314 // Apply active map to the encoder. |
| 328 | 315 |
| 329 if (vpx_codec_control(codec_.get(), VP8E_SET_ACTIVEMAP, &act_map)) { | 316 if (vpx_codec_control(codec_.get(), VP8E_SET_ACTIVEMAP, &act_map)) { |
| 330 LOG(ERROR) << "Unable to apply active map"; | 317 LOG(ERROR) << "Unable to apply active map"; |
| 331 } | 318 } |
| 332 } | 319 } |
| 333 | 320 |
| 334 // Frame rate is adapted based on how well the encoder meets the target | |
| 335 // bandwidth requirement. We specify a target rate of 1 / 15 fps here. | |
| 336 // TODO(isheriff): Investigate if it makes sense to increase the target FPS. | |
| 337 vpx_codec_err_t ret = vpx_codec_encode( | 321 vpx_codec_err_t ret = vpx_codec_encode( |
| 338 codec_.get(), image_.get(), 0, 66666, | 322 codec_.get(), image_.get(), 0, params.duration.InMicroseconds(), |
| 339 (flags & REQUEST_KEY_FRAME) ? VPX_EFLAG_FORCE_KF : 0, VPX_DL_REALTIME); | 323 (params.key_frame) ? VPX_EFLAG_FORCE_KF : 0, VPX_DL_REALTIME); |
| 340 DCHECK_EQ(ret, VPX_CODEC_OK) | 324 DCHECK_EQ(ret, VPX_CODEC_OK) |
| 341 << "Encoding error: " << vpx_codec_err_to_string(ret) << "\n" | 325 << "Encoding error: " << vpx_codec_err_to_string(ret) << "\n" |
| 342 << "Details: " << vpx_codec_error(codec_.get()) << "\n" | 326 << "Details: " << vpx_codec_error(codec_.get()) << "\n" |
| 343 << vpx_codec_error_detail(codec_.get()); | 327 << vpx_codec_error_detail(codec_.get()); |
| 344 | 328 |
| 345 if (!lossless_encode_) { | 329 if (!lossless_encode_) { |
| 346 // VP8 doesn't return active map, so we assume it's the same on the output | 330 // VP8 doesn't return active map, so we assume it's the same on the output |
| 347 // as on the input. | 331 // as on the input. |
| 348 if (use_vp9_) { | 332 if (use_vp9_) { |
| 349 ret = vpx_codec_control(codec_.get(), VP9E_GET_ACTIVEMAP, &act_map); | 333 ret = vpx_codec_control(codec_.get(), VP9E_GET_ACTIVEMAP, &act_map); |
| 350 DCHECK_EQ(ret, VPX_CODEC_OK) | 334 DCHECK_EQ(ret, VPX_CODEC_OK) |
| 351 << "Failed to fetch active map: " << vpx_codec_err_to_string(ret) | 335 << "Failed to fetch active map: " << vpx_codec_err_to_string(ret) |
| 352 << "\n"; | 336 << "\n"; |
| 353 | 337 |
| 354 // If the encoder output no changes then there's nothing left to top-off. | 338 // If the encoder output no changes then there's nothing left to top-off. |
| 355 encode_unchanged_frame_ = !updated_region.is_empty(); | 339 encode_unchanged_frame_ = !updated_region.is_empty(); |
| 356 } else { | 340 } else { |
| 357 // Always set |encode_unchanged_frame_| when using VP8. It will be reset | 341 // Always set |encode_unchanged_frame_| when using VP8. It will be reset |
| 358 // below once the target quantizer value is reached. | 342 // below once the target quantizer value is reached. |
| 359 encode_unchanged_frame_ = true; | 343 encode_unchanged_frame_ = true; |
| 360 } | 344 } |
| 361 | 345 |
| 362 UpdateRegionFromActiveMap(&updated_region); | 346 UpdateRegionFromActiveMap(&updated_region); |
| 363 } | 347 } |
| 364 | 348 |
| 365 // Read the encoded data. | 349 // Read the encoded data. |
| 366 vpx_codec_iter_t iter = nullptr; | 350 vpx_codec_iter_t iter = nullptr; |
| 367 bool got_data = false; | 351 bool got_data = false; |
| 368 | 352 |
| 369 // TODO(hclam): Make sure we get exactly one frame from the packet. | 353 std::unique_ptr<EncodedFrame> encoded_frame(new EncodedFrame()); |
| 370 // TODO(hclam): We should provide the output buffer to avoid one copy. | 354 encoded_frame->size = frame.size(); |
| 371 std::unique_ptr<VideoPacket> packet( | |
| 372 helper_.CreateVideoPacketWithUpdatedRegion(frame, updated_region)); | |
| 373 packet->mutable_format()->set_encoding(VideoPacketFormat::ENCODING_VP8); | |
| 374 | 355 |
| 375 while (!got_data) { | 356 while (!got_data) { |
| 376 const vpx_codec_cx_pkt_t* vpx_packet = | 357 const vpx_codec_cx_pkt_t* vpx_packet = |
| 377 vpx_codec_get_cx_data(codec_.get(), &iter); | 358 vpx_codec_get_cx_data(codec_.get(), &iter); |
| 378 if (!vpx_packet) | 359 if (!vpx_packet) |
| 379 continue; | 360 continue; |
| 380 | 361 |
| 381 switch (vpx_packet->kind) { | 362 switch (vpx_packet->kind) { |
| 382 case VPX_CODEC_CX_FRAME_PKT: { | 363 case VPX_CODEC_CX_FRAME_PKT: { |
| 383 got_data = true; | 364 got_data = true; |
| 384 packet->set_data(vpx_packet->data.frame.buf, vpx_packet->data.frame.sz); | 365 // TODO(sergeyu): Avoid copying the data here.. |
| 385 packet->set_key_frame(vpx_packet->data.frame.flags & VPX_FRAME_IS_KEY); | 366 encoded_frame->data.assign( |
| 367 reinterpret_cast<const char*>(vpx_packet->data.frame.buf), |
| 368 vpx_packet->data.frame.sz); |
| 369 encoded_frame->key_frame = |
| 370 vpx_packet->data.frame.flags & VPX_FRAME_IS_KEY; |
| 386 int quantizer = -1; | 371 int quantizer = -1; |
| 387 CHECK_EQ(vpx_codec_control(codec_.get(), VP8E_GET_LAST_QUANTIZER_64, | 372 CHECK_EQ(vpx_codec_control(codec_.get(), VP8E_GET_LAST_QUANTIZER_64, |
| 388 &quantizer), | 373 &quantizer), |
| 389 VPX_CODEC_OK); | 374 VPX_CODEC_OK); |
| 390 // VP8: Stop top-off as soon as the target quantizer value is reached. | 375 // VP8: Stop top-off as soon as the target quantizer value is reached. |
| 391 if (!use_vp9_ && quantizer <= kTargetQuantizerForVp8TopOff) | 376 if (!use_vp9_ && quantizer <= kTargetQuantizerForVp8TopOff) |
| 392 encode_unchanged_frame_ = false; | 377 encode_unchanged_frame_ = false; |
| 393 break; | 378 break; |
| 394 } | 379 } |
| 395 default: | 380 default: |
| 396 break; | 381 break; |
| 397 } | 382 } |
| 398 } | 383 } |
| 399 | 384 |
| 400 return packet; | 385 return encoded_frame; |
| 401 } | 386 } |
| 402 | 387 |
| 403 WebrtcVideoEncoderVpx::WebrtcVideoEncoderVpx(bool use_vp9) | 388 WebrtcVideoEncoderVpx::WebrtcVideoEncoderVpx(bool use_vp9) |
| 404 : use_vp9_(use_vp9), | 389 : use_vp9_(use_vp9), |
| 405 target_bitrate_kbps_(kDefaultTargetBitrateKbps), | 390 target_bitrate_kbps_(kDefaultTargetBitrateKbps), |
| 406 encode_unchanged_frame_(false), | 391 encode_unchanged_frame_(false), |
| 407 clock_(&default_tick_clock_) { | 392 clock_(&default_tick_clock_) { |
| 408 // Indicates config is still uninitialized. | 393 // Indicates config is still uninitialized. |
| 409 config_.g_timebase.den = 0; | 394 config_.g_timebase.den = 0; |
| 410 } | 395 } |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 460 } | 445 } |
| 461 | 446 |
| 462 // Apply further customizations to the codec now it's initialized. | 447 // Apply further customizations to the codec now it's initialized. |
| 463 if (use_vp9_) { | 448 if (use_vp9_) { |
| 464 SetVp9CodecOptions(codec_.get(), lossless_encode_); | 449 SetVp9CodecOptions(codec_.get(), lossless_encode_); |
| 465 } else { | 450 } else { |
| 466 SetVp8CodecOptions(codec_.get()); | 451 SetVp8CodecOptions(codec_.get()); |
| 467 } | 452 } |
| 468 } | 453 } |
| 469 | 454 |
| 455 void WebrtcVideoEncoderVpx::UpdateTargetBitrate(int new_bitrate_kbps) { |
| 456 target_bitrate_kbps_ = new_bitrate_kbps; |
| 457 |
| 458 // Configuration not initialized. |
| 459 if (config_.g_timebase.den == 0) |
| 460 return; |
| 461 |
| 462 if (config_.rc_target_bitrate == static_cast<unsigned int>(new_bitrate_kbps)) |
| 463 return; |
| 464 config_.rc_target_bitrate = new_bitrate_kbps; |
| 465 |
| 466 // Update encoder context. |
| 467 if (vpx_codec_enc_config_set(codec_.get(), &config_)) |
| 468 NOTREACHED() << "Unable to set encoder config"; |
| 469 |
| 470 VLOG(1) << "New rc_target_bitrate: " << new_bitrate_kbps << " kbps"; |
| 471 } |
| 472 |
| 470 void WebrtcVideoEncoderVpx::PrepareImage( | 473 void WebrtcVideoEncoderVpx::PrepareImage( |
| 471 const webrtc::DesktopFrame& frame, | 474 const webrtc::DesktopFrame& frame, |
| 472 webrtc::DesktopRegion* updated_region) { | 475 webrtc::DesktopRegion* updated_region) { |
| 473 if (frame.updated_region().is_empty()) { | 476 if (frame.updated_region().is_empty()) { |
| 474 updated_region->Clear(); | 477 updated_region->Clear(); |
| 475 return; | 478 return; |
| 476 } | 479 } |
| 477 | 480 |
| 478 updated_region->Clear(); | 481 updated_region->Clear(); |
| 479 if (image_) { | 482 if (image_) { |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 591 kMacroBlockSize * (y + 1))); | 594 kMacroBlockSize * (y + 1))); |
| 592 } | 595 } |
| 593 x0 = x1 + 1; | 596 x0 = x1 + 1; |
| 594 } | 597 } |
| 595 } | 598 } |
| 596 updated_region->IntersectWith( | 599 updated_region->IntersectWith( |
| 597 webrtc::DesktopRect::MakeWH(image_->w, image_->h)); | 600 webrtc::DesktopRect::MakeWH(image_->w, image_->h)); |
| 598 } | 601 } |
| 599 | 602 |
| 600 } // namespace remoting | 603 } // namespace remoting |
| OLD | NEW |