RtspPlayer.cpp 26 KB
Newer Older
xiongziliang committed
1
/*
xiongziliang committed
2 3
 * MIT License
 *
xiongziliang committed
4
 * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com>
5
 * Copyright (c) 2018 huohuo <913481084@qq.com>
xiongziliang committed
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 *
 * This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
 *
 * 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.
 */
xzl committed
27 28 29 30
#include <set>
#include <cmath>
#include <stdarg.h>
#include <algorithm>
xiongziliang committed
31
#include <iomanip>
xzl committed
32

xiongziliang committed
33
#include "Common/config.h"
xiongzilaing committed
34
#include "RtspPlayer.h"
35
#include "Util/MD5.h"
xiongzilaing committed
36
#include "Util/mini.h"
37
#include "Util/util.h"
xiongziliang committed
38
#include "Util/base64.h"
xzl committed
39
#include "Network/sockutil.h"
xiongziliang committed
40
using namespace toolkit;
xiongziliang committed
41
using namespace mediakit::Client;
xzl committed
42

xiongziliang committed
43
namespace mediakit {
xzl committed
44

45 46 47 48 49 50
enum PlayType {
	type_play = 0,
	type_pause,
	type_seek
};

51
RtspPlayer::RtspPlayer(const EventPoller::Ptr &poller) : TcpClient(poller){
xiongziliang committed
52
	RtpReceiver::setPoolSize(64);
xzl committed
53 54
}
RtspPlayer::~RtspPlayer(void) {
55
    DebugL << endl;
xzl committed
56 57 58
}
void RtspPlayer::teardown(){
	if (alive()) {
59
		sendRtspRequest("TEARDOWN" ,_strContentBase);
xiongziliang committed
60
		shutdown(SockException(Err_shutdown,"teardown"));
xzl committed
61
	}
62

xiongziliang committed
63 64
	_rtspMd5Nonce.clear();
	_rtspRealm.clear();
xiongziliang committed
65
	_aTrackInfo.clear();
66 67
    _strSession.clear();
    _strContentBase.clear();
xiongziliang committed
68 69
	RtpReceiver::clear();

70 71
    CLEAR_ARR(_apRtpSock);
    CLEAR_ARR(_apRtcpSock);
72 73 74 75 76
    CLEAR_ARR(_aui16FirstSeq)
    CLEAR_ARR(_aui64RtpRecv)
    CLEAR_ARR(_aui64RtpRecv)
    CLEAR_ARR(_aui16NowSeq)

xiongziliang committed
77
	_pPlayTimer.reset();
78
    _pRtpTimer.reset();
xiongziliang committed
79 80
	_uiCseq = 1;
	_onHandshake = nullptr;
xzl committed
81
}
xiongziliang committed
82

xiongziliang committed
83
void RtspPlayer::play(const string &strUrl){
84 85 86
	RtspUrl url;
	if(!url.parse(strUrl)){
		onPlayResult_l(SockException(Err_other,StrPrinter << "illegal rtsp url:" << strUrl),false);
xiongziliang committed
87 88
		return;
	}
89

xzl committed
90
	teardown();
91

92 93
	if (url._user.size()) {
		(*this)[kRtspUser] = url._user;
xzl committed
94
	}
95 96 97
	if (url._passwd.size()) {
		(*this)[kRtspPwd] = url._passwd;
		(*this)[kRtspPwdIsMD5] = false;
xiongziliang committed
98 99
	}

100 101 102
	_strUrl = url._url;
	_eType = (Rtsp::eRtpType)(int)(*this)[kRtpType];
	DebugL << url._url << " " << (url._user.size() ? url._user : "null") << " " << (url._passwd.size() ? url._passwd : "null") << " " << _eType;
xzl committed
103 104

	weak_ptr<RtspPlayer> weakSelf = dynamic_pointer_cast<RtspPlayer>(shared_from_this());
xiongziliang committed
105
	float playTimeOutSec = (*this)[kTimeoutMS].as<int>() / 1000.0;
106
	_pPlayTimer.reset( new Timer(playTimeOutSec,  [weakSelf]() {
xzl committed
107 108 109 110
		auto strongSelf=weakSelf.lock();
		if(!strongSelf) {
			return false;
		}
111
		strongSelf->onPlayResult_l(SockException(Err_timeout,"play rtsp timeout"),false);
xzl committed
112
		return false;
113
	},getPoller()));
114

xiongziliang committed
115 116
	if(!(*this)[kNetAdapter].empty()){
		setNetAdapter((*this)[kNetAdapter]);
xiongziliang committed
117
	}
118
	startConnect(url._host, url._port, playTimeOutSec);
119
}
120

121
void RtspPlayer::onConnect(const SockException &err){
122 123
	if(err.getErrCode() != Err_success) {
		onPlayResult_l(err,false);
124 125 126 127
		return;
	}

	sendDescribe();
xzl committed
128 129
}

130
void RtspPlayer::onRecv(const Buffer::Ptr& pBuf) {
131
    input(pBuf->data(),pBuf->size());
xzl committed
132 133
}
void RtspPlayer::onErr(const SockException &ex) {
134 135
	//定时器_pPlayTimer为空后表明握手结束了
	onPlayResult_l(ex,!_pPlayTimer);
xzl committed
136
}
137 138
// from live555
bool RtspPlayer::handleAuthenticationFailure(const string &paramsStr) {
xiongziliang committed
139
    if(!_rtspRealm.empty()){
140 141 142 143
        //已经认证过了
        return false;
    }

xiongziliang committed
144 145 146 147 148 149 150 151
    char *realm = new char[paramsStr.size()];
    char *nonce = new char[paramsStr.size()];
    char *stale = new char[paramsStr.size()];
    onceToken token(nullptr,[&](){
        delete[] realm;
        delete[] nonce;
        delete[] stale;
    });
152 153

    if (sscanf(paramsStr.data(), "Digest realm=\"%[^\"]\", nonce=\"%[^\"]\", stale=%[a-zA-Z]", realm, nonce, stale) == 3) {
xiongziliang committed
154 155
        _rtspRealm = (const char *)realm;
        _rtspMd5Nonce = (const char *)nonce;
156 157 158
        return true;
    }
    if (sscanf(paramsStr.data(), "Digest realm=\"%[^\"]\", nonce=\"%[^\"]\"", realm, nonce) == 2) {
xiongziliang committed
159 160
        _rtspRealm = (const char *)realm;
        _rtspMd5Nonce = (const char *)nonce;
161 162 163
        return true;
    }
    if (sscanf(paramsStr.data(), "Basic realm=\"%[^\"]\"", realm) == 1) {
xiongziliang committed
164
        _rtspRealm = (const char *)realm;
165 166 167 168 169 170
        return true;
    }
    return false;
}
void RtspPlayer::handleResDESCRIBE(const Parser& parser) {
	string authInfo = parser["WWW-Authenticate"];
xzl committed
171
	//发送DESCRIBE命令后的回复
172 173 174 175
	if ((parser.Url() == "401") && handleAuthenticationFailure(authInfo)) {
		sendDescribe();
		return;
	}
176
	if(parser.Url() == "302" || parser.Url() == "301"){
177 178 179 180
		auto newUrl = parser["Location"];
		if(newUrl.empty()){
			throw std::runtime_error("未找到Location字段(跳转url)");
		}
xiongziliang committed
181
		play(newUrl);
182 183
		return;
	}
xzl committed
184 185 186 187
	if (parser.Url() != "200") {
		throw std::runtime_error(
		StrPrinter << "DESCRIBE:" << parser.Url() << " " << parser.Tail() << endl);
	}
188
	_strContentBase = parser["Content-Base"];
189

190 191
    if(_strContentBase.empty()){
        _strContentBase = _strUrl;
192
    }
193 194
    if (_strContentBase.back() == '/') {
        _strContentBase.pop_back();
195 196
    }

197
	SdpParser sdpParser(parser.Content());
xzl committed
198
	//解析sdp
199
	_aTrackInfo = sdpParser.getAvailableTrack();
xiongziliang committed
200 201 202 203 204 205 206 207 208 209
	auto title = sdpParser.getTrack(TrackTitle);
	bool isPlayback = false;
	if(title && title->_duration ){
        isPlayback = true;
	}

    for(auto &stamp : _stamp){
        stamp.setPlayBack(isPlayback);
        stamp.setRelativeStamp(0);
    }
xiongziliang committed
210 211

	if (_aTrackInfo.empty()) {
xiongziliang committed
212
		throw std::runtime_error("无有效的Sdp Track");
xzl committed
213
	}
214
	if (!onCheckSDP(sdpParser.toString())) {
xzl committed
215 216
		throw std::runtime_error("onCheckSDP faied");
	}
xiongziliang committed
217

xzl committed
218 219
	sendSetup(0);
}
220

xiongziliang committed
221
//有必要的情况下创建udp端口
222 223 224 225 226
void RtspPlayer::createUdpSockIfNecessary(int track_idx){
	auto &rtpSockRef = _apRtpSock[track_idx];
	auto &rtcpSockRef = _apRtcpSock[track_idx];
	if(!rtpSockRef){
		rtpSockRef.reset(new Socket(getPoller()));
xiongziliang committed
227
		//rtp随机端口
228 229 230 231 232 233 234 235
		if (!rtpSockRef->bindUdpSock(0, get_local_ip().data())) {
			rtpSockRef.reset();
			throw std::runtime_error("open rtp sock failed");
		}
	}

	if(!rtcpSockRef){
		rtcpSockRef.reset(new Socket(getPoller()));
xiongziliang committed
236
		//rtcp端口为rtp端口+1,目的是为了兼容某些服务器,其实更推荐随机端口
237 238 239 240 241
		if (!rtcpSockRef->bindUdpSock(rtpSockRef->get_local_port() + 1, get_local_ip().data())) {
			rtcpSockRef.reset();
			throw std::runtime_error("open rtcp sock failed");
		}
	}
242 243 244 245 246 247 248

	if(rtpSockRef->get_local_port() % 2 != 0){
		//如果rtp端口不是偶数,那么与rtcp端口互换,目的是兼容一些要求严格的服务器
		Socket::Ptr tmp = rtpSockRef;
		rtpSockRef = rtcpSockRef;
		rtcpSockRef = tmp;
	}
249 250 251
}


xzl committed
252
//发送SETUP命令
253
void RtspPlayer::sendSetup(unsigned int trackIndex) {
254 255
    _onHandshake = std::bind(&RtspPlayer::handleResSETUP,this, placeholders::_1,trackIndex);
    auto &track = _aTrackInfo[trackIndex];
xiongziliang committed
256
	auto baseUrl = _strContentBase + "/" + track->_control_surffix;
257
	switch (_eType) {
xiongziliang committed
258
		case Rtsp::RTP_TCP: {
259
			sendRtspRequest("SETUP",baseUrl,{"Transport",StrPrinter << "RTP/AVP/TCP;unicast;interleaved=" << track->_type * 2 << "-" << track->_type * 2 + 1});
xzl committed
260
		}
261
			break;
xiongziliang committed
262
		case Rtsp::RTP_MULTICAST: {
263
			sendRtspRequest("SETUP",baseUrl,{"Transport","Transport: RTP/AVP;multicast"});
264
		}
265
			break;
xiongziliang committed
266
		case Rtsp::RTP_UDP: {
267
			createUdpSockIfNecessary(trackIndex);
268 269 270 271
			sendRtspRequest("SETUP",baseUrl,{"Transport",
                                    StrPrinter << "RTP/AVP;unicast;client_port="
                                    << _apRtpSock[trackIndex]->get_local_port() << "-"
                                    << _apRtcpSock[trackIndex]->get_local_port()});
272
		}
273
			break;
274
		default:
275
			break;
xzl committed
276 277 278
	}
}

279
void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int uiTrackIndex) {
xzl committed
280 281 282 283 284
	if (parser.Url() != "200") {
		throw std::runtime_error(
		StrPrinter << "SETUP:" << parser.Url() << " " << parser.Tail() << endl);
	}
	if (uiTrackIndex == 0) {
285 286 287
		_strSession = parser["Session"];
        _strSession.append(";");
        _strSession = FindField(_strSession.data(), nullptr, ";");
xzl committed
288 289 290
	}

	auto strTransport = parser["Transport"];
291
	if(strTransport.find("TCP") != string::npos || strTransport.find("interleaved") != string::npos){
xiongziliang committed
292
		_eType = Rtsp::RTP_TCP;
xzl committed
293
	}else if(strTransport.find("multicast") != string::npos){
xiongziliang committed
294
		_eType = Rtsp::RTP_MULTICAST;
xzl committed
295
	}else{
xiongziliang committed
296
		_eType = Rtsp::RTP_UDP;
xzl committed
297 298
	}

xiongziliang committed
299
	RtspSplitter::enableRecvRtp(_eType == Rtsp::RTP_TCP);
300

xiongziliang committed
301 302 303
	if(_eType == Rtsp::RTP_TCP)  {
		string interleaved = FindField( FindField((strTransport + ";").data(), "interleaved=", ";").data(), NULL, "-");
		_aTrackInfo[uiTrackIndex]->_interleaved = atoi(interleaved.data());
xzl committed
304
	}else{
xiongziliang committed
305 306
		const char *strPos = (_eType == Rtsp::RTP_MULTICAST ? "port=" : "server_port=") ;
		auto port_str = FindField((strTransport + ";").data(), strPos, ";");
307 308 309 310
		uint16_t rtp_port = atoi(FindField(port_str.data(), NULL, "-").data());
        uint16_t rtcp_port = atoi(FindField(port_str.data(), "-",NULL).data());
        auto &pRtpSockRef = _apRtpSock[uiTrackIndex];
        auto &pRtcpSockRef = _apRtcpSock[uiTrackIndex];
xiongziliang committed
311

xiongziliang committed
312
		if (_eType == Rtsp::RTP_MULTICAST) {
313
		    //udp组播
xiongziliang committed
314
			auto multiAddr = FindField((strTransport + ";").data(), "destination=", ";");
315
            pRtpSockRef.reset(new Socket(getPoller()));
316
			if (!pRtpSockRef->bindUdpSock(rtp_port, multiAddr.data())) {
317
				pRtpSockRef.reset();
xzl committed
318 319
				throw std::runtime_error("open udp sock err");
			}
320
			auto fd = pRtpSockRef->rawFD();
xzl committed
321 322 323 324
			if (-1 == SockUtil::joinMultiAddrFilter(fd, multiAddr.data(), get_peer_ip().data(),get_local_ip().data())) {
				SockUtil::joinMultiAddr(fd, multiAddr.data(),get_local_ip().data());
			}
		} else {
325 326
			createUdpSockIfNecessary(uiTrackIndex);
			//udp单播
xzl committed
327
			struct sockaddr_in rtpto;
328
			rtpto.sin_port = ntohs(rtp_port);
xzl committed
329
			rtpto.sin_family = AF_INET;
xiongziliang committed
330
			rtpto.sin_addr.s_addr = inet_addr(get_peer_ip().data());
331 332 333 334 335 336 337 338 339
			pRtpSockRef->setSendPeerAddr((struct sockaddr *)&(rtpto));
			//发送rtp打洞包
			pRtpSockRef->send("\xce\xfa\xed\xfe", 4);

			//设置rtcp发送目标,为后续发送rtcp做准备
            rtpto.sin_port = ntohs(rtcp_port);
            rtpto.sin_family = AF_INET;
            rtpto.sin_addr.s_addr = inet_addr(get_peer_ip().data());
            pRtcpSockRef->setSendPeerAddr((struct sockaddr *)&(rtpto));
xzl committed
340
		}
341 342 343 344

        auto srcIP = inet_addr(get_peer_ip().data());
        weak_ptr<RtspPlayer> weakSelf = dynamic_pointer_cast<RtspPlayer>(shared_from_this());
        //设置rtp over udp接收回调处理函数
345
        pRtpSockRef->setOnRead([srcIP, uiTrackIndex, weakSelf](const Buffer::Ptr &buf, struct sockaddr *addr , int addr_len) {
346 347 348 349 350 351 352 353 354 355 356 357 358
            auto strongSelf = weakSelf.lock();
            if (!strongSelf) {
                return;
            }
            if (((struct sockaddr_in *) addr)->sin_addr.s_addr != srcIP) {
                WarnL << "收到其他地址的rtp数据:" << inet_ntoa(((struct sockaddr_in *) addr)->sin_addr);
                return;
            }
            strongSelf->handleOneRtp(uiTrackIndex, strongSelf->_aTrackInfo[uiTrackIndex], (unsigned char *) buf->data(), buf->size());
        });

        if(pRtcpSockRef) {
            //设置rtcp over udp接收回调处理函数
359
            pRtcpSockRef->setOnRead([srcIP, uiTrackIndex, weakSelf](const Buffer::Ptr &buf, struct sockaddr *addr , int addr_len) {
360 361 362 363 364 365 366 367
                auto strongSelf = weakSelf.lock();
                if (!strongSelf) {
                    return;
                }
                if (((struct sockaddr_in *) addr)->sin_addr.s_addr != srcIP) {
                    WarnL << "收到其他地址的rtcp数据:" << inet_ntoa(((struct sockaddr_in *) addr)->sin_addr);
                    return;
                }
368
                strongSelf->onRtcpPacket(uiTrackIndex, strongSelf->_aTrackInfo[uiTrackIndex], (unsigned char *) buf->data(), buf->size());
369 370
            });
        }
xzl committed
371 372
	}

xiongziliang committed
373
	if (uiTrackIndex < _aTrackInfo.size() - 1) {
xzl committed
374 375 376 377
		//需要继续发送SETUP命令
		sendSetup(uiTrackIndex + 1);
		return;
	}
378 379
	//所有setup命令发送完毕
	//发送play命令
380
    sendPause(type_play, 0);
xzl committed
381
}
382

