RtspPlayer.cpp 27.5 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 84
void RtspPlayer::play(const string &strUrl){
	Rtsp::eRtpType eType = (Rtsp::eRtpType)(int)(*this)[kRtpType];
85 86 87 88 89 90 91 92 93 94 95 96
	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){
        //并没有用户名密码
        play(isSSL,strUrl,"","",eType);
        return;
xiongziliang committed
97
	}
98 99 100 101

    //包含用户名密码
    auto user_pwd = middle_url.substr(0,pos);
	auto suffix = strUrl.substr(schema.size() + 3 + pos + 1);
xiongziliang committed
102
	auto url = StrPrinter << "rtsp://" << suffix << endl;
103 104
	if(user_pwd.find(":") == string::npos){
		play(isSSL,url,user_pwd,"",eType);
xiongziliang committed
105 106
		return;
	}
107 108 109
	auto user = FindField(user_pwd.data(),nullptr,":");
	auto pwd = FindField(user_pwd.data(),":",nullptr);
	play(isSSL,url,user,pwd,eType);
xiongziliang committed
110
}
111 112

void RtspPlayer::play(bool isSSL,const string &strUrl, const string &strUser, const string &strPwd,  Rtsp::eRtpType eType ) {
xiongziliang committed
113
	DebugL  << strUrl << " "
xiongziliang committed
114 115
			<< (strUser.size() ? strUser : "null") << " "
			<< (strPwd.size() ? strPwd:"null") << " "
xiongziliang committed
116
			<< eType;
xzl committed
117
	teardown();
118

xiongziliang committed
119
	if(strUser.size()){
120 121
        (*this)[kRtspUser] = strUser;
    }
xiongziliang committed
122
    if(strPwd.size()){
123 124
        (*this)[kRtspPwd] = strPwd;
		(*this)[kRtspPwdIsMD5] = false;
xzl committed
125
    }
126

127
	_eType = eType;
128

xiongziliang committed
129
	auto ip = FindField(strUrl.data(), "://", "/");
xiongziliang committed
130
	if (ip.empty()) {
xiongziliang committed
131
		ip = split(FindField(strUrl.data(), "://", NULL),"?")[0];
xzl committed
132
	}
xiongziliang committed
133
	auto port = atoi(FindField(ip.data(), ":", NULL).data());
xzl committed
134 135
	if (port <= 0) {
		//rtsp 默认端口554
136
		port = isSSL ? 322 : 554;
xzl committed
137 138
	} else {
		//服务器域名
xiongziliang committed
139
		ip = FindField(ip.data(), NULL, ":");
xzl committed
140 141
	}

xiongziliang committed
142
	if(ip.empty()){
143
		onPlayResult_l(SockException(Err_other,StrPrinter << "illegal rtsp url:" << strUrl),false);
xiongziliang committed
144 145 146
		return;
	}

147
	_strUrl = strUrl;
xzl committed
148 149

	weak_ptr<RtspPlayer> weakSelf = dynamic_pointer_cast<RtspPlayer>(shared_from_this());
xiongziliang committed
150
	float playTimeOutSec = (*this)[kTimeoutMS].as<int>() / 1000.0;
151
	_pPlayTimer.reset( new Timer(playTimeOutSec,  [weakSelf]() {
xzl committed
152 153 154 155
		auto strongSelf=weakSelf.lock();
		if(!strongSelf) {
			return false;
		}
156
		strongSelf->onPlayResult_l(SockException(Err_timeout,"play rtsp timeout"),false);
xzl committed
157
		return false;
158
	},getPoller()));
159

xiongziliang committed
160 161
	if(!(*this)[kNetAdapter].empty()){
		setNetAdapter((*this)[kNetAdapter]);
xiongziliang committed
162 163
	}
	startConnect(ip, port , playTimeOutSec);
164 165
}
void RtspPlayer::onConnect(const SockException &err){
166 167
	if(err.getErrCode() != Err_success) {
		onPlayResult_l(err,false);
168 169 170 171
		return;
	}

	sendDescribe();
xzl committed
172 173
}

