Rtsp.cpp 12.3 KB
Newer Older
xiongziliang committed
1
/*
xiongziliang committed
2
 * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
xiongziliang committed
3 4 5
 *
 * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
 *
xiongziliang committed
6 7 8
 * 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.
xzl committed
9 10 11
 */

#include <stdlib.h>
xiongzilaing committed
12
#include "Rtsp.h"
xiongziliang committed
13
#include "Common/Parser.h"
xiongzilaing committed
14

xiongziliang committed
15 16
namespace mediakit{

xiongziliang committed
17 18
int RtpPayload::getClockRate(int pt){
    switch (pt){
19
#define SWITCH_CASE(name, type, value, clock_rate, channel, codec_id) case value :  return clock_rate;
xiongziliang committed
20 21 22 23 24 25 26 27
        RTP_PT_MAP(SWITCH_CASE)
#undef SWITCH_CASE
        default: return 90000;
    }
}

TrackType RtpPayload::getTrackType(int pt){
    switch (pt){
28
#define SWITCH_CASE(name, type, value, clock_rate, channel, codec_id) case value :  return type;
xiongziliang committed
29 30 31 32 33 34 35 36
        RTP_PT_MAP(SWITCH_CASE)
#undef SWITCH_CASE
        default: return TrackInvalid;
    }
}

int RtpPayload::getAudioChannel(int pt){
    switch (pt){
37
#define SWITCH_CASE(name, type, value, clock_rate, channel, codec_id) case value :  return channel;
xiongziliang committed
38 39 40 41 42 43 44 45
        RTP_PT_MAP(SWITCH_CASE)
#undef SWITCH_CASE
        default: return 1;
    }
}

const char * RtpPayload::getName(int pt){
    switch (pt){
46
#define SWITCH_CASE(name, type, value, clock_rate, channel, codec_id) case value :  return #name;
xiongziliang committed
47 48 49 50 51 52
        RTP_PT_MAP(SWITCH_CASE)
#undef SWITCH_CASE
        default: return "unknown payload type";
    }
}

53 54 55 56 57 58 59 60 61
CodecId RtpPayload::getCodecId(int pt) {
    switch (pt) {
#define SWITCH_CASE(name, type, value, clock_rate, channel, codec_id) case value :  return codec_id;
        RTP_PT_MAP(SWITCH_CASE)
#undef SWITCH_CASE
        default : return CodecInvalid;
    }
}

62
static void getAttrSdp(const map<string, string> &attr, _StrPrinter &printer){
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
    const map<string, string>::value_type *ptr = nullptr;
    for(auto &pr : attr){
        if(pr.first == "control"){
            ptr = &pr;
            continue;
        }
        if(pr.second.empty()){
            printer << "a=" << pr.first << "\r\n";
        }else{
            printer << "a=" << pr.first << ":" << pr.second << "\r\n";
        }
    }
    if(ptr){
        printer << "a=" << ptr->first << ":" << ptr->second << "\r\n";
    }
78
}
xiongziliang committed
79 80 81

string SdpTrack::getName() const{
    switch (_pt){
82
#define SWITCH_CASE(name, type, value, clock_rate, channel, codec_id) case value :  return #name;
xiongziliang committed
83 84 85 86 87 88
        RTP_PT_MAP(SWITCH_CASE)
#undef SWITCH_CASE
        default: return _codec;
    }
}

89
string SdpTrack::toString() const {
90 91 92 93 94 95 96 97 98 99 100 101 102
    _StrPrinter _printer;
    switch (_type){
        case TrackTitle:{
            _printer << "v=" << 0 << "\r\n";
            if(!_o.empty()){
                _printer << "o="<< _o << "\r\n";
            }
            if(!_c.empty()){
                _printer << "c=" << _c << "\r\n";
            }
            if(!_t.empty()){
                _printer << "t=" << _t << "\r\n";
            }
103

xiongziliang committed
104
            _printer << "s=Streamed by " << SERVER_NAME << "\r\n";
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
            getAttrSdp(_attr,_printer);
        }
            break;
        case TrackAudio:
        case TrackVideo:{
            if(_type == TrackAudio){
                _printer << "m=audio 0 RTP/AVP " << _pt << "\r\n";
            }else{
                _printer << "m=video 0 RTP/AVP " << _pt << "\r\n";
            }
            if(!_b.empty()){
                _printer << "b=" <<_b << "\r\n";
            }
            getAttrSdp(_attr,_printer);
        }
            break;
        default:
            break;
    }
    return _printer;
125
}
126 127

static TrackType toTrackType(const string &str) {
128 129 130
    if (str == "") {
        return TrackTitle;
    }
131

132 133 134
    if (str == "video") {
        return TrackVideo;
    }
135

136 137 138
    if (str == "audio") {
        return TrackAudio;
    }
139

140
    return TrackInvalid;
141 142
}

xiongziliang committed
143
void SdpParser::load(const string &sdp) {
144 145 146
    {
        _track_vec.clear();
        SdpTrack::Ptr track = std::make_shared<SdpTrack>();
xiongziliang committed
147 148
        track->_type = TrackTitle;
        _track_vec.emplace_back(track);
xiongziliang committed
149

150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
        auto lines = split(sdp, "\n");
        for (auto &line : lines) {
            trim(line);
            if (line.size() < 2 || line[1] != '=') {
                continue;
            }
            char opt = line[0];
            string opt_val = line.substr(2);
            switch (opt) {
                case 'o':
                    track->_o = opt_val;
                    break;
                case 's':
                    track->_s = opt_val;
                    break;
                case 'i':
                    track->_i = opt_val;
                    break;
                case 'c':
                    track->_c = opt_val;
                    break;
                case 't':
                    track->_t = opt_val;
                    break;
                case 'b':
                    track->_b = opt_val;
                    break;
                case 'm': {
                    track = std::make_shared<SdpTrack>();
xiongziliang committed
179 180 181 182 183
                    int pt, port;
                    char rtp[16] = {0}, type[16];
                    if (4 == sscanf(opt_val.data(), " %15[^ ] %d %15[^ ] %d", type, &port, rtp, &pt)) {
                        track->_pt = pt;
                        track->_samplerate = RtpPayload::getClockRate(pt) ;
xiongziliang committed
184
                        track->_channel = RtpPayload::getAudioChannel(pt);
xiongziliang committed
185 186 187 188 189
                        track->_type = toTrackType(type);
                        track->_m = opt_val;
                        track->_port = port;
                        _track_vec.emplace_back(track);
                    }
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
                }
                    break;
                case 'a': {
                    string attr = FindField(opt_val.data(), nullptr, ":");
                    if (attr.empty()) {
                        track->_attr[opt_val] = "";
                    } else {
                        track->_attr[attr] = FindField(opt_val.data(), ":", nullptr);
                    }
                }
                    break;
                default:
                    track->_other[opt] = opt_val;
                    break;
            }
        }
    }
xiongziliang committed
207

208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
    for (auto &track_ptr : _track_vec) {
        auto &track = *track_ptr;
        auto it = track._attr.find("range");
        if (it != track._attr.end()) {
            char name[16] = {0}, start[16] = {0}, end[16] = {0};
            int ret = sscanf(it->second.data(), "%15[^=]=%15[^-]-%15s", name, start, end);
            if (3 == ret || 2 == ret) {
                if (strcmp(start, "now") == 0) {
                    strcpy(start, "0");
                }
                track._start = atof(start);
                track._end = atof(end);
                track._duration = track._end - track._start;
            }
        }
xiongziliang committed
223

224 225 226
        it = track._attr.find("rtpmap");
        if(it != track._attr.end()){
            auto rtpmap = it->second;
xiongziliang committed
227
            int pt, samplerate, channel;
228
            char codec[16] = {0};
xiongziliang committed
229 230 231 232 233 234
            if (4 == sscanf(rtpmap.data(), "%d %15[^/]/%d/%d", &pt, codec, &samplerate, &channel)) {
                track._pt = pt;
                track._codec = codec;
                track._samplerate = samplerate;
                track._channel = channel;
            }else if (3 == sscanf(rtpmap.data(), "%d %15[^/]/%d", &pt, codec, &samplerate)) {
235 236 237 238 239
                track._pt = pt;
                track._codec = codec;
                track._samplerate = samplerate;
            }
        }
240

241 242 243 244
        it = track._attr.find("fmtp");
        if(it != track._attr.end()) {
            track._fmtp = it->second;
        }
xiongziliang committed
245

246 247 248 249 250 251 252
        it = track._attr.find("control");
        if(it != track._attr.end()) {
            track._control = it->second;
            auto surffix = string("/") + track._control;
            track._control_surffix = surffix.substr(1 + surffix.rfind('/'));
        }
    }
253
}
xiongziliang committed
254

xiongziliang committed
255
bool SdpParser::available() const {
xiongziliang committed
256 257 258
    return getTrack(TrackAudio) || getTrack(TrackVideo);
}

xiongziliang committed
259
SdpTrack::Ptr SdpParser::getTrack(TrackType type) const {
260 261 262 263 264 265
    for (auto &track : _track_vec){
        if(track->_type == type){
            return track;
        }
    }
    return nullptr;
266
}
xiongziliang committed
267

xiongziliang committed
268
vector<SdpTrack::Ptr> SdpParser::getAvailableTrack() const {
269 270 271 272 273 274 275 276 277 278 279
    vector<SdpTrack::Ptr> ret;
    bool audio_added = false;
    bool video_added = false;
    for (auto &track : _track_vec){
        if(track->_type == TrackAudio ){
            if(!audio_added){
                ret.emplace_back(track);
                audio_added = true;
            }
            continue;
        }
280

281 282 283 284 285 286 287 288
        if(track->_type == TrackVideo ){
            if(!video_added){
                ret.emplace_back(track);
                video_added = true;
            }
            continue;
        }
    }
289
    return ret;
xzl committed
290
}
xiongziliang committed
291

292
string SdpParser::toString() const {
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312
    string title,audio,video;
    for(auto &track : _track_vec){
        switch (track->_type){
            case TrackTitle:{
                title = track->toString();
            }
                break;
            case TrackVideo:{
                video = track->toString();
            }
                break;
            case TrackAudio:{
                audio = track->toString();
            }
                break;
            default:
                break;
        }
    }
    return title + video + audio;
313 314
}

315
bool RtspUrl::parse(const string &strUrl) {
316 317 318 319 320 321 322 323 324 325 326 327
    auto schema = FindField(strUrl.data(), nullptr, "://");
    bool isSSL = strcasecmp(schema.data(), "rtsps") == 0;
    //查找"://"与"/"之间的字符串,用于提取用户名密码
    auto middle_url = FindField(strUrl.data(), "://", "/");
    if (middle_url.empty()) {
        middle_url = FindField(strUrl.data(), "://", nullptr);
    }
    auto pos = middle_url.rfind('@');
    if (pos == string::npos) {
        //并没有用户名密码
        return setup(isSSL, strUrl, "", "");
    }
328

329 330 331 332 333 334 335 336 337 338
    //包含用户名密码
    auto user_pwd = middle_url.substr(0, pos);
    auto suffix = strUrl.substr(schema.size() + 3 + pos + 1);
    auto url = StrPrinter << "rtsp://" << suffix << endl;
    if (user_pwd.find(":") == string::npos) {
        return setup(isSSL, url, user_pwd, "");
    }
    auto user = FindField(user_pwd.data(), nullptr, ":");
    auto pwd = FindField(user_pwd.data(), ":", nullptr);
    return setup(isSSL, url, user, pwd);
339 340 341
}

bool RtspUrl::setup(bool isSSL, const string &strUrl, const string &strUser, const string &strPwd) {
342 343 344 345 346 347 348 349 350 351 352 353
    auto ip = FindField(strUrl.data(), "://", "/");
    if (ip.empty()) {
        ip = split(FindField(strUrl.data(), "://", NULL), "?")[0];
    }
    auto port = atoi(FindField(ip.data(), ":", NULL).data());
    if (port <= 0 || port > UINT16_MAX) {
        //rtsp 默认端口554
        port = isSSL ? 322 : 554;
    } else {
        //服务器域名
        ip = FindField(ip.data(), NULL, ":");
    }
354

355 356 357
    if (ip.empty()) {
        return false;
    }
358

359 360 361 362 363 364 365
    _url = std::move(strUrl);
    _user = std::move(strUser);
    _passwd = std::move(strPwd);
    _host = std::move(ip);
    _port = port;
    _is_ssl = isSSL;
    return true;
366 367
}

368 369 370 371
static void makeSockPair_l(std::pair<Socket::Ptr, Socket::Ptr> &pair, const string &local_ip){
    auto &pSockRtp = pair.first;
    auto &pSockRtcp = pair.second;

372 373 374 375 376 377
    if (!pSockRtp->bindUdpSock(0, local_ip.data())) {
        //分配端口失败
        throw runtime_error("open udp socket failed");
    }

    //是否是偶数
378
    bool even_numbers = pSockRtp->get_local_port() % 2 == 0;
379 380 381 382 383 384 385 386 387 388 389 390 391
    if (!pSockRtcp->bindUdpSock(pSockRtp->get_local_port() + (even_numbers ? 1 : -1), local_ip.data())) {
        //分配端口失败
        throw runtime_error("open udp socket failed");
    }

    if (!even_numbers) {
        //如果rtp端口不是偶数,那么与rtcp端口互换,目的是兼容一些要求严格的播放器或服务器
        Socket::Ptr tmp = pSockRtp;
        pSockRtp = pSockRtcp;
        pSockRtcp = tmp;
    }
}

392 393 394 395 396 397 398 399 400 401 402 403 404
 void makeSockPair(std::pair<Socket::Ptr, Socket::Ptr> &pair, const string &local_ip){
     int try_count = 0;
     while (true) {
         try {
             makeSockPair_l(pair, local_ip);
             break;
         } catch (...) {
             if (++try_count == 3) {
                 throw;
             }
             WarnL << "open udp socket failed, retry: " << try_count;
         }
     }
405
}
406

xiongziliang committed
407 408 409 410 411 412 413 414 415 416
string printSSRC(uint32_t ui32Ssrc) {
    char tmp[9] = { 0 };
    ui32Ssrc = htonl(ui32Ssrc);
    uint8_t *pSsrc = (uint8_t *) &ui32Ssrc;
    for (int i = 0; i < 4; i++) {
        sprintf(tmp + 2 * i, "%02X", pSsrc[i]);
    }
    return tmp;
}

417
}//namespace mediakit