383
void RtspPlayer::sendDescribe() {
384
	//发送DESCRIBE命令后处理函数:handleResDESCRIBE
385
	_onHandshake = std::bind(&RtspPlayer::handleResDESCRIBE,this, placeholders::_1);
386
	sendRtspRequest("DESCRIBE",_strUrl,{"Accept","application/sdp"});
387 388
}

389 390
void RtspPlayer::sendPause(int type , uint32_t seekMS){
	_onHandshake = std::bind(&RtspPlayer::handleResPAUSE,this, placeholders::_1,type);
391
	//开启或暂停rtsp
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
	switch (type){
		case type_pause:
			sendRtspRequest("PAUSE", _strContentBase);
			break;
		case type_play:
			sendRtspRequest("PLAY", _strContentBase);
			break;
		case type_seek:
			sendRtspRequest("PLAY", _strContentBase, {"Range",StrPrinter << "npt=" << setiosflags(ios::fixed) << setprecision(2) << seekMS / 1000.0 << "-"});
			break;
		default:
			WarnL << "unknown type : " << type;
			_onHandshake = nullptr;
			break;
	}
xzl committed
407 408
}
void RtspPlayer::pause(bool bPause) {
409
    sendPause(bPause ? type_pause : type_seek, getProgressMilliSecond());
xzl committed
410 411
}