174
void RtspPlayer::onRecv(const Buffer::Ptr& pBuf) {
175
    input(pBuf->data(),pBuf->size());
xzl committed
176 177
}
void RtspPlayer::onErr(const SockException &ex) {
178 179
	//定时器_pPlayTimer为空后表明握手结束了
	onPlayResult_l(ex,!_pPlayTimer);
xzl committed
180
}
181 182
// from live555
bool RtspPlayer::handleAuthenticationFailure(const string &paramsStr) {
xiongziliang committed
183
    if(!_rtspRealm.empty()){
184 185 186 187
        //已经认证过了
        return false;
    }

xiongziliang committed
188 189 190 191 192 193 194 195
    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;
    });
196 197

    if (sscanf(paramsStr.data(), "Digest realm=\"%[^\"]\", nonce=\"%[^\"]\", stale=%[a-zA-Z]", realm, nonce, stale) == 3) {
xiongziliang committed
198 199
        _rtspRealm = (const char *)realm;
        _rtspMd5Nonce = (const char *)nonce;
200 201 202
        return true;
    }
    if (sscanf(paramsStr.data(), "Digest realm=\"%[^\"]\", nonce=\"%[^\"]\"", realm, nonce) == 2) {
xiongziliang committed
203 204
        _rtspRealm = (const char *)realm;
        _rtspMd5Nonce = (const char *)nonce;
205 206 207
        return true;
    }
    if (sscanf(paramsStr.data(), "Basic realm=\"%[^\"]\"", realm) == 1) {
xiongziliang committed
208
        _rtspRealm = (const char *)realm;
209 210 211 212 213 214
        return true;
    }
    return false;
}
void RtspPlayer::handleResDESCRIBE(const Parser& parser) {
	string authInfo = parser["WWW-Authenticate"];
xzl committed
215
	//发送DESCRIBE命令后的回复
216 217 218 219
	if ((parser.Url() == "401") && handleAuthenticationFailure(authInfo)) {
		sendDescribe();
		return;
	}
220
	if(parser.Url() == "302" || parser.Url() == "301"){
221 222 223 224
		auto newUrl = parser["Location"];
		if(newUrl.empty()){
			throw std::runtime_error("未找到Location字段(跳转url)");
		}
xiongziliang committed
225
		play(newUrl);
226 227
		return;
	}
xzl committed
228 229 230 231
	if (parser.Url() != "200") {
		throw std::runtime_error(
		StrPrinter << "DESCRIBE:" << parser.Url() << " " << parser.Tail() << endl);
	}
232
	_strContentBase = parser["Content-Base"];
233

234 235
    if(_strContentBase.empty()){
        _strContentBase = _strUrl;
236
    }
237 238
    if (_strContentBase.back() == '/') {
        _strContentBase.pop_back();
239 240
    }

241
	SdpParser sdpParser(parser.Content());
xzl committed
242
	//解析sdp
243
	_aTrackInfo = sdpParser.getAvailableTrack();
xiongziliang committed
244 245 246 247 248 249 250 251 252 253
	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
254 255

	if (_aTrackInfo.empty()) {
xiongziliang committed
256
		throw std::runtime_error("无有效的Sdp Track");
xzl committed
257
	}
258
	if (!onCheckSDP(sdpParser.toString())) {
xzl committed
259 260
		throw std::runtime_error("onCheckSDP faied");
	}
xiongziliang committed
261

xzl committed
262 263
	sendSetup(0);
}
264

xiongziliang committed
265
//有必要的情况下创建udp端口
266 267 268 269 270
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
271
		//rtp随机端口
272 273 274 275 276 277 278 279
		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
280
		//rtcp端口为rtp端口+1,目的是为了兼容某些服务器,其实更推荐随机端口
281 282 283 284 285
		if (!rtcpSockRef->bindUdpSock(rtpSockRef->get_local_port() + 1, get_local_ip().data())) {
			rtcpSockRef.reset();
			throw std::runtime_error("open rtcp sock failed");
		}
	}
286 287 288 289 290 291 292

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


