ZLMediaKit/webrtc/Sdp.cpp

1158 lines
38 KiB
C++
Raw Normal View History

2021-03-27 18:33:20 +08:00
//
// Created by xzl on 2021/3/27.
//
#include "Sdp.h"
#include "assert.h"
#include "Common/Parser.h"
2021-03-28 17:32:53 +08:00
#include <inttypes.h>
2021-03-28 09:49:34 +08:00
#define CHECK(exp) Assert_Throw(!(exp), #exp, __FUNCTION__, __FILE__, __LINE__);
2021-03-28 09:49:34 +08:00
using onCreateSdpItem = function<SdpItem::Ptr(const string &key, const string &value)>;
static map<string, onCreateSdpItem, StrCaseCompare> sdpItemCreator;
2021-03-28 09:49:34 +08:00
template <typename Item>
void registerSdpItem(){
onCreateSdpItem func = [](const string &key, const string &value) {
auto ret = std::make_shared<Item>();
ret->parse(value);
return ret;
};
Item item;
sdpItemCreator.emplace(item.getKey(), std::move(func));
}
2021-03-28 17:32:53 +08:00
class DirectionInterface {
public:
2021-03-28 23:31:21 +08:00
virtual RtpDirection getDirection() const = 0;
2021-03-28 17:32:53 +08:00
};
class SdpDirectionSendonly : public SdpItem, public DirectionInterface{
public:
2021-03-28 23:31:21 +08:00
const char* getKey() const override { return getRtpDirectionString(getDirection());}
RtpDirection getDirection() const override {return RtpDirection::sendonly;}
2021-03-28 17:32:53 +08:00
};
class SdpDirectionRecvonly : public SdpItem, public DirectionInterface{
public:
2021-03-28 23:31:21 +08:00
const char* getKey() const override { return getRtpDirectionString(getDirection());}
RtpDirection getDirection() const override {return RtpDirection::recvonly;}
2021-03-28 17:32:53 +08:00
};
class SdpDirectionSendrecv : public SdpItem, public DirectionInterface{
public:
2021-03-28 23:31:21 +08:00
const char* getKey() const override { return getRtpDirectionString(getDirection());}
RtpDirection getDirection() const override {return RtpDirection::sendrecv;}
2021-03-28 17:32:53 +08:00
};
class SdpDirectionInactive : public SdpItem, public DirectionInterface{
public:
2021-03-28 23:31:21 +08:00
const char* getKey() const override { return getRtpDirectionString(getDirection());}
RtpDirection getDirection() const override {return RtpDirection::inactive;}
2021-03-28 17:32:53 +08:00
};
2021-03-28 09:49:34 +08:00
static bool registerAllItem(){
registerSdpItem<SdpString<'v'> >();
registerSdpItem<SdpString<'s'> >();
registerSdpItem<SdpString<'i'> >();
registerSdpItem<SdpString<'u'> >();
registerSdpItem<SdpString<'e'> >();
registerSdpItem<SdpString<'p'> >();
registerSdpItem<SdpString<'z'> >();
registerSdpItem<SdpString<'k'> >();
registerSdpItem<SdpString<'r'> >();
registerSdpItem<SdpTime>();
registerSdpItem<SdpOrigin>();
registerSdpItem<SdpConnection>();
registerSdpItem<SdpBandwidth>();
registerSdpItem<SdpMedia>();
registerSdpItem<SdpAttr>();
registerSdpItem<SdpAttrGroup>();
registerSdpItem<SdpAttrMsidSemantic>();
registerSdpItem<SdpAttrRtcp>();
registerSdpItem<SdpAttrIceUfrag>();
registerSdpItem<SdpAttrIcePwd>();
2021-03-28 23:31:21 +08:00
registerSdpItem<SdpAttrIceOption>();
2021-03-28 09:49:34 +08:00
registerSdpItem<SdpAttrFingerprint>();
registerSdpItem<SdpAttrSetup>();
registerSdpItem<SdpAttrMid>();
registerSdpItem<SdpAttrExtmap>();
registerSdpItem<SdpAttrRtpMap>();
registerSdpItem<SdpAttrRtcpFb>();
registerSdpItem<SdpAttrFmtp>();
registerSdpItem<SdpAttrSSRC>();
2021-03-28 23:31:21 +08:00
registerSdpItem<SdpAttrSSRCGroup>();
2021-03-28 09:49:34 +08:00
registerSdpItem<SdpAttrSctpMap>();
2021-03-28 17:32:53 +08:00
registerSdpItem<SdpAttrCandidate>();
registerSdpItem<SdpDirectionSendonly>();
registerSdpItem<SdpDirectionRecvonly>();
registerSdpItem<SdpDirectionSendrecv>();
registerSdpItem<SdpDirectionInactive>();
2021-03-28 09:49:34 +08:00
return true;
}
static map<string, DtlsRole, StrCaseCompare> dtls_role_map = {
{"active", DtlsRole::active},
{"passive", DtlsRole::passive},
{"actpass", DtlsRole::actpass}
};
DtlsRole getDtlsRole(const string &str) {
auto it = dtls_role_map.find(str);
return it == dtls_role_map.end() ? DtlsRole::invalid : it->second;
2021-03-28 17:32:53 +08:00
}
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";
}
}
static map<string, RtpDirection, StrCaseCompare> direction_map = {
{"sendonly", RtpDirection::sendonly},
{"recvonly", RtpDirection::recvonly},
{"sendrecv", RtpDirection::sendrecv},
{"inactive", RtpDirection::inactive}
};
RtpDirection getRtpDirection(const string &str) {
auto it = direction_map.find(str);
return it == direction_map.end() ? RtpDirection::invalid : it->second;
2021-03-28 17:32:53 +08:00
}
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";
}
}
//////////////////////////////////////////////////////////////////////////////////////////
2021-03-29 11:39:58 +08:00
string RtcSdpBase::toString() const {
_StrPrinter printer;
for (auto &item : items) {
printer << item->getKey() << "=" << item->toString() << "\r\n";
}
return std::move(printer);
}
RtpDirection RtcSdpBase::getDirection() const{
for (auto &item : items) {
auto attr = dynamic_pointer_cast<SdpAttr>(item);
if (attr) {
auto dir = dynamic_pointer_cast<DirectionInterface>(attr->detail);
if (dir) {
return dir->getDirection();
}
}
}
return RtpDirection::invalid;
}
SdpItem::Ptr RtcSdpBase::getItem(char key_c, const char *attr_key) const {
2021-03-29 11:39:58 +08:00
for (auto item : items) {
string key(1, key_c);
if (strcasecmp(item->getKey(), key.data()) == 0) {
2021-03-29 11:39:58 +08:00
if (!attr_key) {
return item;
}
auto attr = dynamic_pointer_cast<SdpAttr>(item);
if (attr && !strcasecmp(attr->detail->getKey() , attr_key)) {
2021-03-29 12:00:42 +08:00
return attr->detail;
2021-03-29 11:39:58 +08:00
}
}
}
return SdpItem::Ptr();
}
int RtcSdpBase::getVersion() const {
return atoi(getStringItem('v').data());
}
SdpOrigin RtcSdpBase::getOrigin() const {
return getItemClass<SdpOrigin>('o');
}
string RtcSdpBase::getSessionName() const {
return getStringItem('s');
}
string RtcSdpBase::getSessionInfo() const {
return getStringItem('i');
}
SdpTime RtcSdpBase::getSessionTime() const{
return getItemClass<SdpTime>('t');
}
SdpConnection RtcSdpBase::getConnection() const {
return getItemClass<SdpConnection>('c');
}
SdpBandwidth RtcSdpBase::getBandwidth() const {
return getItemClass<SdpBandwidth>('b');
}
string RtcSdpBase::getUri() const {
return getStringItem('u');
}
string RtcSdpBase::getEmail() const {
return getStringItem('e');
}
string RtcSdpBase::getPhone() const {
return getStringItem('p');
}
string RtcSdpBase::getTimeZone() const {
return getStringItem('z');
}
string RtcSdpBase::getEncryptKey() const {
return getStringItem('k');
}
string RtcSdpBase::getRepeatTimes() const {
return getStringItem('r');
}
//////////////////////////////////////////////////////////////////////
2021-03-29 10:54:18 +08:00
void RtcSessionSdp::parse(const string &str) {
2021-03-28 09:49:34 +08:00
static auto flag = registerAllItem();
2021-03-29 11:39:58 +08:00
RtcSdpBase *media = nullptr;
2021-03-28 09:49:34 +08:00
auto lines = split(str, "\n");
for(auto &line : lines){
trim(line);
if(line.size() < 3 || line[1] != '='){
continue;
}
auto key = line.substr(0, 1);
auto value = line.substr(2);
2021-03-30 09:58:53 +08:00
if (!strcasecmp(key.data(), "m")) {
2021-03-29 11:39:58 +08:00
medias.emplace_back(RtcSdpBase());
2021-03-28 09:49:34 +08:00
media = &medias.back();
}
SdpItem::Ptr item;
auto it = sdpItemCreator.find(key);
if (it != sdpItemCreator.end()) {
item = it->second(key, value);
} else {
item = std::make_shared<SdpCommon>(key);
item->parse(value);
}
if (media) {
media->items.push_back(std::move(item));
} else {
items.push_back(std::move(item));
}
}
}
2021-03-29 10:54:18 +08:00
string RtcSessionSdp::toString() const {
2021-03-28 17:32:53 +08:00
_StrPrinter printer;
2021-03-29 11:39:58 +08:00
printer << RtcSdpBase::toString();
2021-03-28 17:32:53 +08:00
for (auto &media : medias) {
printer << media.toString();
}
return std::move(printer);
}
//////////////////////////////////////////////////////////////////////////////////////////
#define SDP_THROW() throw std::invalid_argument(StrPrinter << "解析sdp " << getKey() << " 字段失败:" << str)
2021-03-28 23:31:21 +08:00
#define SDP_THROW2() throw std::invalid_argument(StrPrinter << "生成sdp " << getKey() << " 字段失败")
2021-03-28 17:32:53 +08:00
void SdpTime::parse(const string &str) {
2021-03-29 09:53:54 +08:00
if (sscanf(str.data(), "%" SCNu64 " %" SCNu64, &start, &stop) != 2) {
2021-03-28 17:32:53 +08:00
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];
2021-03-29 09:53:54 +08:00
address = vec[5];
2021-03-28 17:32:53 +08:00
}
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];
2021-03-29 09:53:54 +08:00
for (size_t i = 3; i < vec.size(); ++i) {
2021-03-28 17:32:53 +08:00
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();
2021-03-28 09:49:34 +08:00
}
void SdpAttr::parse(const string &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);
auto it = sdpItemCreator.find(key);
if (it != sdpItemCreator.end()) {
detail = it->second(key, value);
} else {
detail = std::make_shared<SdpCommon>(key);
detail->parse(value);
}
}
2021-03-28 17:32:53 +08:00
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};
2021-03-29 00:12:20 +08:00
char direction_buf[32] = {0};
2021-03-29 09:53:54 +08:00
if (sscanf(str.data(), "%" SCNd32 "/%31[^ ] %127s", &index, direction_buf, buf) != 3) {
if (sscanf(str.data(), "%" SCNd32 " %127s", &index, buf) != 2) {
2021-03-29 00:12:20 +08:00
SDP_THROW();
}
2021-03-30 11:51:39 +08:00
direction = RtpDirection::sendrecv;
2021-03-29 00:12:20 +08:00
} else {
direction = getRtpDirection(direction_buf);
2021-03-28 17:32:53 +08:00
}
ext = buf;
}
string SdpAttrExtmap::toString() const {
if (value.empty()) {
2021-03-29 00:12:20 +08:00
if(direction == RtpDirection::invalid){
value = to_string(index) + " " + ext;
} else {
value = to_string(index) + "/" + getRtpDirectionString(direction) + " " + ext;
}
2021-03-28 17:32:53 +08:00
}
return SdpItem::toString();
}
void SdpAttrRtpMap::parse(const string &str) {
char buf[32] = {0};
2021-03-29 09:53:54 +08:00
if (sscanf(str.data(), "%" SCNu8 " %31[^/]/%" SCNd32 "/%" SCNd32, &pt, buf, &sample_rate, &channel) != 4) {
if (sscanf(str.data(), "%" SCNu8 " %31[^/]/%" SCNd32, &pt, buf, &sample_rate) != 3) {
2021-03-28 17:32:53 +08:00
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();
}
2021-03-29 23:03:55 +08:00
void SdpAttrRtcpFb::parse(const string &str_in) {
auto str = str_in + "\n";
char rtcp_type_buf[32] = {0};
if (2 != sscanf(str.data(), "%" SCNu8 " %31[^\n]", &pt, rtcp_type_buf)) {
2021-03-28 17:32:53 +08:00
SDP_THROW();
}
2021-03-29 23:03:55 +08:00
rtcp_type = rtcp_type_buf;
2021-03-28 17:32:53 +08:00
}
string SdpAttrRtcpFb::toString() const {
if (value.empty()) {
2021-03-29 23:03:55 +08:00
value = to_string(pt) + " " + rtcp_type;
2021-03-28 17:32:53 +08:00
}
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();
}
2021-03-29 09:53:54 +08:00
void SdpAttrSSRC::parse(const string &str_in) {
auto str = str_in + '\n';
2021-03-28 17:32:53 +08:00
char attr_buf[32] = {0};
char attr_val_buf[128] = {0};
2021-03-29 09:53:54 +08:00
if (3 == sscanf(str.data(), "%" SCNu32 " %31[^:]:%127[^\n]", &ssrc, attr_buf, attr_val_buf)) {
2021-03-28 17:32:53 +08:00
attribute = attr_buf;
attribute_value = attr_val_buf;
2021-03-29 09:53:54 +08:00
} else if (2 == sscanf(str.data(), "%" SCNu32 " %31s[^\n]", &ssrc, attr_buf)) {
2021-03-28 17:32:53 +08:00
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();
}
2021-03-28 23:31:21 +08:00
void SdpAttrSSRCGroup::parse(const string &str) {
auto vec = split(str, " ");
if (vec.size() == 3) {
if (vec[0] != "FID") {
SDP_THROW();
}
type = std::move(vec[0]);
u.fid.rtp_ssrc = atoi(vec[1].data());
u.fid.rtx_ssrc = atoi(vec[2].data());
} else if (vec.size() == 4) {
if (vec[0] != "SIM") {
SDP_THROW();
}
type = std::move(vec[0]);
u.sim.rtp_ssrc_low = atoi(vec[1].data());
u.sim.rtp_ssrc_mid = atoi(vec[2].data());
u.sim.rtp_ssrc_high = atoi(vec[3].data());
} else {
SDP_THROW();
}
}
string SdpAttrSSRCGroup::toString() const {
if (value.empty()) {
if (type == "FID") {
value = type + " " + to_string(u.fid.rtp_ssrc) + " " + to_string(u.fid.rtx_ssrc);
} else if (type == "SIM") {
value = type + " " + to_string(u.sim.rtp_ssrc_low) + " " + to_string(u.sim.rtp_ssrc_mid) + " " + to_string(u.sim.rtp_ssrc_high);
} else {
SDP_THROW2();
}
}
return SdpItem::toString();
}
2021-03-28 17:32:53 +08:00
void SdpAttrSctpMap::parse(const string &str) {
char subtypes_buf[64] = {0};
2021-03-29 09:53:54 +08:00
if (3 == sscanf(str.data(), "%" SCNu16 " %63[^ ] %" SCNd32, &port, subtypes_buf, &streams)) {
2021-03-28 17:32:53 +08:00
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) {
2021-03-29 12:28:47 +08:00
char foundation_buf[32] = {0};
2021-03-28 17:32:53 +08:00
char transport_buf[16] = {0};
char address_buf[32] = {0};
char type_buf[16] = {0};
2021-03-29 12:28:47 +08:00
if (7 != sscanf(str.data(), "%31[^ ] %" SCNu32 " %15[^ ] %" SCNu32 " %31[^ ] %" SCNu16 " typ %15[^ ]",
foundation_buf, &component, transport_buf, &priority, address_buf, &port, type_buf)) {
2021-03-28 17:32:53 +08:00
SDP_THROW();
}
2021-03-29 12:28:47 +08:00
foundation = foundation_buf;
2021-03-28 17:32:53 +08:00
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()) {
2021-03-29 12:28:47 +08:00
value = foundation + " " + to_string(component) + " " + transport + " " + to_string(priority) +
2021-03-28 17:32:53 +08:00
" " + address + " " + to_string(port) + " typ " + type;
for (auto &pr : arr) {
value += ' ';
value += pr.first;
value += ' ';
value += pr.second;
}
}
return SdpItem::toString();
}
2021-03-28 09:49:34 +08:00
void test_sdp(){
char str1[] = "v=0\n"
"o=- 380154348540553537 2 IN IP4 127.0.0.1\n"
"s=-\n"
2021-03-28 17:32:53 +08:00
"b=CT:1900\n"
2021-03-28 09:49:34 +08:00
"t=0 0\n"
"a=group:BUNDLE video\n"
"a=msid-semantic: WMS\n"
"m=video 9 RTP/SAVPF 96\n"
"c=IN IP4 0.0.0.0\n"
"a=rtcp:9 IN IP4 0.0.0.0\n"
"a=ice-ufrag:1ZFN\n"
"a=ice-pwd:70P3H0jPlGz1fiJl5XZfXMZH\n"
"a=ice-options:trickle\n"
"a=fingerprint:sha-256 3E:10:35:6B:9A:9E:B0:55:AC:2A:88:F5:74:C1:70:32:B5:8D:88:1D:37:B0:9C:69:A6:DD:07:10:73:27:1A:16\n"
"a=setup:active\n"
"a=mid:video\n"
"a=recvonly\n"
"a=rtcp-mux\n"
"a=rtpmap:96 H264/90000\n"
"a=fmtp:96 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f";
char str2[] = "v=0\n"
"o=- 2584450093346841581 2 IN IP4 127.0.0.1\n"
"s=-\n"
"t=0 0\n"
"a=group:BUNDLE audio video data\n"
"a=msid-semantic: WMS 616cfbb1-33a3-4d8c-8275-a199d6005549\n"
"m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126\n"
"c=IN IP4 0.0.0.0\n"
"a=rtcp:9 IN IP4 0.0.0.0\n"
"a=ice-ufrag:sXJ3\n"
"a=ice-pwd:yEclOTrLg1gEubBFefOqtmyV\n"
"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:audio\n"
2021-03-29 00:12:20 +08:00
"a=extmap:1/sendonly urn:ietf:params:rtp-hdrext:ssrc-audio-level\n"
2021-03-28 09:49:34 +08:00
"a=sendrecv\n"
"a=rtcp-mux\n"
"a=rtpmap:111 opus/48000/2\n"
"a=rtcp-fb:111 transport-cc\n"
"a=fmtp:111 minptime=10;useinbandfec=1\n"
"a=rtpmap:103 ISAC/16000\n"
"a=rtpmap:104 ISAC/32000\n"
"a=rtpmap:9 G722/8000\n"
"a=rtpmap:0 PCMU/8000\n"
"a=rtpmap:8 PCMA/8000\n"
"a=rtpmap:106 CN/32000\n"
"a=rtpmap:105 CN/16000\n"
"a=rtpmap:13 CN/8000\n"
"a=rtpmap:110 telephone-event/48000\n"
"a=rtpmap:112 telephone-event/32000\n"
"a=rtpmap:113 telephone-event/16000\n"
"a=rtpmap:126 telephone-event/8000\n"
"a=ssrc:120276603 cname:iSkJ2vn5cYYubTve\n"
"a=ssrc:120276603 msid:616cfbb1-33a3-4d8c-8275-a199d6005549 1da3d329-7399-4fe9-b20f-69606bebd363\n"
"a=ssrc:120276603 mslabel:616cfbb1-33a3-4d8c-8275-a199d6005549\n"
"a=ssrc:120276603 label:1da3d329-7399-4fe9-b20f-69606bebd363\n"
"m=video 9 UDP/TLS/RTP/SAVPF 96 98 100 102 127 97 99 101 125\n"
"c=IN IP4 0.0.0.0\n"
"a=rtcp:9 IN IP4 0.0.0.0\n"
"a=ice-ufrag:sXJ3\n"
"a=ice-pwd:yEclOTrLg1gEubBFefOqtmyV\n"
"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:video\n"
"a=extmap:2 urn:ietf:params:rtp-hdrext:toffset\n"
"a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time\n"
"a=extmap:4 urn:3gpp:video-orientation\n"
"a=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01\n"
"a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay\n"
"a=sendrecv\n"
"a=rtcp-mux\n"
"a=rtcp-rsize\n"
"a=rtpmap:96 VP8/90000\n"
"a=rtcp-fb:96 ccm fir\n"
"a=rtcp-fb:96 nack\n"
"a=rtcp-fb:96 nack pli\n"
"a=rtcp-fb:96 goog-remb\n"
"a=rtcp-fb:96 transport-cc\n"
"a=rtpmap:98 VP9/90000\n"
"a=rtcp-fb:98 ccm fir\n"
"a=rtcp-fb:98 nack\n"
"a=rtcp-fb:98 nack pli\n"
"a=rtcp-fb:98 goog-remb\n"
"a=rtcp-fb:98 transport-cc\n"
"a=rtpmap:100 H264/90000\n"
"a=rtcp-fb:100 ccm fir\n"
"a=rtcp-fb:100 nack\n"
"a=rtcp-fb:100 nack pli\n"
"a=rtcp-fb:100 goog-remb\n"
"a=rtcp-fb:100 transport-cc\n"
"a=fmtp:100 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f\n"
"a=rtpmap:102 red/90000\n"
"a=rtpmap:127 ulpfec/90000\n"
"a=rtpmap:97 rtx/90000\n"
"a=fmtp:97 apt=96\n"
"a=rtpmap:99 rtx/90000\n"
"a=fmtp:99 apt=98\n"
"a=rtpmap:101 rtx/90000\n"
"a=fmtp:101 apt=100\n"
"a=rtpmap:125 rtx/90000\n"
"a=fmtp:125 apt=102\n"
"a=ssrc-group:FID 2580761338 611523443\n"
"a=ssrc:2580761338 cname:iSkJ2vn5cYYubTve\n"
"a=ssrc:2580761338 msid:616cfbb1-33a3-4d8c-8275-a199d6005549 bf270496-a23e-47b5-b901-ef23096cd961\n"
"a=ssrc:2580761338 mslabel:616cfbb1-33a3-4d8c-8275-a199d6005549\n"
"a=ssrc:2580761338 label:bf270496-a23e-47b5-b901-ef23096cd961\n"
"a=ssrc:611523443 cname:iSkJ2vn5cYYubTve\n"
"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"
2021-03-28 17:32:53 +08:00
"a=candidate:3575467457 1 udp 2113937151 10.15.83.23 57857 typ host generation 0 ufrag 6R0z network-cost 999\n"
2021-03-28 09:49:34 +08:00
"m=application 9 DTLS/SCTP 5000\n"
"c=IN IP4 0.0.0.0\n"
"a=ice-ufrag:sXJ3\n"
"a=ice-pwd:yEclOTrLg1gEubBFefOqtmyV\n"
"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"
2021-03-28 17:32:53 +08:00
"a=sctpmap:5000 webrtc-datachannel 1024\n"
"a=sctp-port:5000";
2021-03-28 09:49:34 +08:00
2021-03-29 10:54:18 +08:00
RtcSessionSdp sdp1;
2021-03-28 09:49:34 +08:00
sdp1.parse(str1);
2021-03-29 10:54:18 +08:00
RtcSessionSdp sdp2;
2021-03-28 09:49:34 +08:00
sdp2.parse(str2);
2021-03-28 17:32:53 +08:00
for (auto media : sdp1.medias) {
InfoL << getRtpDirectionString(media.getDirection());
}
for (auto media : sdp2.medias) {
InfoL << getRtpDirectionString(media.getDirection());
}
2021-03-28 09:49:34 +08:00
InfoL << sdp1.toString();
InfoL << sdp2.toString();
2021-03-29 23:03:55 +08:00
RtcSession session1;
session1.loadFrom(str1);
RtcSession session2;
session2.loadFrom(str2);
InfoL;
2021-03-28 09:49:34 +08:00
}
2021-03-29 12:00:42 +08:00
void RtcSession::loadFrom(const string &str) {
RtcSessionSdp sdp;
sdp.parse(str);
version = sdp.getVersion();
origin = sdp.getOrigin();
session_name = sdp.getSessionName();
session_info = sdp.getSessionInfo();
connection = sdp.getConnection();
bandwidth = sdp.getBandwidth();
time = sdp.getSessionTime();
2021-03-29 23:03:55 +08:00
msid_semantic = sdp.getItemClass<SdpAttrMsidSemantic>('a', "msid-semantic");
2021-03-29 12:00:42 +08:00
for (auto &media : sdp.medias) {
auto mline = media.getItemClass<SdpMedia>('m');
switch (mline.type) {
case TrackVideo:
case TrackAudio:
2021-03-29 23:03:55 +08:00
case TrackApplication: break;
2021-03-29 12:00:42 +08:00
default: throw std::invalid_argument(StrPrinter << "不识别的media类型:" << mline.toString());
}
2021-03-29 23:03:55 +08:00
this->media.emplace_back();
auto &rtc_media = this->media.back();
2021-03-29 12:00:42 +08:00
rtc_media.type = mline.type;
rtc_media.mid = media.getStringItem('a', "mid");
2021-03-29 18:31:56 +08:00
rtc_media.proto = mline.proto;
rtc_media.type = mline.type;
rtc_media.port = mline.port;
2021-03-29 23:03:55 +08:00
rtc_media.addr = media.getItemClass<SdpConnection>('c');
rtc_media.ice_ufrag = media.getStringItem('a', "ice-ufrag");
rtc_media.ice_pwd = media.getStringItem('a', "ice-pwd");
rtc_media.role = media.getItemClass<SdpAttrSetup>('a', "setup").role;
2021-03-29 18:31:56 +08:00
rtc_media.fingerprint = media.getItemClass<SdpAttrFingerprint>('a', "fingerprint");
2021-03-29 23:03:55 +08:00
rtc_media.ice_trickle = media.getItem('a', "ice-trickle").operator bool();
rtc_media.ice_lite = media.getItem('a', "ice-lite").operator bool();
rtc_media.ice_renomination = media.getItem('a', "ice-renomination").operator bool();
rtc_media.candidate = media.getAllItem<SdpAttrCandidate>('a', "candidate");
if (mline.type == TrackType::TrackApplication) {
rtc_media.sctp_port = atoi(media.getStringItem('a', "sctp-port").data());
rtc_media.sctpmap = media.getItemClass<SdpAttrSctpMap>('a', "sctpmap");
continue;
}
rtc_media.rtcp_addr = media.getItemClass<SdpAttrRtcp>('a', "rtcp");
rtc_media.direction = media.getDirection();
rtc_media.extmap = media.getAllItem<SdpAttrExtmap>('a', "extmap");
rtc_media.rtcp_mux = media.getItem('a', "rtcp-mux").operator bool();
rtc_media.rtcp_rsize = media.getItem('a', "rtcp-rsize").operator bool();
map<uint32_t, RtcSSRC> rtc_ssrc_map;
for (auto &ssrc : media.getAllItem<SdpAttrSSRC>('a', "ssrc")) {
auto &rtc_ssrc = rtc_ssrc_map[ssrc.ssrc];
rtc_ssrc.ssrc = ssrc.ssrc;
2021-03-30 09:58:53 +08:00
if (!strcasecmp(ssrc.attribute.data(), "cname")) {
2021-03-29 23:03:55 +08:00
rtc_ssrc.cname = ssrc.attribute_value;
continue;
}
2021-03-30 09:58:53 +08:00
if (!strcasecmp(ssrc.attribute.data(), "msid")) {
2021-03-29 23:03:55 +08:00
rtc_ssrc.msid = ssrc.attribute_value;
continue;
}
2021-03-30 09:58:53 +08:00
if (!strcasecmp(ssrc.attribute.data(), "mslabel")) {
2021-03-29 23:03:55 +08:00
rtc_ssrc.mslabel = ssrc.attribute_value;
continue;
}
2021-03-30 09:58:53 +08:00
if (!strcasecmp(ssrc.attribute.data(), "label")) {
2021-03-29 23:03:55 +08:00
rtc_ssrc.label = ssrc.attribute_value;
continue;
}
}
uint32_t ssrc_rtp = 0, ssrc_rtx = 0, ssrc_rtp_low = 0, ssrc_rtp_mid = 0, ssrc_rtp_high = 0;
auto ssrc_groups = media.getAllItem<SdpAttrSSRCGroup>('a', "ssrc-group");
for (auto &group : ssrc_groups) {
if (group.isFID()) {
ssrc_rtp = group.u.fid.rtp_ssrc;
ssrc_rtx = group.u.fid.rtx_ssrc;
} else if (group.isSIM()) {
ssrc_rtp_low = group.u.sim.rtp_ssrc_low;
ssrc_rtp_mid = group.u.sim.rtp_ssrc_mid;
ssrc_rtp_high = group.u.sim.rtp_ssrc_high;
}
}
if (!ssrc_rtp) {
//没有指定ssrc-group字段那么只有一个ssrc
if (rtc_ssrc_map.size() > 1) {
throw std::invalid_argument("sdp中不存在a=ssrc-group:FID字段,但是ssrc却有多个");
}
ssrc_rtp = rtc_ssrc_map.begin()->second.ssrc;
}
for (auto &pr : rtc_ssrc_map) {
auto &rtc_ssrc = pr.second;
if (rtc_ssrc.ssrc == ssrc_rtp) {
rtc_media.rtp_ssrc = rtc_ssrc;
}
if (rtc_ssrc.ssrc == ssrc_rtx) {
rtc_media.rtx_ssrc = rtc_ssrc;
}
if (rtc_ssrc.ssrc == ssrc_rtp_low) {
rtc_media.rtp_ssrc_low = rtc_ssrc;
}
if (rtc_ssrc.ssrc == ssrc_rtp_mid) {
rtc_media.rtp_ssrc_mid = rtc_ssrc;
}
if (rtc_ssrc.ssrc == ssrc_rtp_high) {
rtc_media.rtp_ssrc_high = rtc_ssrc;
}
}
auto rtpmap_arr = media.getAllItem<SdpAttrRtpMap>('a', "rtpmap");
auto rtcpfb_arr = media.getAllItem<SdpAttrRtcpFb>('a', "rtcp-fb");
auto fmtp_aar = media.getAllItem<SdpAttrFmtp>('a', "fmtp");
//方便根据pt查找rtpmap,一个pt必有一条
map<uint8_t, SdpAttrRtpMap &> rtpmap_map;
//方便根据pt查找rtcp-fb,一个pt可能有多条或0条
multimap<uint8_t, SdpAttrRtcpFb &> rtcpfb_map;
//方便根据pt查找fmtp一个pt最多一条
map<uint8_t, SdpAttrFmtp &> fmtp_map;
for (auto &rtpmap : rtpmap_arr) {
if (!rtpmap_map.emplace(rtpmap.pt, rtpmap).second) {
//添加失败,有多条
throw std::invalid_argument(StrPrinter << "该pt存在多条a=rtpmap:" << rtpmap.pt);
}
}
for (auto &rtpfb : rtcpfb_arr) {
rtcpfb_map.emplace(rtpfb.pt, rtpfb);
}
for (auto &fmtp : fmtp_aar) {
if (!fmtp_map.emplace(fmtp.pt, fmtp).second) {
//添加失败,有多条
throw std::invalid_argument(StrPrinter << "该pt存在多条a=fmtp:" << fmtp.pt);
}
}
for (auto &pt : mline.fmts) {
//遍历所有编码方案的pt
rtc_media.plan.emplace_back();
auto &plan = rtc_media.plan.back();
auto rtpmap_it = rtpmap_map.find(pt);
if (rtpmap_it == rtpmap_map.end()) {
throw std::invalid_argument(StrPrinter << "该pt不存在相对于的a=rtpmap:" << pt);
}
plan.pt = rtpmap_it->second.pt;
plan.codec = rtpmap_it->second.codec;
plan.sample_rate = rtpmap_it->second.sample_rate;
plan.channel = rtpmap_it->second.channel;
auto fmtp_it = fmtp_map.find(pt);
if (fmtp_it != fmtp_map.end()) {
plan.fmtp = fmtp_it->second.arr;
}
for (auto rtpfb_it = rtcpfb_map.find(pt);
rtpfb_it != rtcpfb_map.end() && rtpfb_it->second.pt == pt; ++rtpfb_it) {
plan.rtcp_fb.emplace_back(rtpfb_it->second.rtcp_type);
}
}
2021-03-29 12:00:42 +08:00
}
2021-03-29 23:03:55 +08:00
group = sdp.getItemClass<SdpAttrGroup>('a', "group");
2021-03-29 23:55:29 +08:00
checkValid();
2021-03-29 12:00:42 +08:00
}
2021-03-29 23:55:29 +08:00
string RtcCodecPlan::getFmtp(const char *key) const{
for (auto &item : fmtp) {
if (strcasecmp(item.first.data(), key) == 0) {
2021-03-29 23:55:29 +08:00
return item.second;
}
}
return "";
}
const RtcCodecPlan *RtcMedia::getPlan(uint8_t pt) const{
for (auto &item : plan) {
if (item.pt == pt) {
return &item;
}
}
return nullptr;
}
const RtcCodecPlan *RtcMedia::getPlan(const char *codec) const{
2021-03-29 23:55:29 +08:00
for (auto &item : plan) {
if (strcasecmp(item.codec.data(), codec) == 0) {
return &item;
2021-03-29 23:55:29 +08:00
}
}
return nullptr;
}
2021-03-30 10:59:15 +08:00
const RtcCodecPlan *RtcMedia::getRelatedRtxPlan(uint8_t pt) const{
for (auto &item : plan) {
if (strcasecmp(item.codec.data(), "rtx") == 0) {
auto apt = atoi(item.getFmtp("apt").data());
if (pt == apt) {
return &item;
}
}
}
return nullptr;
}
void RtcMedia::checkValid() const{
CHECK(type != TrackInvalid);
CHECK(!mid.empty());
CHECK(!proto.empty());
CHECK(direction != RtpDirection::invalid || type == TrackApplication);
CHECK(!plan.empty() || type == TrackApplication );
bool send_rtp = (direction == RtpDirection::sendonly || direction == RtpDirection::sendrecv);
CHECK(!rtp_ssrc.empty() || !send_rtp);
auto rtx_plan = getPlan("rtx");
if (rtx_plan) {
//开启rtx后必须指定rtx_ssrc
CHECK(!rtx_ssrc.empty() || !send_rtp);
}
2021-03-29 23:55:29 +08:00
}
void RtcSession::checkValid() const{
CHECK(version == 0);
CHECK(!origin.empty());
CHECK(!session_name.empty());
CHECK(!msid_semantic.empty());
CHECK(!media.empty());
CHECK(group.mids.size() <= media.size());
2021-03-29 23:55:29 +08:00
for (auto &item : media) {
item.checkValid();
}
2021-03-30 11:51:39 +08:00
}
void RtcConfigure::RtcTrackConfigure::setDefaultSetting(TrackType type){
enable = true;
rtcp_mux = true;
rtcp_rsize = false;
group_bundle = true;
unified_plan = false;
support_rtx = true;
support_red = false;
support_ulpfec = false;
support_simulcast = false;
ice_lite = true;
ice_trickle = true;
ice_renomination = false;
switch (type) {
case TrackVideo: {
preferred_codec = {CodecAAC, CodecOpus, CodecG711U, CodecG711A};
rtcp_fb = {"transport-cc"};
extmap = {"1 urn:ietf:params:rtp-hdrext:ssrc-audio-level"};
break;
}
case TrackAudio: {
preferred_codec = {CodecH264, CodecH265};
rtcp_fb = {"nack", "ccm fir", "nack pli", "goog-remb", "transport-cc"};
extmap = {"2 urn:ietf:params:rtp-hdrext:toffset",
"3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time",
"4 urn:3gpp:video-orientation",
"5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01",
"6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay"};
break;
}
case TrackApplication: {
enable = false;
break;
}
default: break;
}
}
void RtcConfigure::setDefaultSetting(string ice_ufrag,
string ice_pwd,
DtlsRole role,
RtpDirection direction,
const SdpAttrFingerprint &fingerprint) {
video.setDefaultSetting(TrackVideo);
audio.setDefaultSetting(TrackAudio);
application.setDefaultSetting(TrackApplication);
video.ice_ufrag = audio.ice_ufrag = application.ice_ufrag = ice_ufrag;
video.ice_pwd = audio.ice_pwd = application.ice_pwd = ice_ufrag;
video.role = audio.role = application.role = role;
video.direction = audio.direction = application.direction = direction;
video.fingerprint = audio.fingerprint = application.fingerprint = fingerprint;
}
void RtcConfigure::addCandidate(const SdpAttrCandidate &candidate, TrackType type) {
switch (type) {
case TrackAudio: {
audio.candidate.emplace_back(candidate);
break;
}
case TrackVideo: {
video.candidate.emplace_back(candidate);
break;
}
case TrackApplication: {
application.candidate.emplace_back(candidate);
break;
}
default: {
audio.candidate.emplace_back(candidate);
video.candidate.emplace_back(candidate);
application.candidate.emplace_back(candidate);
break;
}
}
2021-03-29 23:55:29 +08:00
}