412
void RtspPlayer::handleResPAUSE(const Parser& parser,int type) {
xzl committed
413
	if (parser.Url() != "200") {
414 415 416 417 418 419 420 421 422 423 424
		switch (type) {
			case type_pause:
				WarnL << "Pause failed:" << parser.Url() << " " << parser.Tail() << endl;
				break;
			case type_play:
				WarnL << "Play failed:" << parser.Url() << " " << parser.Tail() << endl;
				break;
			case type_seek:
				WarnL << "Seek failed:" << parser.Url() << " " << parser.Tail() << endl;
				break;
		}
xzl committed
425 426
		return;
	}
427 428 429

	if (type == type_pause) {
		//暂停成功!
430
		_pRtpTimer.reset();
431 432 433 434 435 436 437 438 439 440 441 442 443 444
		return;
	}

	//play或seek成功
	uint32_t iSeekTo = 0;
	//修正时间轴
	auto strRange = parser["Range"];
	if (strRange.size()) {
		auto strStart = FindField(strRange.data(), "npt=", "-");
		if (strStart == "now") {
			strStart = "0";
		}
		iSeekTo = 1000 * atof(strStart.data());
		DebugL << "seekTo(ms):" << iSeekTo;
xzl committed
445
	}
446 447 448 449
	//设置相对时间戳
	_stamp[0].setRelativeStamp(iSeekTo);
	_stamp[1].setRelativeStamp(iSeekTo);
	onPlayResult_l(SockException(Err_success, type == type_seek ? "resum rtsp success" : "rtsp play success"), type == type_seek);
xzl committed
450 451
}