xzl committed
296
//发送SETUP命令
297
void RtspPlayer::sendSetup(unsigned int trackIndex) {
298 299
    _onHandshake = std::bind(&RtspPlayer::handleResSETUP,this, placeholders::_1,trackIndex);
    auto &track = _aTrackInfo[trackIndex];
xiongziliang committed
300
	auto baseUrl = _strContentBase + "/" + track->_control_surffix;
301
	switch (_eType) {
xiongziliang committed
302
		case Rtsp::RTP_TCP: {
303
			sendRtspRequest("SETUP",baseUrl,{"Transport",StrPrinter << "RTP/AVP/TCP;unicast;interleaved=" << track->_type * 2 << "-" << track->_type * 2 + 1});
xzl committed
304
		}
305
			break;
xiongziliang committed
306
		case Rtsp::RTP_MULTICAST: {
307
			sendRtspRequest("SETUP",baseUrl,{"Transport","Transport: RTP/AVP;multicast"});
308
		}
309
			break;
xiongziliang committed
310
		case Rtsp::RTP_UDP: {
311
			createUdpSockIfNecessary(trackIndex);
312 313 314 315
			sendRtspRequest("SETUP",baseUrl,{"Transport",
                                    StrPrinter << "RTP/AVP;unicast;client_port="
                                    << _apRtpSock[trackIndex]->get_local_port() << "-"
                                    << _apRtcpSock[trackIndex]->get_local_port()});
316
		}
317
			break;
318
		default:
319
			break;
xzl committed
320 321 322
	}
}

