diff --git a/modules/video_coding/codecs/vp8/libvpx_vp8_encoder.cc b/modules/video_coding/codecs/vp8/libvpx_vp8_encoder.cc index 9e9b47c4ed..95abc5cfd1 100644 --- a/modules/video_coding/codecs/vp8/libvpx_vp8_encoder.cc +++ b/modules/video_coding/codecs/vp8/libvpx_vp8_encoder.cc @@ -27,6 +27,7 @@ namespace webrtc { namespace { const char kVp8GfBoostFieldTrial[] = "WebRTC-VP8-GfBoost"; +const char kVp8DontDropKeyframeFieldTrial[] = "WebRTC-Vp8DontDropKeyFrames"; // QP is obtained from VP8-bitstream for HW, so the QP corresponds to the // bitstream range of [0, 127] and not the user-level range of [0,63]. @@ -152,6 +153,8 @@ vpx_enc_frame_flags_t LibvpxVp8Encoder::EncodeFlags( LibvpxVp8Encoder::LibvpxVp8Encoder() : use_gf_boost_(webrtc::field_trial::IsEnabled(kVp8GfBoostFieldTrial)), + prevent_kf_drop_( + !webrtc::field_trial::IsDisabled(kVp8DontDropKeyframeFieldTrial)), encoded_complete_callback_(nullptr), inited_(false), timestamp_(0), @@ -722,6 +725,9 @@ int LibvpxVp8Encoder::Encode(const VideoFrame& frame, for (size_t i = 0; i < encoders_.size(); ++i) { tl_configs[i] = temporal_layers_[i]->UpdateLayerConfig(frame.timestamp()); if (tl_configs[i].drop_frame) { + if (send_key_frame && prevent_kf_drop_) { + continue; + } // Drop this frame. return WEBRTC_VIDEO_CODEC_OK; } diff --git a/modules/video_coding/codecs/vp8/libvpx_vp8_encoder.h b/modules/video_coding/codecs/vp8/libvpx_vp8_encoder.h index 4e9d374c4c..087db3b59c 100644 --- a/modules/video_coding/codecs/vp8/libvpx_vp8_encoder.h +++ b/modules/video_coding/codecs/vp8/libvpx_vp8_encoder.h @@ -85,6 +85,7 @@ class LibvpxVp8Encoder : public VP8Encoder { uint32_t MaxIntraTarget(uint32_t optimal_buffer_size); const bool use_gf_boost_; + const bool prevent_kf_drop_; EncodedImageCallback* encoded_complete_callback_; VideoCodec codec_; diff --git a/modules/video_coding/codecs/vp8/screenshare_layers.cc b/modules/video_coding/codecs/vp8/screenshare_layers.cc index ebee39bd3f..ccdaa10f1c 100644 --- a/modules/video_coding/codecs/vp8/screenshare_layers.cc +++ b/modules/video_coding/codecs/vp8/screenshare_layers.cc @@ -292,6 +292,7 @@ void ScreenshareLayers::PopulateCodecSpecific( vp8_info->layerSync = true; layers_[0].state = TemporalLayer::State::kKeyFrame; layers_[1].state = TemporalLayer::State::kKeyFrame; + active_layer_ = 1; } } } diff --git a/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc b/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc index a3d7cd985a..116d06c324 100644 --- a/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc +++ b/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc @@ -55,9 +55,16 @@ class TestVp8Impl : public VideoCodecUnitTest { void EncodeAndWaitForFrame(const VideoFrame& input_frame, EncodedImage* encoded_frame, - CodecSpecificInfo* codec_specific_info) { + CodecSpecificInfo* codec_specific_info, + bool keyframe = false) { + std::vector frame_types; + if (keyframe) { + frame_types.emplace_back(FrameType::kVideoFrameKey); + } else { + frame_types.emplace_back(FrameType::kVideoFrameDelta); + } EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, - encoder_->Encode(input_frame, nullptr, nullptr)); + encoder_->Encode(input_frame, nullptr, &frame_types)); ASSERT_TRUE(WaitForEncodedFrame(encoded_frame, codec_specific_info)); VerifyQpParser(*encoded_frame); EXPECT_STREQ("libvpx", codec_specific_info->codec_name); @@ -66,10 +73,12 @@ class TestVp8Impl : public VideoCodecUnitTest { } void EncodeAndExpectFrameWith(const VideoFrame& input_frame, - uint8_t temporal_idx) { + uint8_t temporal_idx, + bool keyframe = false) { EncodedImage encoded_frame; CodecSpecificInfo codec_specific_info; - EncodeAndWaitForFrame(input_frame, &encoded_frame, &codec_specific_info); + EncodeAndWaitForFrame(input_frame, &encoded_frame, &codec_specific_info, + keyframe); EXPECT_EQ(temporal_idx, codec_specific_info.codecSpecific.VP8.temporalIdx); } @@ -330,4 +339,44 @@ TEST_F(TestVp8Impl, ScalingEnabledIfAutomaticResizeOn) { EXPECT_EQ(kDefaultMinPixelsPerFrame, settings.min_pixels_per_frame); } +TEST_F(TestVp8Impl, DontDropKeyframes) { + // Set very high resolution to trigger overuse more easily. + const int kScreenWidth = 1920; + const int kScreenHeight = 1080; + + codec_settings_.width = kScreenWidth; + codec_settings_.height = kScreenHeight; + + // Screensharing has the internal frame dropper off, and instead per frame + // asks ScreenshareLayers to decide if it should be dropped or not. + codec_settings_.VP8()->frameDroppingOn = false; + codec_settings_.mode = VideoCodecMode::kScreensharing; + // ScreenshareLayers triggers on 2 temporal layers and 1000kbps max bitrate. + codec_settings_.VP8()->numberOfTemporalLayers = 2; + codec_settings_.maxBitrate = 1000; + + // Reset the frame generator with large number of squares, leading to lots of + // details and high probability of overshoot. + input_frame_generator_ = test::FrameGenerator::CreateSquareGenerator( + codec_settings_.width, codec_settings_.height, + test::FrameGenerator::OutputType::I420, + /* num_squares = */ absl::optional(300)); + + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, + encoder_->InitEncode(&codec_settings_, kNumCores, kMaxPayloadSize)); + + VideoBitrateAllocation bitrate_allocation; + // Bitrate only enough for TL0. + bitrate_allocation.SetBitrate(0, 0, 200000); + encoder_->SetRateAllocation(bitrate_allocation, 5); + + EncodedImage encoded_frame; + CodecSpecificInfo codec_specific_info; + EncodeAndWaitForFrame(*NextInputFrame(), &encoded_frame, &codec_specific_info, + true); + EncodeAndExpectFrameWith(*NextInputFrame(), 0, true); + EncodeAndExpectFrameWith(*NextInputFrame(), 0, true); + EncodeAndExpectFrameWith(*NextInputFrame(), 0, true); +} + } // namespace webrtc