diff --git a/webrtc/modules/video_coding/codecs/vp8/screenshare_layers.cc b/webrtc/modules/video_coding/codecs/vp8/screenshare_layers.cc index c31fe142e..a678d07dd 100644 --- a/webrtc/modules/video_coding/codecs/vp8/screenshare_layers.cc +++ b/webrtc/modules/video_coding/codecs/vp8/screenshare_layers.cc @@ -14,11 +14,15 @@ #include "vpx/vpx_encoder.h" #include "vpx/vp8cx.h" #include "webrtc/modules/video_coding/codecs/interface/video_codec_interface.h" +#include "webrtc/system_wrappers/interface/field_trial.h" namespace webrtc { enum { kOneSecond90Khz = 90000 }; +const double ScreenshareLayers::kMaxTL0FpsReduction = 2.5; +const double ScreenshareLayers::kAcceptableTargetOvershoot = 2.0; + ScreenshareLayers::ScreenshareLayers(int num_temporal_layers, uint8_t initial_tl0_pic_idx, FrameDropper* tl0_frame_dropper, @@ -97,6 +101,20 @@ bool ScreenshareLayers::ConfigureBitrates(int bitrate_kbit, } tl0_frame_dropper_->SetRates(bitrate_kbit, framerate_); tl1_frame_dropper_->SetRates(max_bitrate_kbit, framerate_); + + if (cfg != NULL && TargetBitrateExperimentEnabled()) { + // Calculate a codec target bitrate. This may be higher than TL0, gaining + // quality at the expense of frame rate at TL0. Constraints: + // - TL0 frame rate should not be less than framerate / kMaxTL0FpsReduction. + // - Target rate * kAcceptableTargetOvershoot should not exceed TL1 rate. + double target_bitrate = + std::min(bitrate_kbit * kMaxTL0FpsReduction, + max_bitrate_kbit / kAcceptableTargetOvershoot); + cfg->rc_target_bitrate = + std::max(static_cast(bitrate_kbit), + static_cast(target_bitrate + 0.5)); + } + return true; } @@ -156,4 +174,11 @@ void ScreenshareLayers::CalculateFramerate(uint32_t timestamp) { timestamp_diff / 2) / timestamp_diff; } } + +bool ScreenshareLayers::TargetBitrateExperimentEnabled() { + std::string group = + field_trial::FindFullName("WebRTC-ScreencastTargetBitrateOvershoot"); + return group == "Enabled"; +} + } // namespace webrtc diff --git a/webrtc/modules/video_coding/codecs/vp8/screenshare_layers.h b/webrtc/modules/video_coding/codecs/vp8/screenshare_layers.h index ce974894f..ac8226f8a 100644 --- a/webrtc/modules/video_coding/codecs/vp8/screenshare_layers.h +++ b/webrtc/modules/video_coding/codecs/vp8/screenshare_layers.h @@ -24,6 +24,9 @@ struct CodecSpecificInfoVP8; class ScreenshareLayers : public TemporalLayers { public: + static const double kMaxTL0FpsReduction; + static const double kAcceptableTargetOvershoot; + ScreenshareLayers(int num_temporal_layers, uint8_t initial_tl0_pic_idx, FrameDropper* tl0_frame_dropper, @@ -47,6 +50,9 @@ class ScreenshareLayers : public TemporalLayers { virtual int CurrentLayerId() const; + protected: + virtual bool TargetBitrateExperimentEnabled(); + private: void CalculateFramerate(uint32_t timestamp); bool TimeToSync(uint32_t timestamp) const; diff --git a/webrtc/modules/video_coding/codecs/vp8/screenshare_layers_unittest.cc b/webrtc/modules/video_coding/codecs/vp8/screenshare_layers_unittest.cc index c3090e10d..5c7b70ad3 100644 --- a/webrtc/modules/video_coding/codecs/vp8/screenshare_layers_unittest.cc +++ b/webrtc/modules/video_coding/codecs/vp8/screenshare_layers_unittest.cc @@ -33,6 +33,22 @@ const int kFlagsTL1 = VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_ARF | const int kFlagsTL1Sync = VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST; +class ScreenshareLayersFT : public ScreenshareLayers { + public: + ScreenshareLayersFT(int num_temporal_layers, + uint8_t initial_tl0_pic_idx, + FrameDropper* tl0_frame_dropper, + FrameDropper* tl1_frame_dropper) + : ScreenshareLayers(num_temporal_layers, + initial_tl0_pic_idx, + tl0_frame_dropper, + tl1_frame_dropper) {} + virtual ~ScreenshareLayersFT() {} + + protected: + virtual bool TargetBitrateExperimentEnabled() OVERRIDE { return true; } +}; + class ScreenshareLayerTest : public ::testing::Test { protected: void SetEncodeExpectations(bool drop_tl0, bool drop_tl1, int framerate) { @@ -77,12 +93,12 @@ class ScreenshareLayerTest : public ::testing::Test { NiceMock tl0_frame_dropper_; NiceMock tl1_frame_dropper_; - scoped_ptr layers_; + scoped_ptr layers_; }; TEST_F(ScreenshareLayerTest, 1Layer) { - layers_.reset(new ScreenshareLayers(1, 0, &tl0_frame_dropper_, - &tl1_frame_dropper_)); + layers_.reset( + new ScreenshareLayersFT(1, 0, &tl0_frame_dropper_, &tl1_frame_dropper_)); EXPECT_TRUE(layers_->ConfigureBitrates(100, 1000, 5, NULL)); int flags = 0; uint32_t timestamp = 0; @@ -116,8 +132,8 @@ TEST_F(ScreenshareLayerTest, 1Layer) { } TEST_F(ScreenshareLayerTest, 2Layer) { - layers_.reset(new ScreenshareLayers(2, 0, &tl0_frame_dropper_, - &tl1_frame_dropper_)); + layers_.reset( + new ScreenshareLayersFT(2, 0, &tl0_frame_dropper_, &tl1_frame_dropper_)); EXPECT_TRUE(layers_->ConfigureBitrates(100, 1000, 5, NULL)); int flags = 0; uint32_t timestamp = 0; @@ -164,8 +180,8 @@ TEST_F(ScreenshareLayerTest, 2Layer) { } TEST_F(ScreenshareLayerTest, 2LayersPeriodicSync) { - layers_.reset(new ScreenshareLayers(2, 0, &tl0_frame_dropper_, - &tl1_frame_dropper_)); + layers_.reset( + new ScreenshareLayersFT(2, 0, &tl0_frame_dropper_, &tl1_frame_dropper_)); EXPECT_TRUE(layers_->ConfigureBitrates(100, 1000, 5, NULL)); int flags = 0; uint32_t timestamp = 0; @@ -188,8 +204,8 @@ TEST_F(ScreenshareLayerTest, 2LayersPeriodicSync) { } TEST_F(ScreenshareLayerTest, 2LayersToggling) { - layers_.reset(new ScreenshareLayers(2, 0, &tl0_frame_dropper_, - &tl1_frame_dropper_)); + layers_.reset( + new ScreenshareLayersFT(2, 0, &tl0_frame_dropper_, &tl1_frame_dropper_)); EXPECT_TRUE(layers_->ConfigureBitrates(100, 1000, 5, NULL)); int flags = 0; uint32_t timestamp = 0; @@ -213,8 +229,8 @@ TEST_F(ScreenshareLayerTest, 2LayersToggling) { } TEST_F(ScreenshareLayerTest, 2LayersBothDrops) { - layers_.reset(new ScreenshareLayers(2, 0, &tl0_frame_dropper_, - &tl1_frame_dropper_)); + layers_.reset( + new ScreenshareLayersFT(2, 0, &tl0_frame_dropper_, &tl1_frame_dropper_)); EXPECT_TRUE(layers_->ConfigureBitrates(100, 1000, 5, NULL)); int flags = 0; uint32_t timestamp = 0; @@ -241,4 +257,37 @@ TEST_F(ScreenshareLayerTest, 2LayersBothDrops) { flags = layers_->EncodeFlags(timestamp); EXPECT_EQ(-1, flags); } + +TEST_F(ScreenshareLayerTest, TargetBitrateCappedByTL0) { + layers_.reset( + new ScreenshareLayersFT(2, 0, &tl0_frame_dropper_, &tl1_frame_dropper_)); + + vpx_codec_enc_cfg_t cfg; + layers_->ConfigureBitrates(100, 1000, 5, &cfg); + + EXPECT_EQ(static_cast( + ScreenshareLayers::kMaxTL0FpsReduction * 100 + 0.5), + cfg.rc_target_bitrate); +} + +TEST_F(ScreenshareLayerTest, TargetBitrateCappedByTL1) { + layers_.reset( + new ScreenshareLayersFT(2, 0, &tl0_frame_dropper_, &tl1_frame_dropper_)); + vpx_codec_enc_cfg_t cfg; + layers_->ConfigureBitrates(100, 450, 5, &cfg); + + EXPECT_EQ(static_cast( + 450 / ScreenshareLayers::kAcceptableTargetOvershoot), + cfg.rc_target_bitrate); +} + +TEST_F(ScreenshareLayerTest, TargetBitrateBelowTL0) { + layers_.reset( + new ScreenshareLayersFT(2, 0, &tl0_frame_dropper_, &tl1_frame_dropper_)); + vpx_codec_enc_cfg_t cfg; + layers_->ConfigureBitrates(100, 100, 5, &cfg); + + EXPECT_EQ(100U, cfg.rc_target_bitrate); +} + } // namespace webrtc