| /* |
| * 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 <cstddef> |
| #include <optional> |
| #include <type_traits> |
| |
| #include "absl/algorithm/container.h" |
| #include "absl/strings/string_view.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 { |
| const ScalabilityMode scalability_mode; |
| const absl::string_view name; |
| const int num_spatial_layers; |
| const int num_temporal_layers; |
| const InterLayerPredMode inter_layer_pred; |
| const std::optional<ScalabilityModeResolutionRatio> ratio = |
| ScalabilityModeResolutionRatio::kTwoToOne; |
| const bool shift = false; |
| }; |
| |
| constexpr size_t kNumScalabilityModes = |
| static_cast<size_t>(ScalabilityMode::kS3T3h) + 1; |
| |
| constexpr ScalabilityModeParameters kScalabilityModeParams[] = { |
| ScalabilityModeParameters{.scalability_mode = ScalabilityMode::kL1T1, |
| .name = "L1T1", |
| .num_spatial_layers = 1, |
| .num_temporal_layers = 1, |
| .inter_layer_pred = InterLayerPredMode::kOff, |
| .ratio = std::nullopt}, |
| ScalabilityModeParameters{.scalability_mode = ScalabilityMode::kL1T2, |
| .name = "L1T2", |
| .num_spatial_layers = 1, |
| .num_temporal_layers = 2, |
| .inter_layer_pred = InterLayerPredMode::kOff, |
| .ratio = std::nullopt}, |
| ScalabilityModeParameters{.scalability_mode = ScalabilityMode::kL1T3, |
| .name = "L1T3", |
| .num_spatial_layers = 1, |
| .num_temporal_layers = 3, |
| .inter_layer_pred = InterLayerPredMode::kOff, |
| .ratio = std::nullopt}, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kL2T1, |
| .name = "L2T1", |
| .num_spatial_layers = 2, |
| .num_temporal_layers = 1, |
| .inter_layer_pred = InterLayerPredMode::kOn, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kL2T1h, |
| .name = "L2T1h", |
| .num_spatial_layers = 2, |
| .num_temporal_layers = 1, |
| .inter_layer_pred = InterLayerPredMode::kOn, |
| .ratio = ScalabilityModeResolutionRatio::kThreeToTwo, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kL2T1_KEY, |
| .name = "L2T1_KEY", |
| .num_spatial_layers = 2, |
| .num_temporal_layers = 1, |
| .inter_layer_pred = InterLayerPredMode::kOnKeyPic, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kL2T2, |
| .name = "L2T2", |
| .num_spatial_layers = 2, |
| .num_temporal_layers = 2, |
| .inter_layer_pred = InterLayerPredMode::kOn, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kL2T2h, |
| .name = "L2T2h", |
| .num_spatial_layers = 2, |
| .num_temporal_layers = 2, |
| .inter_layer_pred = InterLayerPredMode::kOn, |
| .ratio = ScalabilityModeResolutionRatio::kThreeToTwo, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kL2T2_KEY, |
| .name = "L2T2_KEY", |
| .num_spatial_layers = 2, |
| .num_temporal_layers = 2, |
| .inter_layer_pred = InterLayerPredMode::kOnKeyPic, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kL2T2_KEY_SHIFT, |
| .name = "L2T2_KEY_SHIFT", |
| .num_spatial_layers = 2, |
| .num_temporal_layers = 2, |
| .inter_layer_pred = InterLayerPredMode::kOnKeyPic, |
| .shift = true}, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kL2T3, |
| .name = "L2T3", |
| .num_spatial_layers = 2, |
| .num_temporal_layers = 3, |
| .inter_layer_pred = InterLayerPredMode::kOn, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kL2T3h, |
| .name = "L2T3h", |
| .num_spatial_layers = 2, |
| .num_temporal_layers = 3, |
| .inter_layer_pred = InterLayerPredMode::kOn, |
| .ratio = ScalabilityModeResolutionRatio::kThreeToTwo, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kL2T3_KEY, |
| .name = "L2T3_KEY", |
| .num_spatial_layers = 2, |
| .num_temporal_layers = 3, |
| .inter_layer_pred = InterLayerPredMode::kOnKeyPic, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kL3T1, |
| .name = "L3T1", |
| .num_spatial_layers = 3, |
| .num_temporal_layers = 1, |
| .inter_layer_pred = InterLayerPredMode::kOn, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kL3T1h, |
| .name = "L3T1h", |
| .num_spatial_layers = 3, |
| .num_temporal_layers = 1, |
| .inter_layer_pred = InterLayerPredMode::kOn, |
| .ratio = ScalabilityModeResolutionRatio::kThreeToTwo, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kL3T1_KEY, |
| .name = "L3T1_KEY", |
| .num_spatial_layers = 3, |
| .num_temporal_layers = 1, |
| .inter_layer_pred = InterLayerPredMode::kOnKeyPic, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kL3T2, |
| .name = "L3T2", |
| .num_spatial_layers = 3, |
| .num_temporal_layers = 2, |
| .inter_layer_pred = InterLayerPredMode::kOn, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kL3T2h, |
| .name = "L3T2h", |
| .num_spatial_layers = 3, |
| .num_temporal_layers = 2, |
| .inter_layer_pred = InterLayerPredMode::kOn, |
| .ratio = ScalabilityModeResolutionRatio::kThreeToTwo, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kL3T2_KEY, |
| .name = "L3T2_KEY", |
| .num_spatial_layers = 3, |
| .num_temporal_layers = 2, |
| .inter_layer_pred = InterLayerPredMode::kOnKeyPic, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kL3T3, |
| .name = "L3T3", |
| .num_spatial_layers = 3, |
| .num_temporal_layers = 3, |
| .inter_layer_pred = InterLayerPredMode::kOn, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kL3T3h, |
| .name = "L3T3h", |
| .num_spatial_layers = 3, |
| .num_temporal_layers = 3, |
| .inter_layer_pred = InterLayerPredMode::kOn, |
| .ratio = ScalabilityModeResolutionRatio::kThreeToTwo, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kL3T3_KEY, |
| .name = "L3T3_KEY", |
| .num_spatial_layers = 3, |
| .num_temporal_layers = 3, |
| .inter_layer_pred = InterLayerPredMode::kOnKeyPic, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kS2T1, |
| .name = "S2T1", |
| .num_spatial_layers = 2, |
| .num_temporal_layers = 1, |
| .inter_layer_pred = InterLayerPredMode::kOff, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kS2T1h, |
| .name = "S2T1h", |
| .num_spatial_layers = 2, |
| .num_temporal_layers = 1, |
| .inter_layer_pred = InterLayerPredMode::kOff, |
| .ratio = ScalabilityModeResolutionRatio::kThreeToTwo, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kS2T2, |
| .name = "S2T2", |
| .num_spatial_layers = 2, |
| .num_temporal_layers = 2, |
| .inter_layer_pred = InterLayerPredMode::kOff, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kS2T2h, |
| .name = "S2T2h", |
| .num_spatial_layers = 2, |
| .num_temporal_layers = 2, |
| .inter_layer_pred = InterLayerPredMode::kOff, |
| .ratio = ScalabilityModeResolutionRatio::kThreeToTwo, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kS2T3, |
| .name = "S2T3", |
| .num_spatial_layers = 2, |
| .num_temporal_layers = 3, |
| .inter_layer_pred = InterLayerPredMode::kOff, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kS2T3h, |
| .name = "S2T3h", |
| .num_spatial_layers = 2, |
| .num_temporal_layers = 3, |
| .inter_layer_pred = InterLayerPredMode::kOff, |
| .ratio = ScalabilityModeResolutionRatio::kThreeToTwo, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kS3T1, |
| .name = "S3T1", |
| .num_spatial_layers = 3, |
| .num_temporal_layers = 1, |
| .inter_layer_pred = InterLayerPredMode::kOff, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kS3T1h, |
| .name = "S3T1h", |
| .num_spatial_layers = 3, |
| .num_temporal_layers = 1, |
| .inter_layer_pred = InterLayerPredMode::kOff, |
| .ratio = ScalabilityModeResolutionRatio::kThreeToTwo, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kS3T2, |
| .name = "S3T2", |
| .num_spatial_layers = 3, |
| .num_temporal_layers = 2, |
| .inter_layer_pred = InterLayerPredMode::kOff, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kS3T2h, |
| .name = "S3T2h", |
| .num_spatial_layers = 3, |
| .num_temporal_layers = 2, |
| .inter_layer_pred = InterLayerPredMode::kOff, |
| .ratio = ScalabilityModeResolutionRatio::kThreeToTwo, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kS3T3, |
| .name = "S3T3", |
| .num_spatial_layers = 3, |
| .num_temporal_layers = 3, |
| .inter_layer_pred = InterLayerPredMode::kOff, |
| }, |
| ScalabilityModeParameters{ |
| .scalability_mode = ScalabilityMode::kS3T3h, |
| .name = "S3T3h", |
| .num_spatial_layers = 3, |
| .num_temporal_layers = 3, |
| .inter_layer_pred = InterLayerPredMode::kOff, |
| .ratio = ScalabilityModeResolutionRatio::kThreeToTwo, |
| }, |
| }; |
| |
| // This could be replaced with std::all_of in c++20. |
| constexpr bool CheckScalabilityModeParams() { |
| static_assert(std::size(kScalabilityModeParams) == kNumScalabilityModes); |
| for (size_t s = 0; s < kNumScalabilityModes; ++s) { |
| if (kScalabilityModeParams[s].scalability_mode != |
| static_cast<ScalabilityMode>(s)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| static_assert(CheckScalabilityModeParams(), |
| "There is a scalability mode mismatch in the array!"); |
| |
| constexpr auto Idx(ScalabilityMode s) { |
| const auto index = static_cast<std::underlying_type_t<ScalabilityMode>>(s); |
| RTC_CHECK_LT(index, kNumScalabilityModes); |
| return index; |
| } |
| |
| } // namespace |
| |
| std::optional<ScalabilityMode> MakeScalabilityMode( |
| int num_spatial_layers, |
| int num_temporal_layers, |
| InterLayerPredMode inter_layer_pred, |
| std::optional<ScalabilityModeResolutionRatio> ratio, |
| bool shift) { |
| for (const auto& candidate_mode : kScalabilityModeParams) { |
| if (candidate_mode.num_spatial_layers == num_spatial_layers && |
| candidate_mode.num_temporal_layers == num_temporal_layers) { |
| if (num_spatial_layers == 1 || |
| (candidate_mode.inter_layer_pred == inter_layer_pred && |
| candidate_mode.ratio == ratio && candidate_mode.shift == shift)) { |
| return candidate_mode.scalability_mode; |
| } |
| } |
| } |
| return std::nullopt; |
| } |
| |
| std::optional<ScalabilityMode> ScalabilityModeFromString( |
| absl::string_view mode_string) { |
| const auto it = |
| absl::c_find_if(kScalabilityModeParams, |
| [&](const ScalabilityModeParameters& candidate_mode) { |
| return candidate_mode.name == mode_string; |
| }); |
| if (it != std::end(kScalabilityModeParams)) { |
| return it->scalability_mode; |
| } |
| return std::nullopt; |
| } |
| |
| InterLayerPredMode ScalabilityModeToInterLayerPredMode( |
| ScalabilityMode scalability_mode) { |
| return kScalabilityModeParams[Idx(scalability_mode)].inter_layer_pred; |
| } |
| |
| int ScalabilityModeToNumSpatialLayers(ScalabilityMode scalability_mode) { |
| return kScalabilityModeParams[Idx(scalability_mode)].num_spatial_layers; |
| } |
| |
| int ScalabilityModeToNumTemporalLayers(ScalabilityMode scalability_mode) { |
| return kScalabilityModeParams[Idx(scalability_mode)].num_temporal_layers; |
| } |
| |
| std::optional<ScalabilityModeResolutionRatio> ScalabilityModeToResolutionRatio( |
| ScalabilityMode scalability_mode) { |
| return kScalabilityModeParams[Idx(scalability_mode)].ratio; |
| } |
| |
| 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 kScalabilityModeParams[Idx(scalability_mode)].shift; |
| } |
| |
| } // namespace webrtc |