blob: 0ce26ff9b6504314179f969448fe6322143e7a49 [file] [log] [blame]
/*
* Copyright (c) 2004 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/base/media_engine.h"
#include <stddef.h>
#include <cstdint>
#include <string>
#include <utility>
#include "absl/algorithm/container.h"
#include "api/field_trials_view.h"
#include "api/video/video_bitrate_allocation.h"
#include "media/base/codec_comparators.h"
#include "rtc_base/checks.h"
#include "rtc_base/string_encode.h"
namespace cricket {
namespace {
bool SupportsMode(const cricket::Codec& codec,
std::optional<std::string> scalability_mode) {
if (!scalability_mode.has_value()) {
return true;
}
return absl::c_any_of(
codec.scalability_modes, [&](webrtc::ScalabilityMode mode) {
return ScalabilityModeToString(mode) == *scalability_mode;
});
}
} // namespace
RtpCapabilities::RtpCapabilities() = default;
RtpCapabilities::~RtpCapabilities() = default;
webrtc::RtpParameters CreateRtpParametersWithOneEncoding() {
webrtc::RtpParameters parameters;
webrtc::RtpEncodingParameters encoding;
parameters.encodings.push_back(encoding);
return parameters;
}
webrtc::RtpParameters CreateRtpParametersWithEncodings(StreamParams sp) {
std::vector<uint32_t> primary_ssrcs;
sp.GetPrimarySsrcs(&primary_ssrcs);
size_t encoding_count = primary_ssrcs.size();
std::vector<webrtc::RtpEncodingParameters> encodings(encoding_count);
for (size_t i = 0; i < encodings.size(); ++i) {
encodings[i].ssrc = primary_ssrcs[i];
}
const std::vector<RidDescription>& rids = sp.rids();
RTC_DCHECK(rids.size() == 0 || rids.size() == encoding_count);
for (size_t i = 0; i < rids.size(); ++i) {
encodings[i].rid = rids[i].rid;
}
webrtc::RtpParameters parameters;
parameters.encodings = encodings;
parameters.rtcp.cname = sp.cname;
return parameters;
}
std::vector<webrtc::RtpExtension> GetDefaultEnabledRtpHeaderExtensions(
const RtpHeaderExtensionQueryInterface& query_interface) {
std::vector<webrtc::RtpExtension> extensions;
for (const auto& entry : query_interface.GetRtpHeaderExtensions()) {
if (entry.direction != webrtc::RtpTransceiverDirection::kStopped)
extensions.emplace_back(entry.uri, *entry.preferred_id);
}
return extensions;
}
webrtc::RTCError CheckScalabilityModeValues(
const webrtc::RtpParameters& rtp_parameters,
rtc::ArrayView<cricket::Codec> send_codecs,
std::optional<cricket::Codec> send_codec) {
using webrtc::RTCErrorType;
if (send_codecs.empty()) {
// This is an audio sender or an extra check in the stack where the codec
// list is not available and we can't check the scalability_mode values.
return webrtc::RTCError::OK();
}
for (size_t i = 0; i < rtp_parameters.encodings.size(); ++i) {
if (rtp_parameters.encodings[i].codec) {
bool codecFound = false;
for (const cricket::Codec& codec : send_codecs) {
if (IsSameRtpCodec(codec, *rtp_parameters.encodings[i].codec) &&
SupportsMode(codec, rtp_parameters.encodings[i].scalability_mode)) {
codecFound = true;
send_codec = codec;
break;
}
}
if (!codecFound) {
LOG_AND_RETURN_ERROR(
RTCErrorType::INVALID_MODIFICATION,
"Attempted to use an unsupported codec for layer " +
std::to_string(i));
}
}
if (rtp_parameters.encodings[i].scalability_mode) {
if (!send_codec) {
bool scalabilityModeFound = false;
for (const cricket::Codec& codec : send_codecs) {
for (const auto& scalability_mode : codec.scalability_modes) {
if (ScalabilityModeToString(scalability_mode) ==
*rtp_parameters.encodings[i].scalability_mode) {
scalabilityModeFound = true;
break;
}
}
if (scalabilityModeFound)
break;
}
if (!scalabilityModeFound) {
LOG_AND_RETURN_ERROR(
RTCErrorType::INVALID_MODIFICATION,
"Attempted to set RtpParameters scalabilityMode "
"to an unsupported value for the current codecs.");
}
} else {
bool scalabilityModeFound = false;
for (const auto& scalability_mode : send_codec->scalability_modes) {
if (ScalabilityModeToString(scalability_mode) ==
*rtp_parameters.encodings[i].scalability_mode) {
scalabilityModeFound = true;
break;
}
}
if (!scalabilityModeFound) {
LOG_AND_RETURN_ERROR(
RTCErrorType::INVALID_MODIFICATION,
"Attempted to set RtpParameters scalabilityMode "
"to an unsupported value for the current codecs.");
}
}
}
}
return webrtc::RTCError::OK();
}
webrtc::RTCError CheckRtpParametersValues(
const webrtc::RtpParameters& rtp_parameters,
rtc::ArrayView<cricket::Codec> send_codecs,
std::optional<cricket::Codec> send_codec,
const webrtc::FieldTrialsView& field_trials) {
using webrtc::RTCErrorType;
bool has_scale_resolution_down_to = false;
for (size_t i = 0; i < rtp_parameters.encodings.size(); ++i) {
if (rtp_parameters.encodings[i].bitrate_priority <= 0) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE,
"Attempted to set RtpParameters bitrate_priority to "
"an invalid number. bitrate_priority must be > 0.");
}
if (rtp_parameters.encodings[i].scale_resolution_down_by &&
*rtp_parameters.encodings[i].scale_resolution_down_by < 1.0) {
LOG_AND_RETURN_ERROR(
RTCErrorType::INVALID_RANGE,
"Attempted to set RtpParameters scale_resolution_down_by to an "
"invalid value. scale_resolution_down_by must be >= 1.0");
}
if (rtp_parameters.encodings[i].max_framerate &&
*rtp_parameters.encodings[i].max_framerate < 0.0) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE,
"Attempted to set RtpParameters max_framerate to an "
"invalid value. max_framerate must be >= 0.0");
}
if (rtp_parameters.encodings[i].min_bitrate_bps &&
rtp_parameters.encodings[i].max_bitrate_bps) {
if (*rtp_parameters.encodings[i].max_bitrate_bps <
*rtp_parameters.encodings[i].min_bitrate_bps) {
LOG_AND_RETURN_ERROR(webrtc::RTCErrorType::INVALID_RANGE,
"Attempted to set RtpParameters min bitrate "
"larger than max bitrate.");
}
}
if (rtp_parameters.encodings[i].num_temporal_layers) {
if (*rtp_parameters.encodings[i].num_temporal_layers < 1 ||
*rtp_parameters.encodings[i].num_temporal_layers >
webrtc::kMaxTemporalStreams) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_RANGE,
"Attempted to set RtpParameters "
"num_temporal_layers to an invalid number.");
}
}
if (rtp_parameters.encodings[i].scale_resolution_down_to.has_value()) {
has_scale_resolution_down_to = true;
if (rtp_parameters.encodings[i].scale_resolution_down_to->width <= 0 ||
rtp_parameters.encodings[i].scale_resolution_down_to->height <= 0) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION,
"The resolution dimensions must be positive.");
}
}
if (!field_trials.IsEnabled("WebRTC-MixedCodecSimulcast")) {
if (i > 0 && rtp_parameters.encodings[i - 1].codec !=
rtp_parameters.encodings[i].codec) {
LOG_AND_RETURN_ERROR(RTCErrorType::UNSUPPORTED_OPERATION,
"Attempted to use different codec values for "
"different encodings.");
}
}
}
if (has_scale_resolution_down_to &&
absl::c_any_of(rtp_parameters.encodings,
[](const webrtc::RtpEncodingParameters& encoding) {
return encoding.active &&
!encoding.scale_resolution_down_to.has_value();
})) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION,
"If a resolution is specified on any encoding then "
"it must be specified on all encodings.");
}
return CheckScalabilityModeValues(rtp_parameters, send_codecs, send_codec);
}
webrtc::RTCError CheckRtpParametersInvalidModificationAndValues(
const webrtc::RtpParameters& old_rtp_parameters,
const webrtc::RtpParameters& rtp_parameters,
const webrtc::FieldTrialsView& field_trials) {
return CheckRtpParametersInvalidModificationAndValues(
old_rtp_parameters, rtp_parameters, {}, std::nullopt, field_trials);
}
webrtc::RTCError CheckRtpParametersInvalidModificationAndValues(
const webrtc::RtpParameters& old_rtp_parameters,
const webrtc::RtpParameters& rtp_parameters,
rtc::ArrayView<cricket::Codec> send_codecs,
std::optional<cricket::Codec> send_codec,
const webrtc::FieldTrialsView& field_trials) {
using webrtc::RTCErrorType;
if (rtp_parameters.encodings.size() != old_rtp_parameters.encodings.size()) {
LOG_AND_RETURN_ERROR(
RTCErrorType::INVALID_MODIFICATION,
"Attempted to set RtpParameters with different encoding count");
}
if (rtp_parameters.rtcp != old_rtp_parameters.rtcp) {
LOG_AND_RETURN_ERROR(
RTCErrorType::INVALID_MODIFICATION,
"Attempted to set RtpParameters with modified RTCP parameters");
}
if (rtp_parameters.header_extensions !=
old_rtp_parameters.header_extensions) {
LOG_AND_RETURN_ERROR(
RTCErrorType::INVALID_MODIFICATION,
"Attempted to set RtpParameters with modified header extensions");
}
if (!absl::c_equal(old_rtp_parameters.encodings, rtp_parameters.encodings,
[](const webrtc::RtpEncodingParameters& encoding1,
const webrtc::RtpEncodingParameters& encoding2) {
return encoding1.rid == encoding2.rid;
})) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION,
"Attempted to change RID values in the encodings.");
}
if (!absl::c_equal(old_rtp_parameters.encodings, rtp_parameters.encodings,
[](const webrtc::RtpEncodingParameters& encoding1,
const webrtc::RtpEncodingParameters& encoding2) {
return encoding1.ssrc == encoding2.ssrc;
})) {
LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION,
"Attempted to set RtpParameters with modified SSRC");
}
return CheckRtpParametersValues(rtp_parameters, send_codecs, send_codec,
field_trials);
}
CompositeMediaEngine::CompositeMediaEngine(
std::unique_ptr<webrtc::FieldTrialsView> trials,
std::unique_ptr<VoiceEngineInterface> audio_engine,
std::unique_ptr<VideoEngineInterface> video_engine)
: trials_(std::move(trials)),
voice_engine_(std::move(audio_engine)),
video_engine_(std::move(video_engine)) {}
CompositeMediaEngine::CompositeMediaEngine(
std::unique_ptr<VoiceEngineInterface> audio_engine,
std::unique_ptr<VideoEngineInterface> video_engine)
: CompositeMediaEngine(nullptr,
std::move(audio_engine),
std::move(video_engine)) {}
CompositeMediaEngine::~CompositeMediaEngine() = default;
bool CompositeMediaEngine::Init() {
voice().Init();
return true;
}
VoiceEngineInterface& CompositeMediaEngine::voice() {
return *voice_engine_.get();
}
VideoEngineInterface& CompositeMediaEngine::video() {
return *video_engine_.get();
}
const VoiceEngineInterface& CompositeMediaEngine::voice() const {
return *voice_engine_.get();
}
const VideoEngineInterface& CompositeMediaEngine::video() const {
return *video_engine_.get();
}
} // namespace cricket