323
void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int uiTrackIndex) {
xzl committed
324 325 326 327 328
	if (parser.Url() != "200") {
		throw std::runtime_error(
		StrPrinter << "SETUP:" << parser.Url() << " " << parser.Tail() << endl);
	}
	if (uiTrackIndex == 0) {
329 330 331
		_strSession = parser["Session"];
        _strSession.append(";");
        _strSession = FindField(_strSession.data(), nullptr, ";");
xzl committed
332 333 334
	}

	auto strTransport = parser["Transport"];
335
	if(strTransport.find("TCP") != string::npos || strTransport.find("interleaved") != string::npos){
xiongziliang committed
336
		_eType = Rtsp::RTP_TCP;
xzl committed
337
	}else if(strTransport.find("multicast") != string::npos){
xiongziliang committed
338
		_eType = Rtsp::RTP_MULTICAST;
xzl committed
339
	}else{
xiongziliang committed
340
		_eType = Rtsp::RTP_UDP;
xzl committed
341 342
	}

xiongziliang committed
343
	RtspSplitter::enableRecvRtp(_eType == Rtsp::RTP_TCP);
344

xiongziliang committed
345 346 347
	if(_eType == Rtsp::RTP_TCP)  {
		string interleaved = FindField( FindField((strTransport + ";").data(), "interleaved=", ";").data(), NULL, "-");
		_aTrackInfo[uiTrackIndex]->_interleaved = atoi(interleaved.data());
xzl committed
348
	}else{
xiongziliang committed
349 350
		const char *strPos = (_eType == Rtsp::RTP_MULTICAST ? "port=" : "server_port=") ;
		auto port_str = FindField((strTransport + ";").data(), strPos, ";");
351 352 353 354
		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
355

xiongziliang committed
356
		if (_eType == Rtsp::RTP_MULTICAST) {
357
		    //udp组播
xiongziliang committed
358
			auto multiAddr = FindField((strTransport + ";").data(), "destination=", ";");
359
            pRtpSockRef.reset(new Socket(getPoller()));
360
			if (!pRtpSockRef->bindUdpSock(rtp_port, multiAddr.data())) {
361
				pRtpSockRef.reset();
xzl committed
362 363
				throw std::runtime_error("open udp sock err");
			}
364
			auto fd = pRtpSockRef->rawFD();
xzl committed
365 366 367 368
			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 {
369 370
			createUdpSockIfNecessary(uiTrackIndex);
			//udp单播
xzl committed
371
			struct sockaddr_in rtpto;
372
			rtpto.sin_port = ntohs(rtp_port);
xzl committed
373
			rtpto.sin_family = AF_INET;
xiongziliang committed
374
			rtpto.sin_addr.s_addr = inet_addr(get_peer_ip().data());
375 376 377 378 379 380 381 382 383
			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
384
		}
385 386 387 388

        auto srcIP = inet_addr(get_peer_ip().data());
        weak_ptr<RtspPlayer> weakSelf = dynamic_pointer_cast<RtspPlayer>(shared_from_this());
        //设置rtp over udp接收回调处理函数
389
        pRtpSockRef->setOnRead([srcIP, uiTrackIndex, weakSelf](const Buffer::Ptr &buf, struct sockaddr *addr , int addr_len) {
390 391 392 393 394 395 396 397 398 399 400 401 402
            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接收回调处理函数
403
            pRtcpSockRef->setOnRead([srcIP, uiTrackIndex, weakSelf](const Buffer::Ptr &buf, struct sockaddr *addr , int addr_len) {
404 405 406 407 408 409 410 411
                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;
                }
412
                strongSelf->onRtcpPacket(uiTrackIndex, strongSelf->_aTrackInfo[uiTrackIndex], (unsigned char *) buf->data(), buf->size());
413 414
            });
        }
xzl committed
415 416
	}

xiongziliang committed
417
	if (uiTrackIndex < _aTrackInfo.size() - 1) {
xzl committed
418 419 420 421
		//需要继续发送SETUP命令
		sendSetup(uiTrackIndex + 1);
		return;
	}
422 423
	//所有setup命令发送完毕
	//发送play命令
424
    sendPause(type_play, 0);
xzl committed
425
}
426

427
void RtspPlayer::sendDescribe() {
428
	//发送DESCRIBE命令后处理函数:handleResDESCRIBE
429
	_onHandshake = std::bind(&RtspPlayer::handleResDESCRIBE,this, placeholders::_1);
430
	sendRtspRequest("DESCRIBE",_strUrl,{"Accept","application/sdp"});
431 432
}

433 434
void RtspPlayer::sendPause(int type , uint32_t seekMS){
	_onHandshake = std::bind(&RtspPlayer::handleResPAUSE,this, placeholders::_1,type);
435
	//开启或暂停rtsp
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
	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
451 452
}
void RtspPlayer::pause(bool bPause) {
453
    sendPause(bPause ? type_pause : type_seek, getProgressMilliSecond());
xzl committed
454 455
}

456
void RtspPlayer::handleResPAUSE(const Parser& parser,int type) {
xzl committed
457
	if (parser.Url() != "200") {
458 459 460 461 462 463 464 465 466 467 468
		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
469 470
		return;
	}
471 472 473

	if (type == type_pause) {
		//暂停成功!
474
		_pRtpTimer.reset();
475 476 477 478 479 480 481 482 483 484 485 486 487 488
		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
489
	}
490 491 492 493
	//设置相对时间戳
	_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
494 495
}

496 497 498 499 500 501 502 503 504
void RtspPlayer::onWholeRtspPacket(Parser &parser) {
    try {
		decltype(_onHandshake) fun;
		_onHandshake.swap(fun);
        if(fun){
            fun(parser);
        }
        parser.Clear();
    } catch (std::exception &err) {
505 506
		//定时器_pPlayTimer为空后表明握手结束了
		onPlayResult_l(SockException(Err_other, err.what()),!_pPlayTimer);
xzl committed
507 508 509
    }
}

510 511 512
void RtspPlayer::onRtpPacket(const char *data, uint64_t len) {
    int trackIdx = -1;
    uint8_t interleaved = data[1];
513
    if(interleaved %2 == 0){
514
        trackIdx = getTrackIndexByInterleaved(interleaved);
515 516 517 518 519 520
        if (trackIdx != -1) {
            handleOneRtp(trackIdx,_aTrackInfo[trackIdx],(unsigned char *)data + 4, len - 4);
        }
    }else{
        trackIdx = getTrackIndexByInterleaved(interleaved - 1);
        if (trackIdx != -1) {
521
            onRtcpPacket(trackIdx, _aTrackInfo[trackIdx], (unsigned char *) data + 4, len - 4);
522
        }
523
    }
xzl committed
524 525
}

526
void RtspPlayer::onRtcpPacket(int iTrackidx, SdpTrack::Ptr &track, unsigned char *pucData, unsigned int uiLen){
527 528 529

}

xiongziliang committed
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 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652

#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);
    }
653
}
xzl committed
654

xiongziliang committed
655

