Merge Add(Audio|Video)ContentFor(Offer|Answer)

since the algorithms are the same, the codec-specifics have been
separated out in the previous CL.

Manual rebase of https://webrtc-review.googlesource.com/c/src/+/326101

BUG=webrtc:15214

Change-Id: Ic3effe63f3af2ccc22f326d1685f6f55afa2db52
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/327420
Reviewed-by: Florent Castelli <orphis@webrtc.org>
Reviewed-by: Johannes Kron <kron@webrtc.org>
Commit-Queue: Philipp Hancke <phancke@microsoft.com>
Cr-Commit-Position: refs/heads/main@{#41181}
This commit is contained in:
Philipp Hancke 2023-11-15 08:53:58 +01:00 committed by WebRTC LUCI CQ
parent d07900c848
commit db1d4281d4
2 changed files with 357 additions and 524 deletions

View File

@ -1502,6 +1502,233 @@ bool IsDtlsActive(const ContentInfo* content,
.description.secure(); .description.secure();
} }
webrtc::RTCErrorOr<AudioCodecs> GetNegotiatedAudioCodecsForOffer(
const MediaDescriptionOptions& media_description_options,
const MediaSessionOptions& session_options,
const ContentInfo* current_content,
const AudioCodecs& audio_codecs,
const AudioCodecs& supported_audio_codecs) {
AudioCodecs filtered_codecs;
if (!media_description_options.codec_preferences.empty()) {
// Add the codecs from the current transceiver's codec preferences.
// They override any existing codecs from previous negotiations.
filtered_codecs =
MatchCodecPreference(media_description_options.codec_preferences,
audio_codecs, supported_audio_codecs);
} else {
// Add the codecs from current content if it exists and is not rejected nor
// recycled.
if (current_content && !current_content->rejected &&
current_content->name == media_description_options.mid) {
if (!IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO)) {
// Can happen if the remote side re-uses a MID while recycling.
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Media type for content with mid='" +
current_content->name +
"' does not match previous type.");
}
const MediaContentDescription* mcd = current_content->media_description();
for (const Codec& codec : mcd->codecs()) {
if (FindMatchingCodec(mcd->codecs(), audio_codecs, codec)) {
filtered_codecs.push_back(codec);
}
}
}
// Add other supported audio codecs.
for (const Codec& codec : supported_audio_codecs) {
absl::optional<Codec> found_codec =
FindMatchingCodec(supported_audio_codecs, audio_codecs, codec);
if (found_codec &&
!FindMatchingCodec(supported_audio_codecs, filtered_codecs, codec)) {
// Use the `found_codec` from `audio_codecs` because it has the
// correctly mapped payload type.
filtered_codecs.push_back(*found_codec);
}
}
}
if (!session_options.vad_enabled) {
// If application doesn't want CN codecs in offer.
StripCNCodecs(&filtered_codecs);
}
return filtered_codecs;
}
webrtc::RTCErrorOr<VideoCodecs> GetNegotiatedVideoCodecsForOffer(
const MediaDescriptionOptions& media_description_options,
const MediaSessionOptions& session_options,
const ContentInfo* current_content,
const VideoCodecs& video_codecs,
const VideoCodecs& supported_video_codecs) {
VideoCodecs filtered_codecs;
if (!media_description_options.codec_preferences.empty()) {
// Add the codecs from the current transceiver's codec preferences.
// They override any existing codecs from previous negotiations.
filtered_codecs =
MatchCodecPreference(media_description_options.codec_preferences,
video_codecs, supported_video_codecs);
} else {
// Add the codecs from current content if it exists and is not rejected nor
// recycled.
if (current_content && !current_content->rejected &&
current_content->name == media_description_options.mid) {
if (!IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO)) {
// Can happen if the remote side re-uses a MID while recycling.
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Media type for content with mid='" +
current_content->name +
"' does not match previous type.");
}
const MediaContentDescription* mcd = current_content->media_description();
for (const Codec& codec : mcd->codecs()) {
if (FindMatchingCodec(mcd->codecs(), video_codecs, codec)) {
filtered_codecs.push_back(codec);
}
}
}
// Add other supported video codecs.
for (const Codec& codec : supported_video_codecs) {
absl::optional<Codec> found_codec =
FindMatchingCodec(supported_video_codecs, video_codecs, codec);
if (found_codec &&
!FindMatchingCodec(supported_video_codecs, filtered_codecs, codec)) {
// Use the `found_codec` from `video_codecs` because it has the
// correctly mapped payload type.
if (IsRtxCodec(codec)) {
// For RTX we might need to adjust the apt parameter if we got a
// remote offer without RTX for a codec for which we support RTX.
auto referenced_codec =
GetAssociatedCodecForRtx(supported_video_codecs, codec);
RTC_DCHECK(referenced_codec);
// Find the codec we should be referencing and point to it.
absl::optional<Codec> changed_referenced_codec = FindMatchingCodec(
supported_video_codecs, filtered_codecs, *referenced_codec);
if (changed_referenced_codec) {
found_codec->SetParam(kCodecParamAssociatedPayloadType,
changed_referenced_codec->id);
}
}
filtered_codecs.push_back(*found_codec);
}
}
}
if (session_options.raw_packetization_for_video) {
for (Codec& codec : filtered_codecs) {
if (codec.IsMediaCodec()) {
codec.packetization = kPacketizationParamRaw;
}
}
}
return filtered_codecs;
}
webrtc::RTCErrorOr<AudioCodecs> GetNegotiatedAudioCodecsForAnswer(
const MediaDescriptionOptions& media_description_options,
const MediaSessionOptions& session_options,
const ContentInfo* current_content,
const AudioCodecs& audio_codecs,
const AudioCodecs& supported_audio_codecs) {
AudioCodecs filtered_codecs;
if (!media_description_options.codec_preferences.empty()) {
filtered_codecs =
MatchCodecPreference(media_description_options.codec_preferences,
audio_codecs, supported_audio_codecs);
} else {
// Add the codecs from current content if it exists and is not rejected nor
// recycled.
if (current_content && !current_content->rejected &&
current_content->name == media_description_options.mid) {
if (!IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO)) {
// Can happen if the remote side re-uses a MID while recycling.
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Media type for content with mid='" +
current_content->name +
"' does not match previous type.");
}
const MediaContentDescription* mcd = current_content->media_description();
for (const Codec& codec : mcd->codecs()) {
if (FindMatchingCodec(mcd->codecs(), audio_codecs, codec)) {
filtered_codecs.push_back(codec);
}
}
}
// Add other supported audio codecs.
for (const Codec& codec : supported_audio_codecs) {
if (FindMatchingCodec(supported_audio_codecs, audio_codecs, codec) &&
!FindMatchingCodec(supported_audio_codecs, filtered_codecs, codec)) {
// We should use the local codec with local parameters and the codec id
// would be correctly mapped in `NegotiateCodecs`.
filtered_codecs.push_back(codec);
}
}
}
if (!session_options.vad_enabled) {
// If application doesn't want CN codecs in answer.
StripCNCodecs(&filtered_codecs);
}
return filtered_codecs;
}
webrtc::RTCErrorOr<VideoCodecs> GetNegotiatedVideoCodecsForAnswer(
const MediaDescriptionOptions& media_description_options,
const MediaSessionOptions& session_options,
const ContentInfo* current_content,
const VideoCodecs& video_codecs,
const VideoCodecs& supported_video_codecs) {
VideoCodecs filtered_codecs;
if (!media_description_options.codec_preferences.empty()) {
filtered_codecs =
MatchCodecPreference(media_description_options.codec_preferences,
video_codecs, supported_video_codecs);
} else {
// Add the codecs from current content if it exists and is not rejected nor
// recycled.
if (current_content && !current_content->rejected &&
current_content->name == media_description_options.mid) {
if (!IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO)) {
// Can happen if the remote side re-uses a MID while recycling.
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Media type for content with mid='" +
current_content->name +
"' does not match previous type.");
}
const MediaContentDescription* mcd = current_content->media_description();
for (const Codec& codec : mcd->codecs()) {
if (FindMatchingCodec(mcd->codecs(), video_codecs, codec)) {
filtered_codecs.push_back(codec);
}
}
}
// Add other supported video codecs.
VideoCodecs other_video_codecs;
for (const Codec& codec : supported_video_codecs) {
if (FindMatchingCodec(supported_video_codecs, video_codecs, codec) &&
!FindMatchingCodec(supported_video_codecs, filtered_codecs, codec)) {
// We should use the local codec with local parameters and the codec id
// would be correctly mapped in `NegotiateCodecs`.
other_video_codecs.push_back(codec);
}
}
// Use ComputeCodecsUnion to avoid having duplicate payload IDs
filtered_codecs = ComputeCodecsUnion(filtered_codecs, other_video_codecs);
}
if (session_options.raw_packetization_for_video) {
for (Codec& codec : filtered_codecs) {
if (codec.IsMediaCodec()) {
codec.packetization = kPacketizationParamRaw;
}
}
}
return filtered_codecs;
}
} // namespace } // namespace
void MediaDescriptionOptions::AddAudioSender( void MediaDescriptionOptions::AddAudioSender(
@ -1673,15 +1900,16 @@ MediaSessionDescriptionFactory::CreateOfferOrError(
RTCError error; RTCError error;
switch (media_description_options.type) { switch (media_description_options.type) {
case MEDIA_TYPE_AUDIO: case MEDIA_TYPE_AUDIO:
error = AddAudioContentForOffer(
media_description_options, session_options, current_content,
current_description, extensions_with_ids.audio, offer_audio_codecs,
&current_streams, offer.get(), &ice_credentials);
break;
case MEDIA_TYPE_VIDEO: case MEDIA_TYPE_VIDEO:
error = AddVideoContentForOffer( error = AddRtpContentForOffer(
media_description_options, session_options, current_content, media_description_options, session_options, current_content,
current_description, extensions_with_ids.video, offer_video_codecs, current_description,
media_description_options.type == MEDIA_TYPE_AUDIO
? extensions_with_ids.audio
: extensions_with_ids.video,
media_description_options.type == MEDIA_TYPE_AUDIO
? offer_audio_codecs
: offer_video_codecs,
&current_streams, offer.get(), &ice_credentials); &current_streams, offer.get(), &ice_credentials);
break; break;
case MEDIA_TYPE_DATA: case MEDIA_TYPE_DATA:
@ -1842,18 +2070,15 @@ MediaSessionDescriptionFactory::CreateAnswerOrError(
RTCError error; RTCError error;
switch (media_description_options.type) { switch (media_description_options.type) {
case MEDIA_TYPE_AUDIO: case MEDIA_TYPE_AUDIO:
error = AddAudioContentForAnswer(
media_description_options, session_options, offer_content, offer,
current_content, current_description, bundle_transport,
answer_audio_codecs, header_extensions, &current_streams,
answer.get(), &ice_credentials);
break;
case MEDIA_TYPE_VIDEO: case MEDIA_TYPE_VIDEO:
error = AddVideoContentForAnswer( error = AddRtpContentForAnswer(
media_description_options, session_options, offer_content, offer, media_description_options, session_options, offer_content, offer,
current_content, current_description, bundle_transport, current_content, current_description, bundle_transport,
answer_video_codecs, header_extensions, &current_streams, media_description_options.type == MEDIA_TYPE_AUDIO
answer.get(), &ice_credentials); ? answer_audio_codecs
: answer_video_codecs,
header_extensions, &current_streams, answer.get(),
&ice_credentials);
break; break;
case MEDIA_TYPE_DATA: case MEDIA_TYPE_DATA:
error = AddDataContentForAnswer( error = AddDataContentForAnswer(
@ -2220,87 +2445,45 @@ RTCError MediaSessionDescriptionFactory::AddTransportAnswer(
return RTCError::OK(); return RTCError::OK();
} }
webrtc::RTCErrorOr<AudioCodecs> // `codecs` = set of all possible codecs that can be used, with correct
MediaSessionDescriptionFactory::GetNegotiatedAudioCodecsForOffer(
const MediaDescriptionOptions& media_description_options,
const MediaSessionOptions& session_options,
const ContentInfo* current_content,
const AudioCodecs& audio_codecs) const {
// Filter audio_codecs (which includes all codecs, with correctly remapped
// payload types) based on transceiver direction.
const AudioCodecs& supported_audio_codecs =
GetAudioCodecsForOffer(media_description_options.direction);
AudioCodecs filtered_codecs;
if (!media_description_options.codec_preferences.empty()) {
// Add the codecs from the current transceiver's codec preferences.
// They override any existing codecs from previous negotiations.
filtered_codecs =
MatchCodecPreference(media_description_options.codec_preferences,
audio_codecs, supported_audio_codecs);
} else {
// Add the codecs from current content if it exists and is not rejected nor
// recycled.
if (current_content && !current_content->rejected &&
current_content->name == media_description_options.mid) {
if (!IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO)) {
// Can happen if the remote side re-uses a MID while recycling.
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Media type for content with mid='" +
current_content->name +
"' does not match previous type.");
}
const MediaContentDescription* mcd = current_content->media_description();
for (const Codec& codec : mcd->codecs()) {
if (FindMatchingCodec(mcd->codecs(), audio_codecs, codec)) {
filtered_codecs.push_back(codec);
}
}
}
// Add other supported audio codecs.
for (const Codec& codec : supported_audio_codecs) {
absl::optional<Codec> found_codec =
FindMatchingCodec(supported_audio_codecs, audio_codecs, codec);
if (found_codec &&
!FindMatchingCodec(supported_audio_codecs, filtered_codecs, codec)) {
// Use the `found_codec` from `audio_codecs` because it has the
// correctly mapped payload type.
filtered_codecs.push_back(*found_codec);
}
}
}
if (!session_options.vad_enabled) {
// If application doesn't want CN codecs in offer.
StripCNCodecs(&filtered_codecs);
}
return filtered_codecs;
}
// `audio_codecs` = set of all possible codecs that can be used, with correct
// payload type mappings // payload type mappings
// //
// `supported_audio_codecs` = set of codecs that are supported for the direction // `supported_codecs` = set of codecs that are supported for the direction
// of this m= section // of this m= section
// `current_content` = current description, may be null.
// current_content->codecs() = set of previously negotiated codecs for this m=
// section
// //
// mcd->codecs() = set of previously negotiated codecs for this m= section // The payload types should come from codecs, but the order should come
// // from current_content->codecs() and then supported_codecs, to ensure that
// The payload types should come from audio_codecs, but the order should come // re-offers don't change existing codec priority, and that new codecs are added
// from mcd->codecs() and then supported_codecs, to ensure that re-offers don't // with the right priority.
// change existing codec priority, and that new codecs are added with the right RTCError MediaSessionDescriptionFactory::AddRtpContentForOffer(
// priority.
RTCError MediaSessionDescriptionFactory::AddAudioContentForOffer(
const MediaDescriptionOptions& media_description_options, const MediaDescriptionOptions& media_description_options,
const MediaSessionOptions& session_options, const MediaSessionOptions& session_options,
const ContentInfo* current_content, const ContentInfo* current_content,
const SessionDescription* current_description, const SessionDescription* current_description,
const RtpHeaderExtensions& audio_rtp_extensions, const RtpHeaderExtensions& header_extensions,
const AudioCodecs& audio_codecs, const std::vector<Codec>& codecs,
StreamParamsVec* current_streams, StreamParamsVec* current_streams,
SessionDescription* desc, SessionDescription* session_description,
IceCredentialsIterator* ice_credentials) const { IceCredentialsIterator* ice_credentials) const {
auto error_or_filtered_codecs = GetNegotiatedAudioCodecsForOffer( RTC_DCHECK(media_description_options.type == MEDIA_TYPE_AUDIO ||
media_description_options, session_options, current_content, media_description_options.type == MEDIA_TYPE_VIDEO);
audio_codecs); webrtc::RTCErrorOr<std::vector<Codec>> error_or_filtered_codecs;
if (media_description_options.type == MEDIA_TYPE_AUDIO) {
const AudioCodecs& supported_codecs =
GetAudioCodecsForOffer(media_description_options.direction);
error_or_filtered_codecs = GetNegotiatedAudioCodecsForOffer(
media_description_options, session_options, current_content, codecs,
supported_codecs);
} else {
const VideoCodecs& supported_codecs =
GetVideoCodecsForOffer(media_description_options.direction);
error_or_filtered_codecs = GetNegotiatedVideoCodecsForOffer(
media_description_options, session_options, current_content, codecs,
supported_codecs);
}
if (!error_or_filtered_codecs.ok()) { if (!error_or_filtered_codecs.ok()) {
return error_or_filtered_codecs.MoveError(); return error_or_filtered_codecs.MoveError();
} }
@ -2309,162 +2492,44 @@ RTCError MediaSessionDescriptionFactory::AddAudioContentForOffer(
IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
: secure(); : secure();
auto audio = std::make_unique<AudioContentDescription>();
std::vector<std::string> crypto_suites; std::vector<std::string> crypto_suites;
if (media_description_options.type == MEDIA_TYPE_AUDIO) {
GetSupportedAudioSdesCryptoSuiteNames(session_options.crypto_options, GetSupportedAudioSdesCryptoSuiteNames(session_options.crypto_options,
&crypto_suites); &crypto_suites);
auto error = CreateMediaContentOffer(
media_description_options, session_options,
error_or_filtered_codecs.MoveValue(), sdes_policy,
GetCryptos(current_content), crypto_suites, audio_rtp_extensions,
ssrc_generator(), current_streams, audio.get(),
transport_desc_factory_->trials());
if (!error.ok()) {
return error;
}
bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED);
SetMediaProtocol(secure_transport, audio.get());
audio->set_direction(media_description_options.direction);
desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp,
media_description_options.stopped, std::move(audio));
error = AddTransportOffer(media_description_options.mid,
media_description_options.transport_options,
current_description, desc, ice_credentials);
if (!error.ok()) {
return error;
}
return RTCError::OK();
}
webrtc::RTCErrorOr<VideoCodecs>
MediaSessionDescriptionFactory::GetNegotiatedVideoCodecsForOffer(
const MediaDescriptionOptions& media_description_options,
const MediaSessionOptions& session_options,
const ContentInfo* current_content,
const VideoCodecs& video_codecs) const {
// Filter video_codecs (which includes all codecs, with correctly remapped
// payload types) based on transceiver direction.
const VideoCodecs& supported_video_codecs =
GetVideoCodecsForOffer(media_description_options.direction);
VideoCodecs filtered_codecs;
if (!media_description_options.codec_preferences.empty()) {
// Add the codecs from the current transceiver's codec preferences.
// They override any existing codecs from previous negotiations.
filtered_codecs =
MatchCodecPreference(media_description_options.codec_preferences,
video_codecs, supported_video_codecs);
} else { } else {
// Add the codecs from current content if it exists and is not rejected nor
// recycled.
if (current_content && !current_content->rejected &&
current_content->name == media_description_options.mid) {
if (!IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO)) {
// Can happen if the remote side re-uses a MID while recycling.
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Media type for content with mid='" +
current_content->name +
"' does not match previous type.");
}
const MediaContentDescription* mcd = current_content->media_description();
for (const Codec& codec : mcd->codecs()) {
if (FindMatchingCodec(mcd->codecs(), video_codecs, codec)) {
filtered_codecs.push_back(codec);
}
}
}
// Add other supported video codecs.
for (const Codec& codec : supported_video_codecs) {
absl::optional<Codec> found_codec =
FindMatchingCodec(supported_video_codecs, video_codecs, codec);
if (found_codec &&
!FindMatchingCodec(supported_video_codecs, filtered_codecs, codec)) {
// Use the `found_codec` from `video_codecs` because it has the
// correctly mapped payload type.
if (IsRtxCodec(codec)) {
// For RTX we might need to adjust the apt parameter if we got a
// remote offer without RTX for a codec for which we support RTX.
auto referenced_codec =
GetAssociatedCodecForRtx(supported_video_codecs, codec);
RTC_DCHECK(referenced_codec);
// Find the codec we should be referencing and point to it.
absl::optional<Codec> changed_referenced_codec = FindMatchingCodec(
supported_video_codecs, filtered_codecs, *referenced_codec);
if (changed_referenced_codec) {
found_codec->SetParam(kCodecParamAssociatedPayloadType,
changed_referenced_codec->id);
}
}
filtered_codecs.push_back(*found_codec);
}
}
}
if (session_options.raw_packetization_for_video) {
for (Codec& codec : filtered_codecs) {
if (codec.IsMediaCodec()) {
codec.packetization = kPacketizationParamRaw;
}
}
}
return filtered_codecs;
}
// TODO(kron): This function is very similar to AddAudioContentForOffer.
// Refactor to reuse shared code.
RTCError MediaSessionDescriptionFactory::AddVideoContentForOffer(
const MediaDescriptionOptions& media_description_options,
const MediaSessionOptions& session_options,
const ContentInfo* current_content,
const SessionDescription* current_description,
const RtpHeaderExtensions& video_rtp_extensions,
const VideoCodecs& video_codecs,
StreamParamsVec* current_streams,
SessionDescription* desc,
IceCredentialsIterator* ice_credentials) const {
auto error_or_filtered_codecs = GetNegotiatedVideoCodecsForOffer(
media_description_options, session_options, current_content,
video_codecs);
if (!error_or_filtered_codecs.ok()) {
return error_or_filtered_codecs.MoveError();
}
cricket::SecurePolicy sdes_policy =
IsDtlsActive(current_content, current_description) ? cricket::SEC_DISABLED
: secure();
auto video = std::make_unique<VideoContentDescription>();
std::vector<std::string> crypto_suites;
GetSupportedVideoSdesCryptoSuiteNames(session_options.crypto_options, GetSupportedVideoSdesCryptoSuiteNames(session_options.crypto_options,
&crypto_suites); &crypto_suites);
}
std::unique_ptr<MediaContentDescription> content_description;
if (media_description_options.type == MEDIA_TYPE_AUDIO) {
content_description = std::make_unique<AudioContentDescription>();
} else {
content_description = std::make_unique<VideoContentDescription>();
}
auto error = CreateMediaContentOffer( auto error = CreateMediaContentOffer(
media_description_options, session_options, media_description_options, session_options,
error_or_filtered_codecs.MoveValue(), sdes_policy, error_or_filtered_codecs.MoveValue(), sdes_policy,
GetCryptos(current_content), crypto_suites, video_rtp_extensions, GetCryptos(current_content), crypto_suites, header_extensions,
ssrc_generator(), current_streams, video.get(), ssrc_generator(), current_streams, content_description.get(),
transport_desc_factory_->trials()); transport_desc_factory_->trials());
if (!error.ok()) { if (!error.ok()) {
return error; return error;
} }
video->set_bandwidth(kAutoBandwidth); bool secure_transport = transport_desc_factory_->secure() != SEC_DISABLED;
SetMediaProtocol(secure_transport, content_description.get());
bool secure_transport = (transport_desc_factory_->secure() != SEC_DISABLED); content_description->set_direction(media_description_options.direction);
SetMediaProtocol(secure_transport, video.get());
video->set_direction(media_description_options.direction); session_description->AddContent(
media_description_options.mid, MediaProtocolType::kRtp,
desc->AddContent(media_description_options.mid, MediaProtocolType::kRtp, media_description_options.stopped, std::move(content_description));
media_description_options.stopped, std::move(video));
return AddTransportOffer(media_description_options.mid, return AddTransportOffer(media_description_options.mid,
media_description_options.transport_options, media_description_options.transport_options,
current_description, desc, ice_credentials); current_description, session_description,
ice_credentials);
} }
RTCError MediaSessionDescriptionFactory::AddDataContentForOffer( RTCError MediaSessionDescriptionFactory::AddDataContentForOffer(
@ -2532,68 +2597,19 @@ RTCError MediaSessionDescriptionFactory::AddUnsupportedContentForOffer(
current_description, desc, ice_credentials); current_description, desc, ice_credentials);
} }
webrtc::RTCErrorOr<AudioCodecs> // `codecs` = set of all possible codecs that can be used, with correct
MediaSessionDescriptionFactory::GetNegotiatedAudioCodecsForAnswer(
const MediaDescriptionOptions& media_description_options,
const MediaSessionOptions& session_options,
const ContentInfo* current_content,
const AudioCodecs& audio_codecs,
const AudioCodecs& supported_audio_codecs) const {
AudioCodecs filtered_codecs;
if (!media_description_options.codec_preferences.empty()) {
filtered_codecs =
MatchCodecPreference(media_description_options.codec_preferences,
audio_codecs, supported_audio_codecs);
} else {
// Add the codecs from current content if it exists and is not rejected nor
// recycled.
if (current_content && !current_content->rejected &&
current_content->name == media_description_options.mid) {
if (!IsMediaContentOfType(current_content, MEDIA_TYPE_AUDIO)) {
// Can happen if the remote side re-uses a MID while recycling.
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Media type for content with mid='" +
current_content->name +
"' does not match previous type.");
}
const MediaContentDescription* mcd = current_content->media_description();
for (const Codec& codec : mcd->codecs()) {
if (FindMatchingCodec(mcd->codecs(), audio_codecs, codec)) {
filtered_codecs.push_back(codec);
}
}
}
// Add other supported audio codecs.
for (const Codec& codec : supported_audio_codecs) {
if (FindMatchingCodec(supported_audio_codecs, audio_codecs, codec) &&
!FindMatchingCodec(supported_audio_codecs, filtered_codecs, codec)) {
// We should use the local codec with local parameters and the codec id
// would be correctly mapped in `NegotiateCodecs`.
filtered_codecs.push_back(codec);
}
}
}
if (!session_options.vad_enabled) {
// If application doesn't want CN codecs in answer.
StripCNCodecs(&filtered_codecs);
}
return filtered_codecs;
}
// `audio_codecs` = set of all possible codecs that can be used, with correct
// payload type mappings // payload type mappings
// //
// `supported_audio_codecs` = set of codecs that are supported for the direction // `supported_codecs` = set of codecs that are supported for the direction
// of this m= section // of this m= section
// //
// mcd->codecs() = set of previously negotiated codecs for this m= section // mcd->codecs() = set of previously negotiated codecs for this m= section
// //
// The payload types should come from audio_codecs, but the order should come // The payload types should come from codecs, but the order should come
// from mcd->codecs() and then supported_codecs, to ensure that re-offers don't // from mcd->codecs() and then supported_codecs, to ensure that re-offers don't
// change existing codec priority, and that new codecs are added with the right // change existing codec priority, and that new codecs are added with the right
// priority. // priority.
RTCError MediaSessionDescriptionFactory::AddAudioContentForAnswer( RTCError MediaSessionDescriptionFactory::AddRtpContentForAnswer(
const MediaDescriptionOptions& media_description_options, const MediaDescriptionOptions& media_description_options,
const MediaSessionOptions& session_options, const MediaSessionOptions& session_options,
const ContentInfo* offer_content, const ContentInfo* offer_content,
@ -2601,37 +2617,51 @@ RTCError MediaSessionDescriptionFactory::AddAudioContentForAnswer(
const ContentInfo* current_content, const ContentInfo* current_content,
const SessionDescription* current_description, const SessionDescription* current_description,
const TransportInfo* bundle_transport, const TransportInfo* bundle_transport,
const AudioCodecs& audio_codecs, const std::vector<Codec>& codecs,
const RtpHeaderExtensions& rtp_header_extensions, const RtpHeaderExtensions& header_extensions,
StreamParamsVec* current_streams, StreamParamsVec* current_streams,
SessionDescription* answer, SessionDescription* answer,
IceCredentialsIterator* ice_credentials) const { IceCredentialsIterator* ice_credentials) const {
RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_AUDIO)); RTC_DCHECK(media_description_options.type == MEDIA_TYPE_AUDIO ||
const AudioContentDescription* offer_audio_description = media_description_options.type == MEDIA_TYPE_VIDEO);
offer_content->media_description()->as_audio(); RTC_CHECK(
IsMediaContentOfType(offer_content, media_description_options.type));
std::unique_ptr<TransportDescription> audio_transport = CreateTransportAnswer( const RtpMediaContentDescription* offer_content_description;
if (media_description_options.type == MEDIA_TYPE_AUDIO) {
offer_content_description = offer_content->media_description()->as_audio();
} else {
offer_content_description = offer_content->media_description()->as_video();
}
std::unique_ptr<TransportDescription> transport = CreateTransportAnswer(
media_description_options.mid, offer_description, media_description_options.mid, offer_description,
media_description_options.transport_options, current_description, media_description_options.transport_options, current_description,
bundle_transport != nullptr, ice_credentials); bundle_transport != nullptr, ice_credentials);
if (!audio_transport) { if (!transport) {
LOG_AND_RETURN_ERROR( LOG_AND_RETURN_ERROR(
RTCErrorType::INTERNAL_ERROR, RTCErrorType::INTERNAL_ERROR,
"Failed to create transport answer, audio transport is missing"); "Failed to create transport answer, transport is missing");
} }
// Pick codecs based on the requested communications direction in the offer // Pick codecs based on the requested communications direction in the offer
// and the selected direction in the answer. // and the selected direction in the answer.
// Note these will be filtered one final time in CreateMediaContentAnswer. // Note these will be filtered one final time in CreateMediaContentAnswer.
auto wants_rtd = media_description_options.direction; auto wants_rtd = media_description_options.direction;
auto offer_rtd = offer_audio_description->direction(); auto offer_rtd = offer_content_description->direction();
auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd); auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd);
AudioCodecs supported_audio_codecs =
GetAudioCodecsForAnswer(offer_rtd, answer_rtd);
auto error_or_filtered_codecs = GetNegotiatedAudioCodecsForAnswer( std::vector<Codec> supported_codecs;
media_description_options, session_options, current_content, audio_codecs, webrtc::RTCErrorOr<std::vector<Codec>> error_or_filtered_codecs;
supported_audio_codecs); if (media_description_options.type == MEDIA_TYPE_AUDIO) {
supported_codecs = GetAudioCodecsForAnswer(offer_rtd, answer_rtd);
error_or_filtered_codecs = GetNegotiatedAudioCodecsForAnswer(
media_description_options, session_options, current_content, codecs,
supported_codecs);
} else {
supported_codecs = GetVideoCodecsForAnswer(offer_rtd, answer_rtd);
error_or_filtered_codecs = GetNegotiatedVideoCodecsForAnswer(
media_description_options, session_options, current_content, codecs,
supported_codecs);
}
if (!error_or_filtered_codecs.ok()) { if (!error_or_filtered_codecs.ok()) {
return error_or_filtered_codecs.MoveError(); return error_or_filtered_codecs.MoveError();
} }
@ -2640,206 +2670,58 @@ RTCError MediaSessionDescriptionFactory::AddAudioContentForAnswer(
// Determine if we have media codecs in common. // Determine if we have media codecs in common.
bool has_common_media_codecs = bool has_common_media_codecs =
std::find_if(filtered_codecs.begin(), filtered_codecs.end(), std::find_if(filtered_codecs.begin(), filtered_codecs.end(),
[](const AudioCodec& c) { [](const Codec& c) {
return !(IsRedCodec(c) || IsComfortNoiseCodec(c)); return !(IsRedCodec(c) || IsComfortNoiseCodec(c) ||
IsUlpfecCodec(c) || IsFlexfecCodec(c));
}) != filtered_codecs.end(); }) != filtered_codecs.end();
bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) && bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
session_options.bundle_enabled; session_options.bundle_enabled;
auto audio_answer = std::make_unique<AudioContentDescription>(); std::unique_ptr<MediaContentDescription> answer_content;
if (media_description_options.type == MEDIA_TYPE_AUDIO) {
answer_content = std::make_unique<AudioContentDescription>();
} else {
answer_content = std::make_unique<VideoContentDescription>();
}
// Do not require or create SDES cryptos if DTLS is used. // Do not require or create SDES cryptos if DTLS is used.
cricket::SecurePolicy sdes_policy = cricket::SecurePolicy sdes_policy =
audio_transport->secure() ? cricket::SEC_DISABLED : secure(); transport->secure() ? cricket::SEC_DISABLED : secure();
if (!SetCodecsInAnswer(offer_audio_description, filtered_codecs, if (!SetCodecsInAnswer(
media_description_options, session_options, offer_content_description, filtered_codecs, media_description_options,
ssrc_generator(), current_streams, audio_answer.get(), session_options, ssrc_generator(), current_streams,
transport_desc_factory_->trials())) { answer_content.get(), transport_desc_factory_->trials())) {
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Failed to set codecs in answer"); "Failed to set codecs in answer");
} }
if (!CreateMediaContentAnswer( if (!CreateMediaContentAnswer(
offer_audio_description, media_description_options, session_options, offer_content_description, media_description_options, session_options,
sdes_policy, GetCryptos(current_content), sdes_policy, GetCryptos(current_content),
filtered_rtp_header_extensions(rtp_header_extensions), filtered_rtp_header_extensions(header_extensions), ssrc_generator(),
ssrc_generator(), enable_encrypted_rtp_header_extensions_, enable_encrypted_rtp_header_extensions_, current_streams,
current_streams, bundle_enabled, audio_answer.get())) { bundle_enabled, answer_content.get())) {
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR, LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Failed to create answer"); "Failed to create answer");
} }
bool secure = bundle_transport ? bundle_transport->description.secure() bool secure = bundle_transport ? bundle_transport->description.secure()
: audio_transport->secure(); : transport->secure();
bool rejected = media_description_options.stopped || bool rejected = media_description_options.stopped ||
offer_content->rejected || !has_common_media_codecs || offer_content->rejected || !has_common_media_codecs ||
!IsMediaProtocolSupported(MEDIA_TYPE_AUDIO, !IsMediaProtocolSupported(MEDIA_TYPE_AUDIO,
audio_answer->protocol(), secure); answer_content->protocol(), secure);
auto error = AddTransportAnswer(media_description_options.mid,
*(audio_transport.get()), answer);
if (!error.ok()) {
return error;
}
if (rejected) { if (rejected) {
RTC_LOG(LS_INFO) << "Audio m= section '" << media_description_options.mid RTC_LOG(LS_INFO) << "m= section '" << media_description_options.mid
<< "' being rejected in answer."; << "' being rejected in answer.";
} }
answer->AddContent(media_description_options.mid, offer_content->type,
rejected, std::move(audio_answer));
return RTCError::OK();
}
webrtc::RTCErrorOr<AudioCodecs>
MediaSessionDescriptionFactory::GetNegotiatedVideoCodecsForAnswer(
const MediaDescriptionOptions& media_description_options,
const MediaSessionOptions& session_options,
const ContentInfo* current_content,
const VideoCodecs& video_codecs,
const VideoCodecs& supported_video_codecs) const {
VideoCodecs filtered_codecs;
if (!media_description_options.codec_preferences.empty()) {
filtered_codecs =
MatchCodecPreference(media_description_options.codec_preferences,
video_codecs, supported_video_codecs);
} else {
// Add the codecs from current content if it exists and is not rejected nor
// recycled.
if (current_content && !current_content->rejected &&
current_content->name == media_description_options.mid) {
if (!IsMediaContentOfType(current_content, MEDIA_TYPE_VIDEO)) {
// Can happen if the remote side re-uses a MID while recycling.
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Media type for content with mid='" +
current_content->name +
"' does not match previous type.");
}
const MediaContentDescription* mcd = current_content->media_description();
for (const Codec& codec : mcd->codecs()) {
if (FindMatchingCodec(mcd->codecs(), video_codecs, codec)) {
filtered_codecs.push_back(codec);
}
}
}
// Add other supported video codecs.
VideoCodecs other_video_codecs;
for (const Codec& codec : supported_video_codecs) {
if (FindMatchingCodec(supported_video_codecs, video_codecs, codec) &&
!FindMatchingCodec(supported_video_codecs, filtered_codecs, codec)) {
// We should use the local codec with local parameters and the codec id
// would be correctly mapped in `NegotiateCodecs`.
other_video_codecs.push_back(codec);
}
}
// Use ComputeCodecsUnion to avoid having duplicate payload IDs
filtered_codecs = ComputeCodecsUnion(filtered_codecs, other_video_codecs);
}
if (session_options.raw_packetization_for_video) {
for (Codec& codec : filtered_codecs) {
if (codec.IsMediaCodec()) {
codec.packetization = kPacketizationParamRaw;
}
}
}
return filtered_codecs;
}
// TODO(kron): This function is very similar to AddAudioContentForAnswer.
// Refactor to reuse shared code.
RTCError MediaSessionDescriptionFactory::AddVideoContentForAnswer(
const MediaDescriptionOptions& media_description_options,
const MediaSessionOptions& session_options,
const ContentInfo* offer_content,
const SessionDescription* offer_description,
const ContentInfo* current_content,
const SessionDescription* current_description,
const TransportInfo* bundle_transport,
const VideoCodecs& video_codecs,
const RtpHeaderExtensions& default_video_rtp_header_extensions,
StreamParamsVec* current_streams,
SessionDescription* answer,
IceCredentialsIterator* ice_credentials) const {
RTC_CHECK(IsMediaContentOfType(offer_content, MEDIA_TYPE_VIDEO));
const VideoContentDescription* offer_video_description =
offer_content->media_description()->as_video();
std::unique_ptr<TransportDescription> video_transport = CreateTransportAnswer(
media_description_options.mid, offer_description,
media_description_options.transport_options, current_description,
bundle_transport != nullptr, ice_credentials);
if (!video_transport) {
LOG_AND_RETURN_ERROR(
RTCErrorType::INTERNAL_ERROR,
"Failed to create transport answer, video transport is missing");
}
// Pick codecs based on the requested communications direction in the offer
// and the selected direction in the answer.
// Note these will be filtered one final time in CreateMediaContentAnswer.
auto wants_rtd = media_description_options.direction;
auto offer_rtd = offer_video_description->direction();
auto answer_rtd = NegotiateRtpTransceiverDirection(offer_rtd, wants_rtd);
VideoCodecs supported_video_codecs =
GetVideoCodecsForAnswer(offer_rtd, answer_rtd);
auto error_or_filtered_codecs = GetNegotiatedVideoCodecsForAnswer(
media_description_options, session_options, current_content, video_codecs,
supported_video_codecs);
if (!error_or_filtered_codecs.ok()) {
return error_or_filtered_codecs.MoveError();
}
auto filtered_codecs = error_or_filtered_codecs.MoveValue();
// Determine if we have media codecs in common.
bool has_common_media_codecs =
std::find_if(
filtered_codecs.begin(), filtered_codecs.end(), [](const Codec& c) {
return !(IsRedCodec(c) || IsUlpfecCodec(c) || IsFlexfecCodec(c));
}) != filtered_codecs.end();
bool bundle_enabled = offer_description->HasGroup(GROUP_TYPE_BUNDLE) &&
session_options.bundle_enabled;
auto video_answer = std::make_unique<VideoContentDescription>();
// Do not require or create SDES cryptos if DTLS is used.
cricket::SecurePolicy sdes_policy =
video_transport->secure() ? cricket::SEC_DISABLED : secure();
if (!SetCodecsInAnswer(offer_video_description, filtered_codecs,
media_description_options, session_options,
ssrc_generator(), current_streams, video_answer.get(),
transport_desc_factory_->trials())) {
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Failed to set codecs in answer");
}
if (!CreateMediaContentAnswer(
offer_video_description, media_description_options, session_options,
sdes_policy, GetCryptos(current_content),
filtered_rtp_header_extensions(default_video_rtp_header_extensions),
ssrc_generator(), enable_encrypted_rtp_header_extensions_,
current_streams, bundle_enabled, video_answer.get())) {
LOG_AND_RETURN_ERROR(RTCErrorType::INTERNAL_ERROR,
"Failed to create answer");
}
bool secure = bundle_transport ? bundle_transport->description.secure()
: video_transport->secure();
bool rejected = media_description_options.stopped ||
offer_content->rejected || !has_common_media_codecs ||
!IsMediaProtocolSupported(MEDIA_TYPE_VIDEO,
video_answer->protocol(), secure);
auto error = AddTransportAnswer(media_description_options.mid, auto error = AddTransportAnswer(media_description_options.mid,
*(video_transport.get()), answer); *(transport.get()), answer);
if (!error.ok()) { if (!error.ok()) {
return error; return error;
} }
if (!rejected) {
video_answer->set_bandwidth(kAutoBandwidth);
} else {
RTC_LOG(LS_INFO) << "Video m= section '" << media_description_options.mid
<< "' being rejected in answer.";
}
answer->AddContent(media_description_options.mid, offer_content->type, answer->AddContent(media_description_options.mid, offer_content->type,
rejected, std::move(video_answer)); rejected, std::move(answer_content));
return RTCError::OK(); return RTCError::OK();
} }

