Add layer skipping to L2T2_KEY_SHIFT structure
Bug: None Change-Id: Iba019a999cb1812eee12bfe54c2f9ec9ebfa248f Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/189965 Commit-Queue: Danil Chapovalov <danilchap@webrtc.org> Reviewed-by: Philip Eliasson <philipel@webrtc.org> Cr-Commit-Position: refs/heads/master@{#32487}
This commit is contained in:

committed by
Commit Bot

parent
f01bd6c266
commit
71002a226a
@ -73,6 +73,7 @@ if (rtc_include_tests) {
|
|||||||
testonly = true
|
testonly = true
|
||||||
sources = [
|
sources = [
|
||||||
"scalability_structure_key_svc_unittest.cc",
|
"scalability_structure_key_svc_unittest.cc",
|
||||||
|
"scalability_structure_l2t2_key_shift_unittest.cc",
|
||||||
"scalability_structure_l3t3_unittest.cc",
|
"scalability_structure_l3t3_unittest.cc",
|
||||||
"scalability_structure_test_helpers.cc",
|
"scalability_structure_test_helpers.cc",
|
||||||
"scalability_structure_test_helpers.h",
|
"scalability_structure_test_helpers.h",
|
||||||
|
@ -20,21 +20,28 @@
|
|||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr auto kNotPresent = DecodeTargetIndication::kNotPresent;
|
DecodeTargetIndication
|
||||||
constexpr auto kDiscardable = DecodeTargetIndication::kDiscardable;
|
Dti(int sid, int tid, const ScalableVideoController::LayerFrameConfig& config) {
|
||||||
constexpr auto kSwitch = DecodeTargetIndication::kSwitch;
|
if (config.IsKeyframe()) {
|
||||||
|
RTC_DCHECK_EQ(config.TemporalId(), 0);
|
||||||
|
return sid < config.SpatialId() ? DecodeTargetIndication::kNotPresent
|
||||||
|
: DecodeTargetIndication::kSwitch;
|
||||||
|
}
|
||||||
|
|
||||||
constexpr DecodeTargetIndication kDtis[6][4] = {
|
if (sid != config.SpatialId() || tid < config.TemporalId()) {
|
||||||
{kSwitch, kSwitch, kSwitch, kSwitch}, // kKey, S0T0
|
return DecodeTargetIndication::kNotPresent;
|
||||||
{kNotPresent, kNotPresent, kSwitch, kSwitch}, // kKey, S1T0
|
}
|
||||||
{kSwitch, kSwitch, kNotPresent, kNotPresent}, // kDelta0, S0T0
|
if (tid == config.TemporalId() && tid > 0) {
|
||||||
{kNotPresent, kNotPresent, kNotPresent, kDiscardable}, // kDelta0, S1T1
|
return DecodeTargetIndication::kDiscardable;
|
||||||
{kNotPresent, kDiscardable, kNotPresent, kNotPresent}, // kDelta1, S0T1
|
}
|
||||||
{kNotPresent, kNotPresent, kSwitch, kSwitch}, // kDelta1, S1T0
|
return DecodeTargetIndication::kSwitch;
|
||||||
};
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
constexpr int ScalabilityStructureL2T2KeyShift::kNumSpatialLayers;
|
||||||
|
constexpr int ScalabilityStructureL2T2KeyShift::kNumTemporalLayers;
|
||||||
|
|
||||||
ScalabilityStructureL2T2KeyShift::~ScalabilityStructureL2T2KeyShift() = default;
|
ScalabilityStructureL2T2KeyShift::~ScalabilityStructureL2T2KeyShift() = default;
|
||||||
|
|
||||||
ScalableVideoController::StreamLayersConfig
|
ScalableVideoController::StreamLayersConfig
|
||||||
@ -65,52 +72,80 @@ FrameDependencyStructure ScalabilityStructureL2T2KeyShift::DependencyStructure()
|
|||||||
return structure;
|
return structure;
|
||||||
}
|
}
|
||||||
|
|
||||||
ScalableVideoController::LayerFrameConfig
|
|
||||||
ScalabilityStructureL2T2KeyShift::KeyFrameConfig() const {
|
|
||||||
return LayerFrameConfig().Id(0).Keyframe().S(0).T(0).Update(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<ScalableVideoController::LayerFrameConfig>
|
std::vector<ScalableVideoController::LayerFrameConfig>
|
||||||
ScalabilityStructureL2T2KeyShift::NextFrameConfig(bool restart) {
|
ScalabilityStructureL2T2KeyShift::NextFrameConfig(bool restart) {
|
||||||
|
std::vector<LayerFrameConfig> configs;
|
||||||
|
configs.reserve(2);
|
||||||
if (restart) {
|
if (restart) {
|
||||||
next_pattern_ = kKey;
|
next_pattern_ = kKey;
|
||||||
}
|
}
|
||||||
std::vector<LayerFrameConfig> result(2);
|
|
||||||
|
|
||||||
// Buffer0 keeps latest S0T0 frame,
|
// Buffer0 keeps latest S0T0 frame,
|
||||||
// Buffer1 keeps latest S1T0 frame.
|
// Buffer1 keeps latest S1T0 frame.
|
||||||
switch (next_pattern_) {
|
switch (next_pattern_) {
|
||||||
case kKey:
|
case kKey:
|
||||||
result[0] = KeyFrameConfig();
|
if (DecodeTargetIsActive(/*sid=*/0, /*tid=*/0)) {
|
||||||
result[1].Id(1).S(1).T(0).Reference(0).Update(1);
|
configs.emplace_back();
|
||||||
|
configs.back().S(0).T(0).Update(0).Keyframe();
|
||||||
|
}
|
||||||
|
if (DecodeTargetIsActive(/*sid=*/1, /*tid=*/0)) {
|
||||||
|
configs.emplace_back();
|
||||||
|
configs.back().S(1).T(0).Update(1);
|
||||||
|
if (DecodeTargetIsActive(/*sid=*/0, /*tid=*/0)) {
|
||||||
|
configs.back().Reference(0);
|
||||||
|
} else {
|
||||||
|
configs.back().Keyframe();
|
||||||
|
}
|
||||||
|
}
|
||||||
next_pattern_ = kDelta0;
|
next_pattern_ = kDelta0;
|
||||||
break;
|
break;
|
||||||
case kDelta0:
|
case kDelta0:
|
||||||
result[0].Id(2).S(0).T(0).ReferenceAndUpdate(0);
|
if (DecodeTargetIsActive(/*sid=*/0, /*tid=*/0)) {
|
||||||
result[1].Id(3).S(1).T(1).Reference(1);
|
configs.emplace_back();
|
||||||
|
configs.back().S(0).T(0).ReferenceAndUpdate(0);
|
||||||
|
}
|
||||||
|
if (DecodeTargetIsActive(/*sid=*/1, /*tid=*/1)) {
|
||||||
|
configs.emplace_back();
|
||||||
|
configs.back().S(1).T(1).Reference(1);
|
||||||
|
}
|
||||||
|
if (configs.empty() && DecodeTargetIsActive(/*sid=*/1, /*tid=*/0)) {
|
||||||
|
configs.emplace_back();
|
||||||
|
configs.back().S(1).T(0).ReferenceAndUpdate(1);
|
||||||
|
}
|
||||||
next_pattern_ = kDelta1;
|
next_pattern_ = kDelta1;
|
||||||
break;
|
break;
|
||||||
case kDelta1:
|
case kDelta1:
|
||||||
result[0].Id(4).S(0).T(1).Reference(0);
|
if (DecodeTargetIsActive(/*sid=*/0, /*tid=*/1)) {
|
||||||
result[1].Id(5).S(1).T(0).ReferenceAndUpdate(1);
|
configs.emplace_back();
|
||||||
|
configs.back().S(0).T(1).Reference(0);
|
||||||
|
}
|
||||||
|
if (DecodeTargetIsActive(/*sid=*/1, /*tid=*/0)) {
|
||||||
|
configs.emplace_back();
|
||||||
|
configs.back().S(1).T(0).ReferenceAndUpdate(1);
|
||||||
|
}
|
||||||
|
if (configs.empty() && DecodeTargetIsActive(/*sid=*/0, /*tid=*/0)) {
|
||||||
|
configs.emplace_back();
|
||||||
|
configs.back().S(0).T(0).ReferenceAndUpdate(0);
|
||||||
|
}
|
||||||
next_pattern_ = kDelta0;
|
next_pattern_ = kDelta0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
|
RTC_DCHECK(!configs.empty() || active_decode_targets_.none());
|
||||||
|
return configs;
|
||||||
}
|
}
|
||||||
|
|
||||||
GenericFrameInfo ScalabilityStructureL2T2KeyShift::OnEncodeDone(
|
GenericFrameInfo ScalabilityStructureL2T2KeyShift::OnEncodeDone(
|
||||||
const LayerFrameConfig& config) {
|
const LayerFrameConfig& config) {
|
||||||
RTC_CHECK_GE(config.Id(), 0);
|
|
||||||
RTC_CHECK_LT(config.Id(), ABSL_ARRAYSIZE(kDtis));
|
|
||||||
|
|
||||||
GenericFrameInfo frame_info;
|
GenericFrameInfo frame_info;
|
||||||
frame_info.spatial_id = config.SpatialId();
|
frame_info.spatial_id = config.SpatialId();
|
||||||
frame_info.temporal_id = config.TemporalId();
|
frame_info.temporal_id = config.TemporalId();
|
||||||
frame_info.encoder_buffers = config.Buffers();
|
frame_info.encoder_buffers = config.Buffers();
|
||||||
int config_id = config.IsKeyframe() ? 0 : config.Id();
|
for (int sid = 0; sid < kNumSpatialLayers; ++sid) {
|
||||||
frame_info.decode_target_indications.assign(std::begin(kDtis[config_id]),
|
for (int tid = 0; tid < kNumTemporalLayers; ++tid) {
|
||||||
std::end(kDtis[config_id]));
|
frame_info.decode_target_indications.push_back(Dti(sid, tid, config));
|
||||||
|
}
|
||||||
|
}
|
||||||
if (config.IsKeyframe()) {
|
if (config.IsKeyframe()) {
|
||||||
frame_info.part_of_chain = {true, true};
|
frame_info.part_of_chain = {true, true};
|
||||||
} else if (config.TemporalId() == 0) {
|
} else if (config.TemporalId() == 0) {
|
||||||
@ -122,4 +157,20 @@ GenericFrameInfo ScalabilityStructureL2T2KeyShift::OnEncodeDone(
|
|||||||
return frame_info;
|
return frame_info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ScalabilityStructureL2T2KeyShift::OnRatesUpdated(
|
||||||
|
const VideoBitrateAllocation& bitrates) {
|
||||||
|
for (int sid = 0; sid < kNumSpatialLayers; ++sid) {
|
||||||
|
// Enable/disable spatial layers independetely.
|
||||||
|
bool active = bitrates.GetBitrate(sid, /*tid=*/0) > 0;
|
||||||
|
if (!DecodeTargetIsActive(sid, /*tid=*/0) && active) {
|
||||||
|
// Key frame is required to reenable any spatial layer.
|
||||||
|
next_pattern_ = kKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetDecodeTargetIsActive(sid, /*tid=*/0, active);
|
||||||
|
SetDecodeTargetIsActive(sid, /*tid=*/1,
|
||||||
|
active && bitrates.GetBitrate(sid, /*tid=*/1) > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "api/transport/rtp/dependency_descriptor.h"
|
#include "api/transport/rtp/dependency_descriptor.h"
|
||||||
|
#include "api/video/video_bitrate_allocation.h"
|
||||||
#include "common_video/generic_frame_descriptor/generic_frame_info.h"
|
#include "common_video/generic_frame_descriptor/generic_frame_info.h"
|
||||||
#include "modules/video_coding/svc/scalable_video_controller.h"
|
#include "modules/video_coding/svc/scalable_video_controller.h"
|
||||||
|
|
||||||
@ -35,6 +36,7 @@ class ScalabilityStructureL2T2KeyShift : public ScalableVideoController {
|
|||||||
|
|
||||||
std::vector<LayerFrameConfig> NextFrameConfig(bool restart) override;
|
std::vector<LayerFrameConfig> NextFrameConfig(bool restart) override;
|
||||||
GenericFrameInfo OnEncodeDone(const LayerFrameConfig& config) override;
|
GenericFrameInfo OnEncodeDone(const LayerFrameConfig& config) override;
|
||||||
|
void OnRatesUpdated(const VideoBitrateAllocation& bitrates) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum FramePattern {
|
enum FramePattern {
|
||||||
@ -42,9 +44,19 @@ class ScalabilityStructureL2T2KeyShift : public ScalableVideoController {
|
|||||||
kDelta0,
|
kDelta0,
|
||||||
kDelta1,
|
kDelta1,
|
||||||
};
|
};
|
||||||
LayerFrameConfig KeyFrameConfig() const;
|
|
||||||
|
static constexpr int kNumSpatialLayers = 2;
|
||||||
|
static constexpr int kNumTemporalLayers = 2;
|
||||||
|
|
||||||
|
bool DecodeTargetIsActive(int sid, int tid) const {
|
||||||
|
return active_decode_targets_[sid * kNumTemporalLayers + tid];
|
||||||
|
}
|
||||||
|
void SetDecodeTargetIsActive(int sid, int tid, bool value) {
|
||||||
|
active_decode_targets_.set(sid * kNumTemporalLayers + tid, value);
|
||||||
|
}
|
||||||
|
|
||||||
FramePattern next_pattern_ = kKey;
|
FramePattern next_pattern_ = kKey;
|
||||||
|
std::bitset<32> active_decode_targets_ = 0b1111;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
@ -0,0 +1,358 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
#include "modules/video_coding/svc/scalability_structure_l2t2_key_shift.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "api/array_view.h"
|
||||||
|
#include "api/transport/rtp/dependency_descriptor.h"
|
||||||
|
#include "common_video/generic_frame_descriptor/generic_frame_info.h"
|
||||||
|
#include "modules/video_coding/svc/scalability_structure_test_helpers.h"
|
||||||
|
#include "test/gmock.h"
|
||||||
|
#include "test/gtest.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using ::testing::ElementsAre;
|
||||||
|
using ::testing::IsEmpty;
|
||||||
|
using ::testing::SizeIs;
|
||||||
|
|
||||||
|
// S1T1 3 7
|
||||||
|
// / /
|
||||||
|
// S1T0 1---5---9
|
||||||
|
// |
|
||||||
|
// S0T1 | 4 8
|
||||||
|
// | / /
|
||||||
|
// S0T0 0-2---6
|
||||||
|
// Time-> 0 1 2 3 4
|
||||||
|
TEST(ScalabilityStructureL2T2KeyShiftTest, DecodeTargetsAreEnabledByDefault) {
|
||||||
|
ScalabilityStructureL2T2KeyShift structure;
|
||||||
|
ScalabilityStructureWrapper wrapper(structure);
|
||||||
|
std::vector<GenericFrameInfo> frames;
|
||||||
|
wrapper.GenerateFrames(/*num_temporal_units=*/5, frames);
|
||||||
|
ASSERT_THAT(frames, SizeIs(10));
|
||||||
|
|
||||||
|
EXPECT_EQ(frames[0].spatial_id, 0);
|
||||||
|
EXPECT_EQ(frames[1].spatial_id, 1);
|
||||||
|
EXPECT_EQ(frames[2].spatial_id, 0);
|
||||||
|
EXPECT_EQ(frames[3].spatial_id, 1);
|
||||||
|
EXPECT_EQ(frames[4].spatial_id, 0);
|
||||||
|
EXPECT_EQ(frames[5].spatial_id, 1);
|
||||||
|
EXPECT_EQ(frames[6].spatial_id, 0);
|
||||||
|
EXPECT_EQ(frames[7].spatial_id, 1);
|
||||||
|
EXPECT_EQ(frames[8].spatial_id, 0);
|
||||||
|
EXPECT_EQ(frames[9].spatial_id, 1);
|
||||||
|
|
||||||
|
// spatial_id = 0 has the temporal shift.
|
||||||
|
EXPECT_EQ(frames[0].temporal_id, 0);
|
||||||
|
EXPECT_EQ(frames[2].temporal_id, 0);
|
||||||
|
EXPECT_EQ(frames[4].temporal_id, 1);
|
||||||
|
EXPECT_EQ(frames[6].temporal_id, 0);
|
||||||
|
EXPECT_EQ(frames[8].temporal_id, 1);
|
||||||
|
|
||||||
|
// spatial_id = 1 hasn't temporal shift.
|
||||||
|
EXPECT_EQ(frames[1].temporal_id, 0);
|
||||||
|
EXPECT_EQ(frames[3].temporal_id, 1);
|
||||||
|
EXPECT_EQ(frames[5].temporal_id, 0);
|
||||||
|
EXPECT_EQ(frames[7].temporal_id, 1);
|
||||||
|
EXPECT_EQ(frames[9].temporal_id, 0);
|
||||||
|
|
||||||
|
// Key frame diff.
|
||||||
|
EXPECT_THAT(frames[0].frame_diffs, IsEmpty());
|
||||||
|
EXPECT_THAT(frames[1].frame_diffs, ElementsAre(1));
|
||||||
|
// S0T0 frame diffs
|
||||||
|
EXPECT_THAT(frames[2].frame_diffs, ElementsAre(2));
|
||||||
|
EXPECT_THAT(frames[6].frame_diffs, ElementsAre(4));
|
||||||
|
// S1T0 frame diffs
|
||||||
|
EXPECT_THAT(frames[5].frame_diffs, ElementsAre(4));
|
||||||
|
EXPECT_THAT(frames[9].frame_diffs, ElementsAre(4));
|
||||||
|
// T1 frames refer T0 frame of same spatial layer which is 2 frame ids away.
|
||||||
|
EXPECT_THAT(frames[3].frame_diffs, ElementsAre(2));
|
||||||
|
EXPECT_THAT(frames[4].frame_diffs, ElementsAre(2));
|
||||||
|
EXPECT_THAT(frames[7].frame_diffs, ElementsAre(2));
|
||||||
|
EXPECT_THAT(frames[8].frame_diffs, ElementsAre(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// S1T0 1---4---7
|
||||||
|
// |
|
||||||
|
// S0T1 | 3 6
|
||||||
|
// | / /
|
||||||
|
// S0T0 0-2---5--
|
||||||
|
// Time-> 0 1 2 3 4
|
||||||
|
TEST(ScalabilityStructureL2T2KeyShiftTest, DisableS1T1Layer) {
|
||||||
|
ScalabilityStructureL2T2KeyShift structure;
|
||||||
|
structure.OnRatesUpdated(EnableTemporalLayers(/*s0=*/2, /*s1=*/1));
|
||||||
|
ScalabilityStructureWrapper wrapper(structure);
|
||||||
|
std::vector<GenericFrameInfo> frames;
|
||||||
|
wrapper.GenerateFrames(/*num_temporal_units=*/5, frames);
|
||||||
|
ASSERT_THAT(frames, SizeIs(8));
|
||||||
|
|
||||||
|
EXPECT_EQ(frames[0].spatial_id, 0);
|
||||||
|
EXPECT_EQ(frames[1].spatial_id, 1);
|
||||||
|
EXPECT_EQ(frames[2].spatial_id, 0);
|
||||||
|
EXPECT_EQ(frames[3].spatial_id, 0);
|
||||||
|
EXPECT_EQ(frames[4].spatial_id, 1);
|
||||||
|
EXPECT_EQ(frames[5].spatial_id, 0);
|
||||||
|
EXPECT_EQ(frames[6].spatial_id, 0);
|
||||||
|
EXPECT_EQ(frames[7].spatial_id, 1);
|
||||||
|
|
||||||
|
// spatial_id = 0 has the temporal shift.
|
||||||
|
EXPECT_EQ(frames[0].temporal_id, 0);
|
||||||
|
EXPECT_EQ(frames[2].temporal_id, 0);
|
||||||
|
EXPECT_EQ(frames[3].temporal_id, 1);
|
||||||
|
EXPECT_EQ(frames[5].temporal_id, 0);
|
||||||
|
EXPECT_EQ(frames[6].temporal_id, 1);
|
||||||
|
|
||||||
|
// spatial_id = 1 has single temporal layer.
|
||||||
|
EXPECT_EQ(frames[1].temporal_id, 0);
|
||||||
|
EXPECT_EQ(frames[4].temporal_id, 0);
|
||||||
|
EXPECT_EQ(frames[5].temporal_id, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// S1T1 3 |
|
||||||
|
// / |
|
||||||
|
// S1T0 1---5+--7
|
||||||
|
// | |
|
||||||
|
// S0T1 | 4|
|
||||||
|
// | / |
|
||||||
|
// S0T0 0-2--+6---8
|
||||||
|
// Time-> 0 1 2 3 4 5
|
||||||
|
TEST(ScalabilityStructureL2T2KeyShiftTest, DisableT1LayersAfterFewFrames) {
|
||||||
|
ScalabilityStructureL2T2KeyShift structure;
|
||||||
|
ScalabilityStructureWrapper wrapper(structure);
|
||||||
|
std::vector<GenericFrameInfo> frames;
|
||||||
|
|
||||||
|
wrapper.GenerateFrames(/*num_temporal_units=*/3, frames);
|
||||||
|
EXPECT_THAT(frames, SizeIs(6));
|
||||||
|
structure.OnRatesUpdated(EnableTemporalLayers(/*s0=*/1, /*s1=*/1));
|
||||||
|
wrapper.GenerateFrames(/*num_temporal_units=*/3, frames);
|
||||||
|
ASSERT_THAT(frames, SizeIs(9));
|
||||||
|
|
||||||
|
// Skip validation before T1 was disabled as that is covered by the test
|
||||||
|
// where no layers are disabled.
|
||||||
|
EXPECT_EQ(frames[6].spatial_id, 0);
|
||||||
|
EXPECT_EQ(frames[7].spatial_id, 1);
|
||||||
|
EXPECT_EQ(frames[8].spatial_id, 0);
|
||||||
|
|
||||||
|
EXPECT_EQ(frames[6].temporal_id, 0);
|
||||||
|
EXPECT_EQ(frames[7].temporal_id, 0);
|
||||||
|
EXPECT_EQ(frames[8].temporal_id, 0);
|
||||||
|
|
||||||
|
EXPECT_TRUE(wrapper.FrameReferencesAreValid(frames));
|
||||||
|
}
|
||||||
|
|
||||||
|
// S1T1 1 3
|
||||||
|
// / /
|
||||||
|
// S1T0 0---2
|
||||||
|
// Time-> 0 1 2 3 4 5
|
||||||
|
TEST(ScalabilityStructureL2T2KeyShiftTest, DisableS0FromTheStart) {
|
||||||
|
ScalabilityStructureL2T2KeyShift structure;
|
||||||
|
ScalabilityStructureWrapper wrapper(structure);
|
||||||
|
std::vector<GenericFrameInfo> frames;
|
||||||
|
|
||||||
|
structure.OnRatesUpdated(EnableTemporalLayers(/*s0=*/0, /*s1=*/2));
|
||||||
|
wrapper.GenerateFrames(/*num_temporal_units=*/4, frames);
|
||||||
|
EXPECT_THAT(frames, SizeIs(4));
|
||||||
|
|
||||||
|
EXPECT_EQ(frames[0].spatial_id, 1);
|
||||||
|
EXPECT_EQ(frames[1].spatial_id, 1);
|
||||||
|
EXPECT_EQ(frames[2].spatial_id, 1);
|
||||||
|
EXPECT_EQ(frames[3].spatial_id, 1);
|
||||||
|
|
||||||
|
EXPECT_EQ(frames[0].temporal_id, 0);
|
||||||
|
EXPECT_EQ(frames[1].temporal_id, 1);
|
||||||
|
EXPECT_EQ(frames[2].temporal_id, 0);
|
||||||
|
EXPECT_EQ(frames[3].temporal_id, 1);
|
||||||
|
|
||||||
|
EXPECT_TRUE(wrapper.FrameReferencesAreValid(frames));
|
||||||
|
}
|
||||||
|
|
||||||
|
// S1T1 3 |6 8
|
||||||
|
// / / /
|
||||||
|
// S1T0 1---5+--7
|
||||||
|
// | |
|
||||||
|
// S0T1 | 4|
|
||||||
|
// | / |
|
||||||
|
// S0T0 0-2 |
|
||||||
|
// Time-> 0 1 2 3 4 5
|
||||||
|
TEST(ScalabilityStructureL2T2KeyShiftTest, DisableS0AfterFewFrames) {
|
||||||
|
ScalabilityStructureL2T2KeyShift structure;
|
||||||
|
ScalabilityStructureWrapper wrapper(structure);
|
||||||
|
std::vector<GenericFrameInfo> frames;
|
||||||
|
|
||||||
|
wrapper.GenerateFrames(/*num_temporal_units=*/3, frames);
|
||||||
|
EXPECT_THAT(frames, SizeIs(6));
|
||||||
|
structure.OnRatesUpdated(EnableTemporalLayers(/*s0=*/0, /*s1=*/2));
|
||||||
|
wrapper.GenerateFrames(/*num_temporal_units=*/3, frames);
|
||||||
|
ASSERT_THAT(frames, SizeIs(9));
|
||||||
|
|
||||||
|
// Expect frame[6] is delta frame.
|
||||||
|
EXPECT_THAT(frames[6].frame_diffs, ElementsAre(1));
|
||||||
|
// Skip validation before S0 was disabled as that should be covered by
|
||||||
|
// test where no layers are disabled.
|
||||||
|
EXPECT_EQ(frames[6].spatial_id, 1);
|
||||||
|
EXPECT_EQ(frames[7].spatial_id, 1);
|
||||||
|
EXPECT_EQ(frames[8].spatial_id, 1);
|
||||||
|
|
||||||
|
EXPECT_EQ(frames[6].temporal_id, 1);
|
||||||
|
EXPECT_EQ(frames[7].temporal_id, 0);
|
||||||
|
EXPECT_EQ(frames[8].temporal_id, 1);
|
||||||
|
|
||||||
|
EXPECT_TRUE(wrapper.FrameReferencesAreValid(frames));
|
||||||
|
}
|
||||||
|
|
||||||
|
// S1T1 3| | 8
|
||||||
|
// / | | /
|
||||||
|
// S1T0 1 | |6
|
||||||
|
// | | ||
|
||||||
|
// S0T1 | |4||
|
||||||
|
// | / ||
|
||||||
|
// S0T0 0-2| |5-7
|
||||||
|
// Time-> 0 1 2 3 4 5
|
||||||
|
TEST(ScalabilityStructureL2T2KeyShiftTest, ReenableS1TriggersKeyFrame) {
|
||||||
|
ScalabilityStructureL2T2KeyShift structure;
|
||||||
|
ScalabilityStructureWrapper wrapper(structure);
|
||||||
|
std::vector<GenericFrameInfo> frames;
|
||||||
|
|
||||||
|
wrapper.GenerateFrames(/*num_temporal_units=*/2, frames);
|
||||||
|
EXPECT_THAT(frames, SizeIs(4));
|
||||||
|
|
||||||
|
structure.OnRatesUpdated(EnableTemporalLayers(/*s0=*/2, /*s1=*/0));
|
||||||
|
wrapper.GenerateFrames(/*num_temporal_units=*/1, frames);
|
||||||
|
EXPECT_THAT(frames, SizeIs(5));
|
||||||
|
|
||||||
|
structure.OnRatesUpdated(EnableTemporalLayers(/*s0=*/2, /*s1=*/2));
|
||||||
|
wrapper.GenerateFrames(/*num_temporal_units=*/2, frames);
|
||||||
|
ASSERT_THAT(frames, SizeIs(9));
|
||||||
|
|
||||||
|
EXPECT_THAT(frames[4].spatial_id, 0);
|
||||||
|
EXPECT_THAT(frames[4].temporal_id, 1);
|
||||||
|
|
||||||
|
// Expect frame[5] to be a key frame.
|
||||||
|
EXPECT_TRUE(wrapper.FrameReferencesAreValid(
|
||||||
|
rtc::MakeArrayView(frames.data() + 5, 4)));
|
||||||
|
|
||||||
|
EXPECT_THAT(frames[5].spatial_id, 0);
|
||||||
|
EXPECT_THAT(frames[6].spatial_id, 1);
|
||||||
|
EXPECT_THAT(frames[7].spatial_id, 0);
|
||||||
|
EXPECT_THAT(frames[8].spatial_id, 1);
|
||||||
|
|
||||||
|
// S0 should do temporal shift after the key frame.
|
||||||
|
EXPECT_THAT(frames[5].temporal_id, 0);
|
||||||
|
EXPECT_THAT(frames[7].temporal_id, 0);
|
||||||
|
|
||||||
|
// No temporal shift for the top spatial layer.
|
||||||
|
EXPECT_THAT(frames[6].temporal_id, 0);
|
||||||
|
EXPECT_THAT(frames[8].temporal_id, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ScalabilityStructureL2T2KeyShiftTest, EnableOnlyS0T0FromTheStart) {
|
||||||
|
ScalabilityStructureL2T2KeyShift structure;
|
||||||
|
ScalabilityStructureWrapper wrapper(structure);
|
||||||
|
std::vector<GenericFrameInfo> frames;
|
||||||
|
|
||||||
|
structure.OnRatesUpdated(EnableTemporalLayers(/*s0=*/1, /*s1=*/0));
|
||||||
|
wrapper.GenerateFrames(/*num_temporal_units=*/3, frames);
|
||||||
|
ASSERT_THAT(frames, SizeIs(3));
|
||||||
|
|
||||||
|
EXPECT_EQ(frames[0].spatial_id, 0);
|
||||||
|
EXPECT_EQ(frames[1].spatial_id, 0);
|
||||||
|
EXPECT_EQ(frames[2].spatial_id, 0);
|
||||||
|
|
||||||
|
EXPECT_EQ(frames[0].temporal_id, 0);
|
||||||
|
EXPECT_EQ(frames[1].temporal_id, 0);
|
||||||
|
EXPECT_EQ(frames[2].temporal_id, 0);
|
||||||
|
|
||||||
|
EXPECT_TRUE(wrapper.FrameReferencesAreValid(frames));
|
||||||
|
}
|
||||||
|
|
||||||
|
// S1T1 3|
|
||||||
|
// / |
|
||||||
|
// S1T0 1 |
|
||||||
|
// | |
|
||||||
|
// S0T1 | |
|
||||||
|
// | |
|
||||||
|
// S0T0 0-2+4-5-6
|
||||||
|
// Time-> 0 1 2 3 4
|
||||||
|
TEST(ScalabilityStructureL2T2KeyShiftTest, EnableOnlyS0T0AfterFewFrames) {
|
||||||
|
ScalabilityStructureL2T2KeyShift structure;
|
||||||
|
ScalabilityStructureWrapper wrapper(structure);
|
||||||
|
std::vector<GenericFrameInfo> frames;
|
||||||
|
|
||||||
|
wrapper.GenerateFrames(/*num_temporal_units=*/2, frames);
|
||||||
|
EXPECT_THAT(frames, SizeIs(4));
|
||||||
|
structure.OnRatesUpdated(EnableTemporalLayers(/*s0=*/1, /*s1=*/0));
|
||||||
|
wrapper.GenerateFrames(/*num_temporal_units=*/3, frames);
|
||||||
|
ASSERT_THAT(frames, SizeIs(7));
|
||||||
|
|
||||||
|
EXPECT_EQ(frames[4].spatial_id, 0);
|
||||||
|
EXPECT_EQ(frames[5].spatial_id, 0);
|
||||||
|
EXPECT_EQ(frames[6].spatial_id, 0);
|
||||||
|
|
||||||
|
EXPECT_EQ(frames[4].temporal_id, 0);
|
||||||
|
EXPECT_EQ(frames[5].temporal_id, 0);
|
||||||
|
EXPECT_EQ(frames[6].temporal_id, 0);
|
||||||
|
|
||||||
|
EXPECT_TRUE(wrapper.FrameReferencesAreValid(frames));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(ScalabilityStructureL2T2KeyShiftTest, EnableOnlyS1T0FromTheStart) {
|
||||||
|
ScalabilityStructureL2T2KeyShift structure;
|
||||||
|
ScalabilityStructureWrapper wrapper(structure);
|
||||||
|
std::vector<GenericFrameInfo> frames;
|
||||||
|
|
||||||
|
structure.OnRatesUpdated(EnableTemporalLayers(/*s0=*/0, /*s1=*/1));
|
||||||
|
wrapper.GenerateFrames(/*num_temporal_units=*/3, frames);
|
||||||
|
ASSERT_THAT(frames, SizeIs(3));
|
||||||
|
|
||||||
|
EXPECT_EQ(frames[0].spatial_id, 1);
|
||||||
|
EXPECT_EQ(frames[1].spatial_id, 1);
|
||||||
|
EXPECT_EQ(frames[2].spatial_id, 1);
|
||||||
|
|
||||||
|
EXPECT_EQ(frames[0].temporal_id, 0);
|
||||||
|
EXPECT_EQ(frames[1].temporal_id, 0);
|
||||||
|
EXPECT_EQ(frames[2].temporal_id, 0);
|
||||||
|
|
||||||
|
EXPECT_TRUE(wrapper.FrameReferencesAreValid(frames));
|
||||||
|
}
|
||||||
|
|
||||||
|
// S1T1 3|
|
||||||
|
// / |
|
||||||
|
// S1T0 1--+4-5-6
|
||||||
|
// | |
|
||||||
|
// S0T1 | |
|
||||||
|
// | |
|
||||||
|
// S0T0 0-2|
|
||||||
|
// Time-> 0 1 2 3 4
|
||||||
|
TEST(ScalabilityStructureL2T2KeyShiftTest, EnableOnlyS1T0AfterFewFrames) {
|
||||||
|
ScalabilityStructureL2T2KeyShift structure;
|
||||||
|
ScalabilityStructureWrapper wrapper(structure);
|
||||||
|
std::vector<GenericFrameInfo> frames;
|
||||||
|
|
||||||
|
wrapper.GenerateFrames(/*num_temporal_units=*/2, frames);
|
||||||
|
EXPECT_THAT(frames, SizeIs(4));
|
||||||
|
structure.OnRatesUpdated(EnableTemporalLayers(/*s0=*/0, /*s1=*/1));
|
||||||
|
wrapper.GenerateFrames(/*num_temporal_units=*/3, frames);
|
||||||
|
ASSERT_THAT(frames, SizeIs(7));
|
||||||
|
|
||||||
|
EXPECT_EQ(frames[4].spatial_id, 1);
|
||||||
|
EXPECT_EQ(frames[5].spatial_id, 1);
|
||||||
|
EXPECT_EQ(frames[6].spatial_id, 1);
|
||||||
|
|
||||||
|
EXPECT_EQ(frames[4].temporal_id, 0);
|
||||||
|
EXPECT_EQ(frames[5].temporal_id, 0);
|
||||||
|
EXPECT_EQ(frames[6].temporal_id, 0);
|
||||||
|
|
||||||
|
EXPECT_TRUE(wrapper.FrameReferencesAreValid(frames));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace webrtc
|
@ -250,11 +250,7 @@ TEST_P(ScalabilityStructureTest, NoFrameDependsThroughSwitchIndication) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(danilchap): Merge with ScalabilityStructureTest when SetRates
|
TEST_P(ScalabilityStructureTest, ProduceNoFrameForDisabledLayers) {
|
||||||
// implemented for all tested structures.
|
|
||||||
class ScalabilityStructureSetRatesTest : public ScalabilityStructureTest {};
|
|
||||||
|
|
||||||
TEST_P(ScalabilityStructureSetRatesTest, ProduceNoFrameForDisabledLayers) {
|
|
||||||
std::unique_ptr<ScalableVideoController> svc_controller =
|
std::unique_ptr<ScalableVideoController> svc_controller =
|
||||||
CreateScalabilityStructure(GetParam().name);
|
CreateScalabilityStructure(GetParam().name);
|
||||||
ScalableVideoController::StreamLayersConfig structure =
|
ScalableVideoController::StreamLayersConfig structure =
|
||||||
@ -311,24 +307,5 @@ INSTANTIATE_TEST_SUITE_P(
|
|||||||
return info.param.name;
|
return info.param.name;
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO(danilchap): Merge with ScalabilityStructureTest when the functionality
|
|
||||||
// is implemented for all tested structures.
|
|
||||||
INSTANTIATE_TEST_SUITE_P(
|
|
||||||
Svc,
|
|
||||||
ScalabilityStructureSetRatesTest,
|
|
||||||
Values(SvcTestParam{"L1T2", /*num_temporal_units=*/4},
|
|
||||||
SvcTestParam{"L1T3", /*num_temporal_units=*/8},
|
|
||||||
SvcTestParam{"L2T1", /*num_temporal_units=*/3},
|
|
||||||
SvcTestParam{"L2T1_KEY", /*num_temporal_units=*/3},
|
|
||||||
SvcTestParam{"L2T2", /*num_temporal_units=*/4},
|
|
||||||
SvcTestParam{"L2T2_KEY", /*num_temporal_units=*/4},
|
|
||||||
SvcTestParam{"L3T1", /*num_temporal_units=*/3},
|
|
||||||
SvcTestParam{"L3T3", /*num_temporal_units=*/8},
|
|
||||||
SvcTestParam{"L3T3_KEY", /*num_temporal_units=*/8},
|
|
||||||
SvcTestParam{"S2T1", /*num_temporal_units=*/3}),
|
|
||||||
[](const testing::TestParamInfo<SvcTestParam>& info) {
|
|
||||||
return info.param.name;
|
|
||||||
});
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
Reference in New Issue
Block a user