Don't drop keyframes even if TemporalLayers says so.
This CL is a minimum effort/low risk fix. Later CLs take a more thorough approach. Bug: webrtc:9634 Change-Id: I728a061a4e71b38a559ee438646de83cd2cb3517 Reviewed-on: https://webrtc-review.googlesource.com/94760 Commit-Queue: Erik Språng <sprang@webrtc.org> Reviewed-by: Rasmus Brandt <brandtr@webrtc.org> Cr-Commit-Position: refs/heads/master@{#24326}
This commit is contained in:
@ -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;
|
||||
}
|
||||
|
@ -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_;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<FrameType> 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<int>(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
|
||||
|
Reference in New Issue
Block a user