/* * Copyright (c) 2016-present The ZLMediaKit project authors. All Rights Reserved. * * This file is part of ZLMediaKit(https://github.com/ZLMediaKit/ZLMediaKit). * * Use of this source code is governed by MIT-like license that can be found in the * LICENSE file in the root of the source tree. All contributing project authors * may be found in the AUTHORS file in the root of the source tree. */ #ifndef ZLMEDIAKIT_SDP_H #define ZLMEDIAKIT_SDP_H #include #include #include #include #include "RtpExt.h" #include "assert.h" #include "Extension/Frame.h" #include "Common/Parser.h" namespace mediakit { // https://datatracker.ietf.org/doc/rfc4566/?include_text=1 // https://blog.csdn.net/aggresss/article/details/109850434 // https://aggresss.blog.csdn.net/article/details/106436703 // Session description // v= (protocol version) // o= (originator and session identifier) // s= (session name) // i=* (session information) // u=* (URI of description) // e=* (email address) // p=* (phone number) // c=* (connection information -- not required if included in // all media) // b=* (zero or more bandwidth information lines) // One or more time descriptions ("t=" and "r=" lines; see below) // z=* (time zone adjustments) // k=* (encryption key) // a=* (zero or more session attribute lines) // Zero or more media descriptions // // Time description // t= (time the session is active) // r=* (zero or more repeat times) // // Media description, if present // m= (media name and transport address) // i=* (media title) // c=* (connection information -- optional if included at // session level) // b=* (zero or more bandwidth information lines) // k=* (encryption key) // a=* (zero or more media attribute lines) enum class RtpDirection { invalid = -1, // 只发送 [AUTO-TRANSLATED:d7e7fdb7] // Send only sendonly, // 只接收 [AUTO-TRANSLATED:f75ca789] // Receive only recvonly, // 同时发送接收 [AUTO-TRANSLATED:7f900ba1] // Send and receive simultaneously sendrecv, // 禁止发送数据 [AUTO-TRANSLATED:6045b47e] // Prohibit sending data inactive }; enum class DtlsRole { invalid = -1, // 客户端 [AUTO-TRANSLATED:915417a2] // Client active, // 服务端 [AUTO-TRANSLATED:03a80b18] // Server passive, // 既可作做客户端也可以做服务端 [AUTO-TRANSLATED:5ab1162e] // Can be used as both client and server actpass, }; enum class SdpType { invalid = -1, offer, answer }; DtlsRole getDtlsRole(const std::string &str); const char *getDtlsRoleString(DtlsRole role); RtpDirection getRtpDirection(const std::string &str); const char *getRtpDirectionString(RtpDirection val); class SdpItem { public: using Ptr = std::shared_ptr; virtual ~SdpItem() = default; virtual void parse(const std::string &str) { value = str; } virtual std::string toString() const { return value; } virtual const char *getKey() const = 0; void reset() { value.clear(); } protected: mutable std::string value; }; template class SdpString : public SdpItem { public: SdpString() = default; SdpString(std::string val) { value = std::move(val); } // *=* const char* getKey() const override { static std::string key(1, KEY); return key.data();} }; class SdpCommon : public SdpItem { public: std::string key; SdpCommon(std::string key) { this->key = std::move(key); } SdpCommon(std::string key, std::string val) { this->key = std::move(key); this->value = std::move(val); } const char *getKey() const override { return key.data(); } }; class SdpTime : public SdpItem { public: // 5.9. Timing ("t=") // t= uint64_t start { 0 }; uint64_t stop { 0 }; void parse(const std::string &str) override; std::string toString() const override; const char *getKey() const override { return "t"; } }; class SdpOrigin : public SdpItem { public: // 5.2. Origin ("o=") // o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5 // o= std::string username { "-" }; std::string session_id; std::string session_version; std::string nettype { "IN" }; std::string addrtype { "IP4" }; std::string address { "0.0.0.0" }; void parse(const std::string &str) override; std::string toString() const override; const char *getKey() const override { return "o"; } bool empty() const { return username.empty() || session_id.empty() || session_version.empty() || nettype.empty() || addrtype.empty() || address.empty(); } }; class SdpConnection : public SdpItem { public: // 5.7. Connection Data ("c=") // c=IN IP4 224.2.17.12/127 // c= std::string nettype { "IN" }; std::string addrtype { "IP4" }; std::string address { "0.0.0.0" }; void parse(const std::string &str) override; std::string toString() const override; const char *getKey() const override { return "c"; } bool empty() const { return address.empty(); } }; class SdpBandwidth : public SdpItem { public: // 5.8. Bandwidth ("b=") // b=: // AS、CT [AUTO-TRANSLATED:65298206] // AS, CT std::string bwtype { "AS" }; uint32_t bandwidth { 0 }; void parse(const std::string &str) override; std::string toString() const override; const char *getKey() const override { return "b"; } bool empty() const { return bandwidth == 0; } }; class SdpMedia : public SdpItem { public: // 5.14. Media Descriptions ("m=") // m= ... TrackType type; uint16_t port; // RTP/AVP:应用场景为视频/音频的 RTP 协议。参考 RFC 3551 [AUTO-TRANSLATED:7a9d7e86] // RTP/AVP: The application scenario is the RTP protocol for video/audio. Refer to RFC 3551 // RTP/SAVP:应用场景为视频/音频的 SRTP 协议。参考 RFC 3711 [AUTO-TRANSLATED:7989a619] // RTP/SAVP: The application scenario is the SRTP protocol for video/audio. Refer to RFC 3711 // RTP/AVPF: 应用场景为视频/音频的 RTP 协议,支持 RTCP-based Feedback。参考 RFC 4585 [AUTO-TRANSLATED:71241e80] // RTP/AVPF: The application scenario is the RTP protocol for video/audio, supporting RTCP-based Feedback. Refer to RFC 4585 // RTP/SAVPF: 应用场景为视频/音频的 SRTP 协议,支持 RTCP-based Feedback。参考 RFC 5124 [AUTO-TRANSLATED:69015267] // RTP/SAVPF: The application scenario is the SRTP protocol for video/audio, supporting RTCP-based Feedback. Refer to RFC 5124 std::string proto; std::vector fmts; void parse(const std::string &str) override; std::string toString() const override; const char *getKey() const override { return "m"; } }; class SdpAttr : public SdpItem { public: using Ptr = std::shared_ptr; // 5.13. Attributes ("a=") // a= // a=: SdpItem::Ptr detail; void parse(const std::string &str) override; std::string toString() const override; const char *getKey() const override { return "a"; } }; class SdpAttrGroup : public SdpItem { public: // a=group:BUNDLE line with all the 'mid' identifiers part of the // BUNDLE group is included at the session-level. // a=group:LS session level attribute MUST be included wth the 'mid' // identifiers that are part of the same lip sync group. std::string type { "BUNDLE" }; std::vector mids; void parse(const std::string &str) override; std::string toString() const override; const char *getKey() const override { return "group"; } }; class SdpAttrMsidSemantic : public SdpItem { public: // https://tools.ietf.org/html/draft-alvestrand-rtcweb-msid-02#section-3 // 3. The Msid-Semantic Attribute // // In order to fully reproduce the semantics of the SDP and SSRC // grouping frameworks, a session-level attribute is defined for // signalling the semantics associated with an msid grouping. // // This OPTIONAL attribute gives the message ID and its group semantic. // a=msid-semantic: examplefoo LS // // // The ABNF of msid-semantic is: // // msid-semantic-attr = "msid-semantic:" " " msid token // token = // // The semantic field may hold values from the IANA registries // "Semantics for the "ssrc-group" SDP Attribute" and "Semantics for the // "group" SDP Attribute". // a=msid-semantic: WMS 616cfbb1-33a3-4d8c-8275-a199d6005549 std::string msid { "WMS" }; std::string token; void parse(const std::string &str) override; std::string toString() const override; const char *getKey() const override { return "msid-semantic"; } bool empty() const { return msid.empty(); } }; class SdpAttrRtcp : public SdpItem { public: // a=rtcp:9 IN IP4 0.0.0.0 uint16_t port { 0 }; std::string nettype { "IN" }; std::string addrtype { "IP4" }; std::string address { "0.0.0.0" }; void parse(const std::string &str) override; ; std::string toString() const override; const char *getKey() const override { return "rtcp"; } bool empty() const { return address.empty() || !port; } }; class SdpAttrIceUfrag : public SdpItem { public: SdpAttrIceUfrag() = default; SdpAttrIceUfrag(std::string str) { value = std::move(str); } // a=ice-ufrag:sXJ3 const char *getKey() const override { return "ice-ufrag"; } }; class SdpAttrIcePwd : public SdpItem { public: SdpAttrIcePwd() = default; SdpAttrIcePwd(std::string str) { value = std::move(str); } // a=ice-pwd:yEclOTrLg1gEubBFefOqtmyV const char *getKey() const override { return "ice-pwd"; } }; class SdpAttrIceOption : public SdpItem { public: // a=ice-options:trickle bool trickle { false }; bool renomination { false }; void parse(const std::string &str) override; std::string toString() const override; const char *getKey() const override { return "ice-options"; } }; class SdpAttrFingerprint : public SdpItem { public: // a=fingerprint:sha-256 22:14:B5:AF:66:12:C7:C7:8D:EF:4B:DE:40:25:ED:5D:8F:17:54:DD:88:33:C0:13:2E:FD:1A:FA:7E:7A:1B:79 std::string algorithm; std::string hash; void parse(const std::string &str) override; std::string toString() const override; const char *getKey() const override { return "fingerprint"; } bool empty() const { return algorithm.empty() || hash.empty(); } }; class SdpAttrSetup : public SdpItem { public: // a=setup:actpass SdpAttrSetup() = default; SdpAttrSetup(DtlsRole r) { role = r; } DtlsRole role { DtlsRole::actpass }; void parse(const std::string &str) override; std::string toString() const override; const char *getKey() const override { return "setup"; } }; class SdpAttrMid : public SdpItem { public: SdpAttrMid() = default; SdpAttrMid(std::string val) { value = std::move(val); } // a=mid:audio const char *getKey() const override { return "mid"; } }; class SdpAttrExtmap : public SdpItem { public: // https://aggresss.blog.csdn.net/article/details/106436703 // a=extmap:1[/sendonly] urn:ietf:params:rtp-hdrext:ssrc-audio-level uint8_t id; RtpDirection direction { RtpDirection::invalid }; std::string ext; void parse(const std::string &str) override; std::string toString() const override; const char *getKey() const override { return "extmap"; } }; class SdpAttrRtpMap : public SdpItem { public: // a=rtpmap:111 opus/48000/2 uint8_t pt; std::string codec; uint32_t sample_rate; uint32_t channel { 0 }; void parse(const std::string &str) override; std::string toString() const override; const char *getKey() const override { return "rtpmap"; } }; class SdpAttrRtcpFb : public SdpItem { public: // a=rtcp-fb:98 nack pli // a=rtcp-fb:120 nack 支持 nack 重传,nack (Negative-Acknowledgment) 。 [AUTO-TRANSLATED:08d5c4e2] // a=rtcp-fb:120 nack supports nack retransmission, nack (Negative-Acknowledgment). // a=rtcp-fb:120 nack pli 支持 nack 关键帧重传,PLI (Picture Loss Indication) 。 [AUTO-TRANSLATED:c331c1dd] // a=rtcp-fb:120 nack pli supports nack keyframe retransmission, PLI (Picture Loss Indication). // a=rtcp-fb:120 ccm fir 支持编码层关键帧请求,CCM (Codec Control Message),FIR (Full Intra Request ),通常与 nack pli 有同样的效果,但是 nack pli [AUTO-TRANSLATED:7090fdc9] // a=rtcp-fb:120 ccm fir supports keyframe requests for the coding layer, CCM (Codec Control Message), FIR (Full Intra Request), which usually has the same effect as nack pli, but nack pli // 是用于重传时的关键帧请求。 a=rtcp-fb:120 goog-remb 支持 REMB (Receiver Estimated Maximum Bitrate) 。 a=rtcp-fb:120 transport-cc 支持 TCC (Transport [AUTO-TRANSLATED:ffac8e91] // is used for keyframe requests during retransmission. a=rtcp-fb:120 goog-remb supports REMB (Receiver Estimated Maximum Bitrate). a=rtcp-fb:120 transport-cc supports TCC (Transport // Congest Control) 。 [AUTO-TRANSLATED:dcf53e31] // Congest Control). uint8_t pt; std::string rtcp_type; void parse(const std::string &str) override; std::string toString() const override; const char *getKey() const override { return "rtcp-fb"; } }; class SdpAttrFmtp : public SdpItem { public: // fmtp:96 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f uint8_t pt; std::map fmtp; void parse(const std::string &str) override; std::string toString() const override; const char *getKey() const override { return "fmtp"; } }; class SdpAttrSSRC : public SdpItem { public: // a=ssrc:3245185839 cname:Cx4i/VTR51etgjT7 // a=ssrc:3245185839 msid:cb373bff-0fea-4edb-bc39-e49bb8e8e3b9 0cf7e597-36a2-4480-9796-69bf0955eef5 // a=ssrc:3245185839 mslabel:cb373bff-0fea-4edb-bc39-e49bb8e8e3b9 // a=ssrc:3245185839 label:0cf7e597-36a2-4480-9796-69bf0955eef5 // a=ssrc: // a=ssrc: : // cname 是必须的,msid/mslabel/label 这三个属性都是 WebRTC 自创的,或者说 Google 自创的,可以参考 https://tools.ietf.org/html/draft-ietf-mmusic-msid-17, [AUTO-TRANSLATED:d8cb1baf] // cname is required, msid/mslabel/label these three attributes are all created by WebRTC, or Google created, you can refer to https://tools.ietf.org/html/draft-ietf-mmusic-msid-17, // 理解它们三者的关系需要先了解三个概念:RTP stream / MediaStreamTrack / MediaStream : [AUTO-TRANSLATED:7d385cf5] // understanding the relationship between the three requires understanding three concepts: RTP stream / MediaStreamTrack / MediaStream: // 一个 a=ssrc 代表一个 RTP stream ; [AUTO-TRANSLATED:ee1ecc6f] // One a=ssrc represents one RTP stream; // 一个 MediaStreamTrack 通常包含一个或多个 RTP stream,例如一个视频 MediaStreamTrack 中通常包含两个 RTP stream,一个用于常规传输,一个用于 nack 重传; [AUTO-TRANSLATED:e8ddf0fd] // A MediaStreamTrack usually contains one or more RTP streams, for example, a video MediaStreamTrack usually contains two RTP streams, one for regular transmission and one for nack retransmission; // 一个 MediaStream 通常包含一个或多个 MediaStreamTrack ,例如 simulcast 场景下,一个 MediaStream 通常会包含三个不同编码质量的 MediaStreamTrack ; [AUTO-TRANSLATED:31318d43] // A MediaStream usually contains one or more MediaStreamTrack, for example, in a simulcast scenario, a MediaStream usually contains three MediaStreamTrack of different encoding quality; // 这种标记方式并不被 Firefox 认可,在 Firefox 生成的 SDP 中一个 a=ssrc 通常只有一行,例如: [AUTO-TRANSLATED:8c2c424c] // This marking method is not recognized by Firefox, in the SDP generated by Firefox, one a=ssrc usually has only one line, for example: // a=ssrc:3245185839 cname:Cx4i/VTR51etgjT7 uint32_t ssrc; std::string attribute; std::string attribute_value; void parse(const std::string &str) override; std::string toString() const override; const char *getKey() const override { return "ssrc"; } }; class SdpAttrSSRCGroup : public SdpItem { public: // a=ssrc-group 定义参考 RFC 5576(https://tools.ietf.org/html/rfc5576) ,用于描述多个 ssrc 之间的关联,常见的有两种: [AUTO-TRANSLATED:a87cbcc6] // a=ssrc-group definition refers to RFC 5576(https://tools.ietf.org/html/rfc5576), used to describe the association between multiple ssrcs, there are two common types: // a=ssrc-group:FID 2430709021 3715850271 // FID (Flow Identification) 最初用在 FEC 的关联中,WebRTC 中通常用于关联一组常规 RTP stream 和 重传 RTP stream 。 [AUTO-TRANSLATED:f2c0fcbb] // FID (Flow Identification) was originally used in FEC association, and in WebRTC it is usually used to associate a group of regular RTP streams and retransmission RTP streams. // a=ssrc-group:SIM 360918977 360918978 360918980 // 在 Chrome 独有的 SDP munging 风格的 simulcast 中使用,将三组编码质量由低到高的 MediaStreamTrack 关联在一起。 [AUTO-TRANSLATED:61bf7596] // Used in Chrome's unique SDP munging style simulcast, associating three groups of MediaStreamTrack from low to high encoding quality. std::string type { "FID" }; std::vector ssrcs; bool isFID() const { return type == "FID"; } bool isSIM() const { return type == "SIM"; } void parse(const std::string &str) override; std::string toString() const override; const char *getKey() const override { return "ssrc-group"; } }; class SdpAttrSctpMap : public SdpItem { public: // https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-05 // a=sctpmap:5000 webrtc-datachannel 1024 // a=sctpmap: sctpmap-number media-subtypes [streams] uint16_t port = 0; std::string subtypes; uint32_t streams = 0; void parse(const std::string &str) override; std::string toString() const override; const char *getKey() const override { return "sctpmap"; } bool empty() const { return port == 0 && subtypes.empty() && streams == 0; } }; class SdpAttrCandidate : public SdpItem { public: using Ptr = std::shared_ptr; // https://tools.ietf.org/html/rfc5245 // 15.1. "candidate" Attribute // a=candidate:4 1 udp 2 192.168.1.7 58107 typ host // a=candidate:
typ std::string foundation; // 传输媒体的类型,1代表RTP;2代表 RTCP。 [AUTO-TRANSLATED:9ec924a6] // The type of media to be transmitted, 1 represents RTP; 2 represents RTCP. uint32_t component; std::string transport { "udp" }; uint32_t priority; std::string address; uint16_t port; std::string type; std::vector> arr; void parse(const std::string &str) override; std::string toString() const override; const char *getKey() const override { return "candidate"; } }; class SdpAttrMsid : public SdpItem { public: const char *getKey() const override { return "msid"; } }; class SdpAttrExtmapAllowMixed : public SdpItem { public: const char *getKey() const override { return "extmap-allow-mixed"; } }; class SdpAttrSimulcast : public SdpItem { public: // https://www.meetecho.com/blog/simulcast-janus-ssrc/ // https://tools.ietf.org/html/draft-ietf-mmusic-sdp-simulcast-14 const char *getKey() const override { return "simulcast"; } void parse(const std::string &str) override; std::string toString() const override; bool empty() const { return rids.empty(); } std::string direction; std::vector rids; }; class SdpAttrRid : public SdpItem { public: void parse(const std::string &str) override; std::string toString() const override; const char *getKey() const override { return "rid"; } std::string direction; std::string rid; }; class RtcSdpBase { public: void addItem(SdpItem::Ptr item) { items.push_back(std::move(item)); } void addAttr(SdpItem::Ptr attr) { auto item = std::make_shared(); item->detail = std::move(attr); items.push_back(std::move(item)); } virtual ~RtcSdpBase() = default; virtual std::string toString() const; void toRtsp(); RtpDirection getDirection() const; template cls getItemClass(char key, const char *attr_key = nullptr) const { auto item = std::dynamic_pointer_cast(getItem(key, attr_key)); if (!item) { return cls(); } return *item; } std::string getStringItem(char key, const char *attr_key = nullptr) const { auto item = getItem(key, attr_key); if (!item) { return ""; } return item->toString(); } SdpItem::Ptr getItem(char key, const char *attr_key = nullptr) const; template std::vector getAllItem(char key_c, const char *attr_key = nullptr) const { std::vector ret; std::string key(1, key_c); for (auto item : items) { if (strcasecmp(item->getKey(), key.data()) == 0) { if (!attr_key) { auto c = std::dynamic_pointer_cast(item); if (c) { ret.emplace_back(*c); } } else { auto attr = std::dynamic_pointer_cast(item); if (attr && !strcasecmp(attr->detail->getKey(), attr_key)) { auto c = std::dynamic_pointer_cast(attr->detail); if (c) { ret.emplace_back(*c); } } } } } return ret; } private: std::vector items; }; class RtcSessionSdp : public RtcSdpBase { public: using Ptr = std::shared_ptr; int getVersion() const; SdpOrigin getOrigin() const; std::string getSessionName() const; std::string getSessionInfo() const; SdpTime getSessionTime() const; SdpConnection getConnection() const; SdpBandwidth getBandwidth() const; std::string getUri() const; std::string getEmail() const; std::string getPhone() const; std::string getTimeZone() const; std::string getEncryptKey() const; std::string getRepeatTimes() const; std::vector medias; void parse(const std::string &str); std::string toString() const override; }; ////////////////////////////////////////////////////////////////// // ssrc相关信息 [AUTO-TRANSLATED:954c641d] // ssrc related information class RtcSSRC { public: uint32_t ssrc { 0 }; uint32_t rtx_ssrc { 0 }; std::string cname; std::string msid; std::string mslabel; std::string label; bool empty() const { return ssrc == 0 && cname.empty(); } }; // rtc传输编码方案 [AUTO-TRANSLATED:8b911508] // rtc transmission encoding scheme class RtcCodecPlan { public: using Ptr = std::shared_ptr; uint8_t pt; std::string codec; uint32_t sample_rate; // 音频时有效 [AUTO-TRANSLATED:5b230fc8] // Valid for audio uint32_t channel = 0; // rtcp反馈 [AUTO-TRANSLATED:580378bd] // RTCP feedback std::set rtcp_fb; std::map fmtp; std::string getFmtp(const char *key) const; }; // rtc 媒体描述 [AUTO-TRANSLATED:b1711a11] // RTC media description class RtcMedia { public: TrackType type { TrackType::TrackInvalid }; std::string mid; uint16_t port { 0 }; SdpConnection addr; SdpBandwidth bandwidth; std::string proto; RtpDirection direction { RtpDirection::invalid }; std::vector plan; //////// rtp //////// std::vector rtp_rtx_ssrc; //////// simulcast //////// std::vector rtp_ssrc_sim; std::vector rtp_rids; //////// rtcp //////// bool rtcp_mux { false }; bool rtcp_rsize { false }; SdpAttrRtcp rtcp_addr; //////// ice //////// bool ice_trickle { false }; bool ice_lite { false }; bool ice_renomination { false }; std::string ice_ufrag; std::string ice_pwd; std::vector candidate; //////// dtls //////// DtlsRole role { DtlsRole::invalid }; SdpAttrFingerprint fingerprint; //////// extmap //////// std::vector extmap; //////// sctp //////////// SdpAttrSctpMap sctpmap; uint32_t sctp_port { 0 }; void checkValid() const; const RtcCodecPlan *getPlan(uint8_t pt) const; const RtcCodecPlan *getPlan(const char *codec) const; const RtcCodecPlan *getRelatedRtxPlan(uint8_t pt) const; uint32_t getRtpSSRC() const; uint32_t getRtxSSRC() const; bool supportSimulcast() const; }; class RtcSession { public: using Ptr = std::shared_ptr; uint32_t version; SdpOrigin origin; std::string session_name; std::string session_info; SdpTime time; SdpConnection connection; SdpAttrMsidSemantic msid_semantic; std::vector media; SdpAttrGroup group; void loadFrom(const std::string &sdp); void checkValid() const; std::string toString() const; std::string toRtspSdp() const; const RtcMedia *getMedia(TrackType type) const; bool supportRtcpFb(const std::string &name, TrackType type = TrackType::TrackVideo) const; bool supportSimulcast() const; bool isOnlyDatachannel() const; private: RtcSessionSdp::Ptr toRtcSessionSdp() const; }; class RtcConfigure { public: using Ptr = std::shared_ptr; class RtcTrackConfigure { public: bool rtcp_mux; bool rtcp_rsize; bool group_bundle; bool support_rtx; bool support_red; bool support_ulpfec; bool ice_lite; bool ice_trickle; bool ice_renomination; std::string ice_ufrag; std::string ice_pwd; RtpDirection direction { RtpDirection::invalid }; SdpAttrFingerprint fingerprint; std::set rtcp_fb; std::map extmap; std::vector preferred_codec; std::vector candidate; void setDefaultSetting(TrackType type); void enableTWCC(bool enable = true); void enableREMB(bool enable = true); }; RtcTrackConfigure video; RtcTrackConfigure audio; RtcTrackConfigure application; void setDefaultSetting(std::string ice_ufrag, std::string ice_pwd, RtpDirection direction, const SdpAttrFingerprint &fingerprint); void addCandidate(const SdpAttrCandidate &candidate, TrackType type = TrackInvalid); std::shared_ptr createAnswer(const RtcSession &offer) const; void setPlayRtspInfo(const std::string &sdp); void enableTWCC(bool enable = true, TrackType type = TrackInvalid); void enableREMB(bool enable = true, TrackType type = TrackInvalid); private: void matchMedia(const std::shared_ptr &ret, const RtcMedia &media) const; bool onCheckCodecProfile(const RtcCodecPlan &plan, CodecId codec) const; void onSelectPlan(RtcCodecPlan &plan, CodecId codec) const; private: RtcCodecPlan::Ptr _rtsp_video_plan; RtcCodecPlan::Ptr _rtsp_audio_plan; }; class SdpConst { public: static std::string const kTWCCRtcpFb; static std::string const kRembRtcpFb; private: SdpConst() = delete; ~SdpConst() = delete; }; } // namespace mediakit #endif // ZLMEDIAKIT_SDP_H