diff --git a/media/BUILD.gn b/media/BUILD.gn index 2678a31a7c..e98001a4b7 100644 --- a/media/BUILD.gn +++ b/media/BUILD.gn @@ -162,6 +162,8 @@ rtc_static_library("rtc_audio_video") { "engine/videodecodersoftwarefallbackwrapper.h", "engine/videoencodersoftwarefallbackwrapper.cc", "engine/videoencodersoftwarefallbackwrapper.h", + "engine/vp8_encoder_simulcast_proxy.cc", + "engine/vp8_encoder_simulcast_proxy.h", "engine/webrtcmediaengine.cc", "engine/webrtcmediaengine.h", "engine/webrtcvideocapturer.cc", @@ -460,6 +462,7 @@ if (rtc_include_tests) { "engine/simulcast_unittest.cc", "engine/videodecodersoftwarefallbackwrapper_unittest.cc", "engine/videoencodersoftwarefallbackwrapper_unittest.cc", + "engine/vp8_encoder_simulcast_proxy_unittest.cc", "engine/webrtcmediaengine_unittest.cc", "engine/webrtcvideocapturer_unittest.cc", "engine/webrtcvideoencoderfactory_unittest.cc", @@ -541,6 +544,7 @@ if (rtc_include_tests) { "../system_wrappers:metrics_default", "../test:audio_codec_mocks", "../test:test_support", + "../test:video_test_common", "../voice_engine:voice_engine", ] } diff --git a/media/engine/vp8_encoder_simulcast_proxy.cc b/media/engine/vp8_encoder_simulcast_proxy.cc new file mode 100644 index 0000000000..85e37bf08c --- /dev/null +++ b/media/engine/vp8_encoder_simulcast_proxy.cc @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2017 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 "media/engine/vp8_encoder_simulcast_proxy.h" + +#include "media/engine/scopedvideoencoder.h" +#include "media/engine/simulcast_encoder_adapter.h" +#include "rtc_base/checks.h" + +namespace webrtc { +VP8EncoderSimulcastProxy::VP8EncoderSimulcastProxy( + cricket::WebRtcVideoEncoderFactory* factory) + : factory_(factory), callback_(nullptr) { + encoder_ = CreateScopedVideoEncoder(factory_, cricket::VideoCodec("VP8")); +} + +VP8EncoderSimulcastProxy::~VP8EncoderSimulcastProxy() {} + +int VP8EncoderSimulcastProxy::Release() { + return encoder_->Release(); +} + +int VP8EncoderSimulcastProxy::InitEncode(const VideoCodec* inst, + int number_of_cores, + size_t max_payload_size) { + int ret = encoder_->InitEncode(inst, number_of_cores, max_payload_size); + if (ret == WEBRTC_VIDEO_CODEC_ERR_SIMULCAST_PARAMETERS_NOT_SUPPORTED) { + encoder_.reset(new SimulcastEncoderAdapter(factory_)); + if (callback_) { + encoder_->RegisterEncodeCompleteCallback(callback_); + } + ret = encoder_->InitEncode(inst, number_of_cores, max_payload_size); + } + return ret; +} + +int VP8EncoderSimulcastProxy::Encode( + const VideoFrame& input_image, + const CodecSpecificInfo* codec_specific_info, + const std::vector* frame_types) { + return encoder_->Encode(input_image, codec_specific_info, frame_types); +} + +int VP8EncoderSimulcastProxy::RegisterEncodeCompleteCallback( + EncodedImageCallback* callback) { + callback_ = callback; + return encoder_->RegisterEncodeCompleteCallback(callback); +} + +int VP8EncoderSimulcastProxy::SetChannelParameters(uint32_t packet_loss, + int64_t rtt) { + return encoder_->SetChannelParameters(packet_loss, rtt); +} + +int VP8EncoderSimulcastProxy::SetRateAllocation( + const BitrateAllocation& bitrate, + uint32_t new_framerate) { + return encoder_->SetRateAllocation(bitrate, new_framerate); +} + +VideoEncoder::ScalingSettings VP8EncoderSimulcastProxy::GetScalingSettings() + const { + return encoder_->GetScalingSettings(); +} + +int32_t VP8EncoderSimulcastProxy::SetPeriodicKeyFrames(bool enable) { + return encoder_->SetPeriodicKeyFrames(enable); +} + +bool VP8EncoderSimulcastProxy::SupportsNativeHandle() const { + return encoder_->SupportsNativeHandle(); +} + +const char* VP8EncoderSimulcastProxy::ImplementationName() const { + return encoder_->ImplementationName(); +} + +} // namespace webrtc diff --git a/media/engine/vp8_encoder_simulcast_proxy.h b/media/engine/vp8_encoder_simulcast_proxy.h new file mode 100644 index 0000000000..46ccae42a4 --- /dev/null +++ b/media/engine/vp8_encoder_simulcast_proxy.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2017 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. + * + */ + +#ifndef MEDIA_ENGINE_VP8_ENCODER_SIMULCAST_PROXY_H_ +#define MEDIA_ENGINE_VP8_ENCODER_SIMULCAST_PROXY_H_ + +#include +#include + +#include "media/engine/webrtcvideoencoderfactory.h" +#include "modules/video_coding/codecs/vp8/include/vp8.h" + +namespace webrtc { + +// This class provides fallback to SimulcastEncoderAdapter if default VP8Encoder +// doesn't support simulcast for provided settings. +class VP8EncoderSimulcastProxy : public VP8Encoder { + public: + explicit VP8EncoderSimulcastProxy( + cricket::WebRtcVideoEncoderFactory* factory); + virtual ~VP8EncoderSimulcastProxy(); + + // Implements VideoEncoder. + int Release() override; + int InitEncode(const VideoCodec* inst, + int number_of_cores, + size_t max_payload_size) override; + int Encode(const VideoFrame& input_image, + const CodecSpecificInfo* codec_specific_info, + const std::vector* frame_types) override; + int RegisterEncodeCompleteCallback(EncodedImageCallback* callback) override; + int SetChannelParameters(uint32_t packet_loss, int64_t rtt) override; + int SetRateAllocation(const BitrateAllocation& bitrate, + uint32_t new_framerate) override; + + VideoEncoder::ScalingSettings GetScalingSettings() const override; + + int32_t SetPeriodicKeyFrames(bool enable) override; + bool SupportsNativeHandle() const override; + const char* ImplementationName() const override; + + private: + cricket::WebRtcVideoEncoderFactory* const factory_; + std::unique_ptr encoder_; + EncodedImageCallback* callback_; +}; + +} // namespace webrtc + +#endif // MEDIA_ENGINE_VP8_ENCODER_SIMULCAST_PROXY_H_ diff --git a/media/engine/vp8_encoder_simulcast_proxy_unittest.cc b/media/engine/vp8_encoder_simulcast_proxy_unittest.cc new file mode 100644 index 0000000000..0b26bb8545 --- /dev/null +++ b/media/engine/vp8_encoder_simulcast_proxy_unittest.cc @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2017 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 "media/engine/vp8_encoder_simulcast_proxy.h" + +#include + +#include "media/engine/webrtcvideoencoderfactory.h" +#include "modules/video_coding/codecs/vp8/temporal_layers.h" +#include "modules/video_coding/include/video_codec_interface.h" +#include "test/gmock.h" +#include "test/gtest.h" +#include "test/video_codec_settings.h" + +namespace webrtc { +namespace testing { + +using ::testing::Return; +using ::testing::_; +using ::testing::NiceMock; + +class MockEncoder : public VideoEncoder { + public: + // TODO(nisse): Valid overrides commented out, because the gmock + // methods don't use any override declarations, and we want to avoid + // warnings from -Winconsistent-missing-override. See + // http://crbug.com/428099. + MockEncoder() {} + virtual ~MockEncoder() {} + + MOCK_METHOD3(InitEncode, + int32_t(const VideoCodec* codec_settings, + int32_t number_of_cores, + size_t max_payload_size)); + + MOCK_METHOD1(RegisterEncodeCompleteCallback, int32_t(EncodedImageCallback*)); + + MOCK_METHOD0(Release, int32_t()); + + MOCK_METHOD3( + Encode, + int32_t(const VideoFrame& inputImage, + const CodecSpecificInfo* codecSpecificInfo, + const std::vector* frame_types) /* override */); + + MOCK_METHOD2(SetChannelParameters, int32_t(uint32_t packetLoss, int64_t rtt)); + + MOCK_CONST_METHOD0(ImplementationName, const char*()); +}; + +class MockWebRtcVideoEncoderFactory + : public cricket::WebRtcVideoEncoderFactory { + public: + MockWebRtcVideoEncoderFactory() {} + virtual ~MockWebRtcVideoEncoderFactory() {} + + MOCK_METHOD1(CreateVideoEncoder, + webrtc::VideoEncoder*(const cricket::VideoCodec& codec)); + + MOCK_CONST_METHOD0(supported_codecs, std::vector&()); + + MOCK_METHOD1(DestroyVideoEncoder, void(webrtc::VideoEncoder*)); +}; + +TEST(VP8EncoderSimulcastProxy, ChoosesCorrectImplementation) { + const std::string kImplementationName = "Fake"; + const std::string kSimulcastAdaptedImplementationName = + "SimulcastEncoderAdapter (Fake, Fake, Fake)"; + VideoCodec codec_settings; + webrtc::test::CodecSettings(kVideoCodecVP8, &codec_settings); + TemporalLayersFactory tl_factory; + codec_settings.VP8()->tl_factory = &tl_factory; + codec_settings.simulcastStream[0] = { + test::kTestWidth, test::kTestHeight, 2, 2000, 1000, 1000, 56}; + codec_settings.simulcastStream[1] = { + test::kTestWidth, test::kTestHeight, 2, 3000, 1000, 1000, 56}; + codec_settings.simulcastStream[2] = { + test::kTestWidth, test::kTestHeight, 2, 5000, 1000, 1000, 56}; + codec_settings.numberOfSimulcastStreams = 3; + + NiceMock mock_encoder; + NiceMock simulcast_factory; + + EXPECT_CALL(mock_encoder, InitEncode(_, _, _)) + .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); + EXPECT_CALL(mock_encoder, ImplementationName()) + .WillRepeatedly(Return(kImplementationName.c_str())); + + EXPECT_CALL(simulcast_factory, CreateVideoEncoder(_)) + .Times(1) + .WillOnce(Return(&mock_encoder)); + + VP8EncoderSimulcastProxy simulcast_enabled_proxy(&simulcast_factory); + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, + simulcast_enabled_proxy.InitEncode(&codec_settings, 4, 1200)); + EXPECT_EQ(kImplementationName, simulcast_enabled_proxy.ImplementationName()); + + NiceMock mock_encoder1; + NiceMock mock_encoder2; + NiceMock mock_encoder3; + NiceMock mock_encoder4; + NiceMock nonsimulcast_factory; + + EXPECT_CALL(mock_encoder1, InitEncode(_, _, _)) + .WillOnce( + Return(WEBRTC_VIDEO_CODEC_ERR_SIMULCAST_PARAMETERS_NOT_SUPPORTED)); + EXPECT_CALL(mock_encoder1, ImplementationName()) + .WillRepeatedly(Return(kImplementationName.c_str())); + + EXPECT_CALL(mock_encoder2, InitEncode(_, _, _)) + .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); + EXPECT_CALL(mock_encoder2, ImplementationName()) + .WillRepeatedly(Return(kImplementationName.c_str())); + + EXPECT_CALL(mock_encoder3, InitEncode(_, _, _)) + .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); + EXPECT_CALL(mock_encoder3, ImplementationName()) + .WillRepeatedly(Return(kImplementationName.c_str())); + + EXPECT_CALL(mock_encoder4, InitEncode(_, _, _)) + .WillOnce(Return(WEBRTC_VIDEO_CODEC_OK)); + EXPECT_CALL(mock_encoder4, ImplementationName()) + .WillRepeatedly(Return(kImplementationName.c_str())); + + EXPECT_CALL(nonsimulcast_factory, CreateVideoEncoder(_)) + .Times(4) + .WillOnce(Return(&mock_encoder1)) + .WillOnce(Return(&mock_encoder2)) + .WillOnce(Return(&mock_encoder3)) + .WillOnce(Return(&mock_encoder4)); + + VP8EncoderSimulcastProxy simulcast_disabled_proxy(&nonsimulcast_factory); + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, + simulcast_disabled_proxy.InitEncode(&codec_settings, 4, 1200)); + EXPECT_EQ(kSimulcastAdaptedImplementationName, + simulcast_disabled_proxy.ImplementationName()); + + // Cleanup. + simulcast_enabled_proxy.Release(); + simulcast_disabled_proxy.Release(); +} + +} // namespace testing +} // namespace webrtc diff --git a/media/engine/webrtcvideoengine.cc b/media/engine/webrtcvideoengine.cc index 80b450789e..f5c6a2998e 100644 --- a/media/engine/webrtcvideoengine.cc +++ b/media/engine/webrtcvideoengine.cc @@ -42,6 +42,7 @@ #include "rtc_base/timeutils.h" #include "rtc_base/trace_event.h" #include "system_wrappers/include/field_trial.h" +#include "vp8_encoder_simulcast_proxy.h" using DegradationPreference = webrtc::VideoSendStream::DegradationPreference; @@ -67,8 +68,7 @@ class EncoderFactoryAdapter { virtual ~EncoderFactoryAdapter() {} virtual AllocatedEncoder CreateVideoEncoder( - const VideoCodec& codec, - bool is_conference_mode_screenshare) const = 0; + const VideoCodec& codec) const = 0; virtual std::vector GetSupportedCodecs() const = 0; }; @@ -99,9 +99,7 @@ class CricketEncoderFactoryAdapter : public EncoderFactoryAdapter { external_encoder_factory_(std::move(external_encoder_factory)) {} private: - AllocatedEncoder CreateVideoEncoder( - const VideoCodec& codec, - bool is_conference_mode_screenshare) const override; + AllocatedEncoder CreateVideoEncoder(const VideoCodec& codec) const override; std::vector GetSupportedCodecs() const override; @@ -134,9 +132,7 @@ class WebRtcEncoderFactoryAdapter : public EncoderFactoryAdapter { : encoder_factory_(std::move(encoder_factory)) {} private: - AllocatedEncoder CreateVideoEncoder( - const VideoCodec& codec, - bool is_conference_mode_screenshare) const override { + AllocatedEncoder CreateVideoEncoder(const VideoCodec& codec) const override { if (!encoder_factory_) return AllocatedEncoder(); const webrtc::SdpVideoFormat format(codec.name, codec.params); @@ -1688,8 +1684,7 @@ WebRtcVideoChannel::WebRtcVideoSendStream::GetSsrcs() const { EncoderFactoryAdapter::AllocatedEncoder CricketEncoderFactoryAdapter::CreateVideoEncoder( - const VideoCodec& codec, - bool is_conference_mode_screenshare) const { + const VideoCodec& codec) const { // Try creating external encoder. if (external_encoder_factory_ != nullptr && FindMatchingCodec(external_encoder_factory_->supported_codecs(), codec)) { @@ -1719,12 +1714,10 @@ CricketEncoderFactoryAdapter::CreateVideoEncoder( // Try creating internal encoder. std::unique_ptr internal_encoder; if (FindMatchingCodec(internal_encoder_factory_->supported_codecs(), codec)) { - if (CodecNamesEq(codec.name.c_str(), kVp8CodecName) && - is_conference_mode_screenshare && UseSimulcastScreenshare()) { - // TODO(sprang): Remove this adapter once libvpx supports simulcast with - // same-resolution substreams. + if (CodecNamesEq(codec.name.c_str(), kVp8CodecName)) { internal_encoder = std::unique_ptr( - new webrtc::SimulcastEncoderAdapter(internal_encoder_factory_.get())); + new webrtc::VP8EncoderSimulcastProxy( + internal_encoder_factory_.get())); } else { internal_encoder = std::unique_ptr( internal_encoder_factory_->CreateVideoEncoder(codec)); @@ -1753,13 +1746,8 @@ void WebRtcVideoChannel::WebRtcVideoSendStream::SetCodec( std::unique_ptr new_encoder; if (force_encoder_allocation || !allocated_encoder_ || allocated_codec_ != codec_settings.codec) { - const bool is_conference_mode_screenshare = - parameters_.encoder_config.content_type == - webrtc::VideoEncoderConfig::ContentType::kScreen && - parameters_.conference_mode; EncoderFactoryAdapter::AllocatedEncoder new_allocated_encoder = - encoder_factory_->CreateVideoEncoder(codec_settings.codec, - is_conference_mode_screenshare); + encoder_factory_->CreateVideoEncoder(codec_settings.codec); new_encoder = std::unique_ptr( std::move(new_allocated_encoder.encoder)); parameters_.config.encoder_settings.encoder = new_encoder.get(); diff --git a/modules/video_coding/codecs/vp8/simulcast_unittest.cc b/modules/video_coding/codecs/vp8/simulcast_unittest.cc index d8b44f48cf..d6b6dc5008 100644 --- a/modules/video_coding/codecs/vp8/simulcast_unittest.cc +++ b/modules/video_coding/codecs/vp8/simulcast_unittest.cc @@ -67,10 +67,6 @@ TEST_F(TestVp8Impl, TestSaptioTemporalLayers333PatternEncoder) { TestVp8Simulcast::TestSaptioTemporalLayers333PatternEncoder(); } -TEST_F(TestVp8Impl, TestSpatioTemporalLayers321PatternEncoder) { - TestVp8Simulcast::TestSpatioTemporalLayers321PatternEncoder(); -} - TEST_F(TestVp8Impl, TestStrideEncodeDecode) { TestVp8Simulcast::TestStrideEncodeDecode(); } diff --git a/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc b/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc index f6a266ac91..7214d537dc 100644 --- a/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc +++ b/modules/video_coding/codecs/vp8/test/vp8_impl_unittest.cc @@ -286,6 +286,64 @@ TEST_F(TestVp8Impl, DecodedQpEqualsEncodedQp) { EXPECT_EQ(encoded_cb_.encoded_frame_.qp_, *decoded_cb_.qp_); } +TEST_F(TestVp8Impl, ChecksSimulcastSettings) { + codec_settings_.numberOfSimulcastStreams = 2; + // Reslutions are not scaled by 2, temporal layers do not match. + codec_settings_.simulcastStream[0] = {kWidth, kHeight, 2, 4000, + 3000, 2000, 80}; + codec_settings_.simulcastStream[1] = {kWidth, kHeight, 3, 4000, + 3000, 2000, 80}; + EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERR_SIMULCAST_PARAMETERS_NOT_SUPPORTED, + encoder_->InitEncode(&codec_settings_, kNumCores, kMaxPayloadSize)); + codec_settings_.numberOfSimulcastStreams = 3; + // Reslutions are not scaled by 2. + codec_settings_.simulcastStream[0] = {kWidth / 2, kHeight / 2, 1, 4000, + 3000, 2000, 80}; + codec_settings_.simulcastStream[1] = {kWidth / 2, kHeight / 2, 1, 4000, + 3000, 2000, 80}; + codec_settings_.simulcastStream[2] = {kWidth, kHeight, 1, 4000, + 3000, 2000, 80}; + EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERR_SIMULCAST_PARAMETERS_NOT_SUPPORTED, + encoder_->InitEncode(&codec_settings_, kNumCores, kMaxPayloadSize)); + // Reslutions are not scaled by 2. + codec_settings_.simulcastStream[0] = {kWidth, kHeight, 1, 4000, + 3000, 2000, 80}; + codec_settings_.simulcastStream[1] = {kWidth, kHeight, 1, 4000, + 3000, 2000, 80}; + codec_settings_.simulcastStream[2] = {kWidth, kHeight, 1, 4000, + 3000, 2000, 80}; + EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERR_SIMULCAST_PARAMETERS_NOT_SUPPORTED, + encoder_->InitEncode(&codec_settings_, kNumCores, kMaxPayloadSize)); + // Temporal layers do not match. + codec_settings_.simulcastStream[0] = {kWidth / 4, kHeight / 4, 1, 4000, + 3000, 2000, 80}; + codec_settings_.simulcastStream[1] = {kWidth / 2, kHeight / 2, 2, 4000, + 3000, 2000, 80}; + codec_settings_.simulcastStream[2] = {kWidth, kHeight, 3, 4000, + 3000, 2000, 80}; + EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERR_SIMULCAST_PARAMETERS_NOT_SUPPORTED, + encoder_->InitEncode(&codec_settings_, kNumCores, kMaxPayloadSize)); + // Resolutions do not match codec config. + codec_settings_.simulcastStream[0] = { + kWidth / 4 + 1, kHeight / 4 + 1, 1, 4000, 3000, 2000, 80}; + codec_settings_.simulcastStream[1] = { + kWidth / 2 + 2, kHeight / 2 + 2, 1, 4000, 3000, 2000, 80}; + codec_settings_.simulcastStream[2] = {kWidth + 4, kHeight + 4, 1, 4000, + 3000, 2000, 80}; + EXPECT_EQ(WEBRTC_VIDEO_CODEC_ERR_SIMULCAST_PARAMETERS_NOT_SUPPORTED, + encoder_->InitEncode(&codec_settings_, kNumCores, kMaxPayloadSize)); + // Everything fine: scaling by 2, top resolution matches video, temporal + // settings are the same for all layers. + codec_settings_.simulcastStream[0] = {kWidth / 4, kHeight / 4, 1, 4000, + 3000, 2000, 80}; + codec_settings_.simulcastStream[1] = {kWidth / 2, kHeight / 2, 1, 4000, + 3000, 2000, 80}; + codec_settings_.simulcastStream[2] = {kWidth, kHeight, 1, 4000, + 3000, 2000, 80}; + EXPECT_EQ(WEBRTC_VIDEO_CODEC_OK, + encoder_->InitEncode(&codec_settings_, kNumCores, kMaxPayloadSize)); +} + #if defined(WEBRTC_ANDROID) #define MAYBE_AlignedStrideEncodeDecode DISABLED_AlignedStrideEncodeDecode #else diff --git a/modules/video_coding/codecs/vp8/vp8_impl.cc b/modules/video_coding/codecs/vp8/vp8_impl.cc index 53c198b2bb..b97ef2275e 100644 --- a/modules/video_coding/codecs/vp8/vp8_impl.cc +++ b/modules/video_coding/codecs/vp8/vp8_impl.cc @@ -100,6 +100,21 @@ bool ValidSimulcastResolutions(const VideoCodec& codec, int num_streams) { return false; } } + for (int i = 1; i < num_streams; ++i) { + if (codec.simulcastStream[i].width != + codec.simulcastStream[i - 1].width * 2) { + return false; + } + } + return true; +} + +bool ValidSimulcastTemporalLayers(const VideoCodec& codec, int num_streams) { + for (int i = 0; i < num_streams - 1; ++i) { + if (codec.simulcastStream[i].numberOfTemporalLayers != + codec.simulcastStream[i + 1].numberOfTemporalLayers) + return false; + } return true; } @@ -395,8 +410,10 @@ int VP8EncoderImpl::InitEncode(const VideoCodec* inst, int number_of_streams = NumberOfStreams(*inst); bool doing_simulcast = (number_of_streams > 1); - if (doing_simulcast && !ValidSimulcastResolutions(*inst, number_of_streams)) { - return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + if (doing_simulcast && + (!ValidSimulcastResolutions(*inst, number_of_streams) || + !ValidSimulcastTemporalLayers(*inst, number_of_streams))) { + return WEBRTC_VIDEO_CODEC_ERR_SIMULCAST_PARAMETERS_NOT_SUPPORTED; } int num_temporal_layers = diff --git a/modules/video_coding/include/video_error_codes.h b/modules/video_coding/include/video_error_codes.h index e40c8b2d44..40e5a9a8ea 100644 --- a/modules/video_coding/include/video_error_codes.h +++ b/modules/video_coding/include/video_error_codes.h @@ -28,5 +28,6 @@ #define WEBRTC_VIDEO_CODEC_ERR_REQUEST_SLI -12 #define WEBRTC_VIDEO_CODEC_FALLBACK_SOFTWARE -13 #define WEBRTC_VIDEO_CODEC_TARGET_BITRATE_OVERSHOOT -14 +#define WEBRTC_VIDEO_CODEC_ERR_SIMULCAST_PARAMETERS_NOT_SUPPORTED -15 #endif // MODULES_VIDEO_CODING_INCLUDE_VIDEO_ERROR_CODES_H_