/* * Copyright 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. */ #ifndef PC_SESSION_DESCRIPTION_H_ #define PC_SESSION_DESCRIPTION_H_ #include #include #include #include #include #include #include #include #include "absl/memory/memory.h" #include "absl/strings/string_view.h" #include "api/media_types.h" #include "api/rtp_parameters.h" #include "api/rtp_transceiver_direction.h" #include "api/rtp_transceiver_interface.h" #include "media/base/codec.h" #include "media/base/media_channel.h" #include "media/base/media_constants.h" #include "media/base/rid_description.h" #include "media/base/stream_params.h" #include "p2p/base/transport_description.h" #include "p2p/base/transport_info.h" #include "pc/media_protocol_names.h" #include "pc/simulcast_description.h" #include "rtc_base/checks.h" #include "rtc_base/socket_address.h" #include "rtc_base/system/rtc_export.h" namespace cricket { using RtpHeaderExtensions = std::vector; // Options to control how session descriptions are generated. const int kAutoBandwidth = -1; class AudioContentDescription; class VideoContentDescription; class SctpDataContentDescription; class UnsupportedContentDescription; // Describes a session description media section. There are subclasses for each // media type (audio, video, data) that will have additional information. class MediaContentDescription { public: MediaContentDescription() = default; virtual ~MediaContentDescription() = default; virtual MediaType type() const = 0; // Try to cast this media description to an AudioContentDescription. Returns // nullptr if the cast fails. virtual AudioContentDescription* as_audio() { return nullptr; } virtual const AudioContentDescription* as_audio() const { return nullptr; } // Try to cast this media description to a VideoContentDescription. Returns // nullptr if the cast fails. virtual VideoContentDescription* as_video() { return nullptr; } virtual const VideoContentDescription* as_video() const { return nullptr; } virtual SctpDataContentDescription* as_sctp() { return nullptr; } virtual const SctpDataContentDescription* as_sctp() const { return nullptr; } virtual UnsupportedContentDescription* as_unsupported() { return nullptr; } virtual const UnsupportedContentDescription* as_unsupported() const { return nullptr; } // Copy operator that returns an unique_ptr. // Not a virtual function. // If a type-specific variant of Clone() is desired, override it, or // simply use std::make_unique(*this) instead of Clone(). std::unique_ptr Clone() const { return absl::WrapUnique(CloneInternal()); } // `protocol` is the expected media transport protocol, such as RTP/AVPF, // RTP/SAVPF or SCTP/DTLS. std::string protocol() const { return protocol_; } virtual void set_protocol(absl::string_view protocol) { protocol_ = std::string(protocol); } webrtc::RtpTransceiverDirection direction() const { return direction_; } void set_direction(webrtc::RtpTransceiverDirection direction) { direction_ = direction; } bool rtcp_mux() const { return rtcp_mux_; } void set_rtcp_mux(bool mux) { rtcp_mux_ = mux; } bool rtcp_reduced_size() const { return rtcp_reduced_size_; } void set_rtcp_reduced_size(bool reduced_size) { rtcp_reduced_size_ = reduced_size; } // Indicates support for the remote network estimate packet type. This // functionality is experimental and subject to change without notice. bool remote_estimate() const { return remote_estimate_; } void set_remote_estimate(bool remote_estimate) { remote_estimate_ = remote_estimate; } int bandwidth() const { return bandwidth_; } void set_bandwidth(int bandwidth) { bandwidth_ = bandwidth; } std::string bandwidth_type() const { return bandwidth_type_; } void set_bandwidth_type(std::string bandwidth_type) { bandwidth_type_ = bandwidth_type; } // List of RTP header extensions. URIs are **NOT** guaranteed to be unique // as they can appear twice when both encrypted and non-encrypted extensions // are present. // Use RtpExtension::FindHeaderExtensionByUri for finding and // RtpExtension::DeduplicateHeaderExtensions for filtering. const RtpHeaderExtensions& rtp_header_extensions() const { return rtp_header_extensions_; } void set_rtp_header_extensions(const RtpHeaderExtensions& extensions) { rtp_header_extensions_ = extensions; rtp_header_extensions_set_ = true; } void AddRtpHeaderExtension(const webrtc::RtpExtension& ext) { rtp_header_extensions_.push_back(ext); rtp_header_extensions_set_ = true; } void ClearRtpHeaderExtensions() { rtp_header_extensions_.clear(); rtp_header_extensions_set_ = true; } // We can't always tell if an empty list of header extensions is // because the other side doesn't support them, or just isn't hooked up to // signal them. For now we assume an empty list means no signaling, but // provide the ClearRtpHeaderExtensions method to allow "no support" to be // clearly indicated (i.e. when derived from other information). bool rtp_header_extensions_set() const { return rtp_header_extensions_set_; } const StreamParamsVec& streams() const { return send_streams_; } // TODO(pthatcher): Remove this by giving mediamessage.cc access // to MediaContentDescription StreamParamsVec& mutable_streams() { return send_streams_; } void AddStream(const StreamParams& stream) { send_streams_.push_back(stream); } // Legacy streams have an ssrc, but nothing else. void AddLegacyStream(uint32_t ssrc) { AddStream(StreamParams::CreateLegacy(ssrc)); } void AddLegacyStream(uint32_t ssrc, uint32_t fid_ssrc) { StreamParams sp = StreamParams::CreateLegacy(ssrc); sp.AddFidSsrc(ssrc, fid_ssrc); AddStream(sp); } uint32_t first_ssrc() const { if (send_streams_.empty()) { return 0; } return send_streams_[0].first_ssrc(); } bool has_ssrcs() const { if (send_streams_.empty()) { return false; } return send_streams_[0].has_ssrcs(); } void set_conference_mode(bool enable) { conference_mode_ = enable; } bool conference_mode() const { return conference_mode_; } // https://tools.ietf.org/html/rfc4566#section-5.7 // May be present at the media or session level of SDP. If present at both // levels, the media-level attribute overwrites the session-level one. void set_connection_address(const rtc::SocketAddress& address) { connection_address_ = address; } const rtc::SocketAddress& connection_address() const { return connection_address_; } // Determines if it's allowed to mix one- and two-byte rtp header extensions // within the same rtp stream. enum ExtmapAllowMixed { kNo, kSession, kMedia }; void set_extmap_allow_mixed_enum(ExtmapAllowMixed new_extmap_allow_mixed) { if (new_extmap_allow_mixed == kMedia && extmap_allow_mixed_enum_ == kSession) { // Do not downgrade from session level to media level. return; } extmap_allow_mixed_enum_ = new_extmap_allow_mixed; } ExtmapAllowMixed extmap_allow_mixed_enum() const { return extmap_allow_mixed_enum_; } bool extmap_allow_mixed() const { return extmap_allow_mixed_enum_ != kNo; } // Simulcast functionality. bool HasSimulcast() const { return !simulcast_.empty(); } SimulcastDescription& simulcast_description() { return simulcast_; } const SimulcastDescription& simulcast_description() const { return simulcast_; } void set_simulcast_description(const SimulcastDescription& simulcast) { simulcast_ = simulcast; } const std::vector& receive_rids() const { return receive_rids_; } void set_receive_rids(const std::vector& rids) { receive_rids_ = rids; } // Codecs should be in preference order (most preferred codec first). const std::vector& codecs() const { return codecs_; } void set_codecs(const std::vector& codecs) { codecs_ = codecs; } virtual bool has_codecs() const { return !codecs_.empty(); } bool HasCodec(int id) { return absl::c_find_if(codecs_, [id](const cricket::Codec codec) { return codec.id == id; }) != codecs_.end(); } void AddCodec(const Codec& codec) { codecs_.push_back(codec); } void AddOrReplaceCodec(const Codec& codec) { for (auto it = codecs_.begin(); it != codecs_.end(); ++it) { if (it->id == codec.id) { *it = codec; return; } } AddCodec(codec); } void AddCodecs(const std::vector& codecs) { for (const auto& codec : codecs) { AddCodec(codec); } } protected: // TODO(bugs.webrtc.org/15214): move all RTP related things to // RtpMediaDescription that the SCTP content description does // not inherit from. std::string protocol_; private: bool rtcp_mux_ = false; bool rtcp_reduced_size_ = false; bool remote_estimate_ = false; int bandwidth_ = kAutoBandwidth; std::string bandwidth_type_ = kApplicationSpecificBandwidth; std::vector rtp_header_extensions_; bool rtp_header_extensions_set_ = false; StreamParamsVec send_streams_; bool conference_mode_ = false; webrtc::RtpTransceiverDirection direction_ = webrtc::RtpTransceiverDirection::kSendRecv; rtc::SocketAddress connection_address_; ExtmapAllowMixed extmap_allow_mixed_enum_ = kMedia; SimulcastDescription simulcast_; std::vector receive_rids_; // Copy function that returns a raw pointer. Caller will assert ownership. // Should only be called by the Clone() function. Must be implemented // by each final subclass. virtual MediaContentDescription* CloneInternal() const = 0; std::vector codecs_; }; class RtpMediaContentDescription : public MediaContentDescription {}; class AudioContentDescription : public RtpMediaContentDescription { public: void set_protocol(absl::string_view protocol) override { RTC_DCHECK(IsRtpProtocol(protocol)); protocol_ = std::string(protocol); } MediaType type() const override { return MEDIA_TYPE_AUDIO; } AudioContentDescription* as_audio() override { return this; } const AudioContentDescription* as_audio() const override { return this; } private: AudioContentDescription* CloneInternal() const override { return new AudioContentDescription(*this); } }; class VideoContentDescription : public RtpMediaContentDescription { public: void set_protocol(absl::string_view protocol) override { RTC_DCHECK(IsRtpProtocol(protocol)); protocol_ = std::string(protocol); } MediaType type() const override { return MEDIA_TYPE_VIDEO; } VideoContentDescription* as_video() override { return this; } const VideoContentDescription* as_video() const override { return this; } private: VideoContentDescription* CloneInternal() const override { return new VideoContentDescription(*this); } }; class SctpDataContentDescription : public MediaContentDescription { public: SctpDataContentDescription() {} SctpDataContentDescription(const SctpDataContentDescription& o) : MediaContentDescription(o), use_sctpmap_(o.use_sctpmap_), port_(o.port_), max_message_size_(o.max_message_size_) {} MediaType type() const override { return MEDIA_TYPE_DATA; } SctpDataContentDescription* as_sctp() override { return this; } const SctpDataContentDescription* as_sctp() const override { return this; } bool has_codecs() const override { return false; } void set_protocol(absl::string_view protocol) override { RTC_DCHECK(IsSctpProtocol(protocol)); protocol_ = std::string(protocol); } bool use_sctpmap() const { return use_sctpmap_; } void set_use_sctpmap(bool enable) { use_sctpmap_ = enable; } int port() const { return port_; } void set_port(int port) { port_ = port; } int max_message_size() const { return max_message_size_; } void set_max_message_size(int max_message_size) { max_message_size_ = max_message_size; } private: SctpDataContentDescription* CloneInternal() const override { return new SctpDataContentDescription(*this); } bool use_sctpmap_ = true; // Note: "true" is no longer conformant. // Defaults should be constants imported from SCTP. Quick hack. int port_ = 5000; // draft-ietf-mmusic-sdp-sctp-23: Max message size default is 64K int max_message_size_ = 64 * 1024; }; class UnsupportedContentDescription : public MediaContentDescription { public: explicit UnsupportedContentDescription(absl::string_view media_type) : media_type_(media_type) {} MediaType type() const override { return MEDIA_TYPE_UNSUPPORTED; } UnsupportedContentDescription* as_unsupported() override { return this; } const UnsupportedContentDescription* as_unsupported() const override { return this; } bool has_codecs() const override { return false; } const std::string& media_type() const { return media_type_; } private: UnsupportedContentDescription* CloneInternal() const override { return new UnsupportedContentDescription(*this); } std::string media_type_; }; // Protocol used for encoding media. This is the "top level" protocol that may // be wrapped by zero or many transport protocols (UDP, ICE, etc.). enum class MediaProtocolType { kRtp, // Section will use the RTP protocol (e.g., for audio or video). // https://tools.ietf.org/html/rfc3550 kSctp, // Section will use the SCTP protocol (e.g., for a data channel). // https://tools.ietf.org/html/rfc4960 kOther // Section will use another top protocol which is not // explicitly supported. }; // Represents a session description section. Most information about the section // is stored in the description, which is a subclass of MediaContentDescription. // Owns the description. class RTC_EXPORT ContentInfo { public: explicit ContentInfo(MediaProtocolType type) : type(type) {} ~ContentInfo(); // Copy ContentInfo(const ContentInfo& o); ContentInfo& operator=(const ContentInfo& o); ContentInfo(ContentInfo&& o) = default; ContentInfo& operator=(ContentInfo&& o) = default; // Alias for `name`. std::string mid() const { return name; } void set_mid(const std::string& mid) { this->name = mid; } // Alias for `description`. MediaContentDescription* media_description(); const MediaContentDescription* media_description() const; void set_media_description(std::unique_ptr desc) { description_ = std::move(desc); } // TODO(bugs.webrtc.org/8620): Rename this to mid. std::string name; MediaProtocolType type; bool rejected = false; bool bundle_only = false; private: friend class SessionDescription; std::unique_ptr description_; }; typedef std::vector ContentNames; // This class provides a mechanism to aggregate different media contents into a // group. This group can also be shared with the peers in a pre-defined format. // GroupInfo should be populated only with the `content_name` of the // MediaDescription. class ContentGroup { public: explicit ContentGroup(const std::string& semantics); ContentGroup(const ContentGroup&); ContentGroup(ContentGroup&&); ContentGroup& operator=(const ContentGroup&); ContentGroup& operator=(ContentGroup&&); ~ContentGroup(); const std::string& semantics() const { return semantics_; } const ContentNames& content_names() const { return content_names_; } const std::string* FirstContentName() const; bool HasContentName(absl::string_view content_name) const; void AddContentName(absl::string_view content_name); bool RemoveContentName(absl::string_view content_name); // for debugging std::string ToString() const; private: std::string semantics_; ContentNames content_names_; }; typedef std::vector ContentInfos; typedef std::vector ContentGroups; const ContentInfo* FindContentInfoByName(const ContentInfos& contents, const std::string& name); const ContentInfo* FindContentInfoByType(const ContentInfos& contents, const std::string& type); // Determines how the MSID will be signaled in the SDP. // These can be used as bit flags to indicate both or the special value none. enum MsidSignaling { // MSID is not signaled. This is not a bit flag and must be compared for // equality. kMsidSignalingNotUsed = 0x0, // Signal MSID with at least one a=msid line in the media section. // This requires unified plan. kMsidSignalingMediaSection = 0x1, // Signal MSID with a=ssrc: msid lines in the media section. // This should only be used with plan-b but is signalled in // offers for backward compability reasons. kMsidSignalingSsrcAttribute = 0x2, // Signal MSID with a=msid-semantic: WMS in the session section. // This is deprecated but signalled for backward compability reasons. // It is typically combined with 0x1 or 0x2. kMsidSignalingSemantic = 0x4 }; // Describes a collection of contents, each with its own name and // type. Analogous to a or stanza. Assumes that // contents are unique be name, but doesn't enforce that. class SessionDescription { public: SessionDescription(); ~SessionDescription(); std::unique_ptr Clone() const; // Content accessors. const ContentInfos& contents() const { return contents_; } ContentInfos& contents() { return contents_; } const ContentInfo* GetContentByName(const std::string& name) const; ContentInfo* GetContentByName(const std::string& name); const MediaContentDescription* GetContentDescriptionByName( const std::string& name) const; MediaContentDescription* GetContentDescriptionByName(const std::string& name); const ContentInfo* FirstContentByType(MediaProtocolType type) const; const ContentInfo* FirstContent() const; // Content mutators. // Adds a content to this description. Takes ownership of ContentDescription*. void AddContent(const std::string& name, MediaProtocolType type, std::unique_ptr description); void AddContent(const std::string& name, MediaProtocolType type, bool rejected, std::unique_ptr description); void AddContent(const std::string& name, MediaProtocolType type, bool rejected, bool bundle_only, std::unique_ptr description); void AddContent(ContentInfo&& content); bool RemoveContentByName(const std::string& name); // Transport accessors. const TransportInfos& transport_infos() const { return transport_infos_; } TransportInfos& transport_infos() { return transport_infos_; } const TransportInfo* GetTransportInfoByName(const std::string& name) const; TransportInfo* GetTransportInfoByName(const std::string& name); const TransportDescription* GetTransportDescriptionByName( const std::string& name) const { const TransportInfo* tinfo = GetTransportInfoByName(name); return tinfo ? &tinfo->description : NULL; } // Transport mutators. void set_transport_infos(const TransportInfos& transport_infos) { transport_infos_ = transport_infos; } // Adds a TransportInfo to this description. void AddTransportInfo(const TransportInfo& transport_info); bool RemoveTransportInfoByName(const std::string& name); // Group accessors. const ContentGroups& groups() const { return content_groups_; } const ContentGroup* GetGroupByName(const std::string& name) const; std::vector GetGroupsByName( const std::string& name) const; bool HasGroup(const std::string& name) const; // Group mutators. void AddGroup(const ContentGroup& group) { content_groups_.push_back(group); } // Remove the first group with the same semantics specified by `name`. void RemoveGroupByName(const std::string& name); // Global attributes. // Determines how the MSIDs were/will be signaled. Flag value composed of // MsidSignaling bits (see enum above). void set_msid_signaling(int msid_signaling) { msid_signaling_ = msid_signaling; } int msid_signaling() const { return msid_signaling_; } // Determines if it's allowed to mix one- and two-byte rtp header extensions // within the same rtp stream. void set_extmap_allow_mixed(bool supported) { extmap_allow_mixed_ = supported; MediaContentDescription::ExtmapAllowMixed media_level_setting = supported ? MediaContentDescription::kSession : MediaContentDescription::kNo; for (auto& content : contents_) { // Do not set to kNo if the current setting is kMedia. if (supported || content.media_description()->extmap_allow_mixed_enum() != MediaContentDescription::kMedia) { content.media_description()->set_extmap_allow_mixed_enum( media_level_setting); } } } bool extmap_allow_mixed() const { return extmap_allow_mixed_; } private: SessionDescription(const SessionDescription&); ContentInfos contents_; TransportInfos transport_infos_; ContentGroups content_groups_; int msid_signaling_ = kMsidSignalingMediaSection | kMsidSignalingSemantic; bool extmap_allow_mixed_ = true; }; // Indicates whether a session description was sent by the local client or // received from the remote client. enum ContentSource { CS_LOCAL, CS_REMOTE }; } // namespace cricket #endif // PC_SESSION_DESCRIPTION_H_