452 453 454 455 456 457 458 459 460
void RtspPlayer::onWholeRtspPacket(Parser &parser) {
    try {
		decltype(_onHandshake) fun;
		_onHandshake.swap(fun);
        if(fun){
            fun(parser);
        }
        parser.Clear();
    } catch (std::exception &err) {
461 462
		//定时器_pPlayTimer为空后表明握手结束了
		onPlayResult_l(SockException(Err_other, err.what()),!_pPlayTimer);
xzl committed
463 464 465
    }
}

466 467 468
void RtspPlayer::onRtpPacket(const char *data, uint64_t len) {
    int trackIdx = -1;
    uint8_t interleaved = data[1];
469
    if(interleaved %2 == 0){
470
        trackIdx = getTrackIndexByInterleaved(interleaved);
471 472 473 474 475 476
        if (trackIdx != -1) {
            handleOneRtp(trackIdx,_aTrackInfo[trackIdx],(unsigned char *)data + 4, len - 4);
        }
    }else{
        trackIdx = getTrackIndexByInterleaved(interleaved - 1);
        if (trackIdx != -1) {
477
            onRtcpPacket(trackIdx, _aTrackInfo[trackIdx], (unsigned char *) data + 4, len - 4);
478
        }
479
    }
xzl committed
480 481
}