xiongziliang committed
656
void RtspPlayer::onRtpSorted(const RtpPacket::Ptr &rtppt, int trackidx){
xzl committed
657
	//统计丢包率
658 659 660
	if (_aui16FirstSeq[trackidx] == 0 || rtppt->sequence < _aui16FirstSeq[trackidx]) {
		_aui16FirstSeq[trackidx] = rtppt->sequence;
		_aui64RtpRecv[trackidx] = 0;
xzl committed
661
	}
662 663
	_aui64RtpRecv[trackidx] ++;
	_aui16NowSeq[trackidx] = rtppt->sequence;
664

xiongziliang committed
665 666 667 668
	//计算相对时间戳
	int64_t dts_out;
	_stamp[trackidx].revise(rtppt->timeStamp,rtppt->timeStamp,dts_out,dts_out);
    rtppt->timeStamp = dts_out;
669
	onRecvRTP_l(rtppt,_aTrackInfo[trackidx]);
xzl committed
670
}
671
float RtspPlayer::getPacketLossRate(TrackType type) const{
xiongziliang committed
672
	int iTrackIdx = getTrackIndexByTrackType(type);
xzl committed
673 674 675
	if(iTrackIdx == -1){
		uint64_t totalRecv = 0;
		uint64_t totalSend = 0;
xiongziliang committed
676
		for (unsigned int i = 0; i < _aTrackInfo.size(); i++) {
677 678
			totalRecv += _aui64RtpRecv[i];
			totalSend += (_aui16NowSeq[i] - _aui16FirstSeq[i] + 1);
xzl committed
679 680 681 682 683 684 685
		}
		if(totalSend == 0){
			return 0;
		}
		return 1.0 - (double)totalRecv / totalSend;
	}

686
	if(_aui16NowSeq[iTrackIdx] - _aui16FirstSeq[iTrackIdx] + 1 == 0){
xzl committed
687 688
		return 0;
	}
689
	return 1.0 - (double)_aui64RtpRecv[iTrackIdx] / (_aui16NowSeq[iTrackIdx] - _aui16FirstSeq[iTrackIdx] + 1);
xzl committed
690 691
}

692
uint32_t RtspPlayer::getProgressMilliSecond() const{
xiongziliang committed
693
    return MAX(_stamp[0].getRelativeStamp(),_stamp[1].getRelativeStamp());
xzl committed
694
}
695
void RtspPlayer::seekToMilliSecond(uint32_t ms) {
696
    sendPause(type_seek,ms);
xzl committed
697
}
698

