webrtc_m130/media/base/media_engine.cc
Åsa Persson 929c02a479 Add IsSameRtpCodec method to Codec.
This is similar to MatchesRtpCodec but not an exact match of parameters, unspecified parameters are treated as default. Use IsSameRtpCodec for comparison when codec is configured via encodings.

Bug: b:299588022
Change-Id: I0ea800e50af6f5666e3e867a928e15b0aa044635
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/365142
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Åsa Persson <asapersson@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#43272}
2024-10-21 11:25:24 +00:00

325 lines
12 KiB
C++

/*
* 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_requested_resolution = 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].requested_resolution.has_value()) {
has_requested_resolution = true;
if (rtp_parameters.encodings[i].requested_resolution->width <= 0 ||
rtp_parameters.encodings[i].requested_resolution->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_requested_resolution &&
absl::c_any_of(rtp_parameters.encodings,
[](const webrtc::RtpEncodingParameters& encoding) {
return !encoding.requested_resolution.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