482
void RtspPlayer::onRtcpPacket(int iTrackidx, SdpTrack::Ptr &track, unsigned char *pucData, unsigned int uiLen){
483 484 485

}

xiongziliang committed
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608

#if 0
//改代码提取自FFmpeg,参考之
// Receiver Report
    avio_w8(pb, (RTP_VERSION << 6) + 1); /* 1 report block */
    avio_w8(pb, RTCP_RR);
    avio_wb16(pb, 7); /* length in words - 1 */
    // our own SSRC: we use the server's SSRC + 1 to avoid conflicts
    avio_wb32(pb, s->ssrc + 1);
    avio_wb32(pb, s->ssrc); // server SSRC
    // some placeholders we should really fill...
    // RFC 1889/p64
    extended_max          = stats->cycles + stats->max_seq;
    expected              = extended_max - stats->base_seq;
    lost                  = expected - stats->received;
    lost                  = FFMIN(lost, 0xffffff); // clamp it since it's only 24 bits...
    expected_interval     = expected - stats->expected_prior;
    stats->expected_prior = expected;
    received_interval     = stats->received - stats->received_prior;
    stats->received_prior = stats->received;
    lost_interval         = expected_interval - received_interval;
    if (expected_interval == 0 || lost_interval <= 0)
        fraction = 0;
    else
        fraction = (lost_interval << 8) / expected_interval;

    fraction = (fraction << 24) | lost;

    avio_wb32(pb, fraction); /* 8 bits of fraction, 24 bits of total packets lost */
    avio_wb32(pb, extended_max); /* max sequence received */
    avio_wb32(pb, stats->jitter >> 4); /* jitter */

    if (s->last_rtcp_ntp_time == AV_NOPTS_VALUE) {
        avio_wb32(pb, 0); /* last SR timestamp */
        avio_wb32(pb, 0); /* delay since last SR */
    } else {
        uint32_t middle_32_bits   = s->last_rtcp_ntp_time >> 16; // this is valid, right? do we need to handle 64 bit values special?
        uint32_t delay_since_last = av_rescale(av_gettime_relative() - s->last_rtcp_reception_time,
                                               65536, AV_TIME_BASE);

        avio_wb32(pb, middle_32_bits); /* last SR timestamp */
        avio_wb32(pb, delay_since_last); /* delay since last SR */
    }

    // CNAME
    avio_w8(pb, (RTP_VERSION << 6) + 1); /* 1 report block */
    avio_w8(pb, RTCP_SDES);
    len = strlen(s->hostname);
    avio_wb16(pb, (7 + len + 3) / 4); /* length in words - 1 */
    avio_wb32(pb, s->ssrc + 1);
    avio_w8(pb, 0x01);
    avio_w8(pb, len);
    avio_write(pb, s->hostname, len);
    avio_w8(pb, 0); /* END */
    // padding
    for (len = (7 + len) % 4; len % 4; len++)
        avio_w8(pb, 0);
