blob: 6af35b5df984f282b277c35d32b883b6942c9423 [file] [log] [blame]
/*
* Copyright (c) 2024 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/simulcast_to_svc_converter.h"
#include <cstddef>
#include <vector>
#include "modules/video_coding/svc/create_scalability_structure.h"
#include "test/gtest.h"
namespace webrtc {
TEST(SimulcastToSvc, ConvertsConfig) {
VideoCodec codec;
codec.codecType = kVideoCodecVP9;
codec.SetScalabilityMode(ScalabilityMode::kL1T3);
codec.width = 1280;
codec.height = 720;
codec.minBitrate = 10;
codec.maxBitrate = 2500;
codec.numberOfSimulcastStreams = 3;
codec.VP9()->numberOfSpatialLayers = 1;
codec.VP9()->interLayerPred = InterLayerPredMode::kOff;
codec.simulcastStream[0] = {.width = 320,
.height = 180,
.maxFramerate = 30,
.numberOfTemporalLayers = 3,
.maxBitrate = 100,
.targetBitrate = 70,
.minBitrate = 50,
.qpMax = 150,
.active = true};
codec.simulcastStream[1] = {.width = 640,
.height = 360,
.maxFramerate = 30,
.numberOfTemporalLayers = 3,
.maxBitrate = 250,
.targetBitrate = 150,
.minBitrate = 100,
.qpMax = 150,
.active = true};
codec.simulcastStream[2] = {.width = 12800,
.height = 720,
.maxFramerate = 30,
.numberOfTemporalLayers = 3,
.maxBitrate = 1500,
.targetBitrate = 1200,
.minBitrate = 800,
.qpMax = 150,
.active = true};
VideoCodec result = codec;
SimulcastToSvcConverter converter(codec);
result = converter.GetConfig();
EXPECT_EQ(result.numberOfSimulcastStreams, 1);
EXPECT_EQ(result.spatialLayers[0], codec.simulcastStream[0]);
EXPECT_EQ(result.spatialLayers[1], codec.simulcastStream[1]);
EXPECT_EQ(result.spatialLayers[2], codec.simulcastStream[2]);
EXPECT_EQ(result.VP9()->numberOfTemporalLayers, 3);
EXPECT_EQ(result.VP9()->numberOfSpatialLayers, 3);
EXPECT_EQ(result.VP9()->interLayerPred, InterLayerPredMode::kOff);
}
TEST(SimulcastToSvc, ConvertsEncodedImage) {
VideoCodec codec;
codec.codecType = kVideoCodecVP9;
codec.SetScalabilityMode(ScalabilityMode::kL1T3);
codec.width = 1280;
codec.height = 720;
codec.minBitrate = 10;
codec.maxBitrate = 2500;
codec.numberOfSimulcastStreams = 3;
codec.VP9()->numberOfSpatialLayers = 1;
codec.VP9()->interLayerPred = InterLayerPredMode::kOff;
codec.simulcastStream[0] = {.width = 320,
.height = 180,
.maxFramerate = 30,
.numberOfTemporalLayers = 3,
.maxBitrate = 100,
.targetBitrate = 70,
.minBitrate = 50,
.qpMax = 150,
.active = true};
codec.simulcastStream[1] = {.width = 640,
.height = 360,
.maxFramerate = 30,
.numberOfTemporalLayers = 3,
.maxBitrate = 250,
.targetBitrate = 150,
.minBitrate = 100,
.qpMax = 150,
.active = true};
codec.simulcastStream[2] = {.width = 1280,
.height = 720,
.maxFramerate = 30,
.numberOfTemporalLayers = 3,
.maxBitrate = 1500,
.targetBitrate = 1200,
.minBitrate = 800,
.qpMax = 150,
.active = true};
SimulcastToSvcConverter converter(codec);
EncodedImage image;
image.SetRtpTimestamp(123);
image.SetSpatialIndex(1);
image.SetTemporalIndex(0);
image._encodedWidth = 640;
image._encodedHeight = 360;
CodecSpecificInfo codec_specific;
codec_specific.codecType = kVideoCodecVP9;
codec_specific.end_of_picture = false;
codec_specific.codecSpecific.VP9.num_spatial_layers = 3;
codec_specific.codecSpecific.VP9.first_active_layer = 0;
codec_specific.scalability_mode = ScalabilityMode::kS3T3;
converter.EncodeStarted(/*force_keyframe =*/true);
converter.ConvertFrame(image, codec_specific);
EXPECT_EQ(image.SpatialIndex(), std::nullopt);
EXPECT_EQ(image.SimulcastIndex(), 1);
EXPECT_EQ(image.TemporalIndex(), 0);
EXPECT_EQ(codec_specific.end_of_picture, true);
EXPECT_EQ(codec_specific.scalability_mode, ScalabilityMode::kL1T3);
}
// Checks that ScalableVideoController, which actualle is used by the encoder
// in the forced S-mode behaves as SimulcastToSvcConverter assumes.
TEST(SimulcastToSvc, PredictsInternalStateCorrectlyOnFrameDrops) {
VideoCodec codec;
codec.codecType = kVideoCodecVP9;
codec.SetScalabilityMode(ScalabilityMode::kL1T3);
codec.width = 1280;
codec.height = 720;
codec.minBitrate = 10;
codec.maxBitrate = 2500;
codec.numberOfSimulcastStreams = 3;
codec.VP9()->numberOfSpatialLayers = 1;
codec.VP9()->interLayerPred = InterLayerPredMode::kOff;
codec.simulcastStream[0] = {.width = 320,
.height = 180,
.maxFramerate = 30,
.numberOfTemporalLayers = 3,
.maxBitrate = 100,
.targetBitrate = 70,
.minBitrate = 50,
.qpMax = 150,
.active = true};
codec.simulcastStream[1] = {.width = 640,
.height = 360,
.maxFramerate = 30,
.numberOfTemporalLayers = 3,
.maxBitrate = 250,
.targetBitrate = 150,
.minBitrate = 100,
.qpMax = 150,
.active = true};
codec.simulcastStream[2] = {.width = 1280,
.height = 720,
.maxFramerate = 30,
.numberOfTemporalLayers = 3,
.maxBitrate = 1500,
.targetBitrate = 1200,
.minBitrate = 800,
.qpMax = 150,
.active = true};
std::unique_ptr<ScalableVideoController> svc_controller =
CreateScalabilityStructure(ScalabilityMode::kS3T3);
VideoBitrateAllocation dummy_bitrates;
for (int sid = 0; sid < 3; ++sid) {
for (int tid = 0; tid < 3; ++tid) {
dummy_bitrates.SetBitrate(sid, tid, 10000);
}
}
svc_controller->OnRatesUpdated(dummy_bitrates);
SimulcastToSvcConverter converter(codec);
EncodedImage image;
// Simulate complex dropping pattern.
const int kDropInterval[3] = {11, 7, 5};
const int kKeyFrameInterval = 13;
for (int i = 0; i < 100; ++i) {
bool force_restart = ((i + 1) % kKeyFrameInterval == 0) || (i == 0);
auto layer_config = svc_controller->NextFrameConfig(force_restart);
converter.EncodeStarted(force_restart);
for (int sid = 0; sid < 3; ++sid) {
if ((i + 1) % kDropInterval[sid] == 0) {
continue;
}
image.SetRtpTimestamp(123 * i);
image.SetSpatialIndex(sid);
image.SetTemporalIndex(0);
image._encodedWidth = 1280 / (1 << sid);
image._encodedHeight = 720 / (1 << sid);
image.SetSpatialIndex(sid);
image.SetTemporalIndex(layer_config[sid].TemporalId());
CodecSpecificInfo codec_specific;
codec_specific.codecType = kVideoCodecVP9;
codec_specific.end_of_picture = false;
codec_specific.codecSpecific.VP9.num_spatial_layers = 3;
codec_specific.codecSpecific.VP9.first_active_layer = 0;
codec_specific.codecSpecific.VP9.temporal_idx =
layer_config[sid].TemporalId();
codec_specific.generic_frame_info =
svc_controller->OnEncodeDone(layer_config[sid]);
codec_specific.scalability_mode = ScalabilityMode::kS3T3;
EXPECT_TRUE(converter.ConvertFrame(image, codec_specific));
EXPECT_EQ(image.SpatialIndex(), std::nullopt);
EXPECT_EQ(image.SimulcastIndex(), sid);
EXPECT_EQ(image.TemporalIndex(), layer_config[sid].TemporalId());
EXPECT_EQ(codec_specific.scalability_mode, ScalabilityMode::kL1T3);
}
}
}
TEST(SimulcastToSvc, SupportsOnlyContinuousActiveStreams) {
VideoCodec codec;
codec.codecType = kVideoCodecVP9;
codec.SetScalabilityMode(ScalabilityMode::kL1T3);
codec.width = 1280;
codec.height = 720;
codec.minBitrate = 10;
codec.maxBitrate = 2500;
codec.numberOfSimulcastStreams = 3;
codec.VP9()->numberOfSpatialLayers = 1;
codec.VP9()->interLayerPred = InterLayerPredMode::kOff;
codec.simulcastStream[0] = {.width = 320,
.height = 180,
.maxFramerate = 30,
.numberOfTemporalLayers = 3,
.maxBitrate = 100,
.targetBitrate = 70,
.minBitrate = 50,
.qpMax = 150,
.active = true};
codec.simulcastStream[1] = {.width = 640,
.height = 360,
.maxFramerate = 30,
.numberOfTemporalLayers = 3,
.maxBitrate = 250,
.targetBitrate = 150,
.minBitrate = 100,
.qpMax = 150,
.active = true};
codec.simulcastStream[2] = {.width = 1280,
.height = 720,
.maxFramerate = 30,
.numberOfTemporalLayers = 3,
.maxBitrate = 1500,
.targetBitrate = 1200,
.minBitrate = 800,
.qpMax = 150,
.active = true};
EXPECT_TRUE(SimulcastToSvcConverter::IsConfigSupported(codec));
codec.simulcastStream[0].active = false;
codec.simulcastStream[1].active = true;
codec.simulcastStream[2].active = true;
EXPECT_TRUE(SimulcastToSvcConverter::IsConfigSupported(codec));
codec.simulcastStream[0].active = true;
codec.simulcastStream[1].active = true;
codec.simulcastStream[2].active = false;
EXPECT_TRUE(SimulcastToSvcConverter::IsConfigSupported(codec));
codec.simulcastStream[0].active = true;
codec.simulcastStream[1].active = false;
codec.simulcastStream[2].active = true;
EXPECT_FALSE(SimulcastToSvcConverter::IsConfigSupported(codec));
}
TEST(SimulcastToSvc, SupportsOnlySameTemporalStructure) {
VideoCodec codec;
codec.codecType = kVideoCodecVP9;
codec.width = 1280;
codec.height = 720;
codec.minBitrate = 10;
codec.maxBitrate = 2500;
codec.numberOfSimulcastStreams = 3;
codec.VP9()->numberOfSpatialLayers = 1;
codec.VP9()->interLayerPred = InterLayerPredMode::kOff;
codec.simulcastStream[0] = {.width = 320,
.height = 180,
.maxFramerate = 30,
.numberOfTemporalLayers = 3,
.maxBitrate = 100,
.targetBitrate = 70,
.minBitrate = 50,
.qpMax = 150,
.active = true};
codec.simulcastStream[1] = {.width = 640,
.height = 360,
.maxFramerate = 30,
.numberOfTemporalLayers = 3,
.maxBitrate = 250,
.targetBitrate = 150,
.minBitrate = 100,
.qpMax = 150,
.active = true};
codec.simulcastStream[2] = {.width = 1280,
.height = 720,
.maxFramerate = 30,
.numberOfTemporalLayers = 3,
.maxBitrate = 1500,
.targetBitrate = 1200,
.minBitrate = 800,
.qpMax = 150,
.active = true};
EXPECT_TRUE(SimulcastToSvcConverter::IsConfigSupported(codec));
codec.simulcastStream[0].numberOfTemporalLayers = 1;
EXPECT_FALSE(SimulcastToSvcConverter::IsConfigSupported(codec));
}
TEST(SimulcastToSvc, SupportsOnly421Scaling) {
VideoCodec codec;
codec.codecType = kVideoCodecVP9;
codec.width = 1280;
codec.height = 720;
codec.minBitrate = 10;
codec.maxBitrate = 2500;
codec.numberOfSimulcastStreams = 3;
codec.VP9()->numberOfSpatialLayers = 1;
codec.VP9()->interLayerPred = InterLayerPredMode::kOff;
codec.simulcastStream[0] = {.width = 320,
.height = 180,
.maxFramerate = 30,
.numberOfTemporalLayers = 3,
.maxBitrate = 100,
.targetBitrate = 70,
.minBitrate = 50,
.qpMax = 150,
.active = true};
codec.simulcastStream[1] = {.width = 640,
.height = 360,
.maxFramerate = 30,
.numberOfTemporalLayers = 3,
.maxBitrate = 250,
.targetBitrate = 150,
.minBitrate = 100,
.qpMax = 150,
.active = true};
codec.simulcastStream[2] = {.width = 1280,
.height = 720,
.maxFramerate = 30,
.numberOfTemporalLayers = 3,
.maxBitrate = 1500,
.targetBitrate = 1200,
.minBitrate = 800,
.qpMax = 150,
.active = true};
EXPECT_TRUE(SimulcastToSvcConverter::IsConfigSupported(codec));
codec.simulcastStream[0].width = 160;
codec.simulcastStream[0].height = 90;
EXPECT_FALSE(SimulcastToSvcConverter::IsConfigSupported(codec));
}
} // namespace webrtc