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

#include "Util/MD5.h"
#include "Util/base64.h"
#include "RtspPusher.h"
#include "RtspSession.h"

xiongziliang committed
16 17
using namespace mediakit::Client;

xiongziliang committed
18 19
namespace mediakit {

xiongziliang committed
20 21
RtspPusher::RtspPusher(const EventPoller::Ptr &poller, const RtspMediaSource::Ptr &src) : TcpClient(poller) {
    _push_src = src;
xiongziliang committed
22 23 24 25 26 27 28 29 30
}

RtspPusher::~RtspPusher() {
    teardown();
    DebugL << endl;
}

void RtspPusher::teardown() {
    if (alive()) {
xiongziliang committed
31 32
        sendRtspRequest("TEARDOWN", _content_base);
        shutdown(SockException(Err_shutdown, "teardown"));
xiongziliang committed
33 34 35
    }

    reset();
xiongziliang committed
36 37 38 39 40 41 42 43 44 45 46 47 48
    CLEAR_ARR(_udp_socks);
    _nonce.clear();
    _realm.clear();
    _track_vec.clear();
    _session_id.clear();
    _content_base.clear();
    _session_id.clear();
    _cseq = 1;
    _publish_timer.reset();
    _beat_timer.reset();
    _rtsp_reader.reset();
    _track_vec.clear();
    _on_res_func = nullptr;
xiongziliang committed
49 50
}

xiongziliang committed
51
void RtspPusher::publish(const string &url_str) {
52
    RtspUrl url;
xiongziliang committed
53 54
    if (!url.parse(url_str)) {
        onPublishResult(SockException(Err_other, StrPrinter << "illegal rtsp url:" << url_str), false);
xiongziliang committed
55 56
        return;
    }
xiongziliang committed
57

xiongziliang committed
58 59
    teardown();

60 61
    if (url._user.size()) {
        (*this)[kRtspUser] = url._user;
xiongziliang committed
62
    }
63 64
    if (url._passwd.size()) {
        (*this)[kRtspPwd] = url._passwd;
xiongziliang committed
65
        (*this)[kRtspPwdIsMD5] = false;
xiongziliang committed
66 67
    }

xiongziliang committed
68 69 70 71
    _url = url_str;
    _rtp_type = (Rtsp::eRtpType) (int) (*this)[kRtpType];
    DebugL << url._url << " " << (url._user.size() ? url._user : "null") << " "
           << (url._passwd.size() ? url._passwd : "null") << " " << _rtp_type;
xiongziliang committed
72

xiongziliang committed
73 74 75 76 77
    weak_ptr<RtspPusher> weak_self = dynamic_pointer_cast<RtspPusher>(shared_from_this());
    float publish_timeout_sec = (*this)[kTimeoutMS].as<int>() / 1000.0;
    _publish_timer.reset(new Timer(publish_timeout_sec, [weak_self]() {
        auto strong_self = weak_self.lock();
        if (!strong_self) {
xiongziliang committed
78 79
            return false;
        }
xiongziliang committed
80
        strong_self->onPublishResult(SockException(Err_timeout, "publish rtsp timeout"), false);
xiongziliang committed
81
        return false;
xiongziliang committed
82
    }, getPoller()));
xiongziliang committed
83

xiongziliang committed
84
    if (!(*this)[kNetAdapter].empty()) {
xiongziliang committed
85 86
        setNetAdapter((*this)[kNetAdapter]);
    }
87

xiongziliang committed
88
    startConnect(url._host, url._port, publish_timeout_sec);
89 90
}

xiongziliang committed
91 92 93 94 95 96
void RtspPusher::onPublishResult(const SockException &ex, bool handshake_done) {
    if (ex.getErrCode() == Err_shutdown) {
        //主动shutdown的,不触发回调
        return;
    }
    if (!handshake_done) {
97
        //播放结果回调
xiongziliang committed
98 99 100
        _publish_timer.reset();
        if (_on_published) {
            _on_published(ex);
101 102 103
        }
    } else {
        //播放成功后异常断开回调
xiongziliang committed
104 105
        if (_on_shutdown) {
            _on_shutdown(ex);
106 107 108
        }
    }

xiongziliang committed
109
    if (ex) {
110 111
        teardown();
    }
xiongziliang committed
112 113 114
}

void RtspPusher::onErr(const SockException &ex) {
115
    //定时器_pPublishTimer为空后表明握手结束了
xiongziliang committed
116
    onPublishResult(ex, !_publish_timer);
xiongziliang committed
117 118 119
}

void RtspPusher::onConnect(const SockException &err) {
xiongziliang committed
120 121
    if (err) {
        onPublishResult(err, false);
xiongziliang committed
122 123
        return;
    }
124
    //推流器不需要多大的接收缓存,节省内存占用
125
    getSock()->setReadBuffer(std::make_shared<BufferRaw>(1 * 1024));
xiongziliang committed
126 127 128
    sendAnnounce();
}

xiongziliang committed
129
void RtspPusher::onRecv(const Buffer::Ptr &buf){
xiongziliang committed
130
    try {
xiongziliang committed
131
        input(buf->data(), buf->size());
xiongziliang committed
132 133
    } catch (exception &e) {
        SockException ex(Err_other, e.what());
134
        //定时器_pPublishTimer为空后表明握手结束了
xiongziliang committed
135
        onPublishResult(ex, !_publish_timer);
xiongziliang committed
136 137 138 139
    }
}

void RtspPusher::onWholeRtspPacket(Parser &parser) {
xiongziliang committed
140 141 142 143
    decltype(_on_res_func) func;
    _on_res_func.swap(func);
    if (func) {
        func(parser);
xiongziliang committed
144 145 146 147
    }
    parser.Clear();
}

xiongziliang committed
148
void RtspPusher::sendAnnounce() {
xiongziliang committed
149
    auto src = _push_src.lock();
xiongziliang committed
150 151 152 153
    if (!src) {
        throw std::runtime_error("the media source was released");
    }
    //解析sdp
xiongziliang committed
154 155
    _sdp_parser.load(src->getSdp());
    _track_vec = _sdp_parser.getAvailableTrack();
xiongziliang committed
156

xiongziliang committed
157
    if (_track_vec.empty()) {
xiongziliang committed
158 159 160
        throw std::runtime_error("无有效的Sdp Track");
    }

xiongziliang committed
161 162
    _on_res_func = std::bind(&RtspPusher::handleResAnnounce, this, placeholders::_1);
    sendRtspRequest("ANNOUNCE", _url, {}, src->getSdp());
xiongziliang committed
163 164 165 166 167 168 169 170 171
}

void RtspPusher::handleResAnnounce(const Parser &parser) {
    string authInfo = parser["WWW-Authenticate"];
    //发送DESCRIBE命令后的回复
    if ((parser.Url() == "401") && handleAuthenticationFailure(authInfo)) {
        sendAnnounce();
        return;
    }
xiongziliang committed
172
    if (parser.Url() == "302") {
xiongziliang committed
173
        auto newUrl = parser["Location"];
xiongziliang committed
174
        if (newUrl.empty()) {
xiongziliang committed
175 176 177 178 179 180 181 182
            throw std::runtime_error("未找到Location字段(跳转url)");
        }
        publish(newUrl);
        return;
    }
    if (parser.Url() != "200") {
        throw std::runtime_error(StrPrinter << "ANNOUNCE:" << parser.Url() << " " << parser.Tail());
    }
xiongziliang committed
183
    _content_base = parser["Content-Base"];
xiongziliang committed
184

xiongziliang committed
185 186
    if (_content_base.empty()) {
        _content_base = _url;
xiongziliang committed
187
    }
xiongziliang committed
188 189
    if (_content_base.back() == '/') {
        _content_base.pop_back();
xiongziliang committed
190 191 192 193 194
    }

    sendSetup(0);
}

xiongziliang committed
195 196
bool RtspPusher::handleAuthenticationFailure(const string &params_str) {
    if (!_realm.empty()) {
xiongziliang committed
197 198 199 200
        //已经认证过了
        return false;
    }

xiongziliang committed
201 202 203 204
    char *realm = new char[params_str.size()];
    char *nonce = new char[params_str.size()];
    char *stale = new char[params_str.size()];
    onceToken token(nullptr, [&]() {
xiongziliang committed
205 206 207 208 209
        delete[] realm;
        delete[] nonce;
        delete[] stale;
    });

xiongziliang committed
210 211 212
    if (sscanf(params_str.data(), "Digest realm=\"%[^\"]\", nonce=\"%[^\"]\", stale=%[a-zA-Z]", realm, nonce, stale) == 3) {
        _realm = (const char *) realm;
        _nonce = (const char *) nonce;
xiongziliang committed
213 214
        return true;
    }
xiongziliang committed
215 216 217
    if (sscanf(params_str.data(), "Digest realm=\"%[^\"]\", nonce=\"%[^\"]\"", realm, nonce) == 2) {
        _realm = (const char *) realm;
        _nonce = (const char *) nonce;
xiongziliang committed
218 219
        return true;
    }
xiongziliang committed
220 221
    if (sscanf(params_str.data(), "Basic realm=\"%[^\"]\"", realm) == 1) {
        _realm = (const char *) realm;
xiongziliang committed
222 223 224 225 226
        return true;
    }
    return false;
}

xiongziliang committed
227
//有必要的情况下创建udp端口
228
void RtspPusher::createUdpSockIfNecessary(int track_idx){
xiongziliang committed
229 230
    auto &rtp_sock = _udp_socks[track_idx];
    if (!rtp_sock) {
231
        rtp_sock = createSocket();
xiongziliang committed
232
        //rtp随机端口
xiongziliang committed
233 234
        if (!rtp_sock->bindUdpSock(0, get_local_ip().data())) {
            rtp_sock.reset();
235 236 237 238 239
            throw std::runtime_error("open rtp sock failed");
        }
    }
}

xiongziliang committed
240 241 242 243 244
void RtspPusher::sendSetup(unsigned int track_idx) {
    _on_res_func = std::bind(&RtspPusher::handleResSetup, this, placeholders::_1, track_idx);
    auto &track = _track_vec[track_idx];
    auto base_url = _content_base + "/" + track->_control_surffix;
    switch (_rtp_type) {
xiongziliang committed
245
        case Rtsp::RTP_TCP: {
xiongziliang committed
246 247 248
            sendRtspRequest("SETUP", base_url, {"Transport",
                                                StrPrinter << "RTP/AVP/TCP;unicast;interleaved=" << track->_type * 2
                                                           << "-" << track->_type * 2 + 1});
xiongziliang committed
249
        }
xiongziliang committed
250
            break;
xiongziliang committed
251
        case Rtsp::RTP_UDP: {
xiongziliang committed
252 253 254 255
            createUdpSockIfNecessary(track_idx);
            int port = _udp_socks[track_idx]->get_local_port();
            sendRtspRequest("SETUP", base_url,
                            {"Transport", StrPrinter << "RTP/AVP;unicast;client_port=" << port << "-" << port + 1});
xiongziliang committed
256
        }
xiongziliang committed
257
            break;
xiongziliang committed
258
        default:
xiongziliang committed
259
            break;
xiongziliang committed
260 261 262
    }
}

263

xiongziliang committed
264
void RtspPusher::handleResSetup(const Parser &parser, unsigned int track_idx) {
xiongziliang committed
265
    if (parser.Url() != "200") {
xiongziliang committed
266
        throw std::runtime_error(StrPrinter << "SETUP:" << parser.Url() << " " << parser.Tail() << endl);
xiongziliang committed
267
    }
xiongziliang committed
268 269 270 271
    if (track_idx == 0) {
        _session_id = parser["Session"];
        _session_id.append(";");
        _session_id = FindField(_session_id.data(), nullptr, ";");
xiongziliang committed
272 273
    }

xiongziliang committed
274 275 276 277 278 279
    auto transport = parser["Transport"];
    if (transport.find("TCP") != string::npos || transport.find("interleaved") != string::npos) {
        _rtp_type = Rtsp::RTP_TCP;
        string interleaved = FindField(FindField((transport + ";").data(), "interleaved=", ";").data(), NULL, "-");
        _track_vec[track_idx]->_interleaved = atoi(interleaved.data());
    } else if (transport.find("multicast") != string::npos) {
xiongziliang committed
280
        throw std::runtime_error("SETUP rtsp pusher can not support multicast!");
xiongziliang committed
281 282 283 284 285
    } else {
        _rtp_type = Rtsp::RTP_UDP;
        createUdpSockIfNecessary(track_idx);
        const char *strPos = "server_port=";
        auto port_str = FindField((transport + ";").data(), strPos, ";");
xiongziliang committed
286 287 288 289 290
        uint16_t port = atoi(FindField(port_str.data(), NULL, "-").data());
        struct sockaddr_in rtpto;
        rtpto.sin_port = ntohs(port);
        rtpto.sin_family = AF_INET;
        rtpto.sin_addr.s_addr = inet_addr(get_peer_ip().data());
xiongziliang committed
291
        _udp_socks[track_idx]->setSendPeerAddr((struct sockaddr *) &(rtpto));
xiongziliang committed
292 293
    }

xiongziliang committed
294
    RtspSplitter::enableRecvRtp(_rtp_type == Rtsp::RTP_TCP);
xiongziliang committed
295

xiongziliang committed
296
    if (track_idx < _track_vec.size() - 1) {
xiongziliang committed
297
        //需要继续发送SETUP命令
xiongziliang committed
298
        sendSetup(track_idx + 1);
xiongziliang committed
299 300 301 302 303 304
        return;
    }

    sendRecord();
}

xiongziliang committed
305
void RtspPusher::sendOptions() {
xiongziliang committed
306 307
    _on_res_func = [this](const Parser &parser) {};
    sendRtspRequest("OPTIONS", _content_base);
xiongziliang committed
308 309
}

310
inline void RtspPusher::sendRtpPacket(const RtspMediaSource::RingDataType &pkt) {
xiongziliang committed
311
    switch (_rtp_type) {
xiongziliang committed
312
        case Rtsp::RTP_TCP: {
313 314 315 316 317 318 319 320 321 322
            int i = 0;
            int size = pkt->size();
            setSendFlushFlag(false);
            pkt->for_each([&](const RtpPacket::Ptr &rtp) {
                if (++i == size) {
                    setSendFlushFlag(true);
                }
                BufferRtp::Ptr buffer(new BufferRtp(rtp));
                send(buffer);
            });
xiongziliang committed
323
            break;
xiongziliang committed
324 325
        }

xiongziliang committed
326
        case Rtsp::RTP_UDP: {
327 328 329 330
            int i = 0;
            int size = pkt->size();
            pkt->for_each([&](const RtpPacket::Ptr &rtp) {
                int iTrackIndex = getTrackIndexByTrackType(rtp->type);
xiongziliang committed
331
                auto &pSock = _udp_socks[iTrackIndex];
332
                if (!pSock) {
xiongziliang committed
333
                    shutdown(SockException(Err_shutdown, "udp sock not opened yet"));
334 335 336
                    return;
                }

xiongziliang committed
337
                BufferRtp::Ptr buffer(new BufferRtp(rtp, 4));
338 339
                pSock->send(buffer, nullptr, 0, ++i == size);
            });
xiongziliang committed
340
            break;
xiongziliang committed
341 342
        }
        default : break;
xiongziliang committed
343 344 345 346
    }
}

inline int RtspPusher::getTrackIndexByTrackType(TrackType type) {
xiongziliang committed
347 348
    for (unsigned int i = 0; i < _track_vec.size(); i++) {
        if (type == _track_vec[i]->_type) {
xiongziliang committed
349 350 351
            return i;
        }
    }
xiongziliang committed
352
    if (_track_vec.size() == 1) {
xiongziliang committed
353 354
        return 0;
    }
355
    throw SockException(Err_shutdown, StrPrinter << "no such track with type:" << (int) type);
xiongziliang committed
356 357
}

xiongziliang committed
358
void RtspPusher::sendRecord() {
xiongziliang committed
359 360
    _on_res_func = [this](const Parser &parser) {
        auto src = _push_src.lock();
xiongziliang committed
361 362 363 364
        if (!src) {
            throw std::runtime_error("the media source was released");
        }

xiongziliang committed
365 366 367 368 369
        _rtsp_reader = src->getRing()->attach(getPoller());
        weak_ptr<RtspPusher> weak_self = dynamic_pointer_cast<RtspPusher>(shared_from_this());
        _rtsp_reader->setReadCB([weak_self](const RtspMediaSource::RingDataType &pkt) {
            auto strong_self = weak_self.lock();
            if (!strong_self) {
xiongziliang committed
370 371
                return;
            }
xiongziliang committed
372
            strong_self->sendRtpPacket(pkt);
xiongziliang committed
373
        });
xiongziliang committed
374 375 376 377
        _rtsp_reader->setDetachCB([weak_self]() {
            auto strong_self = weak_self.lock();
            if (strong_self) {
                strong_self->onPublishResult(SockException(Err_other, "媒体源被释放"), !strong_self->_publish_timer);
xiongziliang committed
378 379
            }
        });
xiongziliang committed
380
        if (_rtp_type != Rtsp::RTP_TCP) {
xiongziliang committed
381
            /////////////////////////心跳/////////////////////////////////
xiongziliang committed
382 383 384
            _beat_timer.reset(new Timer((*this)[kBeatIntervalMS].as<int>() / 1000.0, [weak_self]() {
                auto strong_self = weak_self.lock();
                if (!strong_self) {
xiongziliang committed
385 386
                    return false;
                }
xiongziliang committed
387
                strong_self->sendOptions();
xiongziliang committed
388
                return true;
xiongziliang committed
389
            }, getPoller()));
xiongziliang committed
390
        }
xiongziliang committed
391
        onPublishResult(SockException(Err_success, "success"), false);
392 393
        //提升发送性能
        setSocketFlags();
xiongziliang committed
394
    };
xiongziliang committed
395
    sendRtspRequest("RECORD", _content_base, {"Range", "npt=0.000-"});
xiongziliang committed
396 397
}

398
void RtspPusher::setSocketFlags(){
xiongziliang committed
399 400
    GET_CONFIG(int, merge_write_ms, General::kMergeWriteMS);
    if (merge_write_ms > 0) {
401
        //提高发送性能
xiongziliang committed
402
        setSendFlags(SOCKET_DEFAULE_FLAGS | FLAG_MORE);
403
        SockUtil::setNoDelay(getSock()->rawFD(), false);
404 405 406
    }
}

xiongziliang committed
407
void RtspPusher::sendRtspRequest(const string &cmd, const string &url, const std::initializer_list<string> &header,const string &sdp ) {
xiongziliang committed
408 409 410
    string key;
    StrCaseMap header_map;
    int i = 0;
xiongziliang committed
411 412 413 414
    for (auto &val : header) {
        if (++i % 2 == 0) {
            header_map.emplace(key, val);
        } else {
xiongziliang committed
415 416 417
            key = val;
        }
    }
xiongziliang committed
418
    sendRtspRequest(cmd, url, header_map, sdp);
xiongziliang committed
419
}
xiongziliang committed
420
void RtspPusher::sendRtspRequest(const string &cmd, const string &url,const StrCaseMap &header_const,const string &sdp ) {
xiongziliang committed
421
    auto header = header_const;
xiongziliang committed
422 423
    header.emplace("CSeq", StrPrinter << _cseq++);
    header.emplace("User-Agent", SERVER_NAME);
xiongziliang committed
424

xiongziliang committed
425 426
    if (!_session_id.empty()) {
        header.emplace("Session", _session_id);
xiongziliang committed
427 428
    }

xiongziliang committed
429 430
    if (!_realm.empty() && !(*this)[kRtspUser].empty()) {
        if (!_nonce.empty()) {
xiongziliang committed
431 432 433 434 435 436 437 438 439 440
            //MD5认证
            /*
            response计算方法如下:
            RTSP客户端应该使用username + password并计算response如下:
            (1)当password为MD5编码,则
                response = md5( password:nonce:md5(public_method:url)  );
            (2)当password为ANSI字符串,则
                response= md5( md5(username:realm:password):nonce:md5(public_method:url) );
             */
            string encrypted_pwd = (*this)[kRtspPwd];
xiongziliang committed
441 442
            if (!(*this)[kRtspPwdIsMD5].as<bool>()) {
                encrypted_pwd = MD5((*this)[kRtspUser] + ":" + _realm + ":" + encrypted_pwd).hexdigest();
xiongziliang committed
443
            }
xiongziliang committed
444
            auto response = MD5(encrypted_pwd + ":" + _nonce + ":" + MD5(cmd + ":" + url).hexdigest()).hexdigest();
xiongziliang committed
445 446 447
            _StrPrinter printer;
            printer << "Digest ";
            printer << "username=\"" << (*this)[kRtspUser] << "\", ";
xiongziliang committed
448 449
            printer << "realm=\"" << _realm << "\", ";
            printer << "nonce=\"" << _nonce << "\", ";
xiongziliang committed
450 451
            printer << "uri=\"" << url << "\", ";
            printer << "response=\"" << response << "\"";
xiongziliang committed
452 453
            header.emplace("Authorization", printer);
        } else if (!(*this)[kRtspPwdIsMD5].as<bool>()) {
xiongziliang committed
454 455 456
            //base64认证
            string authStr = StrPrinter << (*this)[kRtspUser] << ":" << (*this)[kRtspPwd];
            char authStrBase64[1024] = {0};
xiongziliang committed
457 458
            av_base64_encode(authStrBase64, sizeof(authStrBase64), (uint8_t *) authStr.data(), authStr.size());
            header.emplace("Authorization", StrPrinter << "Basic " << authStrBase64);
xiongziliang committed
459 460 461
        }
    }

xiongziliang committed
462 463 464
    if (!sdp.empty()) {
        header.emplace("Content-Length", StrPrinter << sdp.size());
        header.emplace("Content-Type", "application/sdp");
xiongziliang committed
465 466 467 468
    }

    _StrPrinter printer;
    printer << cmd << " " << url << " RTSP/1.0\r\n";
xiongziliang committed
469
    for (auto &pr : header) {
xiongziliang committed
470 471 472 473 474
        printer << pr.first << ": " << pr.second << "\r\n";
    }

    printer << "\r\n";

xiongziliang committed
475
    if (!sdp.empty()) {
xiongziliang committed
476 477
        printer << sdp;
    }
xiongziliang committed
478
    SockSender::send(printer);
xiongziliang committed
479 480 481 482
}


} /* namespace mediakit */