699
void RtspPlayer::sendRtspRequest(const string &cmd, const string &url, const std::initializer_list<string> &header) {
xiongziliang committed
700 701 702 703 704 705 706 707 708 709
	string key;
	StrCaseMap header_map;
	int i = 0;
	for(auto &val : header){
		if(++i % 2 == 0){
			header_map.emplace(key,val);
		}else{
			key = val;
		}
	}
710
	sendRtspRequest(cmd,url,header_map);
xiongziliang committed
711
}
712
void RtspPlayer::sendRtspRequest(const string &cmd, const string &url,const StrCaseMap &header_const) {
713
	auto header = header_const;
714
	header.emplace("CSeq",StrPrinter << _uiCseq++);
xiongziliang committed
715 716
	header.emplace("User-Agent",SERVER_NAME "(build in " __DATE__ " " __TIME__ ")");

717 718
	if(!_strSession.empty()){
		header.emplace("Session",_strSession);
719 720
	}

xiongziliang committed
721
	if(!_rtspRealm.empty() && !(*this)[kRtspUser].empty()){
xiongziliang committed
722
		if(!_rtspMd5Nonce.empty()){
723 724 725 726 727 728 729 730 731
			//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
732 733 734
			string encrypted_pwd = (*this)[kRtspPwd];
			if(!(*this)[kRtspPwdIsMD5].as<bool>()){
				encrypted_pwd = MD5((*this)[kRtspUser]+ ":" + _rtspRealm + ":" + encrypted_pwd).hexdigest();
735
			}
xiongziliang committed
736
			auto response = MD5( encrypted_pwd + ":" + _rtspMd5Nonce  + ":" + MD5(cmd + ":" + url).hexdigest()).hexdigest();
737 738
			_StrPrinter printer;
			printer << "Digest ";
xiongziliang committed
739
			printer << "username=\"" << (*this)[kRtspUser] << "\", ";
xiongziliang committed
740 741
			printer << "realm=\"" << _rtspRealm << "\", ";
			printer << "nonce=\"" << _rtspMd5Nonce << "\", ";
742 743 744
			printer << "uri=\"" << url << "\", ";
			printer << "response=\"" << response << "\"";
			header.emplace("Authorization",printer);
xiongziliang committed
745
		}else if(!(*this)[kRtspPwdIsMD5].as<bool>()){
746
			//base64认证
xiongziliang committed
747
			string authStr = StrPrinter << (*this)[kRtspUser] << ":" << (*this)[kRtspPwd];
748 749 750 751 752 753 754 755 756 757 758
			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";
	}
759
	send(printer << "\r\n");
760 761
}

xiongziliang committed
762
void RtspPlayer::onRecvRTP_l(const RtpPacket::Ptr &pkt, const SdpTrack::Ptr &track) {
763
	_rtpTicker.resetTime();
xiongziliang committed
764 765 766 767 768 769 770 771 772 773 774 775 776
    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
777
        memcpy(&counter.timeStamp, pkt->data() + 8 , 4);
xiongziliang committed
778 779 780 781 782 783 784
        if(counter.lastTimeStamp != 0){
            sendReceiverReport(_eType == Rtsp::RTP_TCP,iTrackIndex);
            ticker.resetTime();
        }
    }


785
}
786
void RtspPlayer::onPlayResult_l(const SockException &ex , bool handshakeCompleted) {
787
	WarnL << ex.getErrCode() << " " << ex.what();
788

789 790 791 792 793
    if(!ex){
        //播放成功,恢复rtp接收超时定时器
        _rtpTicker.resetTime();
        weak_ptr<RtspPlayer> weakSelf = dynamic_pointer_cast<RtspPlayer>(shared_from_this());
        int timeoutMS = (*this)[kMediaTimeoutMS].as<int>();
794 795
		//创建rtp数据接收超时检测定时器
		_pRtpTimer.reset( new Timer(timeoutMS / 2000.0, [weakSelf,timeoutMS]() {
796 797 798 799 800
            auto strongSelf=weakSelf.lock();
            if(!strongSelf) {
                return false;
            }
            if(strongSelf->_rtpTicker.elapsedTime()> timeoutMS) {
801 802
				//接收rtp媒体数据包超时
				strongSelf->onPlayResult_l(SockException(Err_timeout,"receive rtp timeout"), true);
803 804 805 806 807 808
                return false;
            }
            return true;
        },getPoller()));
    }

809
    if (!handshakeCompleted) {
810 811 812
        //开始播放阶段
        _pPlayTimer.reset();
        onPlayResult(ex);
813 814 815 816 817 818
    } else if (ex) {
        //播放成功后异常断开回调
        onShutdown(ex);
    } else {
        //恢复播放
        onResume();
819 820 821 822 823
    }

    if(ex){
        teardown();
    }
824 825
}

xiongziliang committed
826
int RtspPlayer::getTrackIndexByControlSuffix(const string &controlSuffix) const{
xiongziliang committed
827
	for (unsigned int i = 0; i < _aTrackInfo.size(); i++) {
828 829
		auto pos = _aTrackInfo[i]->_control_surffix.find(controlSuffix);
		if (pos == 0) {
830 831 832 833 834
			return i;
		}
	}
	return -1;
}
xiongziliang committed
835
int RtspPlayer::getTrackIndexByInterleaved(int interleaved) const{
xiongziliang committed
836
	for (unsigned int i = 0; i < _aTrackInfo.size(); i++) {
837
		if (_aTrackInfo[i]->_interleaved == interleaved) {
xiongziliang committed
838 839 840 841 842 843 844
			return i;
		}
	}
	return -1;
}

int RtspPlayer::getTrackIndexByTrackType(TrackType trackType) const {
xiongziliang committed
845
	for (unsigned int i = 0; i < _aTrackInfo.size(); i++) {
846
		if (_aTrackInfo[i]->_type == trackType) {
847 848 849 850 851 852
			return i;
		}
	}
	return -1;
}

xiongziliang committed
853
} /* namespace mediakit */
xzl committed
854 855