diff --git a/src/Extension/Frame.h b/src/Extension/Frame.h index 5f60a68d..afaa115a 100644 --- a/src/Extension/Frame.h +++ b/src/Extension/Frame.h @@ -38,7 +38,7 @@ typedef enum { TrackVideo = 0, TrackAudio, TrackTitle, - TrackData, + TrackApplication, TrackMax = 4 } TrackType; diff --git a/webrtc/Sdp.cpp b/webrtc/Sdp.cpp index 294c84a6..e9d6ec6c 100644 --- a/webrtc/Sdp.cpp +++ b/webrtc/Sdp.cpp @@ -3,6 +3,7 @@ // #include "Sdp.h" +#include using onCreateSdpItem = function; static unordered_map sdpItemCreator; @@ -15,10 +16,38 @@ void registerSdpItem(){ return ret; }; Item item; - InfoL << "register sdp item:" << item.getKey(); sdpItemCreator.emplace(item.getKey(), std::move(func)); } +class DirectionInterface { +public: + virtual RtpDirection getDirection() = 0; +}; + +class SdpDirectionSendonly : public SdpItem, public DirectionInterface{ +public: + const char* getKey() override { return getRtpDirectionString(getDirection());} + RtpDirection getDirection() override {return RtpDirection::sendonly;} +}; + +class SdpDirectionRecvonly : public SdpItem, public DirectionInterface{ +public: + const char* getKey() override { return getRtpDirectionString(getDirection());} + RtpDirection getDirection() override {return RtpDirection::recvonly;} +}; + +class SdpDirectionSendrecv : public SdpItem, public DirectionInterface{ +public: + const char* getKey() override { return getRtpDirectionString(getDirection());} + RtpDirection getDirection() override {return RtpDirection::sendrecv;} +}; + +class SdpDirectionInactive : public SdpItem, public DirectionInterface{ +public: + const char* getKey() override { return getRtpDirectionString(getDirection());} + RtpDirection getDirection() override {return RtpDirection::inactive;} +}; + static bool registerAllItem(){ registerSdpItem >(); registerSdpItem >(); @@ -49,9 +78,87 @@ static bool registerAllItem(){ registerSdpItem(); registerSdpItem(); registerSdpItem(); + registerSdpItem(); + registerSdpItem(); + registerSdpItem(); + registerSdpItem(); + registerSdpItem(); + return true; } +TrackType getTrackType(const string &str){ + if (str == "video") { + return TrackVideo; + } + if (str == "audio") { + return TrackAudio; + } + if (str == "application") { + return TrackApplication; + } + return TrackInvalid; +} + +const char* getTrackString(TrackType type){ + switch (type) { + case TrackVideo : return "video"; + case TrackAudio : return "audio"; + case TrackApplication : return "application"; + default: return "invalid"; + } +} + +DtlsRole getDtlsRole(const string &str){ + if (str == "active") { + return DtlsRole::active; + } + if (str == "passive") { + return DtlsRole::passive; + } + if (str == "actpass") { + return DtlsRole::actpass; + } + return DtlsRole::invalid; +} + +const char* getDtlsRoleString(DtlsRole role){ + switch (role) { + case DtlsRole::active : return "active"; + case DtlsRole::passive : return "passive"; + case DtlsRole::actpass : return "actpass"; + default: return "invalid"; + } +} + +RtpDirection getRtpDirection(const string &str){ + if (str == "sendonly") { + return RtpDirection::sendonly; + } + if (str == "recvonly") { + return RtpDirection::recvonly; + } + if (str == "sendrecv") { + return RtpDirection::sendrecv; + } + if (str == "inactive") { + return RtpDirection::inactive; + } + return RtpDirection::invalid; +} + +const char* getRtpDirectionString(RtpDirection val){ + switch (val) { + case RtpDirection::sendonly : return "sendonly"; + case RtpDirection::recvonly : return "recvonly"; + case RtpDirection::sendrecv : return "sendrecv"; + case RtpDirection::inactive : return "inactive"; + default: return "invalid"; + } +} + +////////////////////////////////////////////////////////////////////////////////////////// + void RtcSdp::parse(const string &str) { static auto flag = registerAllItem(); RtcMedia *media = nullptr; @@ -85,11 +192,142 @@ void RtcSdp::parse(const string &str) { } string RtcSdp::toString() const { - return std::string(); + _StrPrinter printer; + for (auto &item : items) { + printer << item->getKey() << "=" << item->toString() << "\r\n"; + } + for (auto &media : medias) { + printer << media.toString(); + } + + return std::move(printer); +} + +////////////////////////////////////////////////////////////////////// + +string RtcMedia::toString() const { + _StrPrinter printer; + for (auto &item : items) { + printer << item->getKey() << "=" << item->toString() << "\r\n"; + } + return std::move(printer); +} + +RtpDirection RtcMedia::getDirection() const{ + for (auto &item : items) { + auto attr = dynamic_pointer_cast(item); + if (attr) { + auto dir = dynamic_pointer_cast(attr->detail); + if (dir) { + return dir->getDirection(); + } + } + } + return RtpDirection::invalid; +} + + +////////////////////////////////////////////////////////////////////////////////////////// + +#define SDP_THROW() throw std::invalid_argument(StrPrinter << "解析sdp " << getKey() << " 字段失败:" << str) +void SdpTime::parse(const string &str) { + if (sscanf(str.data(), "%" PRIu64 " %" PRIu64, &start, &stop) != 2) { + SDP_THROW(); + } +} + +string SdpTime::toString() const { + if (value.empty()) { + value = to_string(start) + " " + to_string(stop); + } + return SdpItem::toString(); +} + +void SdpOrigin::parse(const string &str) { + auto vec = split(str, " "); + if (vec.size() != 6) { + SDP_THROW(); + } + username = vec[0]; + session_id = vec[1]; + session_version = vec[2]; + nettype = vec[3]; + addrtype = vec[4]; + address = vec[6]; +} + +string SdpOrigin::toString() const { + if (value.empty()) { + value = username + " " + session_id + " " + session_version + " " + nettype + " " + addrtype + " " + address; + } + return SdpItem::toString(); +} + +void SdpConnection::parse(const string &str) { + auto vec = split(str, " "); + if (vec.size() != 3) { + SDP_THROW(); + } + nettype = vec[0]; + addrtype = vec[1]; + address = vec[2]; +} + +string SdpConnection::toString() const { + if (value.empty()) { + value = nettype + " " + addrtype + " " + address; + } + return SdpItem::toString(); +} + +void SdpBandwidth::parse(const string &str) { + auto vec = split(str, ":"); + if (vec.size() != 2) { + SDP_THROW(); + } + bwtype = vec[0]; + bandwidth = atoi(vec[1].data()); +} + +string SdpBandwidth::toString() const { + if (value.empty()) { + value = bwtype + ":" + to_string(bandwidth); + } + return SdpItem::toString(); +} + +void SdpMedia::parse(const string &str) { + auto vec = split(str, " "); + if (vec.size() < 4) { + SDP_THROW(); + } + type = getTrackType(vec[0]); + if (type == TrackInvalid) { + SDP_THROW(); + } + port = atoi(vec[1].data()); + proto = vec[2]; + for (int i = 3; i < vec.size(); ++i) { + auto pt = atoi(vec[i].data()); + if (type != TrackApplication && pt > 0xFF) { + SDP_THROW(); + } + fmts.emplace_back(pt); + } +} + +string SdpMedia::toString() const { + if (value.empty()) { + value = string(getTrackString(type)) + " " + to_string(port) + " " + proto; + for (auto fmt : fmts) { + value += ' '; + value += to_string(fmt); + } + } + return SdpItem::toString(); } void SdpAttr::parse(const string &str) { - SdpItem::parse(str); auto pos = str.find(':'); auto key = pos == string::npos ? str : str.substr(0, pos); auto value = pos == string::npos ? string() : str.substr(pos + 1); @@ -102,10 +340,290 @@ void SdpAttr::parse(const string &str) { } } +string SdpAttr::toString() const { + if (value.empty()) { + auto detail_value = detail->toString(); + if (detail_value.empty()) { + value = detail->getKey(); + } else { + value = string(detail->getKey()) + ":" + detail_value; + } + } + return SdpItem::toString(); +} + +void SdpAttrGroup::parse(const string &str) { + auto vec = split(str, " "); + if (vec.size() < 2) { + SDP_THROW(); + } + type = vec[0]; + vec.erase(vec.begin()); + mids = std::move(vec); +} + +string SdpAttrGroup::toString() const { + if (value.empty()) { + value = type; + for (auto mid : mids) { + value += ' '; + value += mid; + } + } + return SdpItem::toString(); +} + +void SdpAttrMsidSemantic::parse(const string &str) { + auto vec = split(str, " "); + if (vec.size() < 1) { + SDP_THROW(); + } + msid = vec[0]; + token = vec.size() > 1 ? vec[1] : ""; +} + +string SdpAttrMsidSemantic::toString() const { + if (value.empty()) { + if (token.empty()) { + value = string(" ") + msid; + } else { + value = string(" ") + msid + " " + token; + } + } + return SdpItem::toString(); +} + +void SdpAttrRtcp::parse(const string &str) { + auto vec = split(str, " "); + if (vec.size() != 4) { + SDP_THROW(); + } + port = atoi(vec[0].data()); + nettype = vec[1]; + addrtype = vec[2]; + address = vec[3]; +} + +string SdpAttrRtcp::toString() const { + if (value.empty()) { + value = to_string(port) + " " + nettype + " " + addrtype + " " + address; + } + return SdpItem::toString(); +} + +void SdpAttrFingerprint::parse(const string &str) { + auto vec = split(str, " "); + if (vec.size() != 2) { + SDP_THROW(); + } + algorithm = vec[0]; + hash = vec[1]; +} + +string SdpAttrFingerprint::toString() const { + if (value.empty()) { + value = algorithm + " " + hash; + } + return SdpItem::toString(); +} + +void SdpAttrSetup::parse(const string &str) { + role = getDtlsRole(str); + if (role == DtlsRole::invalid) { + SDP_THROW(); + } +} + +string SdpAttrSetup::toString() const { + if (value.empty()) { + value = getDtlsRoleString(role); + } + return SdpItem::toString(); +} + +void SdpAttrExtmap::parse(const string &str) { + char buf[128] = {0}; + if (sscanf(str.data(), "%" PRId32 " %127s", &index, buf) != 2) { + SDP_THROW(); + } + ext = buf; +} + +string SdpAttrExtmap::toString() const { + if (value.empty()) { + value = to_string(index) + " " + ext; + } + return SdpItem::toString(); +} + +void SdpAttrRtpMap::parse(const string &str) { + char buf[32] = {0}; + if (sscanf(str.data(), "%" PRId8 " %31[^/]/%" PRId32 "/%" PRId32, &pt, buf, &sample_rate, &channel) != 4) { + if (sscanf(str.data(), "%" PRId8 " %31[^/]/%" PRId32, &pt, buf, &sample_rate) != 3) { + SDP_THROW(); + } + } + codec = buf; +} + +string SdpAttrRtpMap::toString() const { + if (value.empty()) { + value = to_string(pt) + " " + codec + "/" + to_string(sample_rate); + if (channel) { + value += '/'; + value += to_string(channel); + } + } + return SdpItem::toString(); +} + +void SdpAttrRtcpFb::parse(const string &str) { + auto vec = split(str, " "); + if (vec.size() < 2) { + SDP_THROW(); + } + pt = atoi(vec[0].data()); + vec.erase(vec.begin()); + arr = std::move(vec); +} + +string SdpAttrRtcpFb::toString() const { + if (value.empty()) { + value = to_string(pt); + for (auto &item : arr) { + value += ' '; + value += item; + } + } + return SdpItem::toString(); +} + +void SdpAttrFmtp::parse(const string &str) { + auto pos = str.find(' '); + if (pos == string::npos) { + SDP_THROW(); + } + pt = atoi(str.substr(0, pos).data()); + auto vec = split(str.substr(pos + 1), ";"); + for (auto &item : vec) { + trim(item); + auto pr_vec = split(item, "="); + if (pr_vec.size() != 2) { + SDP_THROW(); + } + arr.emplace_back(std::make_pair(pr_vec[0], pr_vec[1])); + } + if (arr.empty()) { + SDP_THROW(); + } +} + +string SdpAttrFmtp::toString() const { + if (value.empty()) { + value = to_string(pt); + int i = 0; + for (auto &pr : arr) { + value += (i++ ? ';' : ' '); + value += pr.first + "=" + pr.second; + } + } + return SdpItem::toString(); +} + +void SdpAttrSSRC::parse(const string &str) { + char attr_buf[32] = {0}; + char attr_val_buf[128] = {0}; + if (3 == sscanf(str.data(), "%" PRIu32 " %31[^:]:%127[^\0]", &ssrc, attr_buf, attr_val_buf)) { + attribute = attr_buf; + attribute_value = attr_val_buf; + } else if (2 == sscanf(str.data(), "%" PRIu32 " %31s", &ssrc, attr_buf)) { + attribute = attr_buf; + } else { + SDP_THROW(); + } +} + +string SdpAttrSSRC::toString() const { + if (value.empty()) { + value = to_string(ssrc) + ' '; + value += attribute; + if (!attribute_value.empty()) { + value += ':'; + value += attribute_value; + } + } + return SdpItem::toString(); +} + +void SdpAttrSctpMap::parse(const string &str) { + char subtypes_buf[64] = {0}; + if (3 == sscanf(str.data(), "%" PRIu16 " %63[^ ] %" PRId32, &port, subtypes_buf, &streams)) { + subtypes = subtypes_buf; + } else { + SDP_THROW(); + } +} + +string SdpAttrSctpMap::toString() const { + if (value.empty()) { + value = to_string(port); + value += ' '; + value += subtypes; + value += ' '; + value += to_string(streams); + } + return SdpItem::toString(); +} + +void SdpAttrCandidate::parse(const string &str) { + char transport_buf[16] = {0}; + char address_buf[32] = {0}; + char type_buf[16] = {0}; + + if (7 != sscanf(str.data(), "%" PRIu32 " %" PRIu32 " %15[^ ] %" PRIu32 " %31[^ ] %" PRIu16 " typ %15[^ \0]", + &foundation, &component, transport_buf, &priority, address_buf, &port, type_buf)) { + SDP_THROW(); + } + transport = transport_buf; + address = address_buf; + type = type_buf; + auto pos = str.find(type); + if (pos != string::npos) { + auto remain = str.substr(pos + type.size()); + trim(remain); + if (!remain.empty()) { + auto vec = split(remain, " "); + string key; + for (auto &item : vec) { + if (key.empty()) { + key = item; + } else { + arr.emplace_back(std::make_pair(std::move(key), std::move(item))); + } + } + } + } +} + +string SdpAttrCandidate::toString() const { + if (value.empty()) { + value = to_string(foundation) + " " + to_string(component) + " " + transport + " " + to_string(priority) + + " " + address + " " + to_string(port) + " typ " + type; + for (auto &pr : arr) { + value += ' '; + value += pr.first; + value += ' '; + value += pr.second; + } + } + return SdpItem::toString(); +} + void test_sdp(){ char str1[] = "v=0\n" "o=- 380154348540553537 2 IN IP4 127.0.0.1\n" "s=-\n" + "b=CT:1900\n" "t=0 0\n" "a=group:BUNDLE video\n" "a=msid-semantic: WMS\n" @@ -212,7 +730,7 @@ void test_sdp(){ "a=ssrc:611523443 msid:616cfbb1-33a3-4d8c-8275-a199d6005549 bf270496-a23e-47b5-b901-ef23096cd961\n" "a=ssrc:611523443 mslabel:616cfbb1-33a3-4d8c-8275-a199d6005549\n" "a=ssrc:611523443 label:bf270496-a23e-47b5-b901-ef23096cd961\n" - "a=candidate:1 1 udp %u %s %u typ host\n" + "a=candidate:3575467457 1 udp 2113937151 10.15.83.23 57857 typ host generation 0 ufrag 6R0z network-cost 999\n" "m=application 9 DTLS/SCTP 5000\n" "c=IN IP4 0.0.0.0\n" "a=ice-ufrag:sXJ3\n" @@ -220,7 +738,8 @@ void test_sdp(){ "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\n" "a=setup:actpass\n" "a=mid:data\n" - "a=sctpmap:5000 webrtc-datachannel 1024"; + "a=sctpmap:5000 webrtc-datachannel 1024\n" + "a=sctp-port:5000"; RtcSdp sdp1; sdp1.parse(str1); @@ -228,6 +747,12 @@ void test_sdp(){ RtcSdp sdp2; sdp2.parse(str2); + for (auto media : sdp1.medias) { + InfoL << getRtpDirectionString(media.getDirection()); + } + for (auto media : sdp2.medias) { + InfoL << getRtpDirectionString(media.getDirection()); + } InfoL << sdp1.toString(); InfoL << sdp2.toString(); } diff --git a/webrtc/Sdp.h b/webrtc/Sdp.h index aafdae04..c77e09c4 100644 --- a/webrtc/Sdp.h +++ b/webrtc/Sdp.h @@ -12,107 +12,6 @@ using namespace std; using namespace mediakit; //https://datatracker.ietf.org/doc/rfc4566/?include_text=1 - -//v=0 -//o=- 2584450093346841581 2 IN IP4 127.0.0.1 -//s=- -//t=0 0 -//a=group:BUNDLE audio video data -//a=msid-semantic: WMS 616cfbb1-33a3-4d8c-8275-a199d6005549 -//m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126 -//c=IN IP4 0.0.0.0 -//a=rtcp:9 IN IP4 0.0.0.0 -//a=ice-ufrag:sXJ3 -//a=ice-pwd:yEclOTrLg1gEubBFefOqtmyV -//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 -//a=setup:actpass -//a=mid:audio -//a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level -//a=sendrecv -//a=rtcp-mux -//a=rtpmap:111 opus/48000/2 -//a=rtcp-fb:111 transport-cc -//a=fmtp:111 minptime=10;useinbandfec=1 -//a=rtpmap:103 ISAC/16000 -//a=rtpmap:104 ISAC/32000 -//a=rtpmap:9 G722/8000 -//a=rtpmap:0 PCMU/8000 -//a=rtpmap:8 PCMA/8000 -//a=rtpmap:106 CN/32000 -//a=rtpmap:105 CN/16000 -//a=rtpmap:13 CN/8000 -//a=rtpmap:110 telephone-event/48000 -//a=rtpmap:112 telephone-event/32000 -//a=rtpmap:113 telephone-event/16000 -//a=rtpmap:126 telephone-event/8000 -//a=ssrc:120276603 cname:iSkJ2vn5cYYubTve -//a=ssrc:120276603 msid:616cfbb1-33a3-4d8c-8275-a199d6005549 1da3d329-7399-4fe9-b20f-69606bebd363 -//a=ssrc:120276603 mslabel:616cfbb1-33a3-4d8c-8275-a199d6005549 -//a=ssrc:120276603 label:1da3d329-7399-4fe9-b20f-69606bebd363 -//m=video 9 UDP/TLS/RTP/SAVPF 96 98 100 102 127 97 99 101 125 -//c=IN IP4 0.0.0.0 -//a=rtcp:9 IN IP4 0.0.0.0 -//a=ice-ufrag:sXJ3 -//a=ice-pwd:yEclOTrLg1gEubBFefOqtmyV -//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 -//a=setup:actpass -//a=mid:video -//a=extmap:2 urn:ietf:params:rtp-hdrext:toffset -//a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time -//a=extmap:4 urn:3gpp:video-orientation -//a=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01 -//a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay -//a=sendrecv -//a=rtcp-mux -//a=rtcp-rsize -//a=rtpmap:96 VP8/90000 -//a=rtcp-fb:96 ccm fir -//a=rtcp-fb:96 nack -//a=rtcp-fb:96 nack pli -//a=rtcp-fb:96 goog-remb -//a=rtcp-fb:96 transport-cc -//a=rtpmap:98 VP9/90000 -//a=rtcp-fb:98 ccm fir -//a=rtcp-fb:98 nack -//a=rtcp-fb:98 nack pli -//a=rtcp-fb:98 goog-remb -//a=rtcp-fb:98 transport-cc -//a=rtpmap:100 H264/90000 -//a=rtcp-fb:100 ccm fir -//a=rtcp-fb:100 nack -//a=rtcp-fb:100 nack pli -//a=rtcp-fb:100 goog-remb -//a=rtcp-fb:100 transport-cc -//a=fmtp:100 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f -//a=rtpmap:102 red/90000 -//a=rtpmap:127 ulpfec/90000 -//a=rtpmap:97 rtx/90000 -//a=fmtp:97 apt=96 -//a=rtpmap:99 rtx/90000 -//a=fmtp:99 apt=98 -//a=rtpmap:101 rtx/90000 -//a=fmtp:101 apt=100 -//a=rtpmap:125 rtx/90000 -//a=fmtp:125 apt=102 -//a=ssrc-group:FID 2580761338 611523443 -//a=ssrc:2580761338 cname:iSkJ2vn5cYYubTve -//a=ssrc:2580761338 msid:616cfbb1-33a3-4d8c-8275-a199d6005549 bf270496-a23e-47b5-b901-ef23096cd961 -//a=ssrc:2580761338 mslabel:616cfbb1-33a3-4d8c-8275-a199d6005549 -//a=ssrc:2580761338 label:bf270496-a23e-47b5-b901-ef23096cd961 -//a=ssrc:611523443 cname:iSkJ2vn5cYYubTve -//a=ssrc:611523443 msid:616cfbb1-33a3-4d8c-8275-a199d6005549 bf270496-a23e-47b5-b901-ef23096cd961 -//a=ssrc:611523443 mslabel:616cfbb1-33a3-4d8c-8275-a199d6005549 -//a=ssrc:611523443 label:bf270496-a23e-47b5-b901-ef23096cd961 -//a=candidate:1 1 udp %u %s %u typ host -//m=application 9 DTLS/SCTP 5000 -//c=IN IP4 0.0.0.0 -//a=ice-ufrag:sXJ3 -//a=ice-pwd:yEclOTrLg1gEubBFefOqtmyV -//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 -//a=setup:actpass -//a=mid:data -//a=sctpmap:5000 webrtc-datachannel 1024 - //Session description // v= (protocol version) // o= (originator and session identifier) @@ -148,7 +47,7 @@ enum class RtpDirection { //只发送 sendonly, //只接收 - revonly, + recvonly, //同时发送接收 sendrecv, //禁止发送数据 @@ -171,10 +70,16 @@ enum class SdpType { answer }; +TrackType getTrackType(const string &str); +const char* getTrackString(TrackType type); +DtlsRole getDtlsRole(const string &str); +const char* getDtlsRoleString(DtlsRole role); +RtpDirection getRtpDirection(const string &str); +const char* getRtpDirectionString(RtpDirection val); + class SdpItem { public: using Ptr = std::shared_ptr; - string value; virtual ~SdpItem() = default; virtual void parse(const string &str) { value = str; @@ -183,6 +88,9 @@ public: return value; } virtual const char* getKey() = 0; + +protected: + mutable string value; }; template @@ -203,10 +111,10 @@ class SdpTime : public SdpItem{ public: //5.9. Timing ("t=") // t= - uint64_t start; - uint64_t stop; - void parse(const string &str) override {SdpItem::parse(str); }; - string toString() const override { return SdpItem::toString(); }; + uint64_t start {0}; + uint64_t stop {0}; + void parse(const string &str) override; + string toString() const override; const char* getKey() override { return "t";} }; @@ -215,14 +123,14 @@ public: // 5.2. Origin ("o=") // o=jdoe 2890844526 2890842807 IN IP4 10.47.16.5 // o= - string username; + string username {"-"}; string session_id; string session_version; - string nettype; - string addrtype; - string address; - void parse(const string &str) override {SdpItem::parse(str); }; - string toString() const override { return SdpItem::toString(); }; + string nettype {"IN"}; + string addrtype {"IP4"}; + string address {"0.0.0.0"}; + void parse(const string &str) override; + string toString() const override; const char* getKey() override { return "o";} }; @@ -231,11 +139,11 @@ public: // 5.7. Connection Data ("c=") // c=IN IP4 224.2.17.12/127 // c= - string nettype; - string addrtype; - string address; - void parse(const string &str) override {SdpItem::parse(str); }; - string toString() const override { return SdpItem::toString(); }; + string nettype {"IN"}; + string addrtype {"IP4"}; + string address {"0.0.0.0"}; + void parse(const string &str) override; + string toString() const override; const char* getKey() override { return "c";} }; @@ -245,11 +153,11 @@ public: //b=: //AS、CT - string bwtype; - int bandwidth; + string bwtype {"AS"}; + uint32_t bandwidth {0}; - void parse(const string &str) override {SdpItem::parse(str); }; - string toString() const override { return SdpItem::toString(); }; + void parse(const string &str) override; + string toString() const override; const char* getKey() override { return "b";} }; @@ -259,11 +167,11 @@ public: // m= ... TrackType type; uint16_t port; - vector proto; - vector fmt; + string proto; + vector fmts; - void parse(const string &str) override {SdpItem::parse(str); }; - string toString() const override { return SdpItem::toString(); }; + void parse(const string &str) override; + string toString() const override; const char* getKey() override { return "m";} }; @@ -275,7 +183,7 @@ public: //a=: SdpItem::Ptr detail; void parse(const string &str) override; - string toString() const override { return SdpItem::toString(); }; + string toString() const override; const char* getKey() override { return "a";} }; @@ -285,50 +193,63 @@ public: // 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. - string type; - vector mid; - void parse(const string &str) override {SdpItem::parse(str); }; - string toString() const override { return SdpItem::toString(); }; + string type {"BUNDLE"}; + vector mids; + void parse(const string &str) override ; + string toString() const override ; const char* getKey() override { return "group";} }; class SdpAttrMsidSemantic : public SdpItem { public: - string name{"WMS"}; - string mid; - void parse(const string &str) override {SdpItem::parse(str); }; - string toString() const override { return SdpItem::toString(); }; + //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 + string msid{"WMS"}; + string token; + void parse(const string &str) override; + string toString() const override; const char* getKey() override { return "msid-semantic";} }; class SdpAttrRtcp : public SdpItem { public: - // c=IN IP4 224.2.17.12/127 - // c= + // a=rtcp:9 IN IP4 0.0.0.0 uint16_t port; - string nettype; - string addrtype; - string address; - void parse(const string &str) override {SdpItem::parse(str); }; - string toString() const override { return SdpItem::toString(); }; + string nettype {"IN"}; + string addrtype {"IP4"}; + string address {"0.0.0.0"}; + void parse(const string &str) override;; + string toString() const override; const char* getKey() override { return "rtcp";} }; class SdpAttrIceUfrag : public SdpItem { public: //a=ice-ufrag:sXJ3 - string value; - void parse(const string &str) override {SdpItem::parse(str); }; - string toString() const override { return SdpItem::toString(); }; const char* getKey() override { return "ice-ufrag";} }; class SdpAttrIcePwd : public SdpItem { public: //a=ice-pwd:yEclOTrLg1gEubBFefOqtmyV - string value; - void parse(const string &str) override {SdpItem::parse(str); }; - string toString() const override { return SdpItem::toString(); }; const char* getKey() override { return "ice-pwd";} }; @@ -336,27 +257,24 @@ 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 string algorithm; - string value; - void parse(const string &str) override {SdpItem::parse(str); }; - string toString() const override { return SdpItem::toString(); }; + string hash; + void parse(const string &str) override; + string toString() const override; const char* getKey() override { return "fingerprint";} }; class SdpAttrSetup : public SdpItem { public: //a=setup:actpass - DtlsRole role{DtlsRole::invalid}; - void parse(const string &str) override {SdpItem::parse(str); }; - string toString() const override { return SdpItem::toString(); }; + DtlsRole role{DtlsRole::actpass}; + void parse(const string &str) override; + string toString() const override; const char* getKey() override { return "setup";} }; class SdpAttrMid : public SdpItem { public: //a=mid:audio - string mid; - void parse(const string &str) override {SdpItem::parse(str); }; - string toString() const override { return SdpItem::toString(); }; const char* getKey() override { return "mid";} }; @@ -364,9 +282,9 @@ class SdpAttrExtmap : public SdpItem { public: //a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level int index; - string value; - void parse(const string &str) override {SdpItem::parse(str); }; - string toString() const override { return SdpItem::toString(); }; + string ext; + void parse(const string &str) override; + string toString() const override; const char* getKey() override { return "extmap";} }; @@ -376,68 +294,85 @@ public: uint8_t pt; string codec; int sample_rate; - int channel; - void parse(const string &str) override {SdpItem::parse(str); }; - string toString() const override { return SdpItem::toString(); }; + int channel {0}; + void parse(const string &str) override; + string toString() const override; const char* getKey() override { return "rtpmap";} }; class SdpAttrRtcpFb : public SdpItem { public: - //a=rtcp-fb:111 transport-cc + //a=rtcp-fb:98 nack pli uint8_t pt; - string value; - void parse(const string &str) override {SdpItem::parse(str); }; - string toString() const override { return SdpItem::toString(); }; + vector arr; + void parse(const string &str) override; + string toString() const override; const char* getKey() override { return "rtcp-fb";} }; class SdpAttrFmtp : public SdpItem { public: - //a=rtcp-fb:111 transport-cc + //fmtp:96 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f uint8_t pt; - map values; - void parse(const string &str) override {SdpItem::parse(str); }; - string toString() const override { return SdpItem::toString(); }; + vector > arr; + void parse(const string &str) override; + string toString() const override; const char* getKey() override { return "fmtp";} }; class SdpAttrSSRC : public SdpItem { public: //a=ssrc:120276603 cname:iSkJ2vn5cYYubTve + //a=ssrc: + //a=ssrc: : uint32_t ssrc; - string key; - string value; - void parse(const string &str) override {SdpItem::parse(str); }; - string toString() const override { return SdpItem::toString(); }; + string attribute; + string attribute_value; + void parse(const string &str) override; + string toString() const override; const char* getKey() override { return "ssrc";} }; class SdpAttrSctpMap : public SdpItem { public: - //a=ssrc:120276603 cname:iSkJ2vn5cYYubTve + //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; - string name; - int mtu; - void parse(const string &str) override {SdpItem::parse(str); }; - string toString() const override { return SdpItem::toString(); }; + string subtypes; + int streams; + void parse(const string &str) override; + string toString() const override; const char* getKey() override { return "sctpmap";} }; class SdpAttrCandidate : public SdpItem { public: - //a=candidate:%s 1 udp %u %s %u typ %s - void parse(const string &str) override {SdpItem::parse(str); }; - string toString() const override { return SdpItem::toString(); }; + //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 + uint32_t foundation; + uint32_t component; + string transport {"udp"}; + uint32_t priority; + string address; + uint16_t port; + string type; + vector > arr; + + void parse(const string &str) override; + string toString() const override; const char* getKey() override { return "candidate";} }; - class RtcMedia { public: vector items; + string toString() const; bool haveAttr(const char *attr) const; string getAttrValue(const char *attr) const; + RtpDirection getDirection() const; }; class RtcSdp { @@ -447,41 +382,6 @@ public: void parse(const string &str); string toString() const; - -#if 0 - /////Session description(会话级别描述)//// - //v= (protocol version) - int version; - //o= (session origin information ) - SdpOrigin origin; - //s= (session name) - string session_name; - //t= (time the session is active) - SdpTime time; - - //// 非必须 //// - //i=* (session information) - string information; - //u=* (URI of description) - string url; - //e=* (email address) - string email; - //p=* (phone number) - string phone; - //c=* (connection information -- not required if included in all media) - SdpConnection connection; - //b=* (zero or more bandwidth information lines) - SdpBandwidth bandwidth; - //z=* (time zone adjustments) - //z= .... - string time_zone; - //k=* (encryption key) - //k=, k=: - string crypt_key; - //r=* (zero or more repeat times) - //r= - string repeat; -#endif };