2021-04-09 20:42:36 +08:00
|
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
|
|
|
|
|
*
|
|
|
|
|
* This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
|
|
|
|
|
*
|
|
|
|
|
* Use of this source code is governed by MIT 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.
|
|
|
|
|
*/
|
2021-03-27 18:33:20 +08:00
|
|
|
|
|
|
|
|
|
#include "Sdp.h"
|
2021-04-02 18:28:01 +08:00
|
|
|
|
#include "Rtsp/Rtsp.h"
|
2021-03-28 17:32:53 +08:00
|
|
|
|
#include <inttypes.h>
|
2021-04-02 18:28:01 +08:00
|
|
|
|
using namespace mediakit;
|
2021-03-28 09:49:34 +08:00
|
|
|
|
|
|
|
|
|
using onCreateSdpItem = function<SdpItem::Ptr(const string &key, const string &value)>;
|
2021-03-30 09:53:58 +08:00
|
|
|
|
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-30 13:09:02 +08:00
|
|
|
|
class DirectionInterfaceImp : public SdpItem, public DirectionInterface{
|
|
|
|
|
public:
|
|
|
|
|
DirectionInterfaceImp(RtpDirection direct){
|
|
|
|
|
direction = direct;
|
|
|
|
|
}
|
|
|
|
|
const char* getKey() const override { return getRtpDirectionString(getDirection());}
|
|
|
|
|
RtpDirection getDirection() const override {return direction;}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
RtpDirection direction;
|
|
|
|
|
};
|
|
|
|
|
|
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-31 17:15:26 +08:00
|
|
|
|
registerSdpItem<SdpAttrMsid>();
|
|
|
|
|
registerSdpItem<SdpAttrExtmapAllowMixed>();
|
|
|
|
|
registerSdpItem<SdpAttrRid>();
|
|
|
|
|
registerSdpItem<SdpAttrSimulcast>();
|
2021-03-28 09:49:34 +08:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-30 09:53:58 +08:00
|
|
|
|
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";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-30 09:53:58 +08:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-30 09:53:58 +08:00
|
|
|
|
SdpItem::Ptr RtcSdpBase::getItem(char key_c, const char *attr_key) const {
|
2021-03-29 11:39:58 +08:00
|
|
|
|
for (auto item : items) {
|
2021-03-30 09:53:58 +08:00
|
|
|
|
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);
|
2021-03-30 09:53:58 +08:00
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-01 10:35:31 +08:00
|
|
|
|
void SdpAttrIceOption::parse(const string &str){
|
|
|
|
|
auto vec = split(str, " ");
|
|
|
|
|
for (auto &v : vec) {
|
|
|
|
|
if (!strcasecmp(v.data(), "trickle")) {
|
|
|
|
|
trickle = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (!strcasecmp(v.data(), "renomination")) {
|
|
|
|
|
renomination = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string SdpAttrIceOption::toString() const{
|
|
|
|
|
if (value.empty()) {
|
|
|
|
|
if (trickle && renomination) {
|
|
|
|
|
value = "trickle renomination";
|
|
|
|
|
} else if (trickle) {
|
|
|
|
|
value = "trickle";
|
|
|
|
|
} else if (renomination) {
|
|
|
|
|
value = "renomination";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return value;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-28 17:32:53 +08:00
|
|
|
|
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-05-07 14:08:43 +08:00
|
|
|
|
if (sscanf(str.data(), "%" SCNd8 "/%31[^ ] %127s", &id, direction_buf, buf) != 3) {
|
|
|
|
|
if (sscanf(str.data(), "%" SCNd8 " %127s", &id, 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-30 13:09:02 +08:00
|
|
|
|
if(direction == RtpDirection::invalid || direction == RtpDirection::sendrecv){
|
2021-05-07 14:08:43 +08:00
|
|
|
|
value = to_string((int)id) + " " + ext;
|
2021-03-29 00:12:20 +08:00
|
|
|
|
} else {
|
2021-05-07 14:08:43 +08:00
|
|
|
|
value = to_string((int)id) + "/" + getRtpDirectionString(direction) + " " + ext;
|
2021-03-29 00:12:20 +08:00
|
|
|
|
}
|
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();
|
|
|
|
|
}
|
2021-04-05 11:07:41 +08:00
|
|
|
|
if (getTrackType(getCodecId(buf)) == TrackAudio) {
|
|
|
|
|
//未指定通道数时,且为音频时,那么通道数默认为1
|
|
|
|
|
channel = 1;
|
|
|
|
|
}
|
2021-03-28 17:32:53 +08:00
|
|
|
|
}
|
|
|
|
|
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);
|
2021-04-02 18:28:01 +08:00
|
|
|
|
auto pos = item.find('=');
|
|
|
|
|
if(pos == string::npos){
|
2021-04-15 19:35:25 +08:00
|
|
|
|
fmtp.emplace(std::make_pair(item, ""));
|
2021-04-06 22:51:16 +08:00
|
|
|
|
} else {
|
2021-04-15 19:35:25 +08:00
|
|
|
|
fmtp.emplace(std::make_pair(item.substr(0, pos), item.substr(pos + 1)));
|
2021-03-28 17:32:53 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-15 19:35:25 +08:00
|
|
|
|
if (fmtp.empty()) {
|
2021-03-28 17:32:53 +08:00
|
|
|
|
SDP_THROW();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string SdpAttrFmtp::toString() const {
|
|
|
|
|
if (value.empty()) {
|
|
|
|
|
value = to_string(pt);
|
|
|
|
|
int i = 0;
|
2021-04-15 19:35:25 +08:00
|
|
|
|
for (auto &pr : fmtp) {
|
2021-03-28 17:32:53 +08:00
|
|
|
|
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]);
|
2021-04-09 12:15:32 +08:00
|
|
|
|
u.fid.rtp_ssrc = atoll(vec[1].data()) & 0xFFFFFFFF;
|
|
|
|
|
u.fid.rtx_ssrc = atoll(vec[2].data()) & 0xFFFFFFFF;
|
2021-03-28 23:31:21 +08:00
|
|
|
|
} else if (vec.size() == 4) {
|
|
|
|
|
if (vec[0] != "SIM") {
|
|
|
|
|
SDP_THROW();
|
|
|
|
|
}
|
|
|
|
|
type = std::move(vec[0]);
|
2021-04-09 12:15:32 +08:00
|
|
|
|
u.sim.rtp_ssrc_low = atoll(vec[1].data()) & 0xFFFFFFFF;
|
|
|
|
|
u.sim.rtp_ssrc_mid = atoll(vec[2].data()) & 0xFFFFFFFF;
|
|
|
|
|
u.sim.rtp_ssrc_high = atoll(vec[3].data()) & 0xFFFFFFFF;
|
2021-03-28 23:31:21 +08:00
|
|
|
|
} else {
|
|
|
|
|
SDP_THROW();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string SdpAttrSSRCGroup::toString() const {
|
|
|
|
|
if (value.empty()) {
|
2021-03-30 13:09:02 +08:00
|
|
|
|
if (isFID()) {
|
2021-03-28 23:31:21 +08:00
|
|
|
|
value = type + " " + to_string(u.fid.rtp_ssrc) + " " + to_string(u.fid.rtx_ssrc);
|
2021-03-30 13:09:02 +08:00
|
|
|
|
} else if (isSIM()) {
|
2021-03-28 23:31:21 +08:00
|
|
|
|
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-04-02 18:28:01 +08:00
|
|
|
|
void RtcSession::loadFrom(const string &str, bool check) {
|
2021-03-29 12:00:42 +08:00
|
|
|
|
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();
|
2021-03-30 09:53:58 +08:00
|
|
|
|
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-04-06 22:51:16 +08:00
|
|
|
|
if (rtc_media.fingerprint.empty()) {
|
|
|
|
|
rtc_media.fingerprint = sdp.getItemClass<SdpAttrFingerprint>('a', "fingerprint");
|
|
|
|
|
}
|
2021-03-29 23:03:55 +08:00
|
|
|
|
rtc_media.ice_lite = media.getItem('a', "ice-lite").operator bool();
|
2021-04-01 10:35:31 +08:00
|
|
|
|
auto ice_options = media.getItemClass<SdpAttrIceOption>('a', "ice-options");
|
|
|
|
|
rtc_media.ice_trickle = ice_options.trickle;
|
|
|
|
|
rtc_media.ice_renomination = ice_options.renomination;
|
2021-03-29 23:03:55 +08:00
|
|
|
|
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();
|
2021-05-07 14:08:43 +08:00
|
|
|
|
{
|
|
|
|
|
rtc_media.extmap.clear();
|
|
|
|
|
auto arr = media.getAllItem<SdpAttrExtmap>('a', "extmap");
|
|
|
|
|
for (auto &ext : arr) {
|
|
|
|
|
rtc_media.extmap.emplace(ext.id, ext);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-29 23:03:55 +08:00
|
|
|
|
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却有多个");
|
|
|
|
|
}
|
2021-04-06 18:29:14 +08:00
|
|
|
|
if (rtc_ssrc_map.size() == 1) {
|
|
|
|
|
ssrc_rtp = rtc_ssrc_map.begin()->second.ssrc;
|
|
|
|
|
}
|
2021-03-29 23:03:55 +08:00
|
|
|
|
}
|
|
|
|
|
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()) {
|
2021-04-02 18:28:01 +08:00
|
|
|
|
plan.pt = pt;
|
2021-04-05 11:07:41 +08:00
|
|
|
|
plan.codec = RtpPayload::getName(pt);
|
2021-04-02 18:28:01 +08:00
|
|
|
|
plan.sample_rate = RtpPayload::getClockRate(pt);
|
|
|
|
|
plan.channel = RtpPayload::getAudioChannel(pt);
|
|
|
|
|
} else {
|
|
|
|
|
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;
|
2021-03-29 23:03:55 +08:00
|
|
|
|
}
|
2021-04-02 18:28:01 +08:00
|
|
|
|
|
2021-03-29 23:03:55 +08:00
|
|
|
|
auto fmtp_it = fmtp_map.find(pt);
|
|
|
|
|
if (fmtp_it != fmtp_map.end()) {
|
2021-04-15 19:35:25 +08:00
|
|
|
|
plan.fmtp = fmtp_it->second.fmtp;
|
2021-03-29 23:03:55 +08:00
|
|
|
|
}
|
|
|
|
|
for (auto rtpfb_it = rtcpfb_map.find(pt);
|
|
|
|
|
rtpfb_it != rtcpfb_map.end() && rtpfb_it->second.pt == pt; ++rtpfb_it) {
|
2021-04-28 15:07:15 +08:00
|
|
|
|
plan.rtcp_fb.emplace(rtpfb_it->second.rtcp_type);
|
2021-03-29 23:03:55 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-29 12:00:42 +08:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-29 23:03:55 +08:00
|
|
|
|
group = sdp.getItemClass<SdpAttrGroup>('a', "group");
|
2021-04-02 18:28:01 +08:00
|
|
|
|
if (check) {
|
|
|
|
|
checkValid();
|
|
|
|
|
}
|
2021-03-29 12:00:42 +08:00
|
|
|
|
}
|
2021-03-29 23:55:29 +08:00
|
|
|
|
|
2021-03-30 13:09:02 +08:00
|
|
|
|
std::shared_ptr<SdpItem> wrapSdpAttr(SdpItem::Ptr item){
|
|
|
|
|
auto ret = std::make_shared<SdpAttr>();
|
|
|
|
|
ret->detail = std::move(item);
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-02 23:01:58 +08:00
|
|
|
|
static void toRtsp(vector <SdpItem::Ptr> &items) {
|
|
|
|
|
for (auto it = items.begin(); it != items.end();) {
|
|
|
|
|
switch ((*it)->getKey()[0]) {
|
|
|
|
|
case 'v':
|
|
|
|
|
case 'o':
|
|
|
|
|
case 's':
|
|
|
|
|
case 'i':
|
|
|
|
|
case 't':
|
|
|
|
|
case 'c':
|
|
|
|
|
case 'b':{
|
|
|
|
|
++it;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
case 'm': {
|
|
|
|
|
auto m = dynamic_pointer_cast<SdpMedia>(*it);
|
|
|
|
|
CHECK(m);
|
|
|
|
|
m->proto = "RTP/AVP";
|
|
|
|
|
++it;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case 'a': {
|
|
|
|
|
auto attr = dynamic_pointer_cast<SdpAttr>(*it);
|
|
|
|
|
CHECK(attr);
|
|
|
|
|
if (!strcasecmp(attr->detail->getKey(), "rtpmap")
|
|
|
|
|
|| !strcasecmp(attr->detail->getKey(), "fmtp")) {
|
|
|
|
|
++it;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
default: {
|
|
|
|
|
it = items.erase(it);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string RtcSession::toRtspSdp() const{
|
2021-03-30 13:09:02 +08:00
|
|
|
|
checkValid();
|
2021-04-02 23:01:58 +08:00
|
|
|
|
RtcSession copy = *this;
|
|
|
|
|
copy.media.clear();
|
|
|
|
|
for (auto &m : media) {
|
|
|
|
|
switch (m.type) {
|
|
|
|
|
case TrackAudio:
|
|
|
|
|
case TrackVideo: {
|
|
|
|
|
copy.media.emplace_back(m);
|
|
|
|
|
copy.media.back().plan.resize(1);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default:
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-06 18:29:14 +08:00
|
|
|
|
copy.session_name = "zlmediakit rtsp stream from webrtc";
|
2021-04-02 23:01:58 +08:00
|
|
|
|
auto sdp = copy.toRtcSessionSdp();
|
|
|
|
|
toRtsp(sdp->items);
|
|
|
|
|
int i = 0;
|
|
|
|
|
for (auto &m : sdp->medias) {
|
|
|
|
|
toRtsp(m.items);
|
|
|
|
|
m.items.push_back(wrapSdpAttr(std::make_shared<SdpCommon>("control", string("trackID=") + to_string(i++))));
|
|
|
|
|
}
|
|
|
|
|
return sdp->toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RtcSessionSdp::Ptr RtcSession::toRtcSessionSdp() const{
|
|
|
|
|
RtcSessionSdp::Ptr ret = std::make_shared<RtcSessionSdp>();
|
|
|
|
|
auto &sdp = *ret;
|
2021-03-30 13:09:02 +08:00
|
|
|
|
sdp.items.emplace_back(std::make_shared<SdpString<'v'> >(to_string(version)));
|
|
|
|
|
sdp.items.emplace_back(std::make_shared<SdpOrigin>(origin));
|
|
|
|
|
sdp.items.emplace_back(std::make_shared<SdpString<'s'> >(session_name));
|
|
|
|
|
if (!session_info.empty()) {
|
|
|
|
|
sdp.items.emplace_back(std::make_shared<SdpString<'i'> >(session_info));
|
|
|
|
|
}
|
|
|
|
|
sdp.items.emplace_back(std::make_shared<SdpTime>(time));
|
2021-04-01 11:02:09 +08:00
|
|
|
|
if(connection.empty()){
|
|
|
|
|
sdp.items.emplace_back(std::make_shared<SdpConnection>(connection));
|
|
|
|
|
}
|
2021-03-30 13:09:02 +08:00
|
|
|
|
if (!bandwidth.empty()) {
|
|
|
|
|
sdp.items.emplace_back(std::make_shared<SdpBandwidth>(bandwidth));
|
|
|
|
|
}
|
|
|
|
|
sdp.items.emplace_back(wrapSdpAttr(std::make_shared<SdpAttrGroup>(group)));
|
2021-04-01 11:02:09 +08:00
|
|
|
|
sdp.items.emplace_back(wrapSdpAttr(std::make_shared<SdpAttrMsidSemantic>(msid_semantic)));
|
2021-03-30 13:09:02 +08:00
|
|
|
|
for (auto &m : media) {
|
|
|
|
|
sdp.medias.emplace_back();
|
|
|
|
|
auto &sdp_media = sdp.medias.back();
|
|
|
|
|
auto mline = std::make_shared<SdpMedia>();
|
|
|
|
|
mline->type = m.type;
|
|
|
|
|
mline->port = m.port;
|
|
|
|
|
mline->proto = m.proto;
|
|
|
|
|
for (auto &p : m.plan) {
|
|
|
|
|
mline->fmts.emplace_back(p.pt);
|
|
|
|
|
}
|
|
|
|
|
if (m.type == TrackApplication) {
|
|
|
|
|
mline->fmts.emplace_back(m.sctp_port);
|
|
|
|
|
}
|
|
|
|
|
sdp_media.items.emplace_back(std::move(mline));
|
|
|
|
|
sdp_media.items.emplace_back(std::make_shared<SdpConnection>(m.addr));
|
2021-04-01 11:02:09 +08:00
|
|
|
|
if (!m.rtcp_addr.empty()) {
|
|
|
|
|
sdp_media.items.emplace_back(wrapSdpAttr(std::make_shared<SdpAttrRtcp>(m.rtcp_addr)));
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-30 13:09:02 +08:00
|
|
|
|
sdp_media.items.emplace_back(wrapSdpAttr(std::make_shared<SdpAttrIceUfrag>(m.ice_ufrag)));
|
|
|
|
|
sdp_media.items.emplace_back(wrapSdpAttr(std::make_shared<SdpAttrIcePwd>(m.ice_pwd)));
|
2021-04-01 10:35:31 +08:00
|
|
|
|
if (m.ice_trickle || m.ice_renomination) {
|
|
|
|
|
auto attr = std::make_shared<SdpAttrIceOption>();
|
|
|
|
|
attr->trickle = m.ice_trickle;
|
|
|
|
|
attr->renomination = m.ice_renomination;
|
|
|
|
|
sdp_media.items.emplace_back(wrapSdpAttr(attr));
|
2021-03-30 13:09:02 +08:00
|
|
|
|
}
|
2021-04-01 11:02:09 +08:00
|
|
|
|
sdp_media.items.emplace_back(wrapSdpAttr(std::make_shared<SdpAttrFingerprint>(m.fingerprint)));
|
|
|
|
|
sdp_media.items.emplace_back(wrapSdpAttr(std::make_shared<SdpAttrSetup>(m.role)));
|
|
|
|
|
sdp_media.items.emplace_back(wrapSdpAttr(std::make_shared<SdpAttrMid>(m.mid)));
|
2021-03-30 13:09:02 +08:00
|
|
|
|
if (m.ice_lite) {
|
|
|
|
|
sdp_media.items.emplace_back(wrapSdpAttr(std::make_shared<SdpCommon>("ice-lite")));
|
|
|
|
|
}
|
2021-05-07 14:08:43 +08:00
|
|
|
|
for (auto &pr : m.extmap) {
|
|
|
|
|
sdp_media.items.emplace_back(wrapSdpAttr(std::make_shared<SdpAttrExtmap>(pr.second)));
|
2021-03-30 13:09:02 +08:00
|
|
|
|
}
|
|
|
|
|
if (m.direction != RtpDirection::invalid) {
|
|
|
|
|
sdp_media.items.emplace_back(wrapSdpAttr(std::make_shared<DirectionInterfaceImp>(m.direction)));
|
|
|
|
|
}
|
|
|
|
|
if (m.rtcp_mux) {
|
|
|
|
|
sdp_media.items.emplace_back(wrapSdpAttr(std::make_shared<SdpCommon>("rtcp-mux")));
|
|
|
|
|
}
|
|
|
|
|
if (m.rtcp_rsize) {
|
|
|
|
|
sdp_media.items.emplace_back(wrapSdpAttr(std::make_shared<SdpCommon>("rtcp-rsize")));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(m.type != TrackApplication) {
|
|
|
|
|
for (auto &p : m.plan) {
|
|
|
|
|
auto rtp_map = std::make_shared<SdpAttrRtpMap>();
|
|
|
|
|
rtp_map->pt = p.pt;
|
|
|
|
|
rtp_map->codec = p.codec;
|
|
|
|
|
rtp_map->sample_rate = p.sample_rate;
|
|
|
|
|
rtp_map->channel = p.channel;
|
|
|
|
|
sdp_media.items.emplace_back(wrapSdpAttr(std::move(rtp_map)));
|
|
|
|
|
|
|
|
|
|
for (auto &fb : p.rtcp_fb) {
|
|
|
|
|
auto rtcp_fb = std::make_shared<SdpAttrRtcpFb>();
|
|
|
|
|
rtcp_fb->pt = p.pt;
|
|
|
|
|
rtcp_fb->rtcp_type = fb;
|
|
|
|
|
sdp_media.items.emplace_back(wrapSdpAttr(std::move(rtcp_fb)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!p.fmtp.empty()) {
|
|
|
|
|
auto fmtp = std::make_shared<SdpAttrFmtp>();
|
|
|
|
|
fmtp->pt = p.pt;
|
2021-04-15 19:35:25 +08:00
|
|
|
|
fmtp->fmtp = p.fmtp;
|
2021-03-30 13:09:02 +08:00
|
|
|
|
sdp_media.items.emplace_back(wrapSdpAttr(std::move(fmtp)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!m.rtp_ssrc.empty() && !m.rtx_ssrc.empty()) {
|
|
|
|
|
auto group = std::make_shared<SdpAttrSSRCGroup>();
|
|
|
|
|
group->type = "FID";
|
|
|
|
|
group->u.fid.rtp_ssrc = m.rtp_ssrc.ssrc;
|
|
|
|
|
group->u.fid.rtx_ssrc = m.rtx_ssrc.ssrc;
|
|
|
|
|
sdp_media.items.emplace_back(wrapSdpAttr(std::move(group)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static auto addSSRCItem = [](const RtcSSRC &rtp_ssrc, vector<SdpItem::Ptr> &items) {
|
|
|
|
|
SdpAttrSSRC ssrc;
|
|
|
|
|
ssrc.ssrc = rtp_ssrc.ssrc;
|
|
|
|
|
|
|
|
|
|
ssrc.attribute = "cname";
|
|
|
|
|
ssrc.attribute_value = rtp_ssrc.cname;
|
|
|
|
|
items.emplace_back(wrapSdpAttr(std::make_shared<SdpAttrSSRC>(ssrc)));
|
|
|
|
|
|
|
|
|
|
if (!rtp_ssrc.msid.empty()) {
|
|
|
|
|
ssrc.attribute = "msid";
|
|
|
|
|
ssrc.attribute_value = rtp_ssrc.msid;
|
|
|
|
|
items.emplace_back(wrapSdpAttr(std::make_shared<SdpAttrSSRC>(ssrc)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!rtp_ssrc.mslabel.empty()) {
|
|
|
|
|
ssrc.attribute = "mslabel";
|
|
|
|
|
ssrc.attribute_value = rtp_ssrc.mslabel;
|
|
|
|
|
items.emplace_back(wrapSdpAttr(std::make_shared<SdpAttrSSRC>(ssrc)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!rtp_ssrc.label.empty()) {
|
|
|
|
|
ssrc.attribute = "label";
|
|
|
|
|
ssrc.attribute_value = rtp_ssrc.label;
|
|
|
|
|
items.emplace_back(wrapSdpAttr(std::make_shared<SdpAttrSSRC>(ssrc)));
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
if (!m.rtp_ssrc.empty()) {
|
|
|
|
|
addSSRCItem(m.rtp_ssrc, sdp_media.items);
|
|
|
|
|
}
|
|
|
|
|
if (!m.rtx_ssrc.empty()) {
|
|
|
|
|
addSSRCItem(m.rtx_ssrc, sdp_media.items);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool enable_sim = false;
|
|
|
|
|
if (!m.rtp_ssrc_low.empty() && !m.rtp_ssrc_mid.empty() && !m.rtp_ssrc_high.empty()) {
|
|
|
|
|
auto group = std::make_shared<SdpAttrSSRCGroup>();
|
|
|
|
|
group->type = "SIM";
|
|
|
|
|
group->u.sim.rtp_ssrc_low = m.rtp_ssrc_low.ssrc;
|
|
|
|
|
group->u.sim.rtp_ssrc_mid = m.rtp_ssrc_mid.ssrc;
|
|
|
|
|
group->u.sim.rtp_ssrc_high = m.rtp_ssrc_high.ssrc;
|
|
|
|
|
sdp_media.items.emplace_back(wrapSdpAttr(std::move(group)));
|
|
|
|
|
enable_sim = true;
|
|
|
|
|
}
|
|
|
|
|
if (enable_sim) {
|
|
|
|
|
addSSRCItem(m.rtp_ssrc_low, sdp_media.items);
|
|
|
|
|
addSSRCItem(m.rtp_ssrc_mid, sdp_media.items);
|
|
|
|
|
addSSRCItem(m.rtp_ssrc_high, sdp_media.items);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
sdp_media.items.emplace_back(wrapSdpAttr(std::make_shared<SdpAttrSctpMap>(m.sctpmap)));
|
|
|
|
|
sdp_media.items.emplace_back(wrapSdpAttr(std::make_shared<SdpCommon>("sctp-port", to_string(m.sctp_port))));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto &cand : m.candidate) {
|
|
|
|
|
sdp_media.items.emplace_back(wrapSdpAttr(std::make_shared<SdpAttrCandidate>(cand)));
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-02 23:01:58 +08:00
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string RtcSession::toString() const{
|
|
|
|
|
checkValid();
|
|
|
|
|
return toRtcSessionSdp()->toString();
|
2021-03-30 13:09:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
2021-03-29 23:55:29 +08:00
|
|
|
|
string RtcCodecPlan::getFmtp(const char *key) const{
|
|
|
|
|
for (auto &item : fmtp) {
|
2021-03-30 09:53:58 +08:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-30 09:53:58 +08:00
|
|
|
|
const RtcCodecPlan *RtcMedia::getPlan(const char *codec) const{
|
2021-03-29 23:55:29 +08:00
|
|
|
|
for (auto &item : plan) {
|
2021-03-30 09:53:58 +08:00
|
|
|
|
if (strcasecmp(item.codec.data(), codec) == 0) {
|
|
|
|
|
return &item;
|
2021-03-29 23:55:29 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-30 09:53:58 +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;
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-30 09:53:58 +08:00
|
|
|
|
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
|
2021-04-02 20:35:43 +08:00
|
|
|
|
//todo 此处不确定
|
|
|
|
|
// CHECK(!rtx_ssrc.empty() || !send_rtp);
|
2021-03-30 09:53:58 +08:00
|
|
|
|
}
|
2021-03-29 23:55:29 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RtcSession::checkValid() const{
|
2021-03-30 09:53:58 +08:00
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
2021-04-02 23:01:58 +08:00
|
|
|
|
const RtcMedia *RtcSession::getMedia(TrackType type) const{
|
2021-04-01 10:09:50 +08:00
|
|
|
|
for(auto &m : media){
|
|
|
|
|
if(m.type == type){
|
|
|
|
|
return &m;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 15:41:36 +08:00
|
|
|
|
bool RtcSession::supportRtcpFb(const string &name, TrackType type) const {
|
|
|
|
|
auto media = getMedia(type);
|
|
|
|
|
if (!media) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
auto &ref = media->plan[0].rtcp_fb;
|
|
|
|
|
return ref.find(name) != ref.end();
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-30 15:15:35 +08:00
|
|
|
|
string const SdpConst::kTWCCRtcpFb = "transport-cc";
|
|
|
|
|
string const SdpConst::kRembRtcpFb = "goog-remb";
|
2021-04-28 15:07:15 +08:00
|
|
|
|
|
|
|
|
|
void RtcConfigure::RtcTrackConfigure::enableTWCC(bool enable){
|
|
|
|
|
if (!enable) {
|
2021-04-30 15:15:35 +08:00
|
|
|
|
rtcp_fb.erase(SdpConst::kTWCCRtcpFb);
|
2021-05-07 14:02:03 +08:00
|
|
|
|
extmap.erase(RtpExtType::abs_send_time);
|
2021-04-28 15:07:15 +08:00
|
|
|
|
} else {
|
2021-04-30 15:15:35 +08:00
|
|
|
|
rtcp_fb.emplace(SdpConst::kTWCCRtcpFb);
|
2021-05-07 14:02:03 +08:00
|
|
|
|
extmap.emplace(RtpExtType::transport_cc);
|
2021-04-28 15:07:15 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-30 15:08:43 +08:00
|
|
|
|
void RtcConfigure::RtcTrackConfigure::enableREMB(bool enable){
|
|
|
|
|
if (!enable) {
|
2021-04-30 15:15:35 +08:00
|
|
|
|
rtcp_fb.erase(SdpConst::kRembRtcpFb);
|
2021-05-07 14:02:03 +08:00
|
|
|
|
extmap.erase(RtpExtType::abs_send_time);
|
2021-04-30 15:08:43 +08:00
|
|
|
|
} else {
|
2021-04-30 15:15:35 +08:00
|
|
|
|
rtcp_fb.emplace(SdpConst::kRembRtcpFb);
|
2021-05-07 14:02:03 +08:00
|
|
|
|
extmap.emplace(RtpExtType::transport_cc);
|
2021-04-30 15:08:43 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-30 11:51:39 +08:00
|
|
|
|
void RtcConfigure::RtcTrackConfigure::setDefaultSetting(TrackType type){
|
|
|
|
|
enable = true;
|
|
|
|
|
rtcp_mux = true;
|
2021-04-01 11:59:35 +08:00
|
|
|
|
rtcp_rsize = false;
|
2021-03-30 11:51:39 +08:00
|
|
|
|
group_bundle = true;
|
|
|
|
|
support_rtx = true;
|
|
|
|
|
support_red = false;
|
|
|
|
|
support_ulpfec = false;
|
|
|
|
|
ice_lite = true;
|
|
|
|
|
ice_trickle = true;
|
|
|
|
|
ice_renomination = false;
|
|
|
|
|
switch (type) {
|
2021-04-01 10:09:50 +08:00
|
|
|
|
case TrackAudio: {
|
2021-04-15 19:35:25 +08:00
|
|
|
|
//此处调整偏好的编码格式优先级
|
|
|
|
|
preferred_codec = {CodecAAC, CodecG711U, CodecG711A, CodecOpus};
|
2021-04-30 15:15:35 +08:00
|
|
|
|
rtcp_fb = {SdpConst::kTWCCRtcpFb, SdpConst::kRembRtcpFb};
|
2021-04-28 15:07:15 +08:00
|
|
|
|
extmap = {
|
2021-05-07 14:02:03 +08:00
|
|
|
|
RtpExtType::ssrc_audio_level,
|
2021-05-07 17:47:53 +08:00
|
|
|
|
RtpExtType::csrc_audio_level,
|
2021-05-07 14:02:03 +08:00
|
|
|
|
RtpExtType::abs_send_time,
|
|
|
|
|
RtpExtType::transport_cc,
|
|
|
|
|
RtpExtType::sdes_mid,
|
|
|
|
|
RtpExtType::sdes_rtp_stream_id,
|
|
|
|
|
RtpExtType::sdes_repaired_rtp_stream_id
|
2021-04-28 15:07:15 +08:00
|
|
|
|
};
|
2021-03-30 11:51:39 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
2021-04-01 10:09:50 +08:00
|
|
|
|
case TrackVideo: {
|
2021-04-15 19:35:25 +08:00
|
|
|
|
//此处调整偏好的编码格式优先级
|
2021-03-30 11:51:39 +08:00
|
|
|
|
preferred_codec = {CodecH264, CodecH265};
|
2021-04-30 15:15:35 +08:00
|
|
|
|
rtcp_fb = {SdpConst::kTWCCRtcpFb, SdpConst::kRembRtcpFb, "nack", "ccm fir", "nack pli"};
|
2021-04-28 15:07:15 +08:00
|
|
|
|
extmap = {
|
2021-05-07 14:02:03 +08:00
|
|
|
|
RtpExtType::abs_send_time,
|
|
|
|
|
RtpExtType::transport_cc,
|
|
|
|
|
RtpExtType::sdes_mid,
|
|
|
|
|
RtpExtType::sdes_rtp_stream_id,
|
|
|
|
|
RtpExtType::sdes_repaired_rtp_stream_id,
|
|
|
|
|
RtpExtType::video_timing,
|
|
|
|
|
RtpExtType::color_space,
|
|
|
|
|
RtpExtType::video_content_type,
|
|
|
|
|
RtpExtType::playout_delay,
|
|
|
|
|
RtpExtType::video_orientation,
|
2021-05-07 17:47:53 +08:00
|
|
|
|
RtpExtType::toffset,
|
|
|
|
|
RtpExtType::framemarking
|
2021-04-28 15:07:15 +08:00
|
|
|
|
};
|
2021-03-30 11:51:39 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case TrackApplication: {
|
2021-04-01 11:59:35 +08:00
|
|
|
|
enable = false;
|
2021-03-30 11:51:39 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RtcConfigure::setDefaultSetting(string ice_ufrag,
|
|
|
|
|
string ice_pwd,
|
|
|
|
|
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;
|
2021-04-01 10:09:50 +08:00
|
|
|
|
video.ice_pwd = audio.ice_pwd = application.ice_pwd = ice_pwd;
|
2021-03-30 11:51:39 +08:00
|
|
|
|
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: {
|
2021-03-30 13:53:45 +08:00
|
|
|
|
if (audio.group_bundle) {
|
|
|
|
|
audio.candidate.emplace_back(candidate);
|
|
|
|
|
}
|
|
|
|
|
if (video.group_bundle) {
|
|
|
|
|
video.candidate.emplace_back(candidate);
|
|
|
|
|
}
|
|
|
|
|
if (application.group_bundle) {
|
|
|
|
|
application.candidate.emplace_back(candidate);
|
|
|
|
|
}
|
2021-03-30 11:51:39 +08:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-30 18:34:17 +08:00
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 15:07:15 +08:00
|
|
|
|
void RtcConfigure::enableTWCC(bool enable, TrackType type){
|
|
|
|
|
switch (type) {
|
|
|
|
|
case TrackAudio: {
|
|
|
|
|
audio.enableTWCC(enable);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case TrackVideo: {
|
|
|
|
|
video.enableTWCC(enable);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case TrackApplication: {
|
|
|
|
|
application.enableTWCC(enable);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: {
|
|
|
|
|
audio.enableTWCC(enable);
|
|
|
|
|
video.enableTWCC(enable);
|
|
|
|
|
application.enableTWCC(enable);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-06 10:40:21 +08:00
|
|
|
|
void RtcConfigure::enableREMB(bool enable, TrackType type){
|
|
|
|
|
switch (type) {
|
|
|
|
|
case TrackAudio: {
|
|
|
|
|
audio.enableREMB(enable);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case TrackVideo: {
|
|
|
|
|
video.enableREMB(enable);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case TrackApplication: {
|
|
|
|
|
application.enableREMB(enable);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: {
|
|
|
|
|
audio.enableREMB(enable);
|
|
|
|
|
video.enableREMB(enable);
|
|
|
|
|
application.enableREMB(enable);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-03-30 18:34:17 +08:00
|
|
|
|
shared_ptr<RtcSession> RtcConfigure::createAnswer(const RtcSession &offer){
|
2021-03-31 18:32:43 +08:00
|
|
|
|
shared_ptr<RtcSession> ret = std::make_shared<RtcSession>();
|
|
|
|
|
ret->version = offer.version;
|
2021-04-02 16:23:40 +08:00
|
|
|
|
//todo 此处设置会话id与会话地址,貌似没什么作用
|
2021-04-01 11:02:09 +08:00
|
|
|
|
ret->origin = offer.origin;
|
|
|
|
|
ret->session_name = offer.session_name;
|
|
|
|
|
ret->msid_semantic = offer.msid_semantic;
|
2021-03-31 18:32:43 +08:00
|
|
|
|
matchMedia(ret, TrackAudio, offer.media, audio);
|
2021-04-01 10:35:31 +08:00
|
|
|
|
matchMedia(ret, TrackVideo, offer.media, video);
|
2021-03-31 18:32:43 +08:00
|
|
|
|
matchMedia(ret, TrackApplication, offer.media, application);
|
2021-04-02 16:23:40 +08:00
|
|
|
|
if (ret->media.empty()) {
|
|
|
|
|
throw std::invalid_argument("生成的answer sdp中媒体个数为0");
|
|
|
|
|
}
|
2021-04-02 17:08:11 +08:00
|
|
|
|
|
|
|
|
|
//设置音视频端口复用
|
|
|
|
|
if (!offer.group.mids.empty()) {
|
|
|
|
|
for (auto &m : ret->media) {
|
|
|
|
|
ret->group.mids.emplace_back(m.mid);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-31 18:32:43 +08:00
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RtcConfigure::matchMedia(shared_ptr<RtcSession> &ret, TrackType type, const vector<RtcMedia> &medias, const RtcTrackConfigure &configure){
|
|
|
|
|
if (!configure.enable) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-04-06 23:47:54 +08:00
|
|
|
|
bool check_profile = true;
|
|
|
|
|
bool check_codec = true;
|
2021-04-04 23:58:59 +08:00
|
|
|
|
|
|
|
|
|
RETRY:
|
|
|
|
|
|
2021-03-31 18:32:43 +08:00
|
|
|
|
for (auto &codec : configure.preferred_codec) {
|
2021-04-01 10:09:50 +08:00
|
|
|
|
for (auto &offer_media : medias) {
|
|
|
|
|
if (offer_media.type != type) {
|
2021-03-31 18:32:43 +08:00
|
|
|
|
continue;
|
|
|
|
|
}
|
2021-04-01 10:09:50 +08:00
|
|
|
|
if (offer_media.ice_lite && configure.ice_lite) {
|
2021-04-02 16:23:40 +08:00
|
|
|
|
WarnL << "answer sdp配置为ice_lite模式,与offer sdp中的ice_lite模式冲突";
|
2021-03-31 18:32:43 +08:00
|
|
|
|
continue;
|
|
|
|
|
}
|
2021-05-08 14:50:19 +08:00
|
|
|
|
const RtcCodecPlan *selected_plan = nullptr;
|
2021-04-01 10:09:50 +08:00
|
|
|
|
for (auto &plan : offer_media.plan) {
|
2021-04-06 23:47:54 +08:00
|
|
|
|
//先检查编码格式是否为偏好
|
|
|
|
|
if (check_codec && getCodecId(plan.codec) != codec) {
|
2021-04-06 23:22:22 +08:00
|
|
|
|
continue;
|
|
|
|
|
}
|
2021-04-06 23:47:54 +08:00
|
|
|
|
//命中偏好的编码格式,然后检查规格
|
|
|
|
|
if (check_profile && !onCheckCodecProfile(plan, codec)) {
|
|
|
|
|
continue;
|
2021-03-31 18:32:43 +08:00
|
|
|
|
}
|
2021-04-01 10:09:50 +08:00
|
|
|
|
//找到中意的codec
|
2021-05-08 14:50:19 +08:00
|
|
|
|
selected_plan = &plan;
|
2021-04-01 10:09:50 +08:00
|
|
|
|
break;
|
2021-03-31 18:32:43 +08:00
|
|
|
|
}
|
2021-05-08 14:50:19 +08:00
|
|
|
|
if (!selected_plan) {
|
2021-04-01 10:09:50 +08:00
|
|
|
|
//offer中该媒体的所有的codec都不支持
|
2021-03-31 18:32:43 +08:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
RtcMedia answer_media;
|
2021-04-01 10:09:50 +08:00
|
|
|
|
answer_media.type = offer_media.type;
|
|
|
|
|
answer_media.mid = offer_media.mid;
|
|
|
|
|
answer_media.proto = offer_media.proto;
|
2021-04-02 16:23:40 +08:00
|
|
|
|
//todo(此处设置rtp端口,貌似没什么作用)
|
2021-04-01 11:02:09 +08:00
|
|
|
|
answer_media.port = offer_media.port;
|
2021-04-02 16:23:40 +08:00
|
|
|
|
//todo(此处设置rtp的ip地址,貌似没什么作用)
|
|
|
|
|
answer_media.addr = offer_media.addr;
|
|
|
|
|
//todo(此处设置rtcp地址,貌似没什么作用)
|
|
|
|
|
answer_media.rtcp_addr = offer_media.rtcp_addr;
|
2021-04-01 10:09:50 +08:00
|
|
|
|
answer_media.rtcp_mux = offer_media.rtcp_mux && configure.rtcp_mux;
|
|
|
|
|
answer_media.rtcp_rsize = offer_media.rtcp_rsize && configure.rtcp_rsize;
|
|
|
|
|
answer_media.ice_trickle = offer_media.ice_trickle && configure.ice_trickle;
|
|
|
|
|
answer_media.ice_renomination = offer_media.ice_renomination && configure.ice_renomination;
|
|
|
|
|
answer_media.ice_ufrag = configure.ice_ufrag;
|
|
|
|
|
answer_media.ice_pwd = configure.ice_pwd;
|
|
|
|
|
answer_media.fingerprint = configure.fingerprint;
|
2021-04-01 11:02:09 +08:00
|
|
|
|
answer_media.ice_lite = configure.ice_lite;
|
2021-04-02 16:23:40 +08:00
|
|
|
|
answer_media.candidate = configure.candidate;
|
2021-04-01 10:09:50 +08:00
|
|
|
|
switch (offer_media.role) {
|
2021-04-01 14:16:42 +08:00
|
|
|
|
case DtlsRole::actpass :
|
2021-03-31 18:32:43 +08:00
|
|
|
|
case DtlsRole::active : {
|
|
|
|
|
answer_media.role = DtlsRole::passive;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case DtlsRole::passive : {
|
|
|
|
|
answer_media.role = DtlsRole::active;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: continue;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-01 10:09:50 +08:00
|
|
|
|
switch (offer_media.direction) {
|
2021-03-31 18:32:43 +08:00
|
|
|
|
case RtpDirection::sendonly : {
|
|
|
|
|
if (configure.direction != RtpDirection::recvonly &&
|
|
|
|
|
configure.direction != RtpDirection::sendrecv) {
|
|
|
|
|
//我们不支持接收
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
answer_media.direction = RtpDirection::recvonly;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case RtpDirection::recvonly : {
|
|
|
|
|
if (configure.direction != RtpDirection::sendonly &&
|
|
|
|
|
configure.direction != RtpDirection::sendrecv) {
|
|
|
|
|
//我们不支持发送
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
answer_media.direction = RtpDirection::sendonly;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case RtpDirection::sendrecv : {
|
|
|
|
|
//对方支持发送接收,那么最终能力根据配置来决定
|
|
|
|
|
answer_media.direction = configure.direction;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: continue;
|
|
|
|
|
}
|
2021-04-02 16:23:40 +08:00
|
|
|
|
|
|
|
|
|
//添加媒体plan
|
2021-05-08 14:50:19 +08:00
|
|
|
|
answer_media.plan.emplace_back(*selected_plan);
|
2021-04-15 19:35:25 +08:00
|
|
|
|
onSelectPlan(answer_media.plan.back(), codec);
|
2021-04-02 16:23:40 +08:00
|
|
|
|
|
2021-05-08 14:50:19 +08:00
|
|
|
|
set<uint8_t> pt_selected = {selected_plan->pt};
|
|
|
|
|
|
2021-04-02 16:23:40 +08:00
|
|
|
|
//添加rtx,red,ulpfec plan
|
2021-04-01 10:09:50 +08:00
|
|
|
|
if (configure.support_red || configure.support_rtx || configure.support_ulpfec) {
|
|
|
|
|
for (auto &plan : offer_media.plan) {
|
|
|
|
|
if (!strcasecmp(plan.codec.data(), "rtx")) {
|
2021-05-08 14:50:19 +08:00
|
|
|
|
if (configure.support_rtx && atoi(plan.getFmtp("apt").data()) == selected_plan->pt) {
|
2021-04-01 10:09:50 +08:00
|
|
|
|
answer_media.plan.emplace_back(plan);
|
2021-05-08 14:50:19 +08:00
|
|
|
|
pt_selected.emplace(plan.pt);
|
2021-04-01 10:09:50 +08:00
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (!strcasecmp(plan.codec.data(), "red")) {
|
|
|
|
|
if (configure.support_red) {
|
|
|
|
|
answer_media.plan.emplace_back(plan);
|
2021-05-08 14:50:19 +08:00
|
|
|
|
pt_selected.emplace(plan.pt);
|
2021-04-01 10:09:50 +08:00
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (!strcasecmp(plan.codec.data(), "ulpfec")) {
|
|
|
|
|
if (configure.support_ulpfec) {
|
|
|
|
|
answer_media.plan.emplace_back(plan);
|
2021-05-08 14:50:19 +08:00
|
|
|
|
pt_selected.emplace(plan.pt);
|
2021-04-01 10:09:50 +08:00
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-28 15:07:15 +08:00
|
|
|
|
//对方和我方都支持的扩展,那么我们才支持
|
2021-05-07 14:08:43 +08:00
|
|
|
|
for (auto &pr : offer_media.extmap) {
|
|
|
|
|
if (configure.extmap.find(RtpExt::getExtType(pr.second.ext)) != configure.extmap.end()) {
|
|
|
|
|
answer_media.extmap.emplace(pr);
|
2021-04-01 10:09:50 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-28 15:07:15 +08:00
|
|
|
|
|
|
|
|
|
auto &rtcp_fb_ref = answer_media.plan[0].rtcp_fb;
|
|
|
|
|
rtcp_fb_ref.clear();
|
|
|
|
|
//对方和我方都支持的rtcpfb,那么我们才支持
|
2021-05-08 14:50:19 +08:00
|
|
|
|
for (auto &fp : selected_plan->rtcp_fb) {
|
2021-04-28 15:07:15 +08:00
|
|
|
|
if (configure.rtcp_fb.find(fp) != configure.rtcp_fb.end()) {
|
2021-04-01 10:09:50 +08:00
|
|
|
|
//对方该rtcp被我们支持
|
2021-04-28 15:07:15 +08:00
|
|
|
|
rtcp_fb_ref.emplace(fp);
|
2021-04-01 10:09:50 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-05-08 14:50:19 +08:00
|
|
|
|
|
|
|
|
|
//其他plan放在后面
|
|
|
|
|
for (auto &plan : offer_media.plan) {
|
|
|
|
|
if (pt_selected.find(plan.pt) == pt_selected.end()) {
|
|
|
|
|
answer_media.plan.emplace_back(plan);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-03-31 18:32:43 +08:00
|
|
|
|
ret->media.emplace_back(answer_media);
|
2021-04-01 10:09:50 +08:00
|
|
|
|
return;
|
2021-03-31 18:32:43 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-04-04 23:58:59 +08:00
|
|
|
|
|
2021-04-06 23:47:54 +08:00
|
|
|
|
if (check_profile) {
|
|
|
|
|
//如果是由于检查profile导致匹配失败,那么重试一次,且不检查profile
|
|
|
|
|
check_profile = false;
|
|
|
|
|
goto RETRY;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (check_codec) {
|
|
|
|
|
//如果是由于检查codec导致匹配失败,那么重试一次,且不检查codec
|
|
|
|
|
check_codec = false;
|
2021-04-04 23:58:59 +08:00
|
|
|
|
goto RETRY;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RtcConfigure::setPlayRtspInfo(const string &sdp){
|
|
|
|
|
RtcSession session;
|
|
|
|
|
session.loadFrom(sdp, false);
|
|
|
|
|
for (auto &m : session.media) {
|
|
|
|
|
switch (m.type) {
|
2021-04-05 00:06:21 +08:00
|
|
|
|
case TrackVideo : {
|
|
|
|
|
_rtsp_video_plan = std::make_shared<RtcCodecPlan>(m.plan[0]);
|
|
|
|
|
video.preferred_codec.clear();
|
|
|
|
|
video.preferred_codec.emplace_back(getCodecId(_rtsp_video_plan->codec));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case TrackAudio : {
|
|
|
|
|
_rtsp_audio_plan = std::make_shared<RtcCodecPlan>(m.plan[0]);
|
|
|
|
|
audio.preferred_codec.clear();
|
|
|
|
|
audio.preferred_codec.emplace_back(getCodecId(_rtsp_audio_plan->codec));
|
|
|
|
|
break;
|
|
|
|
|
}
|
2021-04-04 23:58:59 +08:00
|
|
|
|
default: break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-06 23:21:46 +08:00
|
|
|
|
static const string kProfile{"profile-level-id"};
|
|
|
|
|
static const string kMode{"packetization-mode"};
|
|
|
|
|
|
2021-04-06 23:46:45 +08:00
|
|
|
|
bool RtcConfigure::onCheckCodecProfile(const RtcCodecPlan &plan, CodecId codec){
|
2021-04-04 23:58:59 +08:00
|
|
|
|
if (_rtsp_audio_plan && codec == getCodecId(_rtsp_audio_plan->codec)) {
|
|
|
|
|
if (plan.sample_rate != _rtsp_audio_plan->sample_rate || plan.channel != _rtsp_audio_plan->channel) {
|
|
|
|
|
//音频采样率和通道数必须相同
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-04-06 23:21:46 +08:00
|
|
|
|
return true;
|
2021-04-04 23:58:59 +08:00
|
|
|
|
}
|
2021-04-06 23:21:46 +08:00
|
|
|
|
if (_rtsp_video_plan && codec == CodecH264 && getCodecId(_rtsp_video_plan->codec) == CodecH264) {
|
2021-04-15 19:35:25 +08:00
|
|
|
|
//h264时,profile-level-id
|
|
|
|
|
if (strcasecmp(_rtsp_video_plan->fmtp[kProfile].data(), const_cast<RtcCodecPlan &>(plan).fmtp[kProfile].data())) {
|
2021-04-06 23:21:46 +08:00
|
|
|
|
//profile-level-id 不匹配
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-04 23:58:59 +08:00
|
|
|
|
return true;
|
2021-03-31 18:32:43 +08:00
|
|
|
|
}
|
2021-04-15 19:35:25 +08:00
|
|
|
|
|
|
|
|
|
void RtcConfigure::onSelectPlan(RtcCodecPlan &plan, CodecId codec){
|
|
|
|
|
if (_rtsp_video_plan && codec == CodecH264 && getCodecId(_rtsp_video_plan->codec) == CodecH264) {
|
|
|
|
|
//h264时,设置packetization-mod为一致
|
|
|
|
|
auto mode = _rtsp_video_plan->fmtp[kMode];
|
|
|
|
|
plan.fmtp[kMode] = mode.empty() ? "0" : mode;
|
|
|
|
|
}
|
|
|
|
|
}
|