#endif

void RtspPlayer::sendReceiverReport(bool overTcp,int iTrackIndex){
    static const char s_cname[] = "ZLMediaKitRtsp";
    uint8_t aui8Rtcp[4 + 32 + 10 + sizeof(s_cname) + 1] = {0};
    uint8_t *pui8Rtcp_RR = aui8Rtcp + 4, *pui8Rtcp_SDES = pui8Rtcp_RR + 32;
    auto &track = _aTrackInfo[iTrackIndex];
    auto &counter = _aRtcpCnt[iTrackIndex];

    aui8Rtcp[0] = '$';
    aui8Rtcp[1] = track->_interleaved + 1;
    aui8Rtcp[2] = (sizeof(aui8Rtcp) - 4) >>  8;
    aui8Rtcp[3] = (sizeof(aui8Rtcp) - 4) & 0xFF;

    pui8Rtcp_RR[0] = 0x81;/* 1 report block */
    pui8Rtcp_RR[1] = 0xC9;//RTCP_RR
    pui8Rtcp_RR[2] = 0x00;
    pui8Rtcp_RR[3] = 0x07;/* length in words - 1 */

    uint32_t ssrc=htonl(track->_ssrc + 1);
    // our own SSRC: we use the server's SSRC + 1 to avoid conflicts
    memcpy(&pui8Rtcp_RR[4], &ssrc, 4);
    ssrc=htonl(track->_ssrc);
    // server SSRC
    memcpy(&pui8Rtcp_RR[8], &ssrc, 4);

    //FIXME: 8 bits of fraction, 24 bits of total packets lost
    pui8Rtcp_RR[12] = 0x00;
    pui8Rtcp_RR[13] = 0x00;
    pui8Rtcp_RR[14] = 0x00;
    pui8Rtcp_RR[15] = 0x00;

    //FIXME: max sequence received
    int cycleCount = getCycleCount(iTrackIndex);
    pui8Rtcp_RR[16] = cycleCount >> 8;
    pui8Rtcp_RR[17] = cycleCount & 0xFF;
    pui8Rtcp_RR[18] = counter.pktCnt >> 8;
    pui8Rtcp_RR[19] = counter.pktCnt & 0xFF;

    uint32_t  jitter = htonl(getJitterSize(iTrackIndex));
    //FIXME: jitter
    memcpy(pui8Rtcp_RR + 20, &jitter , 4);
    /* last SR timestamp */
    memcpy(pui8Rtcp_RR + 24, &counter.lastTimeStamp, 4);
    uint32_t msInc = htonl(ntohl(counter.timeStamp) - ntohl(counter.lastTimeStamp));
    /* delay since last SR */
    memcpy(pui8Rtcp_RR + 28, &msInc, 4);

    // CNAME
    pui8Rtcp_SDES[0] = 0x81;
    pui8Rtcp_SDES[1] = 0xCA;
    pui8Rtcp_SDES[2] = 0x00;
    pui8Rtcp_SDES[3] = 0x06;

    memcpy(&pui8Rtcp_SDES[4], &ssrc, 4);

    pui8Rtcp_SDES[8] = 0x01;
    pui8Rtcp_SDES[9] = 0x0f;
    memcpy(&pui8Rtcp_SDES[10], s_cname, sizeof(s_cname));
    pui8Rtcp_SDES[10 + sizeof(s_cname)] = 0x00;

    if(overTcp){
        send(obtainBuffer((char *) aui8Rtcp, sizeof(aui8Rtcp)));
    }else if(_apRtcpSock[iTrackIndex]) {
        _apRtcpSock[iTrackIndex]->send((char *) aui8Rtcp + 4, sizeof(aui8Rtcp) - 4);
    }
609
}
xzl committed
610

