1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-02-15 04:42:04 +00:00
srs/trunk/src/app/srs_app_rtc_sdp.cpp
2020-08-21 21:23:32 +08:00

1074 lines
29 KiB
C++

/**
* The MIT License (MIT)
*
* Copyright (c) 2013-2020 John
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include <srs_app_rtc_sdp.hpp>
using namespace std;
#include <stdlib.h>
#include <iostream>
#include <sstream>
#include <vector>
#include <algorithm>
#include <srs_kernel_error.hpp>
#include <srs_kernel_log.hpp>
// TODO: FIXME: Maybe we should use json.encode to escape it?
const std::string kCRLF = "\r\n";
#define FETCH(is,word) \
if (!(is >> word)) {\
return srs_error_new(ERROR_RTC_SDP_DECODE, "fetch failed");\
}\
#define FETCH_WITH_DELIM(is,word,delim) \
if (!getline(is,word,delim)) {\
return srs_error_new(ERROR_RTC_SDP_DECODE, "fetch with delim failed");\
}\
std::vector<std::string> split_str(const std::string& str, const std::string& delim)
{
std::vector<std::string> ret;
size_t pre_pos = 0;
std::string tmp;
size_t pos = 0;
do {
pos = str.find(delim, pre_pos);
tmp = str.substr(pre_pos, pos - pre_pos);
ret.push_back(tmp);
pre_pos = pos + delim.size();
} while (pos != std::string::npos);
return ret;
}
static void skip_first_spaces(std::string& str)
{
while (! str.empty() && str[0] == ' ') {
str.erase(0, 1);
}
}
srs_error_t srs_parse_h264_fmtp(const std::string& fmtp, H264SpecificParam& h264_param)
{
srs_error_t err = srs_success;
std::vector<std::string> vec = split_str(fmtp, ";");
for (size_t i = 0; i < vec.size(); ++i) {
std::vector<std::string> kv = split_str(vec[i], "=");
if (kv.size() == 2) {
if (kv[0] == "profile-level-id") {
h264_param.profile_level_id = kv[1];
} else if (kv[0] == "packetization-mode") {
// 6.3. Non-Interleaved Mode
// This mode is in use when the value of the OPTIONAL packetization-mode
// media type parameter is equal to 1. This mode SHOULD be supported.
// It is primarily intended for low-delay applications. Only single NAL
// unit packets, STAP-As, and FU-As MAY be used in this mode. STAP-Bs,
// MTAPs, and FU-Bs MUST NOT be used. The transmission order of NAL
// units MUST comply with the NAL unit decoding order.
// @see https://tools.ietf.org/html/rfc6184#section-6.3
h264_param.packetization_mode = kv[1];
} else if (kv[0] == "level-asymmetry-allowed") {
h264_param.level_asymmerty_allow = kv[1];
} else {
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid h264 param=%s", kv[0].c_str());
}
} else {
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid h264 param=%s", vec[i].c_str());
}
}
return err;
}
SrsSessionInfo::SrsSessionInfo()
{
}
SrsSessionInfo::~SrsSessionInfo()
{
}
srs_error_t SrsSessionInfo::parse_attribute(const std::string& attribute, const std::string& value)
{
srs_error_t err = srs_success;
if (attribute == "ice-ufrag") {
ice_ufrag_ = value;
} else if (attribute == "ice-pwd") {
ice_pwd_ = value;
} else if (attribute == "ice-options") {
ice_options_ = value;
} else if (attribute == "fingerprint") {
std::istringstream is(value);
FETCH(is, fingerprint_algo_);
FETCH(is, fingerprint_);
} else if (attribute == "setup") {
// @see: https://tools.ietf.org/html/rfc4145#section-4
setup_ = value;
} else {
srs_trace("ignore attribute=%s, value=%s", attribute.c_str(), value.c_str());
}
return err;
}
srs_error_t SrsSessionInfo::encode(std::ostringstream& os)
{
srs_error_t err = srs_success;
if (!ice_ufrag_.empty()) {
os << "a=ice-ufrag:" << ice_ufrag_ << kCRLF;
}
if (!ice_pwd_.empty()) {
os << "a=ice-pwd:" << ice_pwd_ << kCRLF;
}
// For ICE-lite, we never set the trickle.
if (!ice_options_.empty()) {
os << "a=ice-options:" << ice_options_ << kCRLF;
}
if (!fingerprint_algo_.empty() && ! fingerprint_.empty()) {
os << "a=fingerprint:" << fingerprint_algo_ << " " << fingerprint_ << kCRLF;
}
if (!setup_.empty()) {
os << "a=setup:" << setup_ << kCRLF;
}
return err;
}
bool SrsSessionInfo::operator=(const SrsSessionInfo& rhs)
{
return ice_ufrag_ == rhs.ice_ufrag_ &&
ice_pwd_ == rhs.ice_pwd_ &&
ice_options_ == rhs.ice_options_ &&
fingerprint_algo_ == rhs.fingerprint_algo_ &&
fingerprint_ == rhs.fingerprint_ &&
setup_ == rhs.setup_;
}
SrsSSRCInfo::SrsSSRCInfo()
{
ssrc_ = 0;
}
SrsSSRCInfo::SrsSSRCInfo(uint32_t ssrc, std::string cname, std::string stream_id, std::string track_id)
{
ssrc_ = ssrc;
cname_ = cname;
msid_ = stream_id;
msid_tracker_ = track_id;
mslabel_ = msid_;
label_ = msid_tracker_;
}
SrsSSRCInfo::~SrsSSRCInfo()
{
}
srs_error_t SrsSSRCInfo::encode(std::ostringstream& os)
{
srs_error_t err = srs_success;
if (ssrc_ == 0) {
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid ssrc");
}
os << "a=ssrc:" << ssrc_ << " cname:" << cname_ << kCRLF;
if (!msid_.empty()) {
os << "a=ssrc:" << ssrc_ << " msid:" << msid_;
if (!msid_tracker_.empty()) {
os << " " << msid_tracker_;
}
os << kCRLF;
}
if (!mslabel_.empty()) {
os << "a=ssrc:" << ssrc_ << " mslabel:" << mslabel_ << kCRLF;
}
if (!label_.empty()) {
os << "a=ssrc:" << ssrc_ << " label:" << label_ << kCRLF;
}
return err;
}
SrsSSRCGroup::SrsSSRCGroup()
{
}
SrsSSRCGroup::~SrsSSRCGroup()
{
}
SrsSSRCGroup::SrsSSRCGroup(const std::string& semantic, const std::vector<uint32_t>& ssrcs)
{
semantic_ = semantic;
ssrcs_ = ssrcs;
}
srs_error_t SrsSSRCGroup::encode(std::ostringstream& os)
{
srs_error_t err = srs_success;
if (semantic_.empty()) {
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid semantics");
}
if (ssrcs_.size() == 0) {
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid ssrcs");
}
os << "a=ssrc-group:" << semantic_;
for (int i = 0; i < (int)ssrcs_.size(); i++) {
os << " " << ssrcs_[i];
}
return err;
}
SrsMediaPayloadType::SrsMediaPayloadType(int payload_type)
{
payload_type_ = payload_type;
clock_rate_ = 0;
}
SrsMediaPayloadType::~SrsMediaPayloadType()
{
}
srs_error_t SrsMediaPayloadType::encode(std::ostringstream& os)
{
srs_error_t err = srs_success;
os << "a=rtpmap:" << payload_type_ << " " << encoding_name_ << "/" << clock_rate_;
if (!encoding_param_.empty()) {
os << "/" << encoding_param_;
}
os << kCRLF;
for (std::vector<std::string>::iterator iter = rtcp_fb_.begin(); iter != rtcp_fb_.end(); ++iter) {
os << "a=rtcp-fb:" << payload_type_ << " " << *iter << kCRLF;
}
if (!format_specific_param_.empty()) {
os << "a=fmtp:" << payload_type_ << " " << format_specific_param_
// TODO: FIXME: Remove the test code bellow.
// << ";x-google-max-bitrate=6000;x-google-min-bitrate=5100;x-google-start-bitrate=5000"
<< kCRLF;
}
return err;
}
SrsMediaDesc::SrsMediaDesc(const std::string& type)
{
type_ = type;
port_ = 0;
rtcp_mux_ = false;
rtcp_rsize_ = false;
sendrecv_ = false;
recvonly_ = false;
sendonly_ = false;
inactive_ = false;
}
SrsMediaDesc::~SrsMediaDesc()
{
}
SrsMediaPayloadType* SrsMediaDesc::find_media_with_payload_type(int payload_type)
{
for (size_t i = 0; i < payload_types_.size(); ++i) {
if (payload_types_[i].payload_type_ == payload_type) {
return &payload_types_[i];
}
}
return NULL;
}
vector<SrsMediaPayloadType> SrsMediaDesc::find_media_with_encoding_name(const std::string& encoding_name) const
{
std::vector<SrsMediaPayloadType> payloads;
std::string lower_name(encoding_name), upper_name(encoding_name);
transform(encoding_name.begin(), encoding_name.end(), lower_name.begin(), ::tolower);
transform(encoding_name.begin(), encoding_name.end(), upper_name.begin(), ::toupper);
for (size_t i = 0; i < payload_types_.size(); ++i) {
if (payload_types_[i].encoding_name_ == std::string(lower_name.c_str()) ||
payload_types_[i].encoding_name_ == std::string(upper_name.c_str())) {
payloads.push_back(payload_types_[i]);
}
}
return payloads;
}
srs_error_t SrsMediaDesc::parse_line(const std::string& line)
{
srs_error_t err = srs_success;
std::string content = line.substr(2);
switch (line[0]) {
case 'a': {
return parse_attribute(content);
}
case 'c': {
// TODO: process c-line
break;
}
default: {
srs_trace("ignore media line=%s", line.c_str());
break;
}
}
return err;
}
srs_error_t SrsMediaDesc::encode(std::ostringstream& os)
{
srs_error_t err = srs_success;
os << "m=" << type_ << " " << port_ << " " << protos_;
for (std::vector<SrsMediaPayloadType>::iterator iter = payload_types_.begin(); iter != payload_types_.end(); ++iter) {
os << " " << iter->payload_type_;
}
os << kCRLF;
// TODO:nettype and address type
os << "c=IN IP4 0.0.0.0" << kCRLF;
if ((err = session_info_.encode(os)) != srs_success) {
return srs_error_wrap(err, "encode session info failed");
}
os << "a=mid:" << mid_ << kCRLF;
if (!msid_.empty()) {
os << "a=msid:" << msid_;
if (!msid_tracker_.empty()) {
os << " " << msid_tracker_;
}
os << kCRLF;
}
for(map<int, string>::iterator it = extmaps_.begin(); it != extmaps_.end(); ++it) {
os << "a=extmap:"<< it->first<< " "<< it->second<< kCRLF;
}
if (sendonly_) {
os << "a=sendonly" << kCRLF;
}
if (recvonly_) {
os << "a=recvonly" << kCRLF;
}
if (sendrecv_) {
os << "a=sendrecv" << kCRLF;
}
if (inactive_) {
os << "a=inactive" << kCRLF;
}
if (rtcp_mux_) {
os << "a=rtcp-mux" << kCRLF;
}
if (rtcp_rsize_) {
os << "a=rtcp-rsize" << kCRLF;
}
for (std::vector<SrsMediaPayloadType>::iterator iter = payload_types_.begin(); iter != payload_types_.end(); ++iter) {
if ((err = iter->encode(os)) != srs_success) {
return srs_error_wrap(err, "encode media payload failed");
}
}
for (std::vector<SrsSSRCInfo>::iterator iter = ssrc_infos_.begin(); iter != ssrc_infos_.end(); ++iter) {
SrsSSRCInfo& ssrc_info = *iter;
if ((err = ssrc_info.encode(os)) != srs_success) {
return srs_error_wrap(err, "encode ssrc failed");
}
}
int foundation = 0;
int component_id = 1; /* RTP */
for (std::vector<SrsCandidate>::iterator iter = candidates_.begin(); iter != candidates_.end(); ++iter) {
// @see: https://tools.ietf.org/html/draft-ietf-ice-rfc5245bis-00#section-4.2
uint32_t priority = (1<<24)*(126) + (1<<8)*(65535) + (1)*(256 - component_id);
// @see: https://tools.ietf.org/id/draft-ietf-mmusic-ice-sip-sdp-14.html#rfc.section.5.1
os << "a=candidate:" << foundation++ << " "
<< component_id << " udp " << priority << " "
<< iter->ip_ << " " << iter->port_
<< " typ " << iter->type_
<< " generation 0" << kCRLF;
srs_verbose("local SDP candidate line=%s", os.str().c_str());
}
return err;
}
srs_error_t SrsMediaDesc::parse_attribute(const std::string& content)
{
srs_error_t err = srs_success;
std::string attribute = "";
std::string value = "";
size_t pos = content.find_first_of(":");
if (pos != std::string::npos) {
attribute = content.substr(0, pos);
value = content.substr(pos + 1);
} else {
attribute = content;
}
if (attribute == "extmap") {
return parse_attr_extmap(value);
} else if (attribute == "rtpmap") {
return parse_attr_rtpmap(value);
} else if (attribute == "rtcp") {
return parse_attr_rtcp(value);
} else if (attribute == "rtcp-fb") {
return parse_attr_rtcp_fb(value);
} else if (attribute == "fmtp") {
return parse_attr_fmtp(value);
} else if (attribute == "mid") {
return parse_attr_mid(value);
} else if (attribute == "msid") {
return parse_attr_msid(value);
} else if (attribute == "ssrc") {
return parse_attr_ssrc(value);
} else if (attribute == "ssrc-group") {
return parse_attr_ssrc_group(value);
} else if (attribute == "rtcp-mux") {
rtcp_mux_ = true;
} else if (attribute == "rtcp-rsize") {
rtcp_rsize_ = true;
} else if (attribute == "recvonly") {
recvonly_ = true;
} else if (attribute == "sendonly") {
sendonly_ = true;
} else if (attribute == "sendrecv") {
sendrecv_ = true;
} else if (attribute == "inactive") {
inactive_ = true;
} else {
return session_info_.parse_attribute(attribute, value);
}
return err;
}
srs_error_t SrsMediaDesc::parse_attr_extmap(const std::string& value)
{
srs_error_t err = srs_success;
std::istringstream is(value);
int id = 0;
FETCH(is, id);
if(extmaps_.end() != extmaps_.find(id)) {
return srs_error_new(ERROR_RTC_SDP_DECODE, "duplicate ext id: %d", id);
}
string ext;
FETCH(is, ext);
extmaps_[id] = ext;
return err;
}
srs_error_t SrsMediaDesc::parse_attr_rtpmap(const std::string& value)
{
srs_error_t err = srs_success;
// @see: https://tools.ietf.org/html/rfc4566#page-25
// a=rtpmap:<payload type> <encoding name>/<clock rate> [/<encoding parameters>]
std::istringstream is(value);
int payload_type = 0;
FETCH(is, payload_type);
SrsMediaPayloadType* payload = find_media_with_payload_type(payload_type);
if (payload == NULL) {
return srs_error_new(ERROR_RTC_SDP_DECODE, "can not find payload %d when pase rtpmap", payload_type);
}
std::string word;
FETCH(is, word);
std::vector<std::string> vec = split_str(word, "/");
if (vec.size() < 2) {
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid rtpmap line=%s", value.c_str());
}
payload->encoding_name_ = vec[0];
payload->clock_rate_ = atoi(vec[1].c_str());
if (vec.size() == 3) {
payload->encoding_param_ = vec[2];
}
return err;
}
srs_error_t SrsMediaDesc::parse_attr_rtcp(const std::string& value)
{
srs_error_t err = srs_success;
// TODO:parse rtcp attribute
return err;
}
srs_error_t SrsMediaDesc::parse_attr_rtcp_fb(const std::string& value)
{
srs_error_t err = srs_success;
// @see: https://tools.ietf.org/html/rfc5104#section-7.1
std::istringstream is(value);
int payload_type = 0;
FETCH(is, payload_type);
SrsMediaPayloadType* payload = find_media_with_payload_type(payload_type);
if (payload == NULL) {
return srs_error_new(ERROR_RTC_SDP_DECODE, "can not find payload %d when pase rtcp-fb", payload_type);
}
std::string rtcp_fb = is.str().substr(is.tellg());
skip_first_spaces(rtcp_fb);
payload->rtcp_fb_.push_back(rtcp_fb);
return err;
}
srs_error_t SrsMediaDesc::parse_attr_fmtp(const std::string& value)
{
srs_error_t err = srs_success;
// @see: https://tools.ietf.org/html/rfc4566#page-30
// a=fmtp:<format> <format specific parameters>
std::istringstream is(value);
int payload_type = 0;
FETCH(is, payload_type);
SrsMediaPayloadType* payload = find_media_with_payload_type(payload_type);
if (payload == NULL) {
return srs_error_new(ERROR_RTC_SDP_DECODE, "can not find payload %d when pase fmtp", payload_type);
}
std::string word;
FETCH(is, word);
payload->format_specific_param_ = word;
return err;
}
srs_error_t SrsMediaDesc::parse_attr_mid(const std::string& value)
{
// @see: https://tools.ietf.org/html/rfc3388#section-3
srs_error_t err = srs_success;
std::istringstream is(value);
// mid_ means m-line id
FETCH(is, mid_);
srs_verbose("mid=%s", mid_.c_str());
return err;
}
srs_error_t SrsMediaDesc::parse_attr_msid(const std::string& value)
{
// @see: https://tools.ietf.org/id/draft-ietf-mmusic-msid-08.html#rfc.section.2
// TODO: msid and msid_tracker
srs_error_t err = srs_success;
std::istringstream is(value);
// msid_ means media stream id
FETCH(is, msid_);
is >> msid_tracker_;
return err;
}
srs_error_t SrsMediaDesc::parse_attr_ssrc(const std::string& value)
{
srs_error_t err = srs_success;
// @see: https://tools.ietf.org/html/rfc5576#section-4.1
std::istringstream is(value);
uint32_t ssrc = 0;
FETCH(is, ssrc);
std::string ssrc_attr = "";
FETCH_WITH_DELIM(is, ssrc_attr, ':');
skip_first_spaces(ssrc_attr);
std::string ssrc_value = is.str().substr(is.tellg());
skip_first_spaces(ssrc_value);
SrsSSRCInfo& ssrc_info = fetch_or_create_ssrc_info(ssrc);
if (ssrc_attr == "cname") {
// @see: https://tools.ietf.org/html/rfc5576#section-6.1
ssrc_info.cname_ = ssrc_value;
ssrc_info.ssrc_ = ssrc;
} else if (ssrc_attr == "msid") {
// @see: https://tools.ietf.org/html/draft-alvestrand-mmusic-msid-00#section-2
std::vector<std::string> vec = split_str(ssrc_value, " ");
if (vec.empty()) {
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid ssrc line=%s", value.c_str());
}
ssrc_info.msid_ = vec[0];
if (vec.size() > 1) {
ssrc_info.msid_tracker_ = vec[1];
}
} else if (ssrc_attr == "mslabel") {
ssrc_info.mslabel_ = ssrc_value;
} else if (ssrc_attr == "label") {
ssrc_info.label_ = ssrc_value;
}
return err;
}
srs_error_t SrsMediaDesc::parse_attr_ssrc_group(const std::string& value)
{
srs_error_t err = srs_success;
// @see: https://tools.ietf.org/html/rfc5576#section-4.2
// a=ssrc-group:<semantics> <ssrc-id> ...
std::istringstream is(value);
std::string semantics;
FETCH(is, semantics);
std::string ssrc_ids = is.str().substr(is.tellg());
skip_first_spaces(ssrc_ids);
std::vector<std::string> vec = split_str(ssrc_ids, " ");
if (vec.size() == 0) {
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid ssrc-group line=%s", value.c_str());
}
std::vector<uint32_t> ssrcs;
for (size_t i = 0; i < vec.size(); ++i) {
std::istringstream in_stream(vec[i]);
uint32_t ssrc = 0;
in_stream >> ssrc;
ssrcs.push_back(ssrc);
}
ssrc_groups_.push_back(SrsSSRCGroup(semantics, ssrcs));
return err;
}
SrsSSRCInfo& SrsMediaDesc::fetch_or_create_ssrc_info(uint32_t ssrc)
{
for (size_t i = 0; i < ssrc_infos_.size(); ++i) {
if (ssrc_infos_[i].ssrc_ == ssrc) {
return ssrc_infos_[i];
}
}
SrsSSRCInfo ssrc_info;
ssrc_info.ssrc_ = ssrc;
ssrc_infos_.push_back(ssrc_info);
return ssrc_infos_.back();
}
SrsSdp::SrsSdp()
{
in_media_session_ = false;
start_time_ = 0;
end_time_ = 0;
}
SrsSdp::~SrsSdp()
{
}
srs_error_t SrsSdp::parse(const std::string& sdp_str)
{
srs_error_t err = srs_success;
// All webrtc SrsSdp annotated example
// @see: https://tools.ietf.org/html/draft-ietf-rtcweb-SrsSdp-11
// Sdp example
// session info
// v=
// o=
// s=
// t=
// media description
// m=
// a=
// ...
// media description
// m=
// a=
// ...
std::istringstream is(sdp_str);
std::string line;
while (getline(is, line)) {
srs_verbose("%s", line.c_str());
if (line.size() < 2 || line[1] != '=') {
return srs_error_new(ERROR_RTC_SDP_DECODE, "invalid sdp line=%s", line.c_str());
}
if (!line.empty() && line[line.size()-1] == '\r') {
line.erase(line.size()-1, 1);
}
if ((err = parse_line(line)) != srs_success) {
return srs_error_wrap(err, "parse sdp line failed");
}
}
return err;
}
srs_error_t SrsSdp::encode(std::ostringstream& os)
{
srs_error_t err = srs_success;
os << "v=" << version_ << kCRLF;
os << "o=" << username_ << " " << session_id_ << " " << session_version_ << " " << nettype_ << " " << addrtype_ << " " << unicast_address_ << kCRLF;
os << "s=" << session_name_ << kCRLF;
os << "t=" << start_time_ << " " << end_time_ << kCRLF;
// ice-lite is a minimal version of the ICE specification, intended for servers running on a public IP address.
os << "a=ice-lite" << kCRLF;
if (!groups_.empty()) {
os << "a=group:" << group_policy_;
for (std::vector<std::string>::iterator iter = groups_.begin(); iter != groups_.end(); ++iter) {
os << " " << *iter;
}
os << kCRLF;
}
os << "a=msid-semantic: " << msid_semantic_;
for (std::vector<std::string>::iterator iter = msids_.begin(); iter != msids_.end(); ++iter) {
os << " " << *iter;
}
os << kCRLF;
if ((err = session_info_.encode(os)) != srs_success) {
return srs_error_wrap(err, "encode session info failed");
}
for (std::vector<SrsMediaDesc>::iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
if ((err = (*iter).encode(os)) != srs_success) {
return srs_error_wrap(err, "encode media description failed");
}
}
return err;
}
const SrsMediaDesc* SrsSdp::find_media_desc(const std::string& type) const
{
for (std::vector<SrsMediaDesc>::const_iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
if (iter->type_ == type) {
return &(*iter);
}
}
return NULL;
}
void SrsSdp::set_ice_ufrag(const std::string& ufrag)
{
for (std::vector<SrsMediaDesc>::iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
iter->session_info_.ice_ufrag_ = ufrag;
}
}
void SrsSdp::set_ice_pwd(const std::string& pwd)
{
for (std::vector<SrsMediaDesc>::iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
iter->session_info_.ice_pwd_ = pwd;
}
}
void SrsSdp::set_dtls_role(const std::string& dtls_role)
{
for (std::vector<SrsMediaDesc>::iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
iter->session_info_.setup_ = dtls_role;
}
}
void SrsSdp::set_fingerprint_algo(const std::string& algo)
{
for (std::vector<SrsMediaDesc>::iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
iter->session_info_.fingerprint_algo_ = algo;
}
}
void SrsSdp::set_fingerprint(const std::string& fingerprint)
{
for (std::vector<SrsMediaDesc>::iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
iter->session_info_.fingerprint_ = fingerprint;
}
}
void SrsSdp::add_candidate(const std::string& ip, const int& port, const std::string& type)
{
// @see: https://tools.ietf.org/id/draft-ietf-mmusic-ice-sip-sdp-14.html#rfc.section.5.1
SrsCandidate candidate;
candidate.ip_ = ip;
candidate.port_ = port;
candidate.type_ = type;
for (std::vector<SrsMediaDesc>::iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
iter->candidates_.push_back(candidate);
}
}
std::string SrsSdp::get_ice_ufrag() const
{
// Becaues we use BUNDLE, so we can choose the first element.
for (std::vector<SrsMediaDesc>::const_iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
return iter->session_info_.ice_ufrag_;
}
return "";
}
std::string SrsSdp::get_ice_pwd() const
{
// Becaues we use BUNDLE, so we can choose the first element.
for (std::vector<SrsMediaDesc>::const_iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
return iter->session_info_.ice_pwd_;
}
return "";
}
std::string SrsSdp::get_dtls_role() const
{
// Becaues we use BUNDLE, so we can choose the first element.
for (std::vector<SrsMediaDesc>::const_iterator iter = media_descs_.begin(); iter != media_descs_.end(); ++iter) {
return iter->session_info_.setup_;
}
return "";
}
srs_error_t SrsSdp::parse_line(const std::string& line)
{
srs_error_t err = srs_success;
std::string content = line.substr(2);
switch (line[0]) {
case 'o': {
return parse_origin(content);
}
case 'v': {
return parse_version(content);
}
case 's': {
return parse_session_name(content);
}
case 't': {
return parse_timing(content);
}
case 'a': {
if (in_media_session_) {
return media_descs_.back().parse_line(line);
}
return parse_attribute(content);
}
case 'm': {
return parse_media_description(content);
}
case 'c': {
// TODO: process c-line
break;
}
default: {
srs_trace("ignore sdp line=%s", line.c_str());
break;
}
}
return err;
}
srs_error_t SrsSdp::parse_origin(const std::string& content)
{
srs_error_t err = srs_success;
// @see: https://tools.ietf.org/html/rfc4566#section-5.2
// o=<username> <sess-id> <sess-version> <nettype> <addrtype> <unicast-address>
// eg. o=- 9164462281920464688 2 IN IP4 127.0.0.1
std::istringstream is(content);
FETCH(is, username_);
FETCH(is, session_id_);
FETCH(is, session_version_);
FETCH(is, nettype_);
FETCH(is, addrtype_);
FETCH(is, unicast_address_);
return err;
}
srs_error_t SrsSdp::parse_version(const std::string& content)
{
srs_error_t err = srs_success;
// @see: https://tools.ietf.org/html/rfc4566#section-5.1
std::istringstream is(content);
FETCH(is, version_);
return err;
}
srs_error_t SrsSdp::parse_session_name(const std::string& content)
{
srs_error_t err = srs_success;
// @see: https://tools.ietf.org/html/rfc4566#section-5.3
// s=<session name>
session_name_ = content;
return err;
}
srs_error_t SrsSdp::parse_timing(const std::string& content)
{
srs_error_t err = srs_success;
// @see: https://tools.ietf.org/html/rfc4566#section-5.9
// t=<start-time> <stop-time>
std::istringstream is(content);
FETCH(is, start_time_);
FETCH(is, end_time_);
return err;
}
srs_error_t SrsSdp::parse_attribute(const std::string& content)
{
srs_error_t err = srs_success;
// @see: https://tools.ietf.org/html/rfc4566#section-5.13
// a=<attribute>
// a=<attribute>:<value>
std::string attribute = "";
std::string value = "";
size_t pos = content.find_first_of(":");
if (pos != std::string::npos) {
attribute = content.substr(0, pos);
value = content.substr(pos + 1);
}
if (attribute == "group") {
return parse_attr_group(value);
} else if (attribute == "msid-semantic") {
std::istringstream is(value);
FETCH(is, msid_semantic_);
std::string msid;
while (is >> msid) {
msids_.push_back(msid);
}
} else {
return session_info_.parse_attribute(attribute, value);
}
return err;
}
srs_error_t SrsSdp::parse_attr_group(const std::string& value)
{
srs_error_t err = srs_success;
// @see: https://tools.ietf.org/html/rfc5888#section-5
std::istringstream is(value);
FETCH(is, group_policy_);
std::string word;
while (is >> word) {
groups_.push_back(word);
}
return err;
}
srs_error_t SrsSdp::parse_media_description(const std::string& content)
{
srs_error_t err = srs_success;
// @see: https://tools.ietf.org/html/rfc4566#section-5.14
// m=<media> <port> <proto> <fmt> ...
// m=<media> <port>/<number of ports> <proto> <fmt> ...
std::istringstream is(content);
std::string media;
FETCH(is, media);
int port;
FETCH(is, port);
std::string proto;
FETCH(is, proto);
media_descs_.push_back(SrsMediaDesc(media));
media_descs_.back().protos_ = proto;
media_descs_.back().port_ = port;
int fmt;
while (is >> fmt) {
media_descs_.back().payload_types_.push_back(SrsMediaPayloadType(fmt));
}
if (!in_media_session_) {
in_media_session_ = true;
}
return err;
}
bool SrsSdp::is_unified() const
{
// TODO: FIXME: Maybe we should consider other situations.
return media_descs_.size() > 2;
}