View File

@ -229,37 +229,14 @@ class MediaSessionDescriptionFactory {
const TransportDescription& transport_desc, const TransportDescription& transport_desc,
SessionDescription* answer_desc) const; SessionDescription* answer_desc) const;
// Helpers for adding media contents to the SessionDescription. Returns true // Helpers for adding media contents to the SessionDescription.
// it succeeds or the media content is not needed, or false if there is any webrtc::RTCError AddRtpContentForOffer(
// error.
webrtc::RTCErrorOr<AudioCodecs> GetNegotiatedAudioCodecsForOffer(
const MediaDescriptionOptions& media_description_options,
const MediaSessionOptions& session_options,
const ContentInfo* current_content,
const AudioCodecs& audio_codecs) const;
webrtc::RTCError AddAudioContentForOffer(
const MediaDescriptionOptions& media_description_options, const MediaDescriptionOptions& media_description_options,
const MediaSessionOptions& session_options, const MediaSessionOptions& session_options,
const ContentInfo* current_content, const ContentInfo* current_content,
const SessionDescription* current_description, const SessionDescription* current_description,
const RtpHeaderExtensions& audio_rtp_extensions, const RtpHeaderExtensions& header_extensions,
const AudioCodecs& audio_codecs, const std::vector<Codec>& codecs,
StreamParamsVec* current_streams,
SessionDescription* desc,
IceCredentialsIterator* ice_credentials) const;
webrtc::RTCErrorOr<AudioCodecs> GetNegotiatedVideoCodecsForOffer(
const MediaDescriptionOptions& media_description_options,
const MediaSessionOptions& session_options,
const ContentInfo* current_content,
const VideoCodecs& video_codecs) const;
webrtc::RTCError AddVideoContentForOffer(
const MediaDescriptionOptions& media_description_options,
const MediaSessionOptions& session_options,
const ContentInfo* current_content,
const SessionDescription* current_description,
const RtpHeaderExtensions& video_rtp_extensions,
const VideoCodecs& video_codecs,
StreamParamsVec* current_streams, StreamParamsVec* current_streams,
SessionDescription* desc, SessionDescription* desc,
IceCredentialsIterator* ice_credentials) const; IceCredentialsIterator* ice_credentials) const;
@ -281,13 +258,7 @@ class MediaSessionDescriptionFactory {
SessionDescription* desc, SessionDescription* desc,
IceCredentialsIterator* ice_credentials) const; IceCredentialsIterator* ice_credentials) const;
webrtc::RTCErrorOr<AudioCodecs> GetNegotiatedAudioCodecsForAnswer( webrtc::RTCError AddRtpContentForAnswer(
const MediaDescriptionOptions& media_description_options,
const MediaSessionOptions& session_options,
const ContentInfo* current_content,
const AudioCodecs& audio_codecs,
const AudioCodecs& supported_audio_codecs) const;
webrtc::RTCError AddAudioContentForAnswer(
const MediaDescriptionOptions& media_description_options, const MediaDescriptionOptions& media_description_options,
const MediaSessionOptions& session_options, const MediaSessionOptions& session_options,
const ContentInfo* offer_content, const ContentInfo* offer_content,
@ -295,28 +266,8 @@ class MediaSessionDescriptionFactory {
const ContentInfo* current_content, const ContentInfo* current_content,
const SessionDescription* current_description, const SessionDescription* current_description,
const TransportInfo* bundle_transport, const TransportInfo* bundle_transport,
const AudioCodecs& audio_codecs, const std::vector<Codec>& codecs,
const RtpHeaderExtensions& rtp_header_extensions, const RtpHeaderExtensions& header_extensions,
StreamParamsVec* current_streams,
SessionDescription* answer,
IceCredentialsIterator* ice_credentials) const;
webrtc::RTCErrorOr<AudioCodecs> GetNegotiatedVideoCodecsForAnswer(
const MediaDescriptionOptions& media_description_options,
const MediaSessionOptions& session_options,
const ContentInfo* current_content,
const VideoCodecs& video_codecs,
const VideoCodecs& supported_video_codecs) const;
webrtc::RTCError AddVideoContentForAnswer(
const MediaDescriptionOptions& media_description_options,
const MediaSessionOptions& session_options,
const ContentInfo* offer_content,
const SessionDescription* offer_description,
const ContentInfo* current_content,
const SessionDescription* current_description,
const TransportInfo* bundle_transport,
const VideoCodecs& video_codecs,
const RtpHeaderExtensions& rtp_header_extensions,
StreamParamsVec* current_streams, StreamParamsVec* current_streams,
SessionDescription* answer, SessionDescription* answer,
IceCredentialsIterator* ice_credentials) const; IceCredentialsIterator* ice_credentials) const;