xiongziliang committed
611

xiongziliang committed
612
void RtspPlayer::onRtpSorted(const RtpPacket::Ptr &rtppt, int trackidx){
xzl committed
613
	//统计丢包率
614 615 616
	if (_aui16FirstSeq[trackidx] == 0 || rtppt->sequence < _aui16FirstSeq[trackidx]) {
		_aui16FirstSeq[trackidx] = rtppt->sequence;
		_aui64RtpRecv[trackidx] = 0;
xzl committed
617
	}
618 619
	_aui64RtpRecv[trackidx] ++;
	_aui16NowSeq[trackidx] = rtppt->sequence;
620

xiongziliang committed
621 622 623 624
	//计算相对时间戳
	int64_t dts_out;
	_stamp[trackidx].revise(rtppt->timeStamp,rtppt->timeStamp,dts_out,dts_out);
    rtppt->timeStamp = dts_out;
625
	onRecvRTP_l(rtppt,_aTrackInfo[trackidx]);
xzl committed
626
}
627
float RtspPlayer::getPacketLossRate(TrackType type) const{
xiongziliang committed
628
	int iTrackIdx = getTrackIndexByTrackType(type);
xzl committed
629 630 631
	if(iTrackIdx == -1){
		uint64_t totalRecv = 0;
		uint64_t totalSend = 0;
xiongziliang committed
632
		for (unsigned int i = 0; i < _aTrackInfo.size(); i++) {
633 634
			totalRecv += _aui64RtpRecv[i];
			totalSend += (_aui16NowSeq[i] - _aui16FirstSeq[i] + 1);
xzl committed
635 636 637 638 639 640 641
		}
		if(totalSend == 0){
			return 0;
		}
		return 1.0 - (double)totalRecv / totalSend;
	}

642
	if(_aui16NowSeq[iTrackIdx] - _aui16FirstSeq[iTrackIdx] + 1 == 0){
xzl committed
643 644
		return 0;
	}
645
	return 1.0 - (double)_aui64RtpRecv[iTrackIdx] / (_aui16NowSeq[iTrackIdx] - _aui16FirstSeq[iTrackIdx] + 1);
xzl committed
646 647
}

648
uint32_t RtspPlayer::getProgressMilliSecond() const{
xiongziliang committed
649
    return MAX(_stamp[0].getRelativeStamp(),_stamp[1].getRelativeStamp());
xzl committed
650
}
651
void RtspPlayer::seekToMilliSecond(uint32_t ms) {
652
    sendPause(type_seek,ms);
xzl committed
653
}
654

655
void RtspPlayer::sendRtspRequest(const string &cmd, const string &url, const std::initializer_list<string> &header) {
xiongziliang committed
656 657 658 659 660 661 662 663 664 665
	string key;
	StrCaseMap header_map;
	int i = 0;
	for(auto &val : header){
		if(++i % 2 == 0){
			header_map.emplace(key,val);
		}else{
			key = val;
		}
	}
666
	sendRtspRequest(cmd,url,header_map);
xiongziliang committed
667
}
668
void RtspPlayer::sendRtspRequest(const string &cmd, const string &url,const StrCaseMap &header_const) {
669
	auto header = header_const;
670
	header.emplace("CSeq",StrPrinter << _uiCseq++);
xiongziliang committed
671 672
	header.emplace("User-Agent",SERVER_NAME "(build in " __DATE__ " " __TIME__ ")");

673 674
	if(!_strSession.empty()){
		header.emplace("Session",_strSession);
675 676
	}

xiongziliang committed
677
	if(!_rtspRealm.empty() && !(*this)[kRtspUser].empty()){
xiongziliang committed
678
		if(!_rtspMd5Nonce.empty()){
679 680 681 682 683 684 685 686 687
			//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) );
			 */
xiongziliang committed
688 689 690
			string encrypted_pwd = (*this)[kRtspPwd];
			if(!(*this)[kRtspPwdIsMD5].as<bool>()){
				encrypted_pwd = MD5((*this)[kRtspUser]+ ":" + _rtspRealm + ":" + encrypted_pwd).hexdigest();
691
			}
xiongziliang committed
692
			auto response = MD5( encrypted_pwd + ":" + _rtspMd5Nonce  + ":" + MD5(cmd + ":" + url).hexdigest()).hexdigest();
693 694
			_StrPrinter printer;
			printer << "Digest ";
xiongziliang committed
695
			printer << "username=\"" << (*this)[kRtspUser] << "\", ";
xiongziliang committed
696 697
			printer << "realm=\"" << _rtspRealm << "\", ";
			printer << "nonce=\"" << _rtspMd5Nonce << "\", ";
698 699 700
			printer << "uri=\"" << url << "\", ";
			printer << "response=\"" << response << "\"";
			header.emplace("Authorization",printer);
xiongziliang committed
701
		}else if(!(*this)[kRtspPwdIsMD5].as<bool>()){
702
			//base64认证
xiongziliang committed
703
			string authStr = StrPrinter << (*this)[kRtspUser] << ":" << (*this)[kRtspPwd];
704 705 706 707 708 709 710 711 712 713 714
			char authStrBase64[1024] = {0};
			av_base64_encode(authStrBase64,sizeof(authStrBase64),(uint8_t *)authStr.data(),authStr.size());
			header.emplace("Authorization",StrPrinter << "Basic " << authStrBase64 );
		}
	}

	_StrPrinter printer;
	printer << cmd << " " << url << " RTSP/1.0\r\n";
	for (auto &pr : header){
		printer << pr.first << ": " << pr.second << "\r\n";
	}
715
	send(printer << "\r\n");
716 717
}

