webrtc/modules/video_coding/svc/scalability_mode_util.cc
Evan Shrubsole fe24f58c73 Report proper VP9 scalability mode with layer activation.
This changes the libvpx VP9 encoder to generate the scalability mode based on the current encoding parameters when using layer activation.

Tested: Ran with L3T3_KEY reduced to L2T3_KEY and L1T3 due to bandwidth or layer activation. Added unit tests.
Bug: webrtc:15892
Change-Id: Iaedca4ea5fc3a692996666ceaf0d6aa03fb058a1
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/344760
Commit-Queue: Evan Shrubsole <eshr@google.com>
Reviewed-by: Ilya Nikolaevskiy <ilnik@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#42007}
2024-04-05 14:02:59 +00:00

496 lines
18 KiB
C++

/*
* Copyright (c) 2022 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_mode_util.h"
#include <array>
#include <utility>
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "api/video_codecs/scalability_mode.h"
#include "api/video_codecs/video_codec.h"
#include "rtc_base/checks.h"
namespace webrtc {
namespace {
struct ScalabilityModeParameters {
int num_spatial_layers;
int num_temporal_layers;
InterLayerPredMode inter_layer_pred;
absl::optional<ScalabilityModeResolutionRatio> ratio;
bool shift;
constexpr bool operator==(const ScalabilityModeParameters& other) const {
// For all L1Tx modes, ignore inter_layer_pred, ratio and shift.
if (this->num_spatial_layers == 1) {
return this->num_spatial_layers == other.num_spatial_layers &&
this->num_temporal_layers == other.num_temporal_layers;
}
return this->num_spatial_layers == other.num_spatial_layers &&
this->num_temporal_layers == other.num_temporal_layers &&
this->inter_layer_pred == other.inter_layer_pred &&
this->ratio == other.ratio && this->shift == other.shift;
}
};
struct ScalabilityModeConfiguration {
explicit ScalabilityModeConfiguration(ScalabilityMode scalability_mode)
: scalability_mode(scalability_mode),
params{
.num_spatial_layers =
(ScalabilityModeToNumSpatialLayers(scalability_mode)),
.num_temporal_layers =
(ScalabilityModeToNumTemporalLayers(scalability_mode)),
.inter_layer_pred =
(ScalabilityModeToInterLayerPredMode(scalability_mode)),
.ratio = (ScalabilityModeToResolutionRatio(scalability_mode)),
.shift = (ScalabilityModeIsShiftMode(scalability_mode)),
} {}
const ScalabilityMode scalability_mode;
const ScalabilityModeParameters params;
};
constexpr size_t kNumScalabilityModes =
static_cast<size_t>(ScalabilityMode::kS3T3h) + 1;
} // namespace
absl::optional<ScalabilityMode> MakeScalabilityMode(
int num_spatial_layers,
int num_temporal_layers,
InterLayerPredMode inter_layer_pred,
absl::optional<ScalabilityModeResolutionRatio> ratio,
bool shift) {
ScalabilityModeParameters params{num_spatial_layers, num_temporal_layers,
inter_layer_pred, std::move(ratio), shift};
static const ScalabilityModeConfiguration kScalabilityModeConfigs[] = {
ScalabilityModeConfiguration{ScalabilityMode::kL1T1},
ScalabilityModeConfiguration{ScalabilityMode::kL1T2},
ScalabilityModeConfiguration{ScalabilityMode::kL1T3},
ScalabilityModeConfiguration{ScalabilityMode::kL2T1},
ScalabilityModeConfiguration{ScalabilityMode::kL2T1h},
ScalabilityModeConfiguration{ScalabilityMode::kL2T1_KEY},
ScalabilityModeConfiguration{ScalabilityMode::kL2T2},
ScalabilityModeConfiguration{ScalabilityMode::kL2T2h},
ScalabilityModeConfiguration{ScalabilityMode::kL2T2_KEY},
ScalabilityModeConfiguration{ScalabilityMode::kL2T2_KEY_SHIFT},
ScalabilityModeConfiguration{ScalabilityMode::kL2T3},
ScalabilityModeConfiguration{ScalabilityMode::kL2T3h},
ScalabilityModeConfiguration{ScalabilityMode::kL2T3_KEY},
ScalabilityModeConfiguration{ScalabilityMode::kL3T1},
ScalabilityModeConfiguration{ScalabilityMode::kL3T1h},
ScalabilityModeConfiguration{ScalabilityMode::kL3T1_KEY},
ScalabilityModeConfiguration{ScalabilityMode::kL3T2},
ScalabilityModeConfiguration{ScalabilityMode::kL3T2h},
ScalabilityModeConfiguration{ScalabilityMode::kL3T2_KEY},
ScalabilityModeConfiguration{ScalabilityMode::kL3T3},
ScalabilityModeConfiguration{ScalabilityMode::kL3T3h},
ScalabilityModeConfiguration{ScalabilityMode::kL3T3_KEY},
ScalabilityModeConfiguration{ScalabilityMode::kS2T1},
ScalabilityModeConfiguration{ScalabilityMode::kS2T1h},
ScalabilityModeConfiguration{ScalabilityMode::kS2T2},
ScalabilityModeConfiguration{ScalabilityMode::kS2T2h},
ScalabilityModeConfiguration{ScalabilityMode::kS2T3},
ScalabilityModeConfiguration{ScalabilityMode::kS2T3h},
ScalabilityModeConfiguration{ScalabilityMode::kS3T1},
ScalabilityModeConfiguration{ScalabilityMode::kS3T1h},
ScalabilityModeConfiguration{ScalabilityMode::kS3T2},
ScalabilityModeConfiguration{ScalabilityMode::kS3T2h},
ScalabilityModeConfiguration{ScalabilityMode::kS3T3},
ScalabilityModeConfiguration{ScalabilityMode::kS3T3h},
};
static_assert(std::size(kScalabilityModeConfigs) == kNumScalabilityModes);
for (const auto& candidate_mode : kScalabilityModeConfigs) {
if (candidate_mode.params == params) {
return candidate_mode.scalability_mode;
}
}
return absl::nullopt;
}
absl::optional<ScalabilityMode> ScalabilityModeFromString(
absl::string_view mode_string) {
if (mode_string == "L1T1")
return ScalabilityMode::kL1T1;
if (mode_string == "L1T2")
return ScalabilityMode::kL1T2;
if (mode_string == "L1T3")
return ScalabilityMode::kL1T3;
if (mode_string == "L2T1")
return ScalabilityMode::kL2T1;
if (mode_string == "L2T1h")
return ScalabilityMode::kL2T1h;
if (mode_string == "L2T1_KEY")
return ScalabilityMode::kL2T1_KEY;
if (mode_string == "L2T2")
return ScalabilityMode::kL2T2;
if (mode_string == "L2T2h")
return ScalabilityMode::kL2T2h;
if (mode_string == "L2T2_KEY")
return ScalabilityMode::kL2T2_KEY;
if (mode_string == "L2T2_KEY_SHIFT")
return ScalabilityMode::kL2T2_KEY_SHIFT;
if (mode_string == "L2T3")
return ScalabilityMode::kL2T3;
if (mode_string == "L2T3h")
return ScalabilityMode::kL2T3h;
if (mode_string == "L2T3_KEY")
return ScalabilityMode::kL2T3_KEY;
if (mode_string == "L3T1")
return ScalabilityMode::kL3T1;
if (mode_string == "L3T1h")
return ScalabilityMode::kL3T1h;
if (mode_string == "L3T1_KEY")
return ScalabilityMode::kL3T1_KEY;
if (mode_string == "L3T2")
return ScalabilityMode::kL3T2;
if (mode_string == "L3T2h")
return ScalabilityMode::kL3T2h;
if (mode_string == "L3T2_KEY")
return ScalabilityMode::kL3T2_KEY;
if (mode_string == "L3T3")
return ScalabilityMode::kL3T3;
if (mode_string == "L3T3h")
return ScalabilityMode::kL3T3h;
if (mode_string == "L3T3_KEY")
return ScalabilityMode::kL3T3_KEY;
if (mode_string == "S2T1")
return ScalabilityMode::kS2T1;
if (mode_string == "S2T1h")
return ScalabilityMode::kS2T1h;
if (mode_string == "S2T2")
return ScalabilityMode::kS2T2;
if (mode_string == "S2T2h")
return ScalabilityMode::kS2T2h;
if (mode_string == "S2T3")
return ScalabilityMode::kS2T3;
if (mode_string == "S2T3h")
return ScalabilityMode::kS2T3h;
if (mode_string == "S3T1")
return ScalabilityMode::kS3T1;
if (mode_string == "S3T1h")
return ScalabilityMode::kS3T1h;
if (mode_string == "S3T2")
return ScalabilityMode::kS3T2;
if (mode_string == "S3T2h")
return ScalabilityMode::kS3T2h;
if (mode_string == "S3T3")
return ScalabilityMode::kS3T3;
if (mode_string == "S3T3h")
return ScalabilityMode::kS3T3h;
return absl::nullopt;
}
InterLayerPredMode ScalabilityModeToInterLayerPredMode(
ScalabilityMode scalability_mode) {
switch (scalability_mode) {
case ScalabilityMode::kL1T1:
case ScalabilityMode::kL1T2:
case ScalabilityMode::kL1T3:
case ScalabilityMode::kL2T1:
case ScalabilityMode::kL2T1h:
return InterLayerPredMode::kOn;
case ScalabilityMode::kL2T1_KEY:
return InterLayerPredMode::kOnKeyPic;
case ScalabilityMode::kL2T2:
case ScalabilityMode::kL2T2h:
return InterLayerPredMode::kOn;
case ScalabilityMode::kL2T2_KEY:
case ScalabilityMode::kL2T2_KEY_SHIFT:
return InterLayerPredMode::kOnKeyPic;
case ScalabilityMode::kL2T3:
case ScalabilityMode::kL2T3h:
return InterLayerPredMode::kOn;
case ScalabilityMode::kL2T3_KEY:
return InterLayerPredMode::kOnKeyPic;
case ScalabilityMode::kL3T1:
case ScalabilityMode::kL3T1h:
return InterLayerPredMode::kOn;
case ScalabilityMode::kL3T1_KEY:
return InterLayerPredMode::kOnKeyPic;
case ScalabilityMode::kL3T2:
case ScalabilityMode::kL3T2h:
return InterLayerPredMode::kOn;
case ScalabilityMode::kL3T2_KEY:
return InterLayerPredMode::kOnKeyPic;
case ScalabilityMode::kL3T3:
case ScalabilityMode::kL3T3h:
return InterLayerPredMode::kOn;
case ScalabilityMode::kL3T3_KEY:
return InterLayerPredMode::kOnKeyPic;
case ScalabilityMode::kS2T1:
case ScalabilityMode::kS2T1h:
case ScalabilityMode::kS2T2:
case ScalabilityMode::kS2T2h:
case ScalabilityMode::kS2T3:
case ScalabilityMode::kS2T3h:
case ScalabilityMode::kS3T1:
case ScalabilityMode::kS3T1h:
case ScalabilityMode::kS3T2:
case ScalabilityMode::kS3T2h:
case ScalabilityMode::kS3T3:
case ScalabilityMode::kS3T3h:
return InterLayerPredMode::kOff;
}
RTC_CHECK_NOTREACHED();
}
int ScalabilityModeToNumSpatialLayers(ScalabilityMode scalability_mode) {
switch (scalability_mode) {
case ScalabilityMode::kL1T1:
case ScalabilityMode::kL1T2:
case ScalabilityMode::kL1T3:
return 1;
case ScalabilityMode::kL2T1:
case ScalabilityMode::kL2T1h:
case ScalabilityMode::kL2T1_KEY:
case ScalabilityMode::kL2T2:
case ScalabilityMode::kL2T2h:
case ScalabilityMode::kL2T2_KEY:
case ScalabilityMode::kL2T2_KEY_SHIFT:
case ScalabilityMode::kL2T3:
case ScalabilityMode::kL2T3h:
case ScalabilityMode::kL2T3_KEY:
return 2;
case ScalabilityMode::kL3T1:
case ScalabilityMode::kL3T1h:
case ScalabilityMode::kL3T1_KEY:
case ScalabilityMode::kL3T2:
case ScalabilityMode::kL3T2h:
case ScalabilityMode::kL3T2_KEY:
case ScalabilityMode::kL3T3:
case ScalabilityMode::kL3T3h:
case ScalabilityMode::kL3T3_KEY:
return 3;
case ScalabilityMode::kS2T1:
case ScalabilityMode::kS2T1h:
case ScalabilityMode::kS2T2:
case ScalabilityMode::kS2T2h:
case ScalabilityMode::kS2T3:
case ScalabilityMode::kS2T3h:
return 2;
case ScalabilityMode::kS3T1:
case ScalabilityMode::kS3T1h:
case ScalabilityMode::kS3T2:
case ScalabilityMode::kS3T2h:
case ScalabilityMode::kS3T3:
case ScalabilityMode::kS3T3h:
return 3;
}
RTC_CHECK_NOTREACHED();
}
int ScalabilityModeToNumTemporalLayers(ScalabilityMode scalability_mode) {
switch (scalability_mode) {
case ScalabilityMode::kL1T1:
return 1;
case ScalabilityMode::kL1T2:
return 2;
case ScalabilityMode::kL1T3:
return 3;
case ScalabilityMode::kL2T1:
case ScalabilityMode::kL2T1h:
case ScalabilityMode::kL2T1_KEY:
return 1;
case ScalabilityMode::kL2T2:
case ScalabilityMode::kL2T2h:
case ScalabilityMode::kL2T2_KEY:
case ScalabilityMode::kL2T2_KEY_SHIFT:
return 2;
case ScalabilityMode::kL2T3:
case ScalabilityMode::kL2T3h:
case ScalabilityMode::kL2T3_KEY:
return 3;
case ScalabilityMode::kL3T1:
case ScalabilityMode::kL3T1h:
case ScalabilityMode::kL3T1_KEY:
return 1;
case ScalabilityMode::kL3T2:
case ScalabilityMode::kL3T2h:
case ScalabilityMode::kL3T2_KEY:
return 2;
case ScalabilityMode::kL3T3:
case ScalabilityMode::kL3T3h:
case ScalabilityMode::kL3T3_KEY:
return 3;
case ScalabilityMode::kS2T1:
case ScalabilityMode::kS2T1h:
case ScalabilityMode::kS3T1:
case ScalabilityMode::kS3T1h:
return 1;
case ScalabilityMode::kS2T2:
case ScalabilityMode::kS2T2h:
case ScalabilityMode::kS3T2:
case ScalabilityMode::kS3T2h:
return 2;
case ScalabilityMode::kS2T3:
case ScalabilityMode::kS2T3h:
case ScalabilityMode::kS3T3:
case ScalabilityMode::kS3T3h:
return 3;
}
RTC_CHECK_NOTREACHED();
}
absl::optional<ScalabilityModeResolutionRatio> ScalabilityModeToResolutionRatio(
ScalabilityMode scalability_mode) {
switch (scalability_mode) {
case ScalabilityMode::kL1T1:
case ScalabilityMode::kL1T2:
case ScalabilityMode::kL1T3:
return absl::nullopt;
case ScalabilityMode::kL2T1:
case ScalabilityMode::kL2T1_KEY:
case ScalabilityMode::kL2T2:
case ScalabilityMode::kL2T2_KEY:
case ScalabilityMode::kL2T2_KEY_SHIFT:
case ScalabilityMode::kL2T3:
case ScalabilityMode::kL2T3_KEY:
case ScalabilityMode::kL3T1:
case ScalabilityMode::kL3T1_KEY:
case ScalabilityMode::kL3T2:
case ScalabilityMode::kL3T2_KEY:
case ScalabilityMode::kL3T3:
case ScalabilityMode::kL3T3_KEY:
case ScalabilityMode::kS2T1:
case ScalabilityMode::kS2T2:
case ScalabilityMode::kS2T3:
case ScalabilityMode::kS3T1:
case ScalabilityMode::kS3T2:
case ScalabilityMode::kS3T3:
return ScalabilityModeResolutionRatio::kTwoToOne;
case ScalabilityMode::kL2T1h:
case ScalabilityMode::kL2T2h:
case ScalabilityMode::kL2T3h:
case ScalabilityMode::kL3T1h:
case ScalabilityMode::kL3T2h:
case ScalabilityMode::kL3T3h:
case ScalabilityMode::kS2T1h:
case ScalabilityMode::kS2T2h:
case ScalabilityMode::kS2T3h:
case ScalabilityMode::kS3T1h:
case ScalabilityMode::kS3T2h:
case ScalabilityMode::kS3T3h:
return ScalabilityModeResolutionRatio::kThreeToTwo;
}
RTC_CHECK_NOTREACHED();
}
ScalabilityMode LimitNumSpatialLayers(ScalabilityMode scalability_mode,
int max_spatial_layers) {
int num_spatial_layers = ScalabilityModeToNumSpatialLayers(scalability_mode);
if (max_spatial_layers >= num_spatial_layers) {
return scalability_mode;
}
switch (scalability_mode) {
case ScalabilityMode::kL1T1:
return ScalabilityMode::kL1T1;
case ScalabilityMode::kL1T2:
return ScalabilityMode::kL1T2;
case ScalabilityMode::kL1T3:
return ScalabilityMode::kL1T3;
case ScalabilityMode::kL2T1:
return ScalabilityMode::kL1T1;
case ScalabilityMode::kL2T1h:
return ScalabilityMode::kL1T1;
case ScalabilityMode::kL2T1_KEY:
return ScalabilityMode::kL1T1;
case ScalabilityMode::kL2T2:
return ScalabilityMode::kL1T2;
case ScalabilityMode::kL2T2h:
return ScalabilityMode::kL1T2;
case ScalabilityMode::kL2T2_KEY:
return ScalabilityMode::kL1T2;
case ScalabilityMode::kL2T2_KEY_SHIFT:
return ScalabilityMode::kL1T2;
case ScalabilityMode::kL2T3:
return ScalabilityMode::kL1T3;
case ScalabilityMode::kL2T3h:
return ScalabilityMode::kL1T3;
case ScalabilityMode::kL2T3_KEY:
return ScalabilityMode::kL1T3;
case ScalabilityMode::kL3T1:
return max_spatial_layers == 2 ? ScalabilityMode::kL2T1
: ScalabilityMode::kL1T1;
case ScalabilityMode::kL3T1h:
return max_spatial_layers == 2 ? ScalabilityMode::kL2T1h
: ScalabilityMode::kL1T1;
case ScalabilityMode::kL3T1_KEY:
return max_spatial_layers == 2 ? ScalabilityMode::kL2T1_KEY
: ScalabilityMode::kL1T1;
case ScalabilityMode::kL3T2:
return max_spatial_layers == 2 ? ScalabilityMode::kL2T2
: ScalabilityMode::kL1T2;
case ScalabilityMode::kL3T2h:
return max_spatial_layers == 2 ? ScalabilityMode::kL2T2h
: ScalabilityMode::kL1T2;
case ScalabilityMode::kL3T2_KEY:
return max_spatial_layers == 2 ? ScalabilityMode::kL2T2_KEY
: ScalabilityMode::kL1T2;
case ScalabilityMode::kL3T3:
return max_spatial_layers == 2 ? ScalabilityMode::kL2T3
: ScalabilityMode::kL1T3;
case ScalabilityMode::kL3T3h:
return max_spatial_layers == 2 ? ScalabilityMode::kL2T3h
: ScalabilityMode::kL1T3;
case ScalabilityMode::kL3T3_KEY:
return max_spatial_layers == 2 ? ScalabilityMode::kL2T3_KEY
: ScalabilityMode::kL1T3;
case ScalabilityMode::kS2T1:
return ScalabilityMode::kL1T1;
case ScalabilityMode::kS2T1h:
return ScalabilityMode::kL1T1;
case ScalabilityMode::kS2T2:
return ScalabilityMode::kL1T2;
case ScalabilityMode::kS2T2h:
return ScalabilityMode::kL1T2;
case ScalabilityMode::kS2T3:
return ScalabilityMode::kL1T3;
case ScalabilityMode::kS2T3h:
return ScalabilityMode::kL1T3;
case ScalabilityMode::kS3T1:
return max_spatial_layers == 2 ? ScalabilityMode::kS2T1
: ScalabilityMode::kL1T1;
case ScalabilityMode::kS3T1h:
return max_spatial_layers == 2 ? ScalabilityMode::kS2T1h
: ScalabilityMode::kL1T1;
case ScalabilityMode::kS3T2:
return max_spatial_layers == 2 ? ScalabilityMode::kS2T2
: ScalabilityMode::kL1T2;
case ScalabilityMode::kS3T2h:
return max_spatial_layers == 2 ? ScalabilityMode::kS2T2h
: ScalabilityMode::kL1T2;
case ScalabilityMode::kS3T3:
return max_spatial_layers == 2 ? ScalabilityMode::kS2T3
: ScalabilityMode::kL1T3;
case ScalabilityMode::kS3T3h:
return max_spatial_layers == 2 ? ScalabilityMode::kS2T3h
: ScalabilityMode::kL1T3;
}
RTC_CHECK_NOTREACHED();
}
bool ScalabilityModeIsShiftMode(ScalabilityMode scalability_mode) {
return scalability_mode == ScalabilityMode::kL2T2_KEY_SHIFT;
}
} // namespace webrtc