Base screenshare layers on TemporalReferences.

Decouples encode flags and calculates them the same for both default and
screencast temporal layers.

With this change encoders could start using TemporalReferences for
temporal-layers flags, but they can not be used by asynchronous encoders
(hardware encoders) yet.

Also removes 'timestamp' as a dead parameter to FrameEncoded().

BUG=chromium:702017, webrtc:7349
R=marpan@google.com, sprang@webrtc.org, marpan@webrtc.org

Review-Url: https://codereview.webrtc.org/2769263002 .
Cr-Commit-Position: refs/heads/master@{#17397}
This commit is contained in:
Peter Boström
2017-03-27 15:01:49 -04:00
parent d8cfa1af38
commit 1436c83cd1
8 changed files with 166 additions and 161 deletions

View File

@ -25,14 +25,14 @@
namespace webrtc {
TemporalReferences::TemporalReferences(TemporalBufferUsage last,
TemporalBufferUsage golden,
TemporalBufferUsage arf)
TemporalReferences::TemporalReferences(TemporalBufferFlags last,
TemporalBufferFlags golden,
TemporalBufferFlags arf)
: TemporalReferences(last, golden, arf, false, false) {}
TemporalReferences::TemporalReferences(TemporalBufferUsage last,
TemporalBufferUsage golden,
TemporalBufferUsage arf,
TemporalReferences::TemporalReferences(TemporalBufferFlags last,
TemporalBufferFlags golden,
TemporalBufferFlags arf,
int extra_flags)
: TemporalReferences(last,
golden,
@ -40,17 +40,15 @@ TemporalReferences::TemporalReferences(TemporalBufferUsage last,
(extra_flags & kLayerSync) != 0,
(extra_flags & kFreezeEntropy) != 0) {}
TemporalReferences::TemporalReferences(TemporalBufferUsage last,
TemporalBufferUsage golden,
TemporalBufferUsage arf,
TemporalReferences::TemporalReferences(TemporalBufferFlags last,
TemporalBufferFlags golden,
TemporalBufferFlags arf,
bool layer_sync,
bool freeze_entropy)
: reference_last((last & kReference) != 0),
update_last((last & kUpdate) != 0),
reference_golden((golden & kReference) != 0),
update_golden((golden & kUpdate) != 0),
reference_arf((arf & kReference) != 0),
update_arf((arf & kUpdate) != 0),
: drop_frame(last == kNone && golden == kNone && arf == kNone),
last_buffer_flags(last),
golden_buffer_flags(golden),
arf_buffer_flags(arf),
layer_sync(layer_sync),
freeze_entropy(freeze_entropy) {}
@ -246,26 +244,32 @@ bool DefaultTemporalLayers::UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) {
return true;
}
int DefaultTemporalLayers::EncodeFlags(uint32_t timestamp) {
// TODO(pbos): Name method so that it's obvious that it updates state.
TemporalReferences DefaultTemporalLayers::UpdateLayerConfig(
uint32_t timestamp) {
RTC_DCHECK_GT(num_layers_, 0);
RTC_DCHECK_LT(0, temporal_pattern_.size());
int flags = 0;
// TODO(pbos): Move pattern-update out of EncodeFlags. It's not obvious that
// EncodeFlags() is non-const.
const TemporalReferences& references =
temporal_pattern_[++pattern_idx_ % temporal_pattern_.size()];
return temporal_pattern_[++pattern_idx_ % temporal_pattern_.size()];
}
if (!references.reference_last)
int TemporalLayers::EncodeFlags(uint32_t timestamp) {
TemporalReferences references = UpdateLayerConfig(timestamp);
if (references.drop_frame)
return -1;
int flags = 0;
if ((references.last_buffer_flags & kReference) == 0)
flags |= VP8_EFLAG_NO_REF_LAST;
if (!references.update_last)
if ((references.last_buffer_flags & kUpdate) == 0)
flags |= VP8_EFLAG_NO_UPD_LAST;
if (!references.reference_golden)
if ((references.golden_buffer_flags & kReference) == 0)
flags |= VP8_EFLAG_NO_REF_GF;
if (!references.update_golden)
if ((references.golden_buffer_flags & kUpdate) == 0)
flags |= VP8_EFLAG_NO_UPD_GF;
if (!references.reference_arf)
if ((references.arf_buffer_flags & kReference) == 0)
flags |= VP8_EFLAG_NO_REF_ARF;
if (!references.update_arf)
if ((references.arf_buffer_flags & kUpdate) == 0)
flags |= VP8_EFLAG_NO_UPD_ARF;
if (references.freeze_entropy)
flags |= VP8_EFLAG_NO_UPD_ENTROPY;
@ -274,7 +278,7 @@ int DefaultTemporalLayers::EncodeFlags(uint32_t timestamp) {
}
void DefaultTemporalLayers::PopulateCodecSpecific(
bool base_layer_sync,
bool frame_is_keyframe,
CodecSpecificInfoVP8* vp8_info,
uint32_t timestamp) {
RTC_DCHECK_GT(num_layers_, 0);
@ -284,7 +288,7 @@ void DefaultTemporalLayers::PopulateCodecSpecific(
vp8_info->layerSync = false;
vp8_info->tl0PicIdx = kNoTl0PicIdx;
} else {
if (base_layer_sync) {
if (frame_is_keyframe) {
vp8_info->temporalIdx = 0;
vp8_info->layerSync = true;
} else {
@ -303,7 +307,7 @@ void DefaultTemporalLayers::PopulateCodecSpecific(
timestamp_ = timestamp;
tl0_pic_idx_++;
}
last_base_layer_sync_ = base_layer_sync;
last_base_layer_sync_ = frame_is_keyframe;
vp8_info->tl0PicIdx = tl0_pic_idx_;
}
}

View File

@ -20,44 +20,6 @@
namespace webrtc {
enum TemporalBufferUsage {
kNone = 0,
kReference = 1,
kUpdate = 2,
kReferenceAndUpdate = kReference | kUpdate,
};
enum TemporalFlags { kLayerSync = 1, kFreezeEntropy = 2 };
struct TemporalReferences {
TemporalReferences(TemporalBufferUsage last,
TemporalBufferUsage golden,
TemporalBufferUsage arf);
TemporalReferences(TemporalBufferUsage last,
TemporalBufferUsage golden,
TemporalBufferUsage arf,
int extra_flags);
const bool reference_last;
const bool update_last;
const bool reference_golden;
const bool update_golden;
const bool reference_arf;
const bool update_arf;
// TODO(pbos): Consider breaking these out of here and returning only a
// pattern index that needs to be returned to fill CodecSpecificInfoVP8 or
// EncodeFlags.
const bool layer_sync;
const bool freeze_entropy;
private:
TemporalReferences(TemporalBufferUsage last,
TemporalBufferUsage golden,
TemporalBufferUsage arf,
bool layer_sync,
bool freeze_entropy);
};
class DefaultTemporalLayers : public TemporalLayers {
public:
DefaultTemporalLayers(int number_of_temporal_layers,
@ -66,7 +28,7 @@ class DefaultTemporalLayers : public TemporalLayers {
// Returns the recommended VP8 encode flags needed. May refresh the decoder
// and/or update the reference buffers.
int EncodeFlags(uint32_t timestamp) override;
TemporalReferences UpdateLayerConfig(uint32_t timestamp) override;
// Update state based on new bitrate target and incoming framerate.
// Returns the bitrate allocation for the active temporal layers.
@ -76,11 +38,11 @@ class DefaultTemporalLayers : public TemporalLayers {
bool UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) override;
void PopulateCodecSpecific(bool base_layer_sync,
void PopulateCodecSpecific(bool frame_is_keyframe,
CodecSpecificInfoVP8* vp8_info,
uint32_t timestamp) override;
void FrameEncoded(unsigned int size, uint32_t timestamp, int qp) override {}
void FrameEncoded(unsigned int size, int qp) override {}
int CurrentLayerId() const override;

View File

@ -32,23 +32,6 @@ const double ScreenshareLayers::kAcceptableTargetOvershoot = 2.0;
constexpr int ScreenshareLayers::kMaxNumTemporalLayers;
// Since this is TL0 we only allow updating and predicting from the LAST
// reference frame.
const int ScreenshareLayers::kTl0Flags =
VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_REF_GF |
VP8_EFLAG_NO_REF_ARF;
// Allow predicting from both TL0 and TL1.
const int ScreenshareLayers::kTl1Flags =
VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST;
// Allow predicting from only TL0 to allow participants to switch to the high
// bitrate stream. This means predicting only from the LAST reference frame, but
// only updating GF to not corrupt TL0.
const int ScreenshareLayers::kTl1SyncFlags =
VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_UPD_ARF |
VP8_EFLAG_NO_UPD_LAST;
// Always emit a frame with certain interval, even if bitrate targets have
// been exceeded.
const int ScreenshareLayers::kMaxFrameIntervalMs = 2000;
@ -100,24 +83,27 @@ int ScreenshareLayers::CurrentLayerId() const {
return 0;
}
int ScreenshareLayers::EncodeFlags(uint32_t timestamp) {
TemporalReferences ScreenshareLayers::UpdateLayerConfig(uint32_t timestamp) {
if (number_of_temporal_layers_ <= 1) {
// No flags needed for 1 layer screenshare.
return 0;
// TODO(pbos): Consider updating only last, and not all buffers.
return TemporalReferences(kReferenceAndUpdate, kReferenceAndUpdate,
kReferenceAndUpdate);
}
const int64_t now_ms = clock_->TimeInMilliseconds();
if (target_framerate_.value_or(0) > 0 &&
encode_framerate_.Rate(now_ms).value_or(0) > *target_framerate_) {
// Max framerate exceeded, drop frame.
return -1;
return TemporalReferences(kNone, kNone, kNone);
}
if (stats_.first_frame_time_ms_ == -1)
stats_.first_frame_time_ms_ = now_ms;
int64_t unwrapped_timestamp = time_wrap_handler_.Unwrap(timestamp);
int flags = 0;
enum TemporalLayerState { kDrop, kTl0, kTl1, kTl1Sync };
enum TemporalLayerState layer_state = kDrop;
if (active_layer_ == -1 ||
layers_[active_layer_].state != TemporalLayer::State::kDropped) {
if (last_emitted_tl0_timestamp_ != -1 &&
@ -142,23 +128,22 @@ int ScreenshareLayers::EncodeFlags(uint32_t timestamp) {
switch (active_layer_) {
case 0:
flags = kTl0Flags;
layer_state = kTl0;
last_emitted_tl0_timestamp_ = unwrapped_timestamp;
break;
case 1:
if (TimeToSync(unwrapped_timestamp)) {
last_sync_timestamp_ = unwrapped_timestamp;
flags = kTl1SyncFlags;
layer_state = kTl1Sync;
} else {
flags = kTl1Flags;
layer_state = kTl1;
}
break;
case -1:
flags = -1;
layer_state = kDrop;
++stats_.num_dropped_frames_;
break;
default:
flags = -1;
RTC_NOTREACHED();
}
@ -172,7 +157,25 @@ int ScreenshareLayers::EncodeFlags(uint32_t timestamp) {
layers_[0].UpdateDebt(ts_diff / 90);
layers_[1].UpdateDebt(ts_diff / 90);
last_timestamp_ = timestamp;
return flags;
// TODO(pbos): Consider referencing but not updating the 'alt' buffer for all
// layers.
switch (layer_state) {
case kDrop:
return TemporalReferences(kNone, kNone, kNone);
case kTl0:
// TL0 only references and updates 'last'.
return TemporalReferences(kReferenceAndUpdate, kNone, kNone);
case kTl1:
// TL1 references both 'last' and 'golden' but only updates 'golden'.
return TemporalReferences(kReference, kReferenceAndUpdate, kNone);
case kTl1Sync:
// Predict from only TL0 to allow participants to switch to the high
// bitrate stream. Updates 'golden' so that TL1 can continue to refer to
// and update 'golden' from this point on.
return TemporalReferences(kReference, kUpdate, kNone, kLayerSync);
}
RTC_NOTREACHED();
return TemporalReferences(kNone, kNone, kNone);
}
std::vector<uint32_t> ScreenshareLayers::OnRatesUpdated(int bitrate_kbps,
@ -208,9 +211,7 @@ std::vector<uint32_t> ScreenshareLayers::OnRatesUpdated(int bitrate_kbps,
return allocation;
}
void ScreenshareLayers::FrameEncoded(unsigned int size,
uint32_t timestamp,
int qp) {
void ScreenshareLayers::FrameEncoded(unsigned int size, int qp) {
if (size > 0)
encode_framerate_.Update(1, clock_->TimeInMilliseconds());
@ -245,7 +246,7 @@ void ScreenshareLayers::FrameEncoded(unsigned int size,
}
}
void ScreenshareLayers::PopulateCodecSpecific(bool base_layer_sync,
void ScreenshareLayers::PopulateCodecSpecific(bool frame_is_keyframe,
CodecSpecificInfoVP8* vp8_info,
uint32_t timestamp) {
int64_t unwrapped_timestamp = time_wrap_handler_.Unwrap(timestamp);
@ -256,7 +257,7 @@ void ScreenshareLayers::PopulateCodecSpecific(bool base_layer_sync,
} else {
RTC_DCHECK_NE(-1, active_layer_);
vp8_info->temporalIdx = active_layer_;
if (base_layer_sync) {
if (frame_is_keyframe) {
vp8_info->temporalIdx = 0;
last_sync_timestamp_ = unwrapped_timestamp;
} else if (last_base_layer_sync_ && vp8_info->temporalIdx != 0) {
@ -269,7 +270,7 @@ void ScreenshareLayers::PopulateCodecSpecific(bool base_layer_sync,
if (vp8_info->temporalIdx == 0) {
tl0_pic_idx_++;
}
last_base_layer_sync_ = base_layer_sync;
last_base_layer_sync_ = frame_is_keyframe;
vp8_info->tl0PicIdx = tl0_pic_idx_;
}
}

View File

@ -26,9 +26,6 @@ class ScreenshareLayers : public TemporalLayers {
public:
static const double kMaxTL0FpsReduction;
static const double kAcceptableTargetOvershoot;
static const int kTl0Flags;
static const int kTl1Flags;
static const int kTl1SyncFlags;
static const int kMaxFrameIntervalMs;
ScreenshareLayers(int num_temporal_layers,
@ -38,7 +35,7 @@ class ScreenshareLayers : public TemporalLayers {
// Returns the recommended VP8 encode flags needed. May refresh the decoder
// and/or update the reference buffers.
int EncodeFlags(uint32_t timestamp) override;
TemporalReferences UpdateLayerConfig(uint32_t timestamp) override;
// Update state based on new bitrate target and incoming framerate.
// Returns the bitrate allocation for the active temporal layers.
@ -54,7 +51,7 @@ class ScreenshareLayers : public TemporalLayers {
CodecSpecificInfoVP8* vp8_info,
uint32_t timestamp) override;
void FrameEncoded(unsigned int size, uint32_t timestamp, int qp) override;
void FrameEncoded(unsigned int size, int qp) override;
int CurrentLayerId() const override;

View File

@ -37,6 +37,14 @@ const int kFrameRate = 5;
const int kSyncPeriodSeconds = 5;
const int kMaxSyncPeriodSeconds = 10;
// Expected flags for corresponding temporal layers.
const int kTl0Flags = VP8_EFLAG_NO_UPD_GF | VP8_EFLAG_NO_UPD_ARF |
VP8_EFLAG_NO_REF_GF | VP8_EFLAG_NO_REF_ARF;
const int kTl1Flags =
VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST;
const int kTl1SyncFlags = VP8_EFLAG_NO_REF_ARF | VP8_EFLAG_NO_REF_GF |
VP8_EFLAG_NO_UPD_ARF | VP8_EFLAG_NO_UPD_LAST;
class ScreenshareLayerTest : public ::testing::Test {
protected:
ScreenshareLayerTest()
@ -54,7 +62,7 @@ class ScreenshareLayerTest : public ::testing::Test {
return;
layers_->PopulateCodecSpecific(base_sync, vp8_info, timestamp);
ASSERT_NE(-1, frame_size_);
layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp);
layers_->FrameEncoded(frame_size_, kDefaultQp);
}
void ConfigureBitrates() {
@ -103,7 +111,7 @@ class ScreenshareLayerTest : public ::testing::Test {
timestamp += kTimestampDelta5Fps;
layers_->PopulateCodecSpecific(false, &vp8_info, timestamp);
if (vp8_info.temporalIdx != layer) {
layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp);
layers_->FrameEncoded(frame_size_, kDefaultQp);
} else {
return timestamp;
}
@ -142,7 +150,7 @@ TEST_F(ScreenshareLayerTest, 1Layer) {
EXPECT_EQ(static_cast<uint8_t>(kNoTemporalIdx), vp8_info.temporalIdx);
EXPECT_FALSE(vp8_info.layerSync);
EXPECT_EQ(kNoTl0PicIdx, vp8_info.tl0PicIdx);
layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp);
layers_->FrameEncoded(frame_size_, kDefaultQp);
flags = layers_->EncodeFlags(timestamp);
EXPECT_EQ(kSingleLayerFlags, flags);
timestamp += kTimestampDelta5Fps;
@ -150,7 +158,7 @@ TEST_F(ScreenshareLayerTest, 1Layer) {
EXPECT_EQ(static_cast<uint8_t>(kNoTemporalIdx), vp8_info.temporalIdx);
EXPECT_FALSE(vp8_info.layerSync);
EXPECT_EQ(kNoTl0PicIdx, vp8_info.tl0PicIdx);
layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp);
layers_->FrameEncoded(frame_size_, kDefaultQp);
}
TEST_F(ScreenshareLayerTest, 2Layer) {
@ -160,7 +168,7 @@ TEST_F(ScreenshareLayerTest, 2Layer) {
uint8_t expected_tl0_idx = 0;
CodecSpecificInfoVP8 vp8_info;
EncodeFrame(timestamp, false, &vp8_info, &flags);
EXPECT_EQ(ScreenshareLayers::kTl0Flags, flags);
EXPECT_EQ(kTl0Flags, flags);
EXPECT_EQ(0, vp8_info.temporalIdx);
EXPECT_FALSE(vp8_info.layerSync);
++expected_tl0_idx;
@ -179,7 +187,7 @@ TEST_F(ScreenshareLayerTest, 2Layer) {
// First frame in TL0.
timestamp += kTimestampDelta5Fps;
EncodeFrame(timestamp, false, &vp8_info, &flags);
EXPECT_EQ(ScreenshareLayers::kTl0Flags, flags);
EXPECT_EQ(kTl0Flags, flags);
EXPECT_EQ(0, vp8_info.temporalIdx);
EXPECT_FALSE(vp8_info.layerSync);
++expected_tl0_idx;
@ -189,14 +197,14 @@ TEST_F(ScreenshareLayerTest, 2Layer) {
timestamp += kTimestampDelta5Fps;
EncodeFrame(timestamp, false, &vp8_info, &flags);
// First frame is sync frame.
EXPECT_EQ(ScreenshareLayers::kTl1SyncFlags, flags);
EXPECT_EQ(kTl1SyncFlags, flags);
EXPECT_EQ(1, vp8_info.temporalIdx);
EXPECT_TRUE(vp8_info.layerSync);
EXPECT_EQ(expected_tl0_idx, vp8_info.tl0PicIdx);
timestamp += kTimestampDelta5Fps;
EncodeFrame(timestamp, false, &vp8_info, &flags);
EXPECT_EQ(ScreenshareLayers::kTl1Flags, flags);
EXPECT_EQ(kTl1Flags, flags);
EXPECT_EQ(1, vp8_info.temporalIdx);
EXPECT_FALSE(vp8_info.layerSync);
EXPECT_EQ(expected_tl0_idx, vp8_info.tl0PicIdx);
@ -236,9 +244,9 @@ TEST_F(ScreenshareLayerTest, 2LayersSyncAfterTimeout) {
// Simulate TL1 being at least 8 qp steps better.
if (vp8_info.temporalIdx == 0) {
layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp);
layers_->FrameEncoded(frame_size_, kDefaultQp);
} else {
layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp - 8);
layers_->FrameEncoded(frame_size_, kDefaultQp - 8);
}
if (vp8_info.temporalIdx == 1 && vp8_info.layerSync)
@ -265,9 +273,9 @@ TEST_F(ScreenshareLayerTest, 2LayersSyncAfterSimilarQP) {
// Simulate TL1 being at least 8 qp steps better.
if (vp8_info.temporalIdx == 0) {
layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp);
layers_->FrameEncoded(frame_size_, kDefaultQp);
} else {
layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp - 8);
layers_->FrameEncoded(frame_size_, kDefaultQp - 8);
}
if (vp8_info.temporalIdx == 1 && vp8_info.layerSync)
@ -284,13 +292,13 @@ TEST_F(ScreenshareLayerTest, 2LayersSyncAfterSimilarQP) {
if (vp8_info.temporalIdx == 0) {
// Bump TL0 to same quality as TL1.
layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp - 8);
layers_->FrameEncoded(frame_size_, kDefaultQp - 8);
bumped_tl0_quality = true;
} else {
layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp - 8);
layers_->FrameEncoded(frame_size_, kDefaultQp - 8);
if (bumped_tl0_quality) {
EXPECT_TRUE(vp8_info.layerSync);
EXPECT_EQ(ScreenshareLayers::kTl1SyncFlags, flags);
EXPECT_EQ(kTl1SyncFlags, flags);
return;
}
}
@ -336,7 +344,7 @@ TEST_F(ScreenshareLayerTest, AllFitsLayer0) {
for (int i = 0; i < 50; ++i) {
EncodeFrame(timestamp, false, &vp8_info, &flags);
timestamp += kTimestampDelta5Fps;
EXPECT_EQ(ScreenshareLayers::kTl0Flags, flags);
EXPECT_EQ(kTl0Flags, flags);
EXPECT_EQ(0, vp8_info.temporalIdx);
}
}
@ -425,46 +433,46 @@ TEST_F(ScreenshareLayerTest, EncoderDrop) {
timestamp = SkipUntilTl(0, timestamp);
// Size 0 indicates dropped frame.
layers_->FrameEncoded(0, timestamp, kDefaultQp);
layers_->FrameEncoded(0, kDefaultQp);
timestamp += kTimestampDelta5Fps;
EXPECT_FALSE(layers_->UpdateConfiguration(&cfg));
EXPECT_EQ(ScreenshareLayers::kTl0Flags, layers_->EncodeFlags(timestamp));
EXPECT_EQ(kTl0Flags, layers_->EncodeFlags(timestamp));
layers_->PopulateCodecSpecific(false, &vp8_info, timestamp);
layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp);
layers_->FrameEncoded(frame_size_, kDefaultQp);
timestamp = SkipUntilTl(0, timestamp);
EXPECT_TRUE(layers_->UpdateConfiguration(&cfg));
EXPECT_LT(cfg.rc_max_quantizer, static_cast<unsigned int>(kDefaultQp));
layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp);
layers_->FrameEncoded(frame_size_, kDefaultQp);
layers_->EncodeFlags(timestamp);
timestamp += kTimestampDelta5Fps;
EXPECT_TRUE(layers_->UpdateConfiguration(&cfg));
layers_->PopulateCodecSpecific(false, &vp8_info, timestamp);
EXPECT_EQ(cfg.rc_max_quantizer, static_cast<unsigned int>(kDefaultQp));
layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp);
layers_->FrameEncoded(frame_size_, kDefaultQp);
// Next drop in TL1.
timestamp = SkipUntilTl(1, timestamp);
layers_->FrameEncoded(0, timestamp, kDefaultQp);
layers_->FrameEncoded(0, kDefaultQp);
timestamp += kTimestampDelta5Fps;
EXPECT_FALSE(layers_->UpdateConfiguration(&cfg));
EXPECT_EQ(ScreenshareLayers::kTl1Flags, layers_->EncodeFlags(timestamp));
EXPECT_EQ(kTl1Flags, layers_->EncodeFlags(timestamp));
layers_->PopulateCodecSpecific(false, &vp8_info, timestamp);
layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp);
layers_->FrameEncoded(frame_size_, kDefaultQp);
timestamp = SkipUntilTl(1, timestamp);
EXPECT_TRUE(layers_->UpdateConfiguration(&cfg));
EXPECT_LT(cfg.rc_max_quantizer, static_cast<unsigned int>(kDefaultQp));
layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp);
layers_->FrameEncoded(frame_size_, kDefaultQp);
layers_->EncodeFlags(timestamp);
timestamp += kTimestampDelta5Fps;
EXPECT_TRUE(layers_->UpdateConfiguration(&cfg));
layers_->PopulateCodecSpecific(false, &vp8_info, timestamp);
EXPECT_EQ(cfg.rc_max_quantizer, static_cast<unsigned int>(kDefaultQp));
layers_->FrameEncoded(frame_size_, timestamp, kDefaultQp);
layers_->FrameEncoded(frame_size_, kDefaultQp);
}
TEST_F(ScreenshareLayerTest, RespectsMaxIntervalBetweenFrames) {
@ -476,9 +484,8 @@ TEST_F(ScreenshareLayerTest, RespectsMaxIntervalBetweenFrames) {
layers_->OnRatesUpdated(kLowBitrateKbps, kLowBitrateKbps, 5);
layers_->UpdateConfiguration(&cfg);
EXPECT_EQ(ScreenshareLayers::kTl0Flags,
layers_->EncodeFlags(kStartTimestamp));
layers_->FrameEncoded(kLargeFrameSizeBytes, kStartTimestamp, kDefaultQp);
EXPECT_EQ(kTl0Flags, layers_->EncodeFlags(kStartTimestamp));
layers_->FrameEncoded(kLargeFrameSizeBytes, kDefaultQp);
const uint32_t kTwoSecondsLater =
kStartTimestamp + (ScreenshareLayers::kMaxFrameIntervalMs * 90);
@ -490,8 +497,7 @@ TEST_F(ScreenshareLayerTest, RespectsMaxIntervalBetweenFrames) {
EXPECT_EQ(-1, layers_->EncodeFlags(kTwoSecondsLater));
// More than two seconds has passed since last frame, one should be emitted
// even if bitrate target is then exceeded.
EXPECT_EQ(ScreenshareLayers::kTl0Flags,
layers_->EncodeFlags(kTwoSecondsLater + 90));
EXPECT_EQ(kTl0Flags, layers_->EncodeFlags(kTwoSecondsLater + 90));
}
TEST_F(ScreenshareLayerTest, UpdatesHistograms) {
@ -512,22 +518,21 @@ TEST_F(ScreenshareLayerTest, UpdatesHistograms) {
if (timestamp >= kTimestampDelta5Fps * 5 && !overshoot && flags != -1) {
// Simulate one overshoot.
layers_->FrameEncoded(0, timestamp, 0);
layers_->FrameEncoded(0, 0);
overshoot = true;
flags = layers_->EncodeFlags(timestamp);
}
if (flags == ScreenshareLayers::kTl0Flags) {
if (flags == kTl0Flags) {
if (timestamp >= kTimestampDelta5Fps * 20 && !trigger_drop) {
// Simulate a too large frame, to cause frame drop.
layers_->FrameEncoded(frame_size_ * 5, timestamp, kTl0Qp);
layers_->FrameEncoded(frame_size_ * 5, kTl0Qp);
trigger_drop = true;
} else {
layers_->FrameEncoded(frame_size_, timestamp, kTl0Qp);
layers_->FrameEncoded(frame_size_, kTl0Qp);
}
} else if (flags == ScreenshareLayers::kTl1Flags ||
flags == ScreenshareLayers::kTl1SyncFlags) {
layers_->FrameEncoded(frame_size_, timestamp, kTl1Qp);
} else if (flags == kTl1Flags || flags == kTl1SyncFlags) {
layers_->FrameEncoded(frame_size_, kTl1Qp);
} else if (flags == -1) {
dropped_frame = true;
} else {
@ -593,7 +598,7 @@ TEST_F(ScreenshareLayerTest, RespectsConfiguredFramerate) {
++num_discarded_frames;
} else {
size_t frame_size_bytes = kDefaultTl0BitrateKbps * kFrameIntervalsMs / 8;
layers_->FrameEncoded(frame_size_bytes, timestamp, kDefaultQp);
layers_->FrameEncoded(frame_size_bytes, kDefaultQp);
}
timestamp += kFrameIntervalsMs * 90;
clock_.AdvanceTimeMilliseconds(kFrameIntervalsMs);
@ -609,7 +614,7 @@ TEST_F(ScreenshareLayerTest, RespectsConfiguredFramerate) {
++num_discarded_frames;
} else {
size_t frame_size_bytes = kDefaultTl0BitrateKbps * kFrameIntervalsMs / 8;
layers_->FrameEncoded(frame_size_bytes, timestamp, kDefaultQp);
layers_->FrameEncoded(frame_size_bytes, kDefaultQp);
}
timestamp += kFrameIntervalsMs * 90 / 2;
clock_.AdvanceTimeMilliseconds(kFrameIntervalsMs / 2);

View File

@ -24,6 +24,43 @@ namespace webrtc {
struct CodecSpecificInfoVP8;
enum TemporalBufferFlags {
kNone = 0,
kReference = 1,
kUpdate = 2,
kReferenceAndUpdate = kReference | kUpdate,
};
enum TemporalFlags { kLayerSync = 1, kFreezeEntropy = 2 };
struct TemporalReferences {
TemporalReferences(TemporalBufferFlags last,
TemporalBufferFlags golden,
TemporalBufferFlags arf);
TemporalReferences(TemporalBufferFlags last,
TemporalBufferFlags golden,
TemporalBufferFlags arf,
int extra_flags);
const bool drop_frame;
const TemporalBufferFlags last_buffer_flags;
const TemporalBufferFlags golden_buffer_flags;
const TemporalBufferFlags arf_buffer_flags;
// TODO(pbos): Consider breaking these out of here and returning only a
// pattern index that needs to be returned to fill CodecSpecificInfoVP8 or
// EncodeFlags.
const bool layer_sync;
const bool freeze_entropy;
private:
TemporalReferences(TemporalBufferFlags last,
TemporalBufferFlags golden,
TemporalBufferFlags arf,
bool layer_sync,
bool freeze_entropy);
};
class TemporalLayers {
public:
// Factory for TemporalLayer strategy. Default behavior is a fixed pattern
@ -32,7 +69,9 @@ class TemporalLayers {
// Returns the recommended VP8 encode flags needed. May refresh the decoder
// and/or update the reference buffers.
virtual int EncodeFlags(uint32_t timestamp) = 0;
virtual TemporalReferences UpdateLayerConfig(uint32_t timestamp) = 0;
int EncodeFlags(uint32_t timestamp);
// Update state based on new bitrate target and incoming framerate.
// Returns the bitrate allocation for the active temporal layers.
@ -44,11 +83,11 @@ class TemporalLayers {
// Returns true iff the configuration was actually modified.
virtual bool UpdateConfiguration(vpx_codec_enc_cfg_t* cfg) = 0;
virtual void PopulateCodecSpecific(bool base_layer_sync,
virtual void PopulateCodecSpecific(bool is_keyframe,
CodecSpecificInfoVP8* vp8_info,
uint32_t timestamp) = 0;
virtual void FrameEncoded(unsigned int size, uint32_t timestamp, int qp) = 0;
virtual void FrameEncoded(unsigned int size, int qp) = 0;
virtual int CurrentLayerId() const = 0;
};

View File

@ -812,11 +812,9 @@ void VP8EncoderImpl::PopulateCodecSpecific(
}
vp8Info->simulcastIdx = stream_idx;
vp8Info->keyIdx = kNoKeyIdx; // TODO(hlundin) populate this
vp8Info->nonReference =
(pkt.data.frame.flags & VPX_FRAME_IS_DROPPABLE) ? true : false;
bool base_layer_sync_point = pkt.data.frame.flags & VPX_FRAME_IS_KEY;
temporal_layers_[stream_idx]->PopulateCodecSpecific(base_layer_sync_point,
vp8Info, timestamp);
vp8Info->nonReference = (pkt.data.frame.flags & VPX_FRAME_IS_DROPPABLE) != 0;
temporal_layers_[stream_idx]->PopulateCodecSpecific(
(pkt.data.frame.flags & VPX_FRAME_IS_KEY) != 0, vp8Info, timestamp);
// Prepare next.
picture_id_[stream_idx] = (picture_id_[stream_idx] + 1) & 0x7FFF;
}
@ -884,8 +882,7 @@ int VP8EncoderImpl::GetEncodedPartitions(const VideoFrame& input_image) {
int qp = -1;
vpx_codec_control(&encoders_[encoder_idx], VP8E_GET_LAST_QUANTIZER_64, &qp);
temporal_layers_[stream_idx]->FrameEncoded(
encoded_images_[encoder_idx]._length,
encoded_images_[encoder_idx]._timeStamp, qp);
encoded_images_[encoder_idx]._length, qp);
if (send_stream_[stream_idx]) {
if (encoded_images_[encoder_idx]._length > 0) {
TRACE_COUNTER_ID1("webrtc", "EncodedFrameSize", encoder_idx,