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 |