Move webrtc_sdp serialization methods and SessionDescriptionInterface
...to the jsep target.
This resolves the circular dependency by moving the definitions and implementation of the SDP serialization and deserialization functions and `SessionDescriptionInterface` functions from `pc/` to `api/` and including them with the `jsep` target and in alignment with the JSEP API header.
The webrtc_sdp build target lives on for a bit longer while there may be downstream code that includes the webrtc_sdp.h header.
Bug: webrtc:42222470
Change-Id: If1417455729e128361575f2da161444bfed8b21e
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/415745
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Tomas Gunnarsson <tommi@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#45909}
diff --git a/api/BUILD.gn b/api/BUILD.gn
index 6dfb63c..9579cec 100644
--- a/api/BUILD.gn
+++ b/api/BUILD.gn
@@ -320,17 +320,42 @@
"jsep_ice_candidate.cc",
"jsep_ice_candidate.h",
"jsep_session_description.h",
+ "webrtc_sdp.cc",
+ "webrtc_sdp.h",
]
deps = [
":candidate",
":ref_count",
":rtc_error",
+ ":rtp_parameters",
+ ":rtp_transceiver_direction",
+ ":sctp_transport_interface",
":sequence_checker",
+ "../media:codec",
+ "../media:media_constants",
+ "../media:rid_description",
+ "../media:rtp_utils",
+ "../media:stream_params",
+ "../p2p:p2p_constants",
+ "../p2p:transport_description",
+ "../p2p:transport_info",
+ "../pc:media_protocol_names",
+ "../pc:session_description",
+ "../pc:simulcast_description",
+ "../pc:simulcast_sdp_serializer",
"../rtc_base:checks",
+ "../rtc_base:crypto_random",
+ "../rtc_base:ip_address",
"../rtc_base:logging",
"../rtc_base:macromagic",
+ "../rtc_base:net_helper",
+ "../rtc_base:net_helpers",
+ "../rtc_base:socket_address",
+ "../rtc_base:ssl",
+ "../rtc_base:stringutils",
"../rtc_base/system:no_unique_address",
"../rtc_base/system:rtc_export",
+ "audio:audio_frame_api",
"//third_party/abseil-cpp/absl/algorithm:container",
"//third_party/abseil-cpp/absl/base:nullability",
"//third_party/abseil-cpp/absl/memory",
@@ -1694,6 +1719,7 @@
"scoped_refptr_unittest.cc",
"sequence_checker_unittest.cc",
"test/peerconnection_quality_test_fixture_unittest.cc",
+ "webrtc_sdp_unittest.cc",
]
deps = [
@@ -1709,16 +1735,29 @@
":rtp_headers",
":rtp_packet_info",
":rtp_parameters",
+ ":rtp_transceiver_direction",
":scoped_refptr",
":sequence_checker",
+ "../media:codec",
+ "../media:media_constants",
+ "../media:rid_description",
+ "../media:stream_params",
"../p2p:p2p_constants",
+ "../p2p:transport_description",
+ "../p2p:transport_info",
+ "../pc:media_protocol_names",
+ "../pc:media_session",
+ "../pc:session_description",
+ "../pc:simulcast_description",
"../rtc_base:buffer",
"../rtc_base:checks",
+ "../rtc_base:digest",
"../rtc_base:logging",
"../rtc_base:macromagic",
"../rtc_base:platform_thread",
"../rtc_base:rtc_event",
"../rtc_base:socket_address",
+ "../rtc_base:ssl",
"../rtc_base:task_queue_for_test",
"../rtc_base/containers:flat_set",
"../rtc_base/synchronization:sequence_checker_internal",
@@ -1741,7 +1780,10 @@
"video/corruption_detection:frame_instrumentation_data_unittest",
"video/corruption_detection:frame_instrumentation_evaluation_unittest",
"video/corruption_detection:frame_instrumentation_generator_unittest",
+ "//third_party/abseil-cpp/absl/algorithm:container",
+ "//third_party/abseil-cpp/absl/base:nullability",
"//third_party/abseil-cpp/absl/functional:any_invocable",
+ "//third_party/abseil-cpp/absl/memory",
"//third_party/abseil-cpp/absl/strings",
"//third_party/abseil-cpp/absl/strings:string_view",
]
@@ -1749,6 +1791,10 @@
if (rtc_use_h265) {
deps += [ "video:rtp_video_frame_h265_assembler_unittests" ]
}
+
+ if (is_android) {
+ deps += [ "../pc:android_black_magic" ]
+ }
}
rtc_library("compile_all_headers") {
diff --git a/api/OWNERS b/api/OWNERS
index 49dac85..ebaea3b 100644
--- a/api/OWNERS
+++ b/api/OWNERS
@@ -11,3 +11,11 @@
per-file DEPS=mbonadei@webrtc.org
per-file uma_metrics.h=kron@webrtc.org
+
+# Adding features via SDP munging requires approval from SDP owners
+per-file webrtc_sdp.cc = set noparent
+per-file webrtc_sdp.cc = hta@webrtc.org
+per-file webrtc_sdp.cc = hbos@webrtc.org
+# If none of the above are present, may also try one of these
+per-file webrtc_sdp.cc = tommi@webrtc.org
+per-file webrtc_sdp.cc = guidou@webrtc.org
diff --git a/api/jsep.cc b/api/jsep.cc
index c2e2919..b33155d 100644
--- a/api/jsep.cc
+++ b/api/jsep.cc
@@ -10,13 +10,104 @@
#include "api/jsep.h"
+#include <algorithm>
+#include <cstddef>
+#include <iterator>
#include <memory>
#include <optional>
#include <string>
+#include <utility>
+#include <vector>
+#include "absl/memory/memory.h"
+#include "absl/strings/string_view.h"
#include "api/candidate.h"
+#include "api/sequence_checker.h"
+#include "api/webrtc_sdp.h"
+#include "p2p/base/p2p_constants.h"
+#include "p2p/base/transport_info.h"
+#include "pc/session_description.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/ip_address.h"
+#include "rtc_base/net_helper.h"
+#include "rtc_base/net_helpers.h"
+#include "rtc_base/socket_address.h"
namespace webrtc {
+namespace {
+constexpr char kDummyAddress[] = "0.0.0.0";
+constexpr int kDummyPort = 9;
+
+// Update the connection address for the MediaContentDescription based on the
+// candidates.
+void UpdateConnectionAddress(
+ const JsepCandidateCollection& candidate_collection,
+ MediaContentDescription* media_desc) {
+ int port = kDummyPort;
+ std::string ip = kDummyAddress;
+ std::string hostname;
+ int current_preference = 0; // Start with lowest preference.
+ int current_family = AF_UNSPEC;
+ for (size_t i = 0; i < candidate_collection.count(); ++i) {
+ const IceCandidate* jsep_candidate = candidate_collection.at(i);
+ if (jsep_candidate->candidate().component() !=
+ ICE_CANDIDATE_COMPONENT_RTP) {
+ continue;
+ }
+ // Default destination should be UDP only.
+ if (jsep_candidate->candidate().protocol() != UDP_PROTOCOL_NAME) {
+ continue;
+ }
+ const int preference = jsep_candidate->candidate().type_preference();
+ const int family = jsep_candidate->candidate().address().ipaddr().family();
+ // See if this candidate is more preferable then the current one if it's the
+ // same family. Or if the current family is IPv4 already so we could safely
+ // ignore all IPv6 ones. WebRTC bug 4269.
+ // http://code.google.com/p/webrtc/issues/detail?id=4269
+ if ((preference <= current_preference && current_family == family) ||
+ (current_family == AF_INET && family == AF_INET6)) {
+ continue;
+ }
+ current_preference = preference;
+ current_family = family;
+ const SocketAddress& candidate_addr = jsep_candidate->candidate().address();
+ port = candidate_addr.port();
+ ip = candidate_addr.ipaddr().ToString();
+ hostname = candidate_addr.hostname();
+ }
+ SocketAddress connection_addr(ip, port);
+ if (IPIsUnspec(connection_addr.ipaddr()) && !hostname.empty()) {
+ // When a hostname candidate becomes the (default) connection address,
+ // we use the dummy address 0.0.0.0 and port 9 in the c= and the m= lines.
+ //
+ // We have observed in deployment that with a FQDN in a c= line, SDP parsing
+ // could fail in other JSEP implementations. We note that the wildcard
+ // addresses (0.0.0.0 or ::) with port 9 are given the exception as the
+ // connection address that will not result in an ICE mismatch
+ // (draft-ietf-mmusic-ice-sip-sdp). Also, 0.0.0.0 or :: can be used as the
+ // connection address in the initial offer or answer with trickle ICE
+ // if the offerer or answerer does not want to include the host IP address
+ // (draft-ietf-mmusic-trickle-ice-sip), and in particular 0.0.0.0 has been
+ // widely deployed for this use without outstanding compatibility issues.
+ // Combining the above considerations, we use 0.0.0.0 with port 9 to
+ // populate the c= and the m= lines. See `BuildMediaDescription` in
+ // webrtc_sdp.cc for the SDP generation with
+ // `media_desc->connection_address()`.
+ connection_addr = SocketAddress(kDummyAddress, kDummyPort);
+ }
+ media_desc->set_connection_address(connection_addr);
+}
+
+std::vector<IceCandidateCollection> CloneCandidateCollection(
+ const std::vector<IceCandidateCollection>& original) {
+ std::vector<IceCandidateCollection> ret;
+ ret.reserve(original.size());
+ for (const auto& collection : original) {
+ ret.push_back(collection.Clone());
+ }
+ return ret;
+}
+} // namespace
const char SessionDescriptionInterface::kOffer[] = "offer";
const char SessionDescriptionInterface::kPrAnswer[] = "pranswer";
@@ -64,4 +155,213 @@
return IceCandidate::Create(sdp_mid, sdp_mline_index, sdp, error).release();
}
+std::unique_ptr<SessionDescriptionInterface> CreateSessionDescription(
+ SdpType type,
+ const std::string& sdp) {
+ return CreateSessionDescription(type, sdp, nullptr);
+}
+
+std::unique_ptr<SessionDescriptionInterface> CreateSessionDescription(
+ SdpType type,
+ const std::string& sdp,
+ SdpParseError* error_out) {
+ if (type == SdpType::kRollback) {
+ return CreateRollbackSessionDescription();
+ }
+ return SdpDeserialize(type, sdp, error_out);
+}
+
+std::unique_ptr<SessionDescriptionInterface> CreateSessionDescription(
+ SdpType type,
+ const std::string& session_id,
+ const std::string& session_version,
+ std::unique_ptr<SessionDescription> description) {
+ return SessionDescriptionInterface::Create(type, std::move(description),
+ session_id, session_version);
+}
+
+std::unique_ptr<SessionDescriptionInterface> CreateRollbackSessionDescription(
+ absl::string_view session_id,
+ absl::string_view session_version) {
+ return SessionDescriptionInterface::Create(
+ SdpType::kRollback, /*description=*/nullptr, session_id, session_version);
+}
+
+// static
+std::unique_ptr<SessionDescriptionInterface>
+SessionDescriptionInterface::Create(
+ SdpType type,
+ std::unique_ptr<SessionDescription> description,
+ absl::string_view id,
+ absl::string_view version,
+ std::vector<IceCandidateCollection> candidates) {
+ if (!description && type != SdpType::kRollback)
+ return nullptr;
+ return absl::WrapUnique(new SessionDescriptionInterface(
+ type, std::move(description), id, version, std::move(candidates)));
+}
+
+SessionDescriptionInternal::SessionDescriptionInternal(
+ SdpType type,
+ std::unique_ptr<SessionDescription> description,
+ absl::string_view id,
+ absl::string_view version)
+ : sdp_type_(type),
+ id_(id),
+ version_(version),
+ description_(std::move(description)) {
+ RTC_DCHECK(description_ || sdp_type_ == SdpType::kRollback);
+}
+
+SessionDescriptionInternal::~SessionDescriptionInternal() = default;
+
+size_t SessionDescriptionInternal::mediasection_count() const {
+ return description_ ? description_->contents().size() : 0u;
+}
+
+void SessionDescriptionInterface::RelinquishThreadOwnership() {
+ // Ideally we should require that the method can only be called from the
+ // thread that the sequence checker is currently attached to. However that's
+ // not compatible with some cases outside of webrtc where initializations
+ // happens on one thread and then the object is moved to a second thread (e.g.
+ // signaling) where a call is made into webrtc. At that point we'd hit a
+ // dcheck like this in webrtc: RTC_DCHECK_RUN_ON(&sequence_checker_);
+ sequence_checker_.Detach();
+ // Tie the checker to the current thread, which permits iterating
+ // `candidate_collection_`
+ RTC_DCHECK_RUN_ON(sequence_checker());
+ for (IceCandidateCollection& collection : candidate_collection_) {
+ collection.RelinquishThreadOwnership();
+ }
+ sequence_checker_.Detach(); // Unties the checker from the current thread.
+}
+
+SessionDescriptionInterface::SessionDescriptionInterface(
+ SdpType type,
+ std::unique_ptr<SessionDescription> desc,
+ absl::string_view id,
+ absl::string_view version,
+ std::vector<IceCandidateCollection> candidates)
+ : SessionDescriptionInternal(type, std::move(desc), id, version),
+ candidate_collection_(std::move(candidates)) {
+ RTC_DCHECK(description() || type == SdpType::kRollback);
+ RTC_DCHECK(candidate_collection_.empty() ||
+ candidate_collection_.size() == number_of_mediasections());
+ candidate_collection_.resize(number_of_mediasections());
+}
+
+std::unique_ptr<SessionDescriptionInterface>
+SessionDescriptionInterface::Clone() const {
+ RTC_DCHECK_RUN_ON(sequence_checker());
+ return SessionDescriptionInterface::Create(
+ sdp_type(), description() ? description()->Clone() : nullptr, id(),
+ version(), CloneCandidateCollection(candidate_collection_));
+}
+
+bool SessionDescriptionInterface::AddCandidate(const IceCandidate* candidate) {
+ RTC_DCHECK_RUN_ON(sequence_checker());
+ if (!candidate)
+ return false;
+ size_t index = 0;
+ if (!GetMediasectionIndex(candidate, &index)) {
+ return false;
+ }
+ ContentInfo& content = description()->contents()[index];
+ const TransportInfo* transport_info =
+ description()->GetTransportInfoByName(content.mid());
+ if (!transport_info) {
+ return false;
+ }
+
+ Candidate updated_candidate = candidate->candidate();
+ if (updated_candidate.username().empty()) {
+ updated_candidate.set_username(transport_info->description.ice_ufrag);
+ }
+ if (updated_candidate.password().empty()) {
+ updated_candidate.set_password(transport_info->description.ice_pwd);
+ }
+
+ // Use `content.mid()` as the mid for the updated candidate. The
+ // `candidate->sdp_mid()` property *should* be the same. However, in some
+ // cases specifying an empty mid but a valid index is a way to add a candidate
+ // without knowing (or caring about) the mid. This is done in several tests.
+ RTC_DCHECK(candidate->sdp_mid().empty() ||
+ candidate->sdp_mid() == content.mid())
+ << "sdp_mid='" << candidate->sdp_mid() << "' content.mid()='"
+ << content.mid() << "'";
+ auto updated_candidate_wrapper = std::make_unique<IceCandidate>(
+ content.mid(), static_cast<int>(index), updated_candidate);
+ IceCandidateCollection& candidates = candidate_collection_[index];
+ if (!candidates.HasCandidate(updated_candidate_wrapper.get())) {
+ candidates.add(std::move(updated_candidate_wrapper));
+ UpdateConnectionAddress(candidates, content.media_description());
+ }
+
+ return true;
+}
+
+bool SessionDescriptionInterface::RemoveCandidate(
+ const IceCandidate* candidate) {
+ RTC_DCHECK_RUN_ON(sequence_checker());
+ size_t index = 0u;
+ if (!GetMediasectionIndex(candidate, &index)) {
+ return false;
+ }
+ IceCandidateCollection& candidates = candidate_collection_[index];
+ if (!candidates.remove(candidate)) {
+ return false;
+ }
+ UpdateConnectionAddress(candidates,
+ description()->contents()[index].media_description());
+ return true;
+}
+
+const IceCandidateCollection* SessionDescriptionInterface::candidates(
+ size_t mediasection_index) const {
+ RTC_DCHECK_RUN_ON(sequence_checker());
+ if (mediasection_index >= candidate_collection_.size())
+ return nullptr;
+ return &candidate_collection_[mediasection_index];
+}
+
+bool SessionDescriptionInterface::ToString(std::string* out) const {
+ if (!description() || !out) {
+ return false;
+ }
+ *out = SdpSerialize(*this);
+ return !out->empty();
+}
+
+bool SessionDescriptionInterface::IsValidMLineIndex(int index) const {
+ RTC_DCHECK(description());
+ return index >= 0 &&
+ index < static_cast<int>(description()->contents().size());
+}
+
+bool SessionDescriptionInterface::GetMediasectionIndex(
+ const IceCandidate* candidate,
+ size_t* index) const {
+ if (!candidate || !index || !description()) {
+ return false;
+ }
+
+ auto mid = candidate->sdp_mid();
+ if (!mid.empty()) {
+ *index = GetMediasectionIndex(mid);
+ } else {
+ // An sdp_mline_index of -1 will be treated as invalid.
+ *index = static_cast<size_t>(candidate->sdp_mline_index());
+ }
+ return IsValidMLineIndex(*index);
+}
+
+int SessionDescriptionInterface::GetMediasectionIndex(
+ absl::string_view mid) const {
+ const auto& contents = description()->contents();
+ auto it =
+ std::find_if(contents.begin(), contents.end(),
+ [&](const auto& content) { return mid == content.mid(); });
+ return it == contents.end() ? -1 : std::distance(contents.begin(), it);
+}
+
} // namespace webrtc
diff --git a/pc/webrtc_sdp.cc b/api/webrtc_sdp.cc
similarity index 98%
rename from pc/webrtc_sdp.cc
rename to api/webrtc_sdp.cc
index 08d598c..5dc3d04 100644
--- a/pc/webrtc_sdp.cc
+++ b/api/webrtc_sdp.cc
@@ -8,7 +8,7 @@
* be found in the AUTHORS file in the root of the source tree.
*/
-#include "pc/webrtc_sdp.h"
+#include "api/webrtc_sdp.h"
#include <algorithm>
#include <cctype>
@@ -46,7 +46,6 @@
#include "p2p/base/transport_description.h"
#include "p2p/base/transport_info.h"
#include "pc/media_protocol_names.h"
-#include "pc/media_session.h"
#include "pc/session_description.h"
#include "pc/simulcast_description.h"
#include "pc/simulcast_sdp_serializer.h"
@@ -3108,6 +3107,28 @@
return true;
}
+bool IsMediaType(const MediaContentDescription* description, MediaType type) {
+ return description && description->type() == type;
+}
+
+const ContentInfo* GetFirstMediaContent(const ContentInfos& contents,
+ MediaType type) {
+ for (const ContentInfo& content : contents) {
+ if (IsMediaType(content.media_description(), type)) {
+ return &content;
+ }
+ }
+ return nullptr;
+}
+
+const ContentInfo* LegacyGetFirstAudioContent(const SessionDescription& sdesc) {
+ return GetFirstMediaContent(sdesc.contents(), MediaType::AUDIO);
+}
+
+const ContentInfo* LegacyGetFirstVideoContent(const SessionDescription& sdesc) {
+ return GetFirstMediaContent(sdesc.contents(), MediaType::VIDEO);
+}
+
} // namespace
std::string SdpSerialize(const SessionDescriptionInterface& jdesc) {
@@ -3171,11 +3192,11 @@
// audio/video content. Fixing that might result in much larger SDP and the
// msid-semantic line should eventually go away so this is not worth fixing.
std::set<std::string> media_stream_ids;
- const ContentInfo* audio_content = GetFirstAudioContent(desc);
+ const ContentInfo* audio_content = LegacyGetFirstAudioContent(*desc);
if (audio_content)
GetMediaStreamIds(audio_content, &media_stream_ids);
- const ContentInfo* video_content = GetFirstVideoContent(desc);
+ const ContentInfo* video_content = LegacyGetFirstVideoContent(*desc);
if (video_content)
GetMediaStreamIds(video_content, &media_stream_ids);
diff --git a/api/webrtc_sdp.h b/api/webrtc_sdp.h
new file mode 100644
index 0000000..c3d06b9
--- /dev/null
+++ b/api/webrtc_sdp.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2011 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.
+ */
+
+// This file contain functions for parsing and serializing SDP messages.
+// Related RFC/draft including:
+// * RFC 4566 - SDP
+// * RFC 5245 - ICE
+// * RFC 3388 - Grouping of Media Lines in SDP
+// * RFC 4568 - SDP Security Descriptions for Media Streams
+// * draft-lennox-mmusic-sdp-source-selection-02 -
+// Mechanisms for Media Source Selection in SDP
+
+#ifndef API_WEBRTC_SDP_H_
+#define API_WEBRTC_SDP_H_
+
+#include <memory>
+#include <string>
+
+#include "absl/base/nullability.h"
+#include "absl/strings/string_view.h"
+#include "api/jsep.h"
+
+namespace webrtc {
+// Serializes the passed in SessionDescriptionInterface.
+// Serialize SessionDescription including candidates if
+// SessionDescriptionInterface has candidates.
+// jdesc - The SessionDescriptionInterface object to be serialized.
+// return - SDP string serialized from the arguments.
+std::string SdpSerialize(const SessionDescriptionInterface& jdesc);
+
+// Deserializes the `sdp` to construct a SessionDescriptionInterface object.
+// sdp_type - The type of session description object that should be constructed.
+// sdp - The SDP string to be Deserialized.
+// error - Optional detail error information when parsing fails.
+// return - A new session description object if successful, otherwise nullptr.
+absl_nullable std::unique_ptr<SessionDescriptionInterface> SdpDeserialize(
+ SdpType sdp_type,
+ absl::string_view sdp,
+ SdpParseError* absl_nullable error = nullptr);
+
+} // namespace webrtc
+
+#endif // API_WEBRTC_SDP_H_
diff --git a/pc/webrtc_sdp_unittest.cc b/api/webrtc_sdp_unittest.cc
similarity index 99%
rename from pc/webrtc_sdp_unittest.cc
rename to api/webrtc_sdp_unittest.cc
index 6b220d4..911623f 100644
--- a/pc/webrtc_sdp_unittest.cc
+++ b/api/webrtc_sdp_unittest.cc
@@ -52,7 +52,7 @@
#ifdef WEBRTC_ANDROID
#include "pc/test/android_test_initializer.h"
#endif
-#include "pc/webrtc_sdp.h"
+#include "api/webrtc_sdp.h"
namespace webrtc {
diff --git a/pc/BUILD.gn b/pc/BUILD.gn
index e911ad2..f0a9f37 100644
--- a/pc/BUILD.gn
+++ b/pc/BUILD.gn
@@ -829,7 +829,11 @@
}
rtc_library("media_protocol_names") {
- visibility = [ ":*" ]
+ visibility = [
+ ":*",
+ "../api:jsep",
+ "../api:rtc_api_unittests",
+ ]
sources = [
"media_protocol_names.cc",
"media_protocol_names.h",
@@ -1313,7 +1317,10 @@
}
rtc_library("simulcast_sdp_serializer") {
- visibility = [ ":*" ]
+ visibility = [
+ ":*",
+ "../api:jsep",
+ ]
sources = [
"simulcast_sdp_serializer.cc",
@@ -1429,54 +1436,14 @@
"../rtc_base:threading",
]
}
-rtc_library("webrtc_sdp") {
- # TODO(bugs.webrtc.org/13661): Reduce visibility if possible
- visibility = [ "*" ] # Used by Chrome and more
- sources = [
- "jsep_session_description.cc",
- "webrtc_sdp.cc",
- "webrtc_sdp.h",
- ]
- deps = [
- ":media_protocol_names",
- ":media_session",
- ":session_description",
- ":simulcast_description",
- ":simulcast_sdp_serializer",
- "../api:candidate",
- "../api:jsep",
- "../api:rtc_error",
- "../api:rtp_parameters",
- "../api:rtp_transceiver_direction",
- "../api:sctp_transport_interface",
- "../api:sequence_checker",
- "../api/audio:audio_frame_api",
- "../media:codec",
- "../media:media_constants",
- "../media:rid_description",
- "../media:rtp_utils",
- "../media:stream_params",
- "../p2p:p2p_constants",
- "../p2p:transport_description",
- "../p2p:transport_info",
- "../rtc_base:checks",
- "../rtc_base:crypto_random",
- "../rtc_base:ip_address",
- "../rtc_base:logging",
- "../rtc_base:net_helper",
- "../rtc_base:net_helpers",
- "../rtc_base:socket_address",
- "../rtc_base:ssl",
- "../rtc_base:stringutils",
- "../rtc_base/system:rtc_export",
- "//third_party/abseil-cpp/absl/algorithm:container",
- "//third_party/abseil-cpp/absl/base:nullability",
- "//third_party/abseil-cpp/absl/memory",
- "//third_party/abseil-cpp/absl/strings",
- "//third_party/abseil-cpp/absl/strings:string_view",
- ]
+# TODO(bugs.webrtc.org/42222470): Remove this target.
+rtc_library("webrtc_sdp") {
+ visibility = [ "*" ] # Used by Chrome and more
+ sources = [ "webrtc_sdp.h" ]
+ deps = [ "../api:jsep" ]
}
+
rtc_library("webrtc_session_description_factory") {
visibility = [ ":*" ]
sources = [
@@ -2509,7 +2476,6 @@
"track_media_info_map_unittest.cc",
"video_rtp_track_source_unittest.cc",
"video_track_unittest.cc",
- "webrtc_sdp_unittest.cc",
]
deps = [
diff --git a/pc/OWNERS b/pc/OWNERS
index 8f054a6..c194b50 100644
--- a/pc/OWNERS
+++ b/pc/OWNERS
@@ -3,11 +3,3 @@
perkj@webrtc.org
tommi@webrtc.org
deadbeef@webrtc.org
-
-# Adding features via SDP munging requires approval from SDP owners
-per-file webrtc_sdp.cc = set noparent
-per-file webrtc_sdp.cc = hta@webrtc.org
-per-file webrtc_sdp.cc = hbos@webrtc.org
-# If none of the above are present, may also try one of these
-per-file webrtc_sdp.cc = tommi@webrtc.org
-per-file webrtc_sdp.cc = guidou@webrtc.org
diff --git a/pc/jsep_session_description.cc b/pc/jsep_session_description.cc
deleted file mode 100644
index 63b6dfa..0000000
--- a/pc/jsep_session_description.cc
+++ /dev/null
@@ -1,328 +0,0 @@
-/*
- * Copyright 2012 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 "api/jsep_session_description.h"
-
-#include <algorithm>
-#include <cstddef>
-#include <iterator>
-#include <memory>
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "absl/memory/memory.h"
-#include "absl/strings/string_view.h"
-#include "api/candidate.h"
-#include "api/jsep.h"
-#include "api/sequence_checker.h"
-#include "p2p/base/p2p_constants.h"
-#include "p2p/base/transport_description.h"
-#include "p2p/base/transport_info.h"
-#include "pc/media_session.h" // IWYU pragma: keep
-#include "pc/session_description.h"
-#include "pc/webrtc_sdp.h"
-#include "rtc_base/checks.h"
-#include "rtc_base/ip_address.h"
-#include "rtc_base/net_helper.h"
-#include "rtc_base/net_helpers.h"
-#include "rtc_base/socket_address.h"
-
-using webrtc::Candidate;
-using ::webrtc::SessionDescription;
-
-namespace webrtc {
-namespace {
-
-constexpr char kDummyAddress[] = "0.0.0.0";
-constexpr int kDummyPort = 9;
-
-// Update the connection address for the MediaContentDescription based on the
-// candidates.
-void UpdateConnectionAddress(
- const JsepCandidateCollection& candidate_collection,
- MediaContentDescription* media_desc) {
- int port = kDummyPort;
- std::string ip = kDummyAddress;
- std::string hostname;
- int current_preference = 0; // Start with lowest preference.
- int current_family = AF_UNSPEC;
- for (size_t i = 0; i < candidate_collection.count(); ++i) {
- const IceCandidate* jsep_candidate = candidate_collection.at(i);
- if (jsep_candidate->candidate().component() !=
- ICE_CANDIDATE_COMPONENT_RTP) {
- continue;
- }
- // Default destination should be UDP only.
- if (jsep_candidate->candidate().protocol() != UDP_PROTOCOL_NAME) {
- continue;
- }
- const int preference = jsep_candidate->candidate().type_preference();
- const int family = jsep_candidate->candidate().address().ipaddr().family();
- // See if this candidate is more preferable then the current one if it's the
- // same family. Or if the current family is IPv4 already so we could safely
- // ignore all IPv6 ones. WebRTC bug 4269.
- // http://code.google.com/p/webrtc/issues/detail?id=4269
- if ((preference <= current_preference && current_family == family) ||
- (current_family == AF_INET && family == AF_INET6)) {
- continue;
- }
- current_preference = preference;
- current_family = family;
- const SocketAddress& candidate_addr = jsep_candidate->candidate().address();
- port = candidate_addr.port();
- ip = candidate_addr.ipaddr().ToString();
- hostname = candidate_addr.hostname();
- }
- SocketAddress connection_addr(ip, port);
- if (IPIsUnspec(connection_addr.ipaddr()) && !hostname.empty()) {
- // When a hostname candidate becomes the (default) connection address,
- // we use the dummy address 0.0.0.0 and port 9 in the c= and the m= lines.
- //
- // We have observed in deployment that with a FQDN in a c= line, SDP parsing
- // could fail in other JSEP implementations. We note that the wildcard
- // addresses (0.0.0.0 or ::) with port 9 are given the exception as the
- // connection address that will not result in an ICE mismatch
- // (draft-ietf-mmusic-ice-sip-sdp). Also, 0.0.0.0 or :: can be used as the
- // connection address in the initial offer or answer with trickle ICE
- // if the offerer or answerer does not want to include the host IP address
- // (draft-ietf-mmusic-trickle-ice-sip), and in particular 0.0.0.0 has been
- // widely deployed for this use without outstanding compatibility issues.
- // Combining the above considerations, we use 0.0.0.0 with port 9 to
- // populate the c= and the m= lines. See `BuildMediaDescription` in
- // webrtc_sdp.cc for the SDP generation with
- // `media_desc->connection_address()`.
- connection_addr = SocketAddress(kDummyAddress, kDummyPort);
- }
- media_desc->set_connection_address(connection_addr);
-}
-
-std::vector<IceCandidateCollection> CloneCandidateCollection(
- const std::vector<IceCandidateCollection>& original) {
- std::vector<IceCandidateCollection> ret;
- ret.reserve(original.size());
- for (const auto& collection : original) {
- ret.push_back(collection.Clone());
- }
- return ret;
-}
-
-} // namespace
-
-std::unique_ptr<SessionDescriptionInterface> CreateSessionDescription(
- SdpType type,
- const std::string& sdp) {
- return CreateSessionDescription(type, sdp, nullptr);
-}
-
-std::unique_ptr<SessionDescriptionInterface> CreateSessionDescription(
- SdpType type,
- const std::string& sdp,
- SdpParseError* error_out) {
- if (type == SdpType::kRollback) {
- return CreateRollbackSessionDescription();
- }
- return SdpDeserialize(type, sdp, error_out);
-}
-
-std::unique_ptr<SessionDescriptionInterface> CreateSessionDescription(
- SdpType type,
- const std::string& session_id,
- const std::string& session_version,
- std::unique_ptr<SessionDescription> description) {
- return SessionDescriptionInterface::Create(type, std::move(description),
- session_id, session_version);
-}
-
-std::unique_ptr<SessionDescriptionInterface> CreateRollbackSessionDescription(
- absl::string_view session_id,
- absl::string_view session_version) {
- return SessionDescriptionInterface::Create(
- SdpType::kRollback, /*description=*/nullptr, session_id, session_version);
-}
-
-// static
-std::unique_ptr<SessionDescriptionInterface>
-SessionDescriptionInterface::Create(
- SdpType type,
- std::unique_ptr<SessionDescription> description,
- absl::string_view id,
- absl::string_view version,
- std::vector<IceCandidateCollection> candidates) {
- if (!description && type != SdpType::kRollback)
- return nullptr;
- return absl::WrapUnique(new SessionDescriptionInterface(
- type, std::move(description), id, version, std::move(candidates)));
-}
-
-SessionDescriptionInternal::SessionDescriptionInternal(
- SdpType type,
- std::unique_ptr<SessionDescription> description,
- absl::string_view id,
- absl::string_view version)
- : sdp_type_(type),
- id_(id),
- version_(version),
- description_(std::move(description)) {
- RTC_DCHECK(description_ || sdp_type_ == SdpType::kRollback);
-}
-
-SessionDescriptionInternal::~SessionDescriptionInternal() = default;
-
-size_t SessionDescriptionInternal::mediasection_count() const {
- return description_ ? description_->contents().size() : 0u;
-}
-
-void SessionDescriptionInterface::RelinquishThreadOwnership() {
- // Ideally we should require that the method can only be called from the
- // thread that the sequence checker is currently attached to. However that's
- // not compatible with some cases outside of webrtc where initializations
- // happens on one thread and then the object is moved to a second thread (e.g.
- // signaling) where a call is made into webrtc. At that point we'd hit a
- // dcheck like this in webrtc: RTC_DCHECK_RUN_ON(&sequence_checker_);
- sequence_checker_.Detach();
- // Tie the checker to the current thread, which permits iterating
- // `candidate_collection_`
- RTC_DCHECK_RUN_ON(sequence_checker());
- for (IceCandidateCollection& collection : candidate_collection_) {
- collection.RelinquishThreadOwnership();
- }
- sequence_checker_.Detach(); // Unties the checker from the current thread.
-}
-
-SessionDescriptionInterface::SessionDescriptionInterface(
- SdpType type,
- std::unique_ptr<SessionDescription> desc,
- absl::string_view id,
- absl::string_view version,
- std::vector<IceCandidateCollection> candidates)
- : SessionDescriptionInternal(type, std::move(desc), id, version),
- candidate_collection_(std::move(candidates)) {
- RTC_DCHECK(description() || type == SdpType::kRollback);
- RTC_DCHECK(candidate_collection_.empty() ||
- candidate_collection_.size() == number_of_mediasections());
- candidate_collection_.resize(number_of_mediasections());
-}
-
-std::unique_ptr<SessionDescriptionInterface>
-SessionDescriptionInterface::Clone() const {
- RTC_DCHECK_RUN_ON(sequence_checker());
- return SessionDescriptionInterface::Create(
- sdp_type(), description() ? description()->Clone() : nullptr, id(),
- version(), CloneCandidateCollection(candidate_collection_));
-}
-
-bool SessionDescriptionInterface::AddCandidate(const IceCandidate* candidate) {
- RTC_DCHECK_RUN_ON(sequence_checker());
- if (!candidate)
- return false;
- size_t index = 0;
- if (!GetMediasectionIndex(candidate, &index)) {
- return false;
- }
- ContentInfo& content = description()->contents()[index];
- const TransportInfo* transport_info =
- description()->GetTransportInfoByName(content.mid());
- if (!transport_info) {
- return false;
- }
-
- Candidate updated_candidate = candidate->candidate();
- if (updated_candidate.username().empty()) {
- updated_candidate.set_username(transport_info->description.ice_ufrag);
- }
- if (updated_candidate.password().empty()) {
- updated_candidate.set_password(transport_info->description.ice_pwd);
- }
-
- // Use `content.mid()` as the mid for the updated candidate. The
- // `candidate->sdp_mid()` property *should* be the same. However, in some
- // cases specifying an empty mid but a valid index is a way to add a candidate
- // without knowing (or caring about) the mid. This is done in several tests.
- RTC_DCHECK(candidate->sdp_mid().empty() ||
- candidate->sdp_mid() == content.mid())
- << "sdp_mid='" << candidate->sdp_mid() << "' content.mid()='"
- << content.mid() << "'";
- auto updated_candidate_wrapper = std::make_unique<IceCandidate>(
- content.mid(), static_cast<int>(index), updated_candidate);
- IceCandidateCollection& candidates = candidate_collection_[index];
- if (!candidates.HasCandidate(updated_candidate_wrapper.get())) {
- candidates.add(std::move(updated_candidate_wrapper));
- UpdateConnectionAddress(candidates, content.media_description());
- }
-
- return true;
-}
-
-bool SessionDescriptionInterface::RemoveCandidate(
- const IceCandidate* candidate) {
- RTC_DCHECK_RUN_ON(sequence_checker());
- size_t index = 0u;
- if (!GetMediasectionIndex(candidate, &index)) {
- return false;
- }
- IceCandidateCollection& candidates = candidate_collection_[index];
- if (!candidates.remove(candidate)) {
- return false;
- }
- UpdateConnectionAddress(candidates,
- description()->contents()[index].media_description());
- return true;
-}
-
-const IceCandidateCollection* SessionDescriptionInterface::candidates(
- size_t mediasection_index) const {
- RTC_DCHECK_RUN_ON(sequence_checker());
- if (mediasection_index >= candidate_collection_.size())
- return nullptr;
- return &candidate_collection_[mediasection_index];
-}
-
-bool SessionDescriptionInterface::ToString(std::string* out) const {
- if (!description() || !out) {
- return false;
- }
- *out = SdpSerialize(*this);
- return !out->empty();
-}
-
-bool SessionDescriptionInterface::IsValidMLineIndex(int index) const {
- RTC_DCHECK(description());
- return index >= 0 &&
- index < static_cast<int>(description()->contents().size());
-}
-
-bool SessionDescriptionInterface::GetMediasectionIndex(
- const IceCandidate* candidate,
- size_t* index) const {
- if (!candidate || !index || !description()) {
- return false;
- }
-
- auto mid = candidate->sdp_mid();
- if (!mid.empty()) {
- *index = GetMediasectionIndex(mid);
- } else {
- // An sdp_mline_index of -1 will be treated as invalid.
- *index = static_cast<size_t>(candidate->sdp_mline_index());
- }
- return IsValidMLineIndex(*index);
-}
-
-int SessionDescriptionInterface::GetMediasectionIndex(
- absl::string_view mid) const {
- const auto& contents = description()->contents();
- auto it =
- std::find_if(contents.begin(), contents.end(),
- [&](const auto& content) { return mid == content.mid(); });
- return it == contents.end() ? -1 : std::distance(contents.begin(), it);
-}
-
-} // namespace webrtc
diff --git a/pc/jsep_session_description_unittest.cc b/pc/jsep_session_description_unittest.cc
index 31e9bc9..47e9137 100644
--- a/pc/jsep_session_description_unittest.cc
+++ b/pc/jsep_session_description_unittest.cc
@@ -8,8 +8,6 @@
* be found in the AUTHORS file in the root of the source tree.
*/
-#include "api/jsep_session_description.h"
-
#include <cstddef>
#include <cstdint>
#include <memory>
@@ -20,12 +18,12 @@
#include "absl/strings/str_cat.h"
#include "api/candidate.h"
#include "api/jsep.h"
+#include "api/webrtc_sdp.h"
#include "media/base/codec.h"
#include "p2p/base/p2p_constants.h"
#include "p2p/base/transport_description.h"
#include "p2p/base/transport_info.h"
#include "pc/session_description.h"
-#include "pc/webrtc_sdp.h"
#include "rtc_base/crypto_random.h"
#include "rtc_base/net_helper.h"
#include "rtc_base/socket_address.h"
diff --git a/pc/peer_connection_histogram_unittest.cc b/pc/peer_connection_histogram_unittest.cc
index ace3889..6208aa4 100644
--- a/pc/peer_connection_histogram_unittest.cc
+++ b/pc/peer_connection_histogram_unittest.cc
@@ -26,12 +26,12 @@
#include "api/test/mock_async_dns_resolver.h"
#include "api/test/rtc_error_matchers.h"
#include "api/units/time_delta.h"
+#include "api/webrtc_sdp.h"
#include "pc/peer_connection.h"
#include "pc/peer_connection_wrapper.h"
#include "pc/test/enable_fake_media.h"
#include "pc/test/mock_peer_connection_observers.h"
#include "pc/usage_pattern.h"
-#include "pc/webrtc_sdp.h"
#include "rtc_base/checks.h"
#include "rtc_base/fake_mdns_responder.h"
#include "rtc_base/fake_network.h"
diff --git a/pc/simulcast_sdp_serializer.h b/pc/simulcast_sdp_serializer.h
index 84d28dd..66f0608 100644
--- a/pc/simulcast_sdp_serializer.h
+++ b/pc/simulcast_sdp_serializer.h
@@ -28,8 +28,8 @@
// format without knowing about the SDP attribute details (a=simulcast:)
// Usage:
// Consider the SDP attribute for simulcast a=simulcast:<configuration>.
-// The SDP serializtion code (webrtc_sdp.h) should use `SdpSerializer` to
-// serialize and deserialize the <configuration> section.
+// The SDP serialization code (webrtc_sdp.h) should use `SdpSerialize`
+// to serialize and deserialize the <configuration> section.
// This class will allow testing the serialization of components without
// having to serialize the entire SDP while hiding implementation details
// from callers of sdp serialization (webrtc_sdp.h).
diff --git a/pc/webrtc_sdp.h b/pc/webrtc_sdp.h
index fa4d722..86f2825 100644
--- a/pc/webrtc_sdp.h
+++ b/pc/webrtc_sdp.h
@@ -8,43 +8,9 @@
* be found in the AUTHORS file in the root of the source tree.
*/
-// This file contain functions for parsing and serializing SDP messages.
-// Related RFC/draft including:
-// * RFC 4566 - SDP
-// * RFC 5245 - ICE
-// * RFC 3388 - Grouping of Media Lines in SDP
-// * RFC 4568 - SDP Security Descriptions for Media Streams
-// * draft-lennox-mmusic-sdp-source-selection-02 -
-// Mechanisms for Media Source Selection in SDP
-
#ifndef PC_WEBRTC_SDP_H_
#define PC_WEBRTC_SDP_H_
-#include <memory>
-#include <string>
-
-#include "absl/base/nullability.h"
-#include "absl/strings/string_view.h"
-#include "api/jsep.h"
-
-namespace webrtc {
-// Serializes the passed in SessionDescriptionInterface.
-// Serialize SessionDescription including candidates if
-// SessionDescriptionInterface has candidates.
-// jdesc - The SessionDescriptionInterface object to be serialized.
-// return - SDP string serialized from the arguments.
-std::string SdpSerialize(const SessionDescriptionInterface& jdesc);
-
-// Deserializes the `sdp` to construct a SessionDescriptionInterface object.
-// sdp_type - The type of session description object that should be constructed.
-// sdp - The SDP string to be Deserialized.
-// error - Optional detail error information when parsing fails.
-// return - A new session description object if successful, otherwise nullptr.
-absl_nullable std::unique_ptr<SessionDescriptionInterface> SdpDeserialize(
- SdpType sdp_type,
- absl::string_view sdp,
- SdpParseError* absl_nullable error = nullptr);
-
-} // namespace webrtc
+#include "api/webrtc_sdp.h" // IWYU pragma: keep
#endif // PC_WEBRTC_SDP_H_
diff --git a/sdk/android/src/jni/pc/ice_candidate.cc b/sdk/android/src/jni/pc/ice_candidate.cc
index 1a76d13..aa57497 100644
--- a/sdk/android/src/jni/pc/ice_candidate.cc
+++ b/sdk/android/src/jni/pc/ice_candidate.cc
@@ -14,7 +14,7 @@
#include "absl/strings/string_view.h"
#include "api/jsep.h"
-#include "pc/webrtc_sdp.h"
+#include "api/webrtc_sdp.h"
#include "sdk/android/generated_peerconnection_jni/IceCandidate_jni.h"
#include "sdk/android/native_api/jni/java_types.h"
#include "sdk/android/src/jni/pc/media_stream_track.h"