blob: acaca73b8387986bb72fdee7c71c9c4cf4dcff15 [file] [log] [blame]
/*
* Copyright 2025 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 "pc/sdp_payload_type_suggester.h"
#include <map>
#include <string>
#include <utility>
#include "absl/strings/string_view.h"
#include "api/jsep.h"
#include "api/rtc_error.h"
#include "call/payload_type.h"
#include "call/payload_type_picker.h"
#include "media/base/codec.h"
#include "pc/session_description.h"
#include "rtc_base/checks.h"
#include "rtc_base/thread.h"
#include "rtc_base/trace_event.h"
namespace webrtc {
// Implementation of the SdpPayloadTypeSuggester
RTCErrorOr<PayloadType> SdpPayloadTypeSuggester::SuggestPayloadType(
absl::string_view mid,
const Codec& codec) {
RTC_DCHECK_DISALLOW_THREAD_BLOCKING_CALLS();
PayloadTypeRecorder& local_recorder = LookupRecorder(mid, /* local= */ true);
{ // Local scope to limit lifetime of local_result.
RTCErrorOr<PayloadType> local_result =
local_recorder.LookupPayloadType(codec);
if (local_result.ok()) {
return local_result;
}
}
PayloadTypeRecorder& remote_recorder =
LookupRecorder(mid, /* local= */ false);
RTCErrorOr<PayloadType> remote_result =
remote_recorder.LookupPayloadType(codec);
if (remote_result.ok()) {
RTCErrorOr<Codec> local_codec =
local_recorder.LookupCodec(remote_result.value());
if (!local_codec.ok()) {
// Tell the local payload type registry that we've taken this
RTC_DCHECK(local_codec.error().type() == RTCErrorType::INVALID_PARAMETER);
AddLocalMapping(mid, remote_result.value(), codec);
return remote_result;
}
// If we get here, PT is already in use, possibly for something else.
// Fall through to SuggestMapping.
}
return payload_type_picker_.SuggestMapping(codec, &local_recorder);
}
RTCError SdpPayloadTypeSuggester::AddLocalMapping(absl::string_view mid,
PayloadType payload_type,
const Codec& codec) {
RTC_DCHECK_DISALLOW_THREAD_BLOCKING_CALLS();
PayloadTypeRecorder& recorder = LookupRecorder(mid, /* local= */ true);
return recorder.AddMapping(payload_type, codec);
}
RTCError SdpPayloadTypeSuggester::Update(const SessionDescription* description,
bool local,
SdpType type) {
bundle_manager_.Update(description, type);
if (type == SdpType::kAnswer) {
bundle_manager_.Commit();
}
for (const ContentInfo& content : description->contents()) {
if (content.rejected) {
continue;
}
PayloadTypeRecorder& recorder = LookupRecorder(content.mid(), local);
recorder.DisallowRedefinition();
RTCError error;
for (auto codec : content.media_description()->codecs()) {
error = recorder.AddMapping(codec.id, codec);
if (!error.ok()) {
break;
}
}
recorder.ReallowRedefinition();
if (!error.ok()) {
return error;
}
}
return RTCError::OK();
}
PayloadTypeRecorder& SdpPayloadTypeSuggester::LookupRecorder(
absl::string_view mid,
bool local) {
const ContentGroup* group = bundle_manager_.LookupGroupByMid(mid);
std::string transport_mapped_name;
if (group) {
const std::string* group_name = group->FirstContentName();
RTC_CHECK(group_name); // empty groups should be impossible here
transport_mapped_name = *group_name;
} else {
// Not in a group.
transport_mapped_name = mid;
}
if (!recorder_by_mid_.contains(transport_mapped_name)) {
recorder_by_mid_.emplace(std::make_pair(
transport_mapped_name, BundleTypeRecorder(payload_type_picker_)));
}
BundleTypeRecorder& recorder = recorder_by_mid_.at(transport_mapped_name);
return local ? recorder.local_payload_types()
: recorder.remote_payload_types();
}
} // namespace webrtc