xiongziliang committed
718
void RtspPlayer::onRecvRTP_l(const RtpPacket::Ptr &pkt, const SdpTrack::Ptr &track) {
719
	_rtpTicker.resetTime();
xiongziliang committed
720 721 722 723 724 725 726 727 728 729 730 731 732
    onRecvRTP(pkt,track);

    int iTrackIndex = getTrackIndexByInterleaved(pkt->interleaved);
    if(iTrackIndex == -1){
        return;
    }
    RtcpCounter &counter = _aRtcpCnt[iTrackIndex];
    counter.pktCnt = pkt->sequence;
    auto &ticker = _aRtcpTicker[iTrackIndex];
    if (ticker.elapsedTime() > 5 * 1000) {
        //send rtcp every 5 second
        counter.lastTimeStamp = counter.timeStamp;
        //直接保存网络字节序
xiongziliang committed
733
        memcpy(&counter.timeStamp, pkt->data() + 8 , 4);
xiongziliang committed
734 735 736 737 738 739 740
        if(counter.lastTimeStamp != 0){
            sendReceiverReport(_eType == Rtsp::RTP_TCP,iTrackIndex);
            ticker.resetTime();
        }
    }


741
}
742
void RtspPlayer::onPlayResult_l(const SockException &ex , bool handshakeCompleted) {
743
	WarnL << ex.getErrCode() << " " << ex.what();
744

745 746 747 748 749
    if(!ex){
        //播放成功,恢复rtp接收超时定时器
        _rtpTicker.resetTime();
        weak_ptr<RtspPlayer> weakSelf = dynamic_pointer_cast<RtspPlayer>(shared_from_this());
        int timeoutMS = (*this)[kMediaTimeoutMS].as<int>();
750 751
		//创建rtp数据接收超时检测定时器
		_pRtpTimer.reset( new Timer(timeoutMS / 2000.0, [weakSelf,timeoutMS]() {
752 753 754 755 756
            auto strongSelf=weakSelf.lock();
            if(!strongSelf) {
                return false;
            }
            if(strongSelf->_rtpTicker.elapsedTime()> timeoutMS) {
757 758
				//接收rtp媒体数据包超时
				strongSelf->onPlayResult_l(SockException(Err_timeout,"receive rtp timeout"), true);
759 760 761 762 763 764
                return false;
            }
            return true;
        },getPoller()));
    }

765
    if (!handshakeCompleted) {
766 767 768
        //开始播放阶段
        _pPlayTimer.reset();
        onPlayResult(ex);
769 770 771 772 773 774
    } else if (ex) {
        //播放成功后异常断开回调
        onShutdown(ex);
    } else {
        //恢复播放
        onResume();
775 776 777 778 779
    }

    if(ex){
        teardown();
    }
780 781
}

xiongziliang committed
782
int RtspPlayer::getTrackIndexByInterleaved(int interleaved) const{
xiongziliang committed
783
	for (unsigned int i = 0; i < _aTrackInfo.size(); i++) {
784
		if (_aTrackInfo[i]->_interleaved == interleaved) {
xiongziliang committed
785 786 787
			return i;
		}
	}
xiongziliang committed
788 789 790
	if(_aTrackInfo.size() == 1){
		return 0;
	}
xiongziliang committed
791 792 793 794
	return -1;
}

int RtspPlayer::getTrackIndexByTrackType(TrackType trackType) const {
xiongziliang committed
795
	for (unsigned int i = 0; i < _aTrackInfo.size(); i++) {
796
		if (_aTrackInfo[i]->_type == trackType) {
797 798 799
			return i;
		}
	}
xiongziliang committed
800 801 802
	if(_aTrackInfo.size() == 1){
		return 0;
	}
803 804 805
	return -1;
}

xiongziliang committed
806
} /* namespace mediakit */
xzl committed
807 808