/** * 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 using namespace std; #include #include #include #include #include #include #include // 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 split_str(const std::string& str, const std::string& delim) { std::vector 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 vec = split_str(fmtp, ";"); for (size_t i = 0; i < vec.size(); ++i) { std::vector 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& 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::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 SrsMediaDesc::find_media_with_encoding_name(const std::string& encoding_name) const { std::vector 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::update_msid(string id) { srs_error_t err = srs_success; for(vector::iterator it = ssrc_infos_.begin(); it != ssrc_infos_.end(); ++it) { SrsSSRCInfo& info = *it; info.msid_ = id; info.mslabel_ = id; } return err; } 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::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::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::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::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::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: / [/] 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 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: 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 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: ... 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 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 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::iterator iter = groups_.begin(); iter != groups_.end(); ++iter) { os << " " << *iter; } os << kCRLF; } os << "a=msid-semantic: " << msid_semantic_; for (std::vector::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::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::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::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::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::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::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::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::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::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::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::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= // 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_ = 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= 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= // a=: 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= ... // m= / ... 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; } srs_error_t SrsSdp::update_msid(string id) { srs_error_t err = srs_success; msids_.clear(); msids_.push_back(id); for (vector::iterator it = media_descs_.begin(); it != media_descs_.end(); ++it) { SrsMediaDesc& desc = *it; if ((err = desc.update_msid(id)) != srs_success) { return srs_error_wrap(err, "desc %s update msid %s", desc.mid_.c_str(), id.c_str()); } } return err; }