2019-04-24 11:40:54 +08:00
|
|
|
|
/*
|
2017-09-27 16:20:30 +08:00
|
|
|
|
* MIT License
|
2017-04-01 16:35:56 +08:00
|
|
|
|
*
|
2019-05-08 15:40:07 +08:00
|
|
|
|
* Copyright (c) 2016-2019 xiongziliang <771730766@qq.com>
|
2017-09-27 16:20:30 +08:00
|
|
|
|
*
|
|
|
|
|
* 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.
|
2017-04-01 16:35:56 +08:00
|
|
|
|
*/
|
2017-08-09 18:39:30 +08:00
|
|
|
|
|
2017-04-25 11:35:41 +08:00
|
|
|
|
#include <atomic>
|
2018-12-14 14:59:12 +08:00
|
|
|
|
#include <iomanip>
|
2017-05-02 17:15:12 +08:00
|
|
|
|
#include "Common/config.h"
|
2017-04-25 11:35:41 +08:00
|
|
|
|
#include "UDPServer.h"
|
2017-04-01 16:35:56 +08:00
|
|
|
|
#include "RtspSession.h"
|
2017-04-25 11:35:41 +08:00
|
|
|
|
#include "Util/mini.h"
|
2017-12-10 01:34:43 +08:00
|
|
|
|
#include "Util/MD5.h"
|
2018-09-20 15:43:49 +08:00
|
|
|
|
#include "Util/base64.h"
|
2017-04-25 11:35:41 +08:00
|
|
|
|
#include "Util/onceToken.h"
|
2017-04-01 16:35:56 +08:00
|
|
|
|
#include "Util/TimeTicker.h"
|
|
|
|
|
#include "Util/NoticeCenter.h"
|
2017-04-25 11:35:41 +08:00
|
|
|
|
#include "Network/sockutil.h"
|
2017-04-01 16:35:56 +08:00
|
|
|
|
|
2019-06-11 18:31:34 +08:00
|
|
|
|
#define RTSP_SERVER_SEND_RTCP 0
|
|
|
|
|
|
2018-10-24 17:17:55 +08:00
|
|
|
|
using namespace std;
|
|
|
|
|
using namespace toolkit;
|
2017-04-01 16:35:56 +08:00
|
|
|
|
|
2018-10-24 17:17:55 +08:00
|
|
|
|
namespace mediakit {
|
2017-04-01 16:35:56 +08:00
|
|
|
|
|
2018-12-17 13:14:49 +08:00
|
|
|
|
/**
|
|
|
|
|
* rtsp协议有多种方式传输rtp数据包,目前已支持包括以下4种
|
|
|
|
|
* 1: rtp over udp ,这种方式是rtp通过单独的udp端口传输
|
|
|
|
|
* 2: rtp over udp_multicast,这种方式是rtp通过共享udp组播端口传输
|
|
|
|
|
* 3: rtp over tcp,这种方式是通过rtsp信令tcp通道完成传输
|
|
|
|
|
* 4: rtp over http,下面着重讲解:rtp over http
|
|
|
|
|
*
|
|
|
|
|
* rtp over http 是把rtsp协议伪装成http协议以达到穿透防火墙的目的,
|
|
|
|
|
* 此时播放器会发送两次http请求至rtsp服务器,第一次是http get请求,
|
|
|
|
|
* 第二次是http post请求。
|
|
|
|
|
*
|
|
|
|
|
* 这两次请求通过http请求头中的x-sessioncookie键完成绑定
|
|
|
|
|
*
|
|
|
|
|
* 第一次http get请求用于接收rtp、rtcp和rtsp回复,后续该链接不再发送其他请求
|
|
|
|
|
* 第二次http post请求用于发送rtsp请求,rtsp握手结束后可能会断开连接,此时我们还要维持rtp发送
|
|
|
|
|
* 需要指出的是http post请求中的content负载就是base64编码后的rtsp请求包,
|
|
|
|
|
* 播放器会把rtsp请求伪装成http content负载发送至rtsp服务器,然后rtsp服务器又把回复发送给第一次http get请求的tcp链接
|
|
|
|
|
* 这样,对防火墙而言,本次rtsp会话就是两次http请求,防火墙就会放行数据
|
|
|
|
|
*
|
|
|
|
|
* zlmediakit在处理rtsp over http的请求时,会把http poster中的content数据base64解码后转发给http getter处理
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//rtsp over http 情况下get请求实例,在请求实例用于接收rtp数据包
|
|
|
|
|
static unordered_map<string, weak_ptr<RtspSession> > g_mapGetter;
|
|
|
|
|
//对g_mapGetter上锁保护
|
|
|
|
|
static recursive_mutex g_mtxGetter;
|
2018-12-17 15:21:23 +08:00
|
|
|
|
static int kSockFlags = SOCKET_DEFAULE_FLAGS | FLAG_MORE;
|
|
|
|
|
|
2018-12-20 10:42:51 +08:00
|
|
|
|
RtspSession::RtspSession(const Socket::Ptr &pSock) : TcpSession(pSock) {
|
2019-05-29 18:08:50 +08:00
|
|
|
|
DebugP(this);
|
2019-05-30 10:41:25 +08:00
|
|
|
|
GET_CONFIG(uint32_t,keep_alive_sec,Rtsp::kKeepAliveSecond);
|
|
|
|
|
pSock->setSendTimeOutSecond(keep_alive_sec);
|
2019-05-29 18:08:50 +08:00
|
|
|
|
//起始接收buffer缓存设置为4K,节省内存
|
|
|
|
|
pSock->setReadBuffer(std::make_shared<BufferRaw>(4 * 1024));
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RtspSession::~RtspSession() {
|
2019-05-28 18:46:52 +08:00
|
|
|
|
DebugP(this);
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RtspSession::onError(const SockException& err) {
|
2019-05-29 18:08:50 +08:00
|
|
|
|
WarnP(this) << err.what();
|
2019-03-27 18:41:52 +08:00
|
|
|
|
if (_rtpType == Rtsp::RTP_MULTICAST) {
|
2017-11-08 16:56:02 +08:00
|
|
|
|
//取消UDP端口监听
|
2018-02-23 15:36:51 +08:00
|
|
|
|
UDPServer::Instance().stopListenPeer(get_peer_ip().data(), this);
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-17 13:14:49 +08:00
|
|
|
|
if (_http_x_sessioncookie.size() != 0) {
|
|
|
|
|
//移除http getter的弱引用记录
|
|
|
|
|
lock_guard<recursive_mutex> lock(g_mtxGetter);
|
|
|
|
|
g_mapGetter.erase(_http_x_sessioncookie);
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
2018-02-06 15:28:27 +08:00
|
|
|
|
|
|
|
|
|
//流量统计事件广播
|
2019-05-28 17:14:36 +08:00
|
|
|
|
GET_CONFIG(uint32_t,iFlowThreshold,General::kFlowThreshold);
|
2018-10-24 15:43:52 +08:00
|
|
|
|
if(_ui64TotalBytes > iFlowThreshold * 1024){
|
2019-05-20 17:46:06 +08:00
|
|
|
|
bool isPlayer = !_pushSrc;
|
2018-10-09 09:36:03 +08:00
|
|
|
|
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastFlowReport,
|
2018-10-24 15:43:52 +08:00
|
|
|
|
_mediaInfo,
|
|
|
|
|
_ui64TotalBytes,
|
|
|
|
|
_ticker.createdTime()/1000,
|
2019-05-20 17:46:06 +08:00
|
|
|
|
isPlayer,
|
2018-10-09 09:36:03 +08:00
|
|
|
|
*this);
|
2018-02-06 15:28:27 +08:00
|
|
|
|
}
|
2018-12-17 13:14:49 +08:00
|
|
|
|
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RtspSession::onManager() {
|
2019-05-29 18:24:35 +08:00
|
|
|
|
GET_CONFIG(uint32_t,handshake_sec,Rtsp::kKeepAliveSecond);
|
|
|
|
|
GET_CONFIG(uint32_t,keep_alive_sec,Rtsp::kKeepAliveSecond);
|
|
|
|
|
|
|
|
|
|
if (_ticker.createdTime() > handshake_sec * 1000) {
|
2018-10-24 15:43:52 +08:00
|
|
|
|
if (_strSession.size() == 0) {
|
2019-05-29 18:08:50 +08:00
|
|
|
|
shutdown(SockException(Err_timeout,"illegal connection"));
|
2017-04-01 16:35:56 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-12-14 17:10:24 +08:00
|
|
|
|
|
2019-01-16 17:58:54 +08:00
|
|
|
|
|
2019-05-29 18:24:35 +08:00
|
|
|
|
if ((_rtpType == Rtsp::RTP_UDP || _pushSrc ) && _ticker.elapsedTime() > keep_alive_sec * 1000) {
|
2019-01-16 17:58:54 +08:00
|
|
|
|
//如果是推流端或者rtp over udp类型的播放端,那么就做超时检测
|
2019-05-29 18:08:50 +08:00
|
|
|
|
shutdown(SockException(Err_timeout,"rtp over udp session timeouted"));
|
|
|
|
|
return;
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-17 15:21:23 +08:00
|
|
|
|
void RtspSession::onRecv(const Buffer::Ptr &pBuf) {
|
|
|
|
|
_ticker.resetTime();
|
|
|
|
|
_ui64TotalBytes += pBuf->size();
|
|
|
|
|
if (_onRecv) {
|
|
|
|
|
//http poster的请求数据转发给http getter处理
|
|
|
|
|
_onRecv(pBuf);
|
|
|
|
|
} else {
|
2019-05-28 18:46:52 +08:00
|
|
|
|
// TraceP(this) << pBuf->size() << "\r\n" << pBuf->data();
|
2018-12-17 15:21:23 +08:00
|
|
|
|
input(pBuf->data(),pBuf->size());
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-09-20 18:44:32 +08:00
|
|
|
|
|
2018-12-17 15:21:23 +08:00
|
|
|
|
void RtspSession::onWholeRtspPacket(Parser &parser) {
|
|
|
|
|
string strCmd = parser.Method(); //提取出请求命令字
|
|
|
|
|
_iCseq = atoi(parser["CSeq"].data());
|
2019-05-10 18:40:30 +08:00
|
|
|
|
if(_strContentBase.empty() && strCmd != "GET"){
|
2018-12-21 17:12:26 +08:00
|
|
|
|
_strContentBase = parser.Url();
|
|
|
|
|
_mediaInfo.parse(parser.FullUrl());
|
2019-05-30 16:01:56 +08:00
|
|
|
|
_mediaInfo._schema = RTSP_SCHEMA;
|
|
|
|
|
}
|
2017-04-01 16:35:56 +08:00
|
|
|
|
|
2019-05-30 12:14:20 +08:00
|
|
|
|
typedef void (RtspSession::*rtsp_request_handler)(const Parser &parser);
|
2018-12-14 14:59:12 +08:00
|
|
|
|
static unordered_map<string, rtsp_request_handler> s_handler_map;
|
2018-08-10 11:55:18 +08:00
|
|
|
|
static onceToken token( []() {
|
2018-12-14 14:59:12 +08:00
|
|
|
|
s_handler_map.emplace("OPTIONS",&RtspSession::handleReq_Options);
|
|
|
|
|
s_handler_map.emplace("DESCRIBE",&RtspSession::handleReq_Describe);
|
|
|
|
|
s_handler_map.emplace("ANNOUNCE",&RtspSession::handleReq_ANNOUNCE);
|
2018-12-17 15:21:23 +08:00
|
|
|
|
s_handler_map.emplace("RECORD",&RtspSession::handleReq_RECORD);
|
2018-12-14 14:59:12 +08:00
|
|
|
|
s_handler_map.emplace("SETUP",&RtspSession::handleReq_Setup);
|
|
|
|
|
s_handler_map.emplace("PLAY",&RtspSession::handleReq_Play);
|
|
|
|
|
s_handler_map.emplace("PAUSE",&RtspSession::handleReq_Pause);
|
|
|
|
|
s_handler_map.emplace("TEARDOWN",&RtspSession::handleReq_Teardown);
|
|
|
|
|
s_handler_map.emplace("GET",&RtspSession::handleReq_Get);
|
|
|
|
|
s_handler_map.emplace("POST",&RtspSession::handleReq_Post);
|
|
|
|
|
s_handler_map.emplace("SET_PARAMETER",&RtspSession::handleReq_SET_PARAMETER);
|
|
|
|
|
s_handler_map.emplace("GET_PARAMETER",&RtspSession::handleReq_SET_PARAMETER);
|
2018-08-10 11:55:18 +08:00
|
|
|
|
}, []() {});
|
|
|
|
|
|
2018-12-14 14:59:12 +08:00
|
|
|
|
auto it = s_handler_map.find(strCmd);
|
2019-05-30 12:14:20 +08:00
|
|
|
|
if (it == s_handler_map.end()) {
|
|
|
|
|
sendRtspResponse("403 Forbidden");
|
2019-05-29 18:08:50 +08:00
|
|
|
|
shutdown(SockException(Err_shutdown,StrPrinter << "403 Forbidden:" << strCmd));
|
2019-05-30 12:14:20 +08:00
|
|
|
|
return;
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
2019-05-30 12:14:20 +08:00
|
|
|
|
|
|
|
|
|
auto &fun = it->second;
|
|
|
|
|
try {
|
|
|
|
|
(this->*fun)(parser);
|
|
|
|
|
}catch (SockException &ex){
|
|
|
|
|
if(ex){
|
|
|
|
|
shutdown(ex);
|
|
|
|
|
}
|
|
|
|
|
}catch (exception &ex){
|
|
|
|
|
shutdown(SockException(Err_shutdown,ex.what()));
|
|
|
|
|
}
|
|
|
|
|
parser.Clear();
|
2018-09-20 18:44:32 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-17 15:21:23 +08:00
|
|
|
|
void RtspSession::onRtpPacket(const char *data, uint64_t len) {
|
2018-12-20 17:06:33 +08:00
|
|
|
|
if(!_pushSrc){
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-04-24 12:03:20 +08:00
|
|
|
|
|
2018-12-17 15:21:23 +08:00
|
|
|
|
int trackIdx = -1;
|
|
|
|
|
uint8_t interleaved = data[1];
|
|
|
|
|
if(interleaved %2 == 0){
|
|
|
|
|
trackIdx = getTrackIndexByInterleaved(interleaved);
|
2019-05-08 14:23:18 +08:00
|
|
|
|
if (trackIdx != -1) {
|
|
|
|
|
handleOneRtp(trackIdx,_aTrackInfo[trackIdx],(unsigned char *)data + 4, len - 4);
|
|
|
|
|
}
|
|
|
|
|
}else{
|
|
|
|
|
trackIdx = getTrackIndexByInterleaved(interleaved - 1);
|
|
|
|
|
if (trackIdx != -1) {
|
2019-05-09 10:49:50 +08:00
|
|
|
|
onRtcpPacket(trackIdx, _aTrackInfo[trackIdx], (unsigned char *) data + 4, len - 4);
|
2019-05-08 14:23:18 +08:00
|
|
|
|
}
|
2018-09-20 18:44:32 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-09 10:49:50 +08:00
|
|
|
|
void RtspSession::onRtcpPacket(int iTrackidx, SdpTrack::Ptr &track, unsigned char *pucData, unsigned int uiLen){
|
2019-05-08 14:23:18 +08:00
|
|
|
|
|
|
|
|
|
}
|
2018-12-17 15:21:23 +08:00
|
|
|
|
int64_t RtspSession::getContentLength(Parser &parser) {
|
|
|
|
|
if(parser.Method() == "POST"){
|
|
|
|
|
//http post请求的content数据部分是base64编码后的rtsp请求信令包
|
|
|
|
|
return remainDataSize();
|
2018-09-20 18:44:32 +08:00
|
|
|
|
}
|
2018-12-17 15:21:23 +08:00
|
|
|
|
return RtspSplitter::getContentLength(parser);
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-17 15:21:23 +08:00
|
|
|
|
|
2019-05-30 12:14:20 +08:00
|
|
|
|
void RtspSession::handleReq_Options(const Parser &parser) {
|
2018-12-14 14:59:12 +08:00
|
|
|
|
//支持这些命令
|
2018-12-14 17:10:24 +08:00
|
|
|
|
sendRtspResponse("200 OK",{"Public" , "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, ANNOUNCE, RECORD, SET_PARAMETER, GET_PARAMETER"});
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-30 12:14:20 +08:00
|
|
|
|
void RtspSession::handleReq_ANNOUNCE(const Parser &parser) {
|
2018-12-17 15:21:23 +08:00
|
|
|
|
auto src = dynamic_pointer_cast<RtmpMediaSource>(MediaSource::find(RTSP_SCHEMA,
|
|
|
|
|
_mediaInfo._vhost,
|
|
|
|
|
_mediaInfo._app,
|
|
|
|
|
_mediaInfo._streamid,
|
|
|
|
|
false));
|
|
|
|
|
if(src){
|
|
|
|
|
sendRtspResponse("406 Not Acceptable", {"Content-Type", "text/plain"}, "Already publishing.");
|
2019-05-30 12:14:20 +08:00
|
|
|
|
string err = StrPrinter << "ANNOUNCE:"
|
|
|
|
|
<< "Already publishing:"
|
|
|
|
|
<< _mediaInfo._vhost << " "
|
|
|
|
|
<< _mediaInfo._app << " "
|
|
|
|
|
<< _mediaInfo._streamid << endl;
|
|
|
|
|
throw SockException(Err_shutdown,err);
|
2018-12-14 14:59:12 +08:00
|
|
|
|
}
|
2018-12-14 18:13:05 +08:00
|
|
|
|
|
2018-12-17 15:21:23 +08:00
|
|
|
|
_strSession = makeRandStr(12);
|
2018-12-21 17:12:26 +08:00
|
|
|
|
_strSdp = parser.Content();
|
|
|
|
|
_aTrackInfo = SdpAttr(_strSdp).getAvailableTrack();
|
2018-12-14 18:13:05 +08:00
|
|
|
|
|
2018-12-17 15:21:23 +08:00
|
|
|
|
_pushSrc = std::make_shared<RtspToRtmpMediaSource>(_mediaInfo._vhost,_mediaInfo._app,_mediaInfo._streamid);
|
|
|
|
|
_pushSrc->setListener(dynamic_pointer_cast<MediaSourceEvent>(shared_from_this()));
|
|
|
|
|
_pushSrc->onGetSDP(_strSdp);
|
|
|
|
|
sendRtspResponse("200 OK");
|
2018-12-14 14:59:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-30 12:14:20 +08:00
|
|
|
|
void RtspSession::handleReq_RECORD(const Parser &parser){
|
2018-12-17 15:21:23 +08:00
|
|
|
|
if (_aTrackInfo.empty() || parser["Session"] != _strSession) {
|
2018-12-14 17:10:24 +08:00
|
|
|
|
send_SessionNotFound();
|
2019-05-30 12:14:20 +08:00
|
|
|
|
throw SockException(Err_shutdown,_aTrackInfo.empty() ? "can not find any availabe track when record" : "session not found when record");
|
2018-12-14 17:10:24 +08:00
|
|
|
|
}
|
|
|
|
|
auto onRes = [this](const string &err){
|
|
|
|
|
bool authSuccess = err.empty();
|
|
|
|
|
if(!authSuccess){
|
|
|
|
|
sendRtspResponse("401 Unauthorized", {"Content-Type", "text/plain"}, err);
|
2019-05-29 18:08:50 +08:00
|
|
|
|
shutdown(SockException(Err_shutdown,StrPrinter << "401 Unauthorized:" << err));
|
2018-12-14 17:10:24 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_StrPrinter rtp_info;
|
|
|
|
|
for(auto &track : _aTrackInfo){
|
|
|
|
|
if (track->_inited == false) {
|
|
|
|
|
//还有track没有setup
|
2019-05-29 18:08:50 +08:00
|
|
|
|
shutdown(SockException(Err_shutdown,"track not setuped"));
|
|
|
|
|
return;
|
2018-12-14 17:10:24 +08:00
|
|
|
|
}
|
2018-12-21 17:12:26 +08:00
|
|
|
|
rtp_info << "url=" << _strContentBase << "/" << track->_control_surffix << ",";
|
2018-12-14 17:10:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rtp_info.pop_back();
|
|
|
|
|
sendRtspResponse("200 OK", {"RTP-Info",rtp_info});
|
2018-12-17 13:14:49 +08:00
|
|
|
|
SockUtil::setNoDelay(_sock->rawFD(),false);
|
2019-04-23 11:43:48 +08:00
|
|
|
|
if(_rtpType == Rtsp::RTP_TCP){
|
|
|
|
|
//如果是rtsp推流服务器,并且是TCP推流,那么加大TCP接收缓存,这样能提升接收性能
|
2019-04-23 11:52:40 +08:00
|
|
|
|
_sock->setReadBuffer(std::make_shared<BufferRaw>(256 * 1024));
|
2019-04-23 11:43:48 +08:00
|
|
|
|
}
|
2018-12-17 13:14:49 +08:00
|
|
|
|
(*this) << SocketFlags(kSockFlags);
|
2018-12-14 17:10:24 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
weak_ptr<RtspSession> weakSelf = dynamic_pointer_cast<RtspSession>(shared_from_this());
|
|
|
|
|
Broadcast::AuthInvoker invoker = [weakSelf,onRes](const string &err){
|
|
|
|
|
auto strongSelf = weakSelf.lock();
|
|
|
|
|
if(!strongSelf){
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
strongSelf->async([weakSelf,onRes,err](){
|
|
|
|
|
auto strongSelf = weakSelf.lock();
|
|
|
|
|
if(!strongSelf){
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
onRes(err);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//rtsp推流需要鉴权
|
|
|
|
|
auto flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPublish,_mediaInfo,invoker,*this);
|
|
|
|
|
if(!flag){
|
|
|
|
|
//该事件无人监听,默认不鉴权
|
|
|
|
|
onRes("");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-30 12:14:20 +08:00
|
|
|
|
void RtspSession::handleReq_Describe(const Parser &parser) {
|
2019-05-30 16:01:56 +08:00
|
|
|
|
weak_ptr<RtspSession> weakSelf = dynamic_pointer_cast<RtspSession>(shared_from_this());
|
|
|
|
|
//该请求中的认证信息
|
2019-05-27 22:32:07 +08:00
|
|
|
|
auto authorization = parser["Authorization"];
|
2019-05-30 16:01:56 +08:00
|
|
|
|
onGetRealm invoker = [weakSelf,authorization](const string &realm){
|
|
|
|
|
auto strongSelf = weakSelf.lock();
|
|
|
|
|
if(!strongSelf){
|
|
|
|
|
//本对象已经销毁
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
//切换到自己的线程然后执行
|
|
|
|
|
strongSelf->async([weakSelf,realm,authorization](){
|
|
|
|
|
auto strongSelf = weakSelf.lock();
|
|
|
|
|
if(!strongSelf){
|
|
|
|
|
//本对象已经销毁
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if(realm.empty()){
|
|
|
|
|
//无需认证,回复sdp
|
|
|
|
|
strongSelf->onAuthSuccess();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
//该流需要认证
|
|
|
|
|
strongSelf->onAuthUser(realm,authorization);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//广播是否需要认证事件
|
|
|
|
|
if(!NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastOnGetRtspRealm,
|
|
|
|
|
_mediaInfo,
|
|
|
|
|
invoker,
|
|
|
|
|
*this)){
|
|
|
|
|
//无人监听此事件,说明无需认证
|
|
|
|
|
invoker("");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void RtspSession::onAuthSuccess() {
|
|
|
|
|
TraceP(this);
|
2019-05-27 22:32:07 +08:00
|
|
|
|
weak_ptr<RtspSession> weakSelf = dynamic_pointer_cast<RtspSession>(shared_from_this());
|
2019-05-30 16:01:56 +08:00
|
|
|
|
MediaSource::findAsync(_mediaInfo,weakSelf.lock(), true,[weakSelf](const MediaSource::Ptr &src){
|
2019-05-27 22:32:07 +08:00
|
|
|
|
auto strongSelf = weakSelf.lock();
|
|
|
|
|
if(!strongSelf){
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
auto rtsp_src = dynamic_pointer_cast<RtspMediaSource>(src);
|
|
|
|
|
if (!rtsp_src) {
|
|
|
|
|
//未找到相应的MediaSource
|
2019-05-29 18:08:50 +08:00
|
|
|
|
string err = StrPrinter << "no such stream:" << strongSelf->_mediaInfo._vhost << " " << strongSelf->_mediaInfo._app << " " << strongSelf->_mediaInfo._streamid;
|
2019-05-27 22:32:07 +08:00
|
|
|
|
strongSelf->send_StreamNotFound();
|
2019-05-29 18:08:50 +08:00
|
|
|
|
strongSelf->shutdown(SockException(Err_shutdown,err));
|
2019-05-27 22:32:07 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
//找到了响应的rtsp流
|
|
|
|
|
strongSelf->_strSdp = rtsp_src->getSdp();
|
|
|
|
|
SdpAttr sdpAttr(strongSelf->_strSdp);
|
|
|
|
|
strongSelf->_aTrackInfo = sdpAttr.getAvailableTrack();
|
|
|
|
|
if (strongSelf->_aTrackInfo.empty()) {
|
|
|
|
|
//该流无效
|
|
|
|
|
strongSelf->send_StreamNotFound();
|
2019-05-30 16:03:06 +08:00
|
|
|
|
strongSelf->shutdown(SockException(Err_shutdown,"can not find any availabe track in sdp"));
|
2019-05-27 22:32:07 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
strongSelf->_strSession = makeRandStr(12);
|
|
|
|
|
strongSelf->_pMediaSrc = rtsp_src;
|
|
|
|
|
for(auto &track : strongSelf->_aTrackInfo){
|
|
|
|
|
track->_ssrc = rtsp_src->getSsrc(track->_type);
|
|
|
|
|
track->_seq = rtsp_src->getSeqence(track->_type);
|
|
|
|
|
track->_time_stamp = rtsp_src->getTimeStamp(track->_type);
|
|
|
|
|
}
|
2018-10-29 15:02:18 +08:00
|
|
|
|
|
2019-05-30 16:01:56 +08:00
|
|
|
|
strongSelf->sendRtspResponse("200 OK",
|
|
|
|
|
{"Content-Base",strongSelf->_strContentBase + "/",
|
|
|
|
|
"x-Accept-Retransmit","our-retransmit",
|
|
|
|
|
"x-Accept-Dynamic-Rate","1"
|
|
|
|
|
},strongSelf->_strSdp);
|
2018-10-29 14:59:22 +08:00
|
|
|
|
});
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
2019-05-30 12:14:20 +08:00
|
|
|
|
void RtspSession::onAuthFailed(const string &realm,const string &why,bool close) {
|
|
|
|
|
GET_CONFIG(bool,authBasic,Rtsp::kAuthBasic);
|
|
|
|
|
if (!authBasic) {
|
|
|
|
|
//我们需要客户端优先以md5方式认证
|
|
|
|
|
_strNonce = makeRandStr(32);
|
|
|
|
|
sendRtspResponse("401 Unauthorized",
|
|
|
|
|
{"WWW-Authenticate",
|
|
|
|
|
StrPrinter << "Digest realm=\"" << realm << "\",nonce=\"" << _strNonce << "\"" });
|
|
|
|
|
}else {
|
|
|
|
|
//当然我们也支持base64认证,但是我们不建议这样做
|
|
|
|
|
sendRtspResponse("401 Unauthorized",
|
|
|
|
|
{"WWW-Authenticate",
|
|
|
|
|
StrPrinter << "Basic realm=\"" << realm << "\"" });
|
|
|
|
|
}
|
|
|
|
|
if(close){
|
|
|
|
|
shutdown(SockException(Err_shutdown,StrPrinter << "401 Unauthorized:" << why));
|
2017-12-10 01:34:43 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-30 12:14:20 +08:00
|
|
|
|
void RtspSession::onAuthBasic(const string &realm,const string &strBase64){
|
2017-12-10 01:34:43 +08:00
|
|
|
|
//base64认证
|
|
|
|
|
char user_pwd_buf[512];
|
|
|
|
|
av_base64_decode((uint8_t *)user_pwd_buf,strBase64.data(),strBase64.size());
|
2018-02-02 18:06:08 +08:00
|
|
|
|
auto user_pwd_vec = split(user_pwd_buf,":");
|
2017-12-10 01:34:43 +08:00
|
|
|
|
if(user_pwd_vec.size() < 2){
|
|
|
|
|
//认证信息格式不合法,回复401 Unauthorized
|
2019-05-30 12:14:20 +08:00
|
|
|
|
onAuthFailed(realm,"can not find user and passwd when basic64 auth");
|
2017-12-10 01:34:43 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
auto user = user_pwd_vec[0];
|
|
|
|
|
auto pwd = user_pwd_vec[1];
|
2019-05-30 12:14:20 +08:00
|
|
|
|
weak_ptr<RtspSession> weakSelf = dynamic_pointer_cast<RtspSession>(shared_from_this());
|
2017-12-10 01:34:43 +08:00
|
|
|
|
onAuth invoker = [pwd,realm,weakSelf](bool encrypted,const string &good_pwd){
|
2019-05-30 12:14:20 +08:00
|
|
|
|
auto strongSelf = weakSelf.lock();
|
|
|
|
|
if(!strongSelf){
|
|
|
|
|
//本对象已经销毁
|
|
|
|
|
return;
|
2017-12-10 01:34:43 +08:00
|
|
|
|
}
|
2019-05-30 12:14:20 +08:00
|
|
|
|
//切换到自己的线程执行
|
|
|
|
|
strongSelf->async([weakSelf,good_pwd,pwd,realm](){
|
|
|
|
|
auto strongSelf = weakSelf.lock();
|
|
|
|
|
if(!strongSelf){
|
|
|
|
|
//本对象已经销毁
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
//base64忽略encrypted参数,上层必须传入明文密码
|
|
|
|
|
if(pwd == good_pwd){
|
|
|
|
|
//提供的密码且匹配正确
|
|
|
|
|
strongSelf->onAuthSuccess();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
//密码错误
|
|
|
|
|
strongSelf->onAuthFailed(realm,StrPrinter << "password mismatch when base64 auth:" << pwd << " != " << good_pwd);
|
|
|
|
|
});
|
2017-12-10 01:34:43 +08:00
|
|
|
|
};
|
2018-02-09 15:50:21 +08:00
|
|
|
|
|
2017-12-10 01:34:43 +08:00
|
|
|
|
//此时必须提供明文密码
|
2019-05-30 12:14:20 +08:00
|
|
|
|
if(!NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastOnRtspAuth,_mediaInfo,realm,user, true,invoker,*this)){
|
2017-12-10 01:34:43 +08:00
|
|
|
|
//表明该流需要认证却没监听请求密码事件,这一般是大意的程序所为,警告之
|
2019-05-30 12:14:20 +08:00
|
|
|
|
WarnP(this) << "请监听kBroadcastOnRtspAuth事件!";
|
2017-12-10 01:34:43 +08:00
|
|
|
|
//但是我们还是忽略认证以便完成播放
|
|
|
|
|
//我们输入的密码是明文
|
|
|
|
|
invoker(false,pwd);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-02-02 18:06:08 +08:00
|
|
|
|
|
2019-05-30 12:14:20 +08:00
|
|
|
|
void RtspSession::onAuthDigest(const string &realm,const string &strMd5){
|
|
|
|
|
DebugP(this) << strMd5;
|
2017-12-10 01:34:43 +08:00
|
|
|
|
auto mapTmp = Parser::parseArgs(strMd5,",","=");
|
|
|
|
|
decltype(mapTmp) map;
|
|
|
|
|
for(auto &pr : mapTmp){
|
2018-02-02 18:06:08 +08:00
|
|
|
|
map[trim(string(pr.first)," \"")] = trim(pr.second," \"");
|
2017-12-10 01:34:43 +08:00
|
|
|
|
}
|
|
|
|
|
//check realm
|
|
|
|
|
if(realm != map["realm"]){
|
2019-05-30 12:14:20 +08:00
|
|
|
|
onAuthFailed(realm,StrPrinter << "realm not mached:" << realm << " != " << map["realm"]);
|
2017-12-10 01:34:43 +08:00
|
|
|
|
return ;
|
|
|
|
|
}
|
|
|
|
|
//check nonce
|
|
|
|
|
auto nonce = map["nonce"];
|
2019-05-30 12:14:20 +08:00
|
|
|
|
if(_strNonce != nonce){
|
|
|
|
|
onAuthFailed(realm,StrPrinter << "nonce not mached:" << nonce << " != " << _strNonce);
|
2017-12-10 01:34:43 +08:00
|
|
|
|
return ;
|
|
|
|
|
}
|
|
|
|
|
//check username and uri
|
|
|
|
|
auto username = map["username"];
|
|
|
|
|
auto uri = map["uri"];
|
|
|
|
|
auto response = map["response"];
|
|
|
|
|
if(username.empty() || uri.empty() || response.empty()){
|
2019-05-30 12:14:20 +08:00
|
|
|
|
onAuthFailed(realm,StrPrinter << "username/uri/response empty:" << username << "," << uri << "," << response);
|
2017-12-10 01:34:43 +08:00
|
|
|
|
return ;
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-30 12:14:20 +08:00
|
|
|
|
auto realInvoker = [this,realm,nonce,uri,username,response](bool ignoreAuth,bool encrypted,const string &good_pwd){
|
2017-12-10 01:34:43 +08:00
|
|
|
|
if(ignoreAuth){
|
|
|
|
|
//忽略认证
|
2019-05-30 12:14:20 +08:00
|
|
|
|
TraceP(this) << "auth ignored";
|
|
|
|
|
onAuthSuccess();
|
2017-12-10 01:34:43 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
/*
|
|
|
|
|
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) );
|
|
|
|
|
*/
|
|
|
|
|
auto encrypted_pwd = good_pwd;
|
|
|
|
|
if(!encrypted){
|
|
|
|
|
//提供的是明文密码
|
|
|
|
|
encrypted_pwd = MD5(username+ ":" + realm + ":" + good_pwd).hexdigest();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto good_response = MD5( encrypted_pwd + ":" + nonce + ":" + MD5(string("DESCRIBE") + ":" + uri).hexdigest()).hexdigest();
|
2018-11-27 11:05:44 +08:00
|
|
|
|
if(strcasecmp(good_response.data(),response.data()) == 0){
|
2017-12-10 01:34:43 +08:00
|
|
|
|
//认证成功!md5不区分大小写
|
2019-05-30 12:14:20 +08:00
|
|
|
|
onAuthSuccess();
|
2017-12-10 01:34:43 +08:00
|
|
|
|
}else{
|
|
|
|
|
//认证失败!
|
2019-05-30 12:14:20 +08:00
|
|
|
|
onAuthFailed(realm, StrPrinter << "password mismatch when md5 auth:" << good_response << " != " << response );
|
2017-12-10 01:34:43 +08:00
|
|
|
|
}
|
|
|
|
|
};
|
2019-05-30 12:14:20 +08:00
|
|
|
|
|
|
|
|
|
weak_ptr<RtspSession> weakSelf = dynamic_pointer_cast<RtspSession>(shared_from_this());
|
|
|
|
|
onAuth invoker = [realInvoker,weakSelf](bool encrypted,const string &good_pwd){
|
|
|
|
|
auto strongSelf = weakSelf.lock();
|
|
|
|
|
if(!strongSelf){
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
//切换到自己的线程确保realInvoker执行时,this指针有效
|
|
|
|
|
strongSelf->async([realInvoker,weakSelf,encrypted,good_pwd](){
|
|
|
|
|
auto strongSelf = weakSelf.lock();
|
|
|
|
|
if(!strongSelf){
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
realInvoker(false,encrypted,good_pwd);
|
|
|
|
|
});
|
2017-12-10 01:34:43 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//此时可以提供明文或md5加密的密码
|
2019-05-30 12:14:20 +08:00
|
|
|
|
if(!NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastOnRtspAuth,_mediaInfo,realm,username, false,invoker,*this)){
|
2017-12-10 01:34:43 +08:00
|
|
|
|
//表明该流需要认证却没监听请求密码事件,这一般是大意的程序所为,警告之
|
2019-05-30 12:14:20 +08:00
|
|
|
|
WarnP(this) << "请监听kBroadcastOnRtspAuth事件!";
|
2017-12-10 01:34:43 +08:00
|
|
|
|
//但是我们还是忽略认证以便完成播放
|
|
|
|
|
realInvoker(true,true,"");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-30 12:14:20 +08:00
|
|
|
|
void RtspSession::onAuthUser(const string &realm,const string &authorization){
|
|
|
|
|
if(authorization.empty()){
|
|
|
|
|
onAuthFailed(realm,"", false);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2017-12-10 01:34:43 +08:00
|
|
|
|
//请求中包含认证信息
|
|
|
|
|
auto authType = FindField(authorization.data(),NULL," ");
|
|
|
|
|
auto authStr = FindField(authorization.data()," ",NULL);
|
|
|
|
|
if(authType.empty() || authStr.empty()){
|
|
|
|
|
//认证信息格式不合法,回复401 Unauthorized
|
2019-05-30 12:14:20 +08:00
|
|
|
|
onAuthFailed(realm,"can not find auth type or auth string");
|
2017-12-10 01:34:43 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if(authType == "Basic"){
|
|
|
|
|
//base64认证,需要明文密码
|
2019-05-30 12:14:20 +08:00
|
|
|
|
onAuthBasic(realm,authStr);
|
2017-12-10 01:34:43 +08:00
|
|
|
|
}else if(authType == "Digest"){
|
|
|
|
|
//md5认证
|
2019-05-30 12:14:20 +08:00
|
|
|
|
onAuthDigest(realm,authStr);
|
2017-12-10 01:34:43 +08:00
|
|
|
|
}else{
|
|
|
|
|
//其他认证方式?不支持!
|
2019-05-30 12:14:20 +08:00
|
|
|
|
onAuthFailed(realm,StrPrinter << "unsupported auth type:" << authType);
|
2017-12-10 01:34:43 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-04-01 16:35:56 +08:00
|
|
|
|
inline void RtspSession::send_StreamNotFound() {
|
2018-12-14 14:59:12 +08:00
|
|
|
|
sendRtspResponse("404 Stream Not Found",{"Connection","Close"});
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
inline void RtspSession::send_UnsupportedTransport() {
|
2018-12-14 14:59:12 +08:00
|
|
|
|
sendRtspResponse("461 Unsupported Transport",{"Connection","Close"});
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void RtspSession::send_SessionNotFound() {
|
2018-12-14 14:59:12 +08:00
|
|
|
|
sendRtspResponse("454 Session Not Found",{"Connection","Close"});
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
2019-05-30 12:14:20 +08:00
|
|
|
|
|
|
|
|
|
void RtspSession::handleReq_Setup(const Parser &parser) {
|
2017-04-01 16:35:56 +08:00
|
|
|
|
//处理setup命令,该函数可能进入多次
|
2019-01-11 10:45:03 +08:00
|
|
|
|
auto controlSuffix = split(parser.Url(),"/").back();// parser.FullUrl().substr(_strContentBase.size());
|
2018-12-21 17:12:26 +08:00
|
|
|
|
if(controlSuffix.front() == '/'){
|
|
|
|
|
controlSuffix = controlSuffix.substr(1);
|
|
|
|
|
}
|
2018-03-26 18:56:22 +08:00
|
|
|
|
int trackIdx = getTrackIndexByControlSuffix(controlSuffix);
|
2017-04-01 16:35:56 +08:00
|
|
|
|
if (trackIdx == -1) {
|
|
|
|
|
//未找到相应track
|
2019-05-30 12:14:20 +08:00
|
|
|
|
throw SockException(Err_shutdown, StrPrinter << "can not find any track by control suffix:" << controlSuffix);
|
|
|
|
|
}
|
2018-10-26 09:56:29 +08:00
|
|
|
|
SdpTrack::Ptr &trackRef = _aTrackInfo[trackIdx];
|
2018-10-26 10:59:13 +08:00
|
|
|
|
if (trackRef->_inited) {
|
2017-04-01 16:35:56 +08:00
|
|
|
|
//已经初始化过该Track
|
2019-05-30 12:14:20 +08:00
|
|
|
|
throw SockException(Err_shutdown, "can not setup one track twice");
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
2018-10-26 10:59:13 +08:00
|
|
|
|
trackRef->_inited = true; //现在初始化
|
2017-04-01 16:35:56 +08:00
|
|
|
|
|
2019-03-27 18:41:52 +08:00
|
|
|
|
if(_rtpType == Rtsp::RTP_Invalid){
|
2019-06-27 12:21:01 +08:00
|
|
|
|
auto &strTransport = parser["Transport"];
|
2017-04-01 16:35:56 +08:00
|
|
|
|
if(strTransport.find("TCP") != string::npos){
|
2019-03-27 18:41:52 +08:00
|
|
|
|
_rtpType = Rtsp::RTP_TCP;
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}else if(strTransport.find("multicast") != string::npos){
|
2019-03-27 18:41:52 +08:00
|
|
|
|
_rtpType = Rtsp::RTP_MULTICAST;
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}else{
|
2019-03-27 18:41:52 +08:00
|
|
|
|
_rtpType = Rtsp::RTP_UDP;
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-27 12:21:01 +08:00
|
|
|
|
//允许接收rtp、rtcp包
|
2019-03-27 18:41:52 +08:00
|
|
|
|
RtspSplitter::enableRecvRtp(_rtpType == Rtsp::RTP_TCP);
|
2018-12-17 15:21:23 +08:00
|
|
|
|
|
2018-10-24 15:43:52 +08:00
|
|
|
|
switch (_rtpType) {
|
2019-03-27 18:41:52 +08:00
|
|
|
|
case Rtsp::RTP_TCP: {
|
2019-06-27 12:21:01 +08:00
|
|
|
|
if(_pushSrc){
|
|
|
|
|
//rtsp推流时,interleaved由推流者决定
|
|
|
|
|
auto key_values = Parser::parseArgs(parser["Transport"],";","=");
|
|
|
|
|
int interleaved_rtp = -1 , interleaved_rtcp = -1;
|
|
|
|
|
if(2 == sscanf(key_values["interleaved"].data(),"%d-%d",&interleaved_rtp,&interleaved_rtcp)){
|
|
|
|
|
trackRef->_interleaved = interleaved_rtp;
|
|
|
|
|
}else{
|
|
|
|
|
throw SockException(Err_shutdown, "can not find interleaved when setup of rtp over tcp");
|
|
|
|
|
}
|
|
|
|
|
}else{
|
|
|
|
|
//rtsp播放时,由于数据共享分发,所以interleaved必须由服务器决定
|
|
|
|
|
trackRef->_interleaved = 2 * trackRef->_type;
|
|
|
|
|
}
|
2018-12-14 14:59:12 +08:00
|
|
|
|
sendRtspResponse("200 OK",
|
|
|
|
|
{"Transport",StrPrinter << "RTP/AVP/TCP;unicast;"
|
2019-06-27 12:21:01 +08:00
|
|
|
|
<< "interleaved=" << (int)trackRef->_interleaved << "-" << (int)trackRef->_interleaved + 1 << ";"
|
2018-12-17 13:14:49 +08:00
|
|
|
|
<< "ssrc=" << printSSRC(trackRef->_ssrc),
|
2018-12-14 14:59:12 +08:00
|
|
|
|
"x-Transport-Options" , "late-tolerance=1.400000",
|
|
|
|
|
"x-Dynamic-Rate" , "1"
|
|
|
|
|
});
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
break;
|
2019-03-27 18:41:52 +08:00
|
|
|
|
case Rtsp::RTP_UDP: {
|
2017-11-08 16:56:02 +08:00
|
|
|
|
//我们用trackIdx区分rtp和rtcp包
|
2018-12-14 17:10:24 +08:00
|
|
|
|
auto pSockRtp = std::make_shared<Socket>(_sock->getPoller());
|
|
|
|
|
if (!pSockRtp->bindUdpSock(0,get_local_ip().data())) {
|
2017-11-08 16:56:02 +08:00
|
|
|
|
//分配端口失败
|
|
|
|
|
send_NotAcceptable();
|
2019-05-30 12:14:20 +08:00
|
|
|
|
throw SockException(Err_shutdown, "open rtp socket failed");
|
|
|
|
|
}
|
2018-12-14 17:10:24 +08:00
|
|
|
|
auto pSockRtcp = std::make_shared<Socket>(_sock->getPoller());
|
|
|
|
|
if (!pSockRtcp->bindUdpSock(pSockRtp->get_local_port() + 1,get_local_ip().data())) {
|
2017-04-01 16:35:56 +08:00
|
|
|
|
//分配端口失败
|
|
|
|
|
send_NotAcceptable();
|
2019-05-30 12:14:20 +08:00
|
|
|
|
throw SockException(Err_shutdown, "open rtcp socket failed");
|
|
|
|
|
}
|
2018-12-14 17:10:24 +08:00
|
|
|
|
_apRtpSock[trackIdx] = pSockRtp;
|
|
|
|
|
_apRtcpSock[trackIdx] = pSockRtcp;
|
2017-11-08 16:56:02 +08:00
|
|
|
|
//设置客户端内网端口信息
|
2018-12-17 15:21:23 +08:00
|
|
|
|
string strClientPort = FindField(parser["Transport"].data(), "client_port=", NULL);
|
2019-05-08 16:19:00 +08:00
|
|
|
|
uint16_t ui16RtpPort = atoi( FindField(strClientPort.data(), NULL, "-").data());
|
|
|
|
|
uint16_t ui16RtcpPort = atoi( FindField(strClientPort.data(), "-" , NULL).data());
|
|
|
|
|
|
|
|
|
|
struct sockaddr_in peerAddr;
|
|
|
|
|
//设置rtp发送目标地址
|
2017-04-01 16:35:56 +08:00
|
|
|
|
peerAddr.sin_family = AF_INET;
|
2019-05-08 16:19:00 +08:00
|
|
|
|
peerAddr.sin_port = htons(ui16RtpPort);
|
2018-02-23 15:36:51 +08:00
|
|
|
|
peerAddr.sin_addr.s_addr = inet_addr(get_peer_ip().data());
|
2017-04-01 16:35:56 +08:00
|
|
|
|
bzero(&(peerAddr.sin_zero), sizeof peerAddr.sin_zero);
|
2019-03-18 18:44:16 +08:00
|
|
|
|
pSockRtp->setSendPeerAddr((struct sockaddr *)(&peerAddr));
|
2019-05-08 16:19:00 +08:00
|
|
|
|
|
|
|
|
|
//设置rtcp发送目标地址
|
|
|
|
|
peerAddr.sin_family = AF_INET;
|
|
|
|
|
peerAddr.sin_port = htons(ui16RtcpPort);
|
|
|
|
|
peerAddr.sin_addr.s_addr = inet_addr(get_peer_ip().data());
|
|
|
|
|
bzero(&(peerAddr.sin_zero), sizeof peerAddr.sin_zero);
|
|
|
|
|
pSockRtcp->setSendPeerAddr((struct sockaddr *)(&peerAddr));
|
|
|
|
|
|
2017-11-08 16:56:02 +08:00
|
|
|
|
//尝试获取客户端nat映射地址
|
2018-12-14 17:10:24 +08:00
|
|
|
|
startListenPeerUdpData(trackIdx);
|
2019-05-28 18:46:52 +08:00
|
|
|
|
//InfoP(this) << "分配端口:" << srv_port;
|
2018-12-14 14:59:12 +08:00
|
|
|
|
|
|
|
|
|
sendRtspResponse("200 OK",
|
|
|
|
|
{"Transport",StrPrinter << "RTP/AVP/UDP;unicast;"
|
|
|
|
|
<< "client_port=" << strClientPort << ";"
|
|
|
|
|
<< "server_port=" << pSockRtp->get_local_port() << "-" << pSockRtcp->get_local_port() << ";"
|
2018-12-17 13:14:49 +08:00
|
|
|
|
<< "ssrc=" << printSSRC(trackRef->_ssrc)
|
2018-12-14 14:59:12 +08:00
|
|
|
|
});
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
break;
|
2019-03-27 18:41:52 +08:00
|
|
|
|
case Rtsp::RTP_MULTICAST: {
|
2018-10-24 15:43:52 +08:00
|
|
|
|
if(!_pBrdcaster){
|
2019-05-10 18:33:23 +08:00
|
|
|
|
_pBrdcaster = RtpBroadCaster::get(getPoller(),get_local_ip(),_mediaInfo._vhost, _mediaInfo._app, _mediaInfo._streamid);
|
2018-10-24 15:43:52 +08:00
|
|
|
|
if (!_pBrdcaster) {
|
2017-04-01 16:35:56 +08:00
|
|
|
|
send_NotAcceptable();
|
2019-05-30 12:14:20 +08:00
|
|
|
|
throw SockException(Err_shutdown, "can not get a available udp multicast socket");
|
|
|
|
|
}
|
2017-04-01 16:35:56 +08:00
|
|
|
|
weak_ptr<RtspSession> weakSelf = dynamic_pointer_cast<RtspSession>(shared_from_this());
|
2018-10-24 15:43:52 +08:00
|
|
|
|
_pBrdcaster->setDetachCB(this, [weakSelf]() {
|
2017-04-01 16:35:56 +08:00
|
|
|
|
auto strongSelf = weakSelf.lock();
|
|
|
|
|
if(!strongSelf) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-05-29 18:08:50 +08:00
|
|
|
|
strongSelf->safeShutdown(SockException(Err_shutdown,"ring buffer detached"));
|
2017-04-01 16:35:56 +08:00
|
|
|
|
});
|
|
|
|
|
}
|
2018-10-26 10:59:13 +08:00
|
|
|
|
int iSrvPort = _pBrdcaster->getPort(trackRef->_type);
|
2017-11-08 16:56:02 +08:00
|
|
|
|
//我们用trackIdx区分rtp和rtcp包
|
2018-12-14 17:10:24 +08:00
|
|
|
|
//由于组播udp端口是共享的,而rtcp端口为组播udp端口+1,所以rtcp端口需要改成共享端口
|
2019-05-31 18:01:38 +08:00
|
|
|
|
auto pSockRtcp = UDPServer::Instance().getSock(getPoller(),get_local_ip().data(),2*trackIdx + 1,iSrvPort + 1);
|
2017-11-08 16:56:02 +08:00
|
|
|
|
if (!pSockRtcp) {
|
|
|
|
|
//分配端口失败
|
|
|
|
|
send_NotAcceptable();
|
2019-05-30 12:14:20 +08:00
|
|
|
|
throw SockException(Err_shutdown, "open shared rtcp socket failed");
|
2017-11-08 16:56:02 +08:00
|
|
|
|
}
|
2018-12-14 17:10:24 +08:00
|
|
|
|
startListenPeerUdpData(trackIdx);
|
2019-05-28 17:14:36 +08:00
|
|
|
|
GET_CONFIG(uint32_t,udpTTL,MultiCast::kUdpTTL);
|
2018-12-14 14:59:12 +08:00
|
|
|
|
|
|
|
|
|
sendRtspResponse("200 OK",
|
|
|
|
|
{"Transport",StrPrinter << "RTP/AVP;multicast;"
|
|
|
|
|
<< "destination=" << _pBrdcaster->getIP() << ";"
|
|
|
|
|
<< "source=" << get_local_ip() << ";"
|
|
|
|
|
<< "port=" << iSrvPort << "-" << pSockRtcp->get_local_port() << ";"
|
|
|
|
|
<< "ttl=" << udpTTL << ";"
|
|
|
|
|
<< "ssrc=" << printSSRC(trackRef->_ssrc)
|
|
|
|
|
});
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-30 12:14:20 +08:00
|
|
|
|
void RtspSession::handleReq_Play(const Parser &parser) {
|
2018-12-17 15:21:23 +08:00
|
|
|
|
if (_aTrackInfo.empty() || parser["Session"] != _strSession) {
|
2017-04-01 16:35:56 +08:00
|
|
|
|
send_SessionNotFound();
|
2019-05-30 12:14:20 +08:00
|
|
|
|
throw SockException(Err_shutdown,_aTrackInfo.empty() ? "can not find any availabe track when play" : "session not found when play");
|
|
|
|
|
}
|
2018-12-17 15:21:23 +08:00
|
|
|
|
auto strRange = parser["Range"];
|
2018-05-22 18:41:56 +08:00
|
|
|
|
auto onRes = [this,strRange](const string &err){
|
2018-02-06 16:17:37 +08:00
|
|
|
|
bool authSuccess = err.empty();
|
2018-10-26 22:48:03 +08:00
|
|
|
|
if(!authSuccess){
|
2018-02-05 15:56:44 +08:00
|
|
|
|
//第一次play是播放,否则是恢复播放。只对播放鉴权
|
2018-12-14 14:59:12 +08:00
|
|
|
|
sendRtspResponse("401 Unauthorized", {"Content-Type", "text/plain"}, err);
|
2019-05-29 18:08:50 +08:00
|
|
|
|
shutdown(SockException(Err_shutdown,StrPrinter << "401 Unauthorized:" << err));
|
2018-02-05 15:56:44 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2017-04-01 16:35:56 +08:00
|
|
|
|
|
2018-10-24 15:43:52 +08:00
|
|
|
|
auto pMediaSrc = _pMediaSrc.lock();
|
2018-10-26 22:32:50 +08:00
|
|
|
|
if(!pMediaSrc){
|
|
|
|
|
send_StreamNotFound();
|
2019-05-29 18:08:50 +08:00
|
|
|
|
shutdown(SockException(Err_shutdown,"rtsp stream released"));
|
2018-10-26 22:32:50 +08:00
|
|
|
|
return;
|
2018-02-05 15:56:44 +08:00
|
|
|
|
}
|
2018-10-26 22:32:50 +08:00
|
|
|
|
|
2018-10-29 17:19:14 +08:00
|
|
|
|
bool useBuf = true;
|
|
|
|
|
_enableSendRtp = false;
|
|
|
|
|
|
2018-10-26 22:32:50 +08:00
|
|
|
|
if (strRange.size() && !_bFirstPlay) {
|
2018-10-26 22:48:03 +08:00
|
|
|
|
//这个是seek操作
|
2018-10-26 22:32:50 +08:00
|
|
|
|
auto strStart = FindField(strRange.data(), "npt=", "-");
|
|
|
|
|
if (strStart == "now") {
|
|
|
|
|
strStart = "0";
|
|
|
|
|
}
|
|
|
|
|
auto iStartTime = 1000 * atof(strStart.data());
|
2019-05-28 18:46:52 +08:00
|
|
|
|
InfoP(this) << "rtsp seekTo(ms):" << iStartTime;
|
2018-10-29 17:19:14 +08:00
|
|
|
|
useBuf = !pMediaSrc->seekTo(iStartTime);
|
2019-05-27 16:09:29 +08:00
|
|
|
|
}else if(pMediaSrc->readerCount() == 0){
|
2018-10-26 22:32:50 +08:00
|
|
|
|
//第一个消费者
|
|
|
|
|
pMediaSrc->seekTo(0);
|
|
|
|
|
}
|
|
|
|
|
_bFirstPlay = false;
|
2018-02-05 15:56:44 +08:00
|
|
|
|
|
2018-12-14 14:59:12 +08:00
|
|
|
|
_StrPrinter rtp_info;
|
2018-10-26 10:12:37 +08:00
|
|
|
|
for(auto &track : _aTrackInfo){
|
2018-10-26 10:59:13 +08:00
|
|
|
|
if (track->_inited == false) {
|
2018-10-26 10:12:37 +08:00
|
|
|
|
//还有track没有setup
|
2019-05-29 18:08:50 +08:00
|
|
|
|
shutdown(SockException(Err_shutdown,"track not setuped"));
|
|
|
|
|
return;
|
2018-10-26 10:12:37 +08:00
|
|
|
|
}
|
2018-10-26 22:32:50 +08:00
|
|
|
|
track->_ssrc = pMediaSrc->getSsrc(track->_type);
|
|
|
|
|
track->_seq = pMediaSrc->getSeqence(track->_type);
|
|
|
|
|
track->_time_stamp = pMediaSrc->getTimeStamp(track->_type);
|
|
|
|
|
|
2018-12-21 17:12:26 +08:00
|
|
|
|
rtp_info << "url=" << _strContentBase << "/" << track->_control_surffix << ";"
|
2018-12-14 14:59:12 +08:00
|
|
|
|
<< "seq=" << track->_seq << ";"
|
|
|
|
|
<< "rtptime=" << (int)(track->_time_stamp * (track->_samplerate / 1000)) << ",";
|
2018-10-26 10:12:37 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-14 14:59:12 +08:00
|
|
|
|
rtp_info.pop_back();
|
|
|
|
|
|
|
|
|
|
sendRtspResponse("200 OK",
|
|
|
|
|
{"Range", StrPrinter << "npt=" << setiosflags(ios::fixed) << setprecision(2) << pMediaSrc->getTimeStamp(TrackInvalid) / 1000.0,
|
|
|
|
|
"RTP-Info",rtp_info
|
|
|
|
|
});
|
2018-09-26 23:12:03 +08:00
|
|
|
|
|
2018-10-29 17:19:14 +08:00
|
|
|
|
_enableSendRtp = true;
|
|
|
|
|
|
2018-09-26 23:12:03 +08:00
|
|
|
|
//提高发送性能
|
2018-12-17 13:14:49 +08:00
|
|
|
|
SockUtil::setNoDelay(_sock->rawFD(),false);
|
|
|
|
|
(*this) << SocketFlags(kSockFlags);
|
2018-10-26 22:48:03 +08:00
|
|
|
|
|
2019-03-27 18:41:52 +08:00
|
|
|
|
if (!_pRtpReader && _rtpType != Rtsp::RTP_MULTICAST) {
|
2018-10-29 17:19:14 +08:00
|
|
|
|
weak_ptr<RtspSession> weakSelf = dynamic_pointer_cast<RtspSession>(shared_from_this());
|
2019-03-21 22:28:12 +08:00
|
|
|
|
_pRtpReader = pMediaSrc->getRing()->attach(getPoller(),useBuf);
|
2018-10-29 17:19:14 +08:00
|
|
|
|
_pRtpReader->setDetachCB([weakSelf]() {
|
|
|
|
|
auto strongSelf = weakSelf.lock();
|
|
|
|
|
if(!strongSelf) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-05-29 18:08:50 +08:00
|
|
|
|
strongSelf->shutdown(SockException(Err_shutdown,"rtsp ring buffer detached"));
|
|
|
|
|
});
|
2018-10-29 17:19:14 +08:00
|
|
|
|
_pRtpReader->setReadCB([weakSelf](const RtpPacket::Ptr &pack) {
|
|
|
|
|
auto strongSelf = weakSelf.lock();
|
|
|
|
|
if(!strongSelf) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-03-21 22:28:12 +08:00
|
|
|
|
if(strongSelf->_enableSendRtp) {
|
|
|
|
|
strongSelf->sendRtpPacket(pack);
|
|
|
|
|
}
|
2018-10-29 17:19:14 +08:00
|
|
|
|
});
|
|
|
|
|
}
|
2018-02-05 15:56:44 +08:00
|
|
|
|
};
|
2017-04-01 16:35:56 +08:00
|
|
|
|
|
2018-02-05 15:56:44 +08:00
|
|
|
|
weak_ptr<RtspSession> weakSelf = dynamic_pointer_cast<RtspSession>(shared_from_this());
|
2018-02-06 16:17:37 +08:00
|
|
|
|
Broadcast::AuthInvoker invoker = [weakSelf,onRes](const string &err){
|
2018-02-05 15:56:44 +08:00
|
|
|
|
auto strongSelf = weakSelf.lock();
|
|
|
|
|
if(!strongSelf){
|
|
|
|
|
return;
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
2018-02-06 16:17:37 +08:00
|
|
|
|
strongSelf->async([weakSelf,onRes,err](){
|
2018-02-05 15:56:44 +08:00
|
|
|
|
auto strongSelf = weakSelf.lock();
|
|
|
|
|
if(!strongSelf){
|
|
|
|
|
return;
|
|
|
|
|
}
|
2018-02-06 16:17:37 +08:00
|
|
|
|
onRes(err);
|
2018-02-05 15:56:44 +08:00
|
|
|
|
});
|
|
|
|
|
};
|
2018-10-26 22:48:03 +08:00
|
|
|
|
if(_bFirstPlay){
|
|
|
|
|
//第一次收到play命令,需要鉴权
|
|
|
|
|
auto flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPlayed,_mediaInfo,invoker,*this);
|
|
|
|
|
if(!flag){
|
|
|
|
|
//该事件无人监听,默认不鉴权
|
|
|
|
|
onRes("");
|
|
|
|
|
}
|
|
|
|
|
}else{
|
|
|
|
|
//后面是seek或恢复命令,不需要鉴权
|
2018-02-06 16:17:37 +08:00
|
|
|
|
onRes("");
|
2018-02-05 15:56:44 +08:00
|
|
|
|
}
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-30 12:14:20 +08:00
|
|
|
|
void RtspSession::handleReq_Pause(const Parser &parser) {
|
2018-12-17 15:21:23 +08:00
|
|
|
|
if (parser["Session"] != _strSession) {
|
2017-04-01 16:35:56 +08:00
|
|
|
|
send_SessionNotFound();
|
2019-05-30 12:14:20 +08:00
|
|
|
|
throw SockException(Err_shutdown,"session not found when pause");
|
|
|
|
|
}
|
2018-12-14 14:59:12 +08:00
|
|
|
|
|
|
|
|
|
sendRtspResponse("200 OK");
|
2018-10-29 17:19:14 +08:00
|
|
|
|
_enableSendRtp = false;
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-30 12:14:20 +08:00
|
|
|
|
void RtspSession::handleReq_Teardown(const Parser &parser) {
|
2018-12-14 14:59:12 +08:00
|
|
|
|
sendRtspResponse("200 OK");
|
2019-05-30 12:14:20 +08:00
|
|
|
|
throw SockException(Err_shutdown,"rtsp player send teardown request");
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-30 12:14:20 +08:00
|
|
|
|
void RtspSession::handleReq_Get(const Parser &parser) {
|
2018-12-17 15:21:23 +08:00
|
|
|
|
_http_x_sessioncookie = parser["x-sessioncookie"];
|
2018-12-14 14:59:12 +08:00
|
|
|
|
sendRtspResponse("200 OK",
|
2019-05-31 10:16:31 +08:00
|
|
|
|
{"Cache-Control","no-store",
|
2018-12-14 14:59:12 +08:00
|
|
|
|
"Pragma","no-store",
|
|
|
|
|
"Content-Type","application/x-rtsp-tunnelled",
|
|
|
|
|
},"","HTTP/1.0");
|
|
|
|
|
|
2018-12-17 13:14:49 +08:00
|
|
|
|
//注册http getter,以便http poster绑定
|
2017-04-01 16:35:56 +08:00
|
|
|
|
lock_guard<recursive_mutex> lock(g_mtxGetter);
|
2018-12-17 13:14:49 +08:00
|
|
|
|
g_mapGetter[_http_x_sessioncookie] = dynamic_pointer_cast<RtspSession>(shared_from_this());
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-30 12:14:20 +08:00
|
|
|
|
void RtspSession::handleReq_Post(const Parser &parser) {
|
2017-04-01 16:35:56 +08:00
|
|
|
|
lock_guard<recursive_mutex> lock(g_mtxGetter);
|
2018-12-17 15:21:23 +08:00
|
|
|
|
string sessioncookie = parser["x-sessioncookie"];
|
2018-12-17 13:14:49 +08:00
|
|
|
|
//Poster 找到 Getter
|
2017-04-01 16:35:56 +08:00
|
|
|
|
auto it = g_mapGetter.find(sessioncookie);
|
|
|
|
|
if (it == g_mapGetter.end()) {
|
2019-05-30 12:14:20 +08:00
|
|
|
|
throw SockException(Err_shutdown,"can not find http getter by x-sessioncookie");
|
|
|
|
|
}
|
2018-12-17 13:14:49 +08:00
|
|
|
|
|
|
|
|
|
//Poster 找到Getter的SOCK
|
|
|
|
|
auto httpGetterWeak = it->second;
|
|
|
|
|
//移除http getter的弱引用记录
|
2017-04-01 16:35:56 +08:00
|
|
|
|
g_mapGetter.erase(sessioncookie);
|
2018-12-17 13:14:49 +08:00
|
|
|
|
|
|
|
|
|
//http poster收到请求后转发给http getter处理
|
|
|
|
|
_onRecv = [this,httpGetterWeak](const Buffer::Ptr &pBuf){
|
|
|
|
|
auto httpGetterStrong = httpGetterWeak.lock();
|
|
|
|
|
if(!httpGetterStrong){
|
2019-05-29 18:08:50 +08:00
|
|
|
|
shutdown(SockException(Err_shutdown,"http getter released"));
|
2018-12-17 13:14:49 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//切换到http getter的线程
|
|
|
|
|
httpGetterStrong->async([pBuf,httpGetterWeak](){
|
|
|
|
|
auto httpGetterStrong = httpGetterWeak.lock();
|
|
|
|
|
if(!httpGetterStrong){
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
httpGetterStrong->onRecv(std::make_shared<BufferString>(decodeBase64(string(pBuf->data(),pBuf->size()))));
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2018-12-17 15:21:23 +08:00
|
|
|
|
if(!parser.Content().empty()){
|
|
|
|
|
//http poster后面的粘包
|
|
|
|
|
_onRecv(std::make_shared<BufferString>(parser.Content()));
|
|
|
|
|
}
|
2019-05-31 10:16:31 +08:00
|
|
|
|
|
|
|
|
|
sendRtspResponse("200 OK",
|
|
|
|
|
{"Cache-Control","no-store",
|
|
|
|
|
"Pragma","no-store",
|
|
|
|
|
"Content-Type","application/x-rtsp-tunnelled",
|
|
|
|
|
},"","HTTP/1.0");
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-05-30 12:14:20 +08:00
|
|
|
|
void RtspSession::handleReq_SET_PARAMETER(const Parser &parser) {
|
2019-05-28 18:46:52 +08:00
|
|
|
|
//TraceP(this) <<endl;
|
2018-12-14 14:59:12 +08:00
|
|
|
|
sendRtspResponse("200 OK");
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void RtspSession::send_NotAcceptable() {
|
2018-12-14 14:59:12 +08:00
|
|
|
|
sendRtspResponse("406 Not Acceptable",{"Connection","Close"});
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
2018-02-02 18:06:08 +08:00
|
|
|
|
|
2018-01-30 09:35:54 +08:00
|
|
|
|
|
2018-12-14 18:13:05 +08:00
|
|
|
|
void RtspSession::onRtpSorted(const RtpPacket::Ptr &rtppt, int trackidx) {
|
2018-12-20 17:06:33 +08:00
|
|
|
|
_pushSrc->onWrite(rtppt, false);
|
2018-12-14 18:13:05 +08:00
|
|
|
|
}
|
2019-05-08 14:23:18 +08:00
|
|
|
|
inline void RtspSession::onRcvPeerUdpData(int intervaled, const Buffer::Ptr &pBuf, const struct sockaddr& addr) {
|
2018-12-14 17:10:24 +08:00
|
|
|
|
//这是rtcp心跳包,说明播放器还存活
|
|
|
|
|
_ticker.resetTime();
|
|
|
|
|
|
2019-05-08 14:23:18 +08:00
|
|
|
|
if(intervaled % 2 == 0){
|
2018-12-20 17:06:33 +08:00
|
|
|
|
if(_pushSrc){
|
2019-06-27 12:53:35 +08:00
|
|
|
|
//这是rtsp推流上来的rtp包
|
2019-05-08 14:23:18 +08:00
|
|
|
|
handleOneRtp(intervaled / 2,_aTrackInfo[intervaled / 2],( unsigned char *)pBuf->data(),pBuf->size());
|
2019-06-27 12:53:35 +08:00
|
|
|
|
}else if(!_udpSockConnected.count(intervaled)){
|
|
|
|
|
//这是rtsp播放器的rtp打洞包
|
2019-05-08 16:19:00 +08:00
|
|
|
|
_udpSockConnected.emplace(intervaled);
|
|
|
|
|
_apRtpSock[intervaled / 2]->setSendPeerAddr(&addr);
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
2019-05-08 14:23:18 +08:00
|
|
|
|
}else{
|
|
|
|
|
//rtcp包
|
2019-06-27 12:53:35 +08:00
|
|
|
|
if(!_udpSockConnected.count(intervaled)){
|
|
|
|
|
_udpSockConnected.emplace(intervaled);
|
2019-05-08 16:19:00 +08:00
|
|
|
|
_apRtcpSock[(intervaled - 1) / 2]->setSendPeerAddr(&addr);
|
|
|
|
|
}
|
2019-06-27 12:53:35 +08:00
|
|
|
|
onRtcpPacket((intervaled - 1) / 2, _aTrackInfo[(intervaled - 1) / 2], (unsigned char *) pBuf->data(),pBuf->size());
|
2019-05-08 14:23:18 +08:00
|
|
|
|
}
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-12-14 17:10:24 +08:00
|
|
|
|
inline void RtspSession::startListenPeerUdpData(int trackIdx) {
|
2017-04-01 16:35:56 +08:00
|
|
|
|
weak_ptr<RtspSession> weakSelf = dynamic_pointer_cast<RtspSession>(shared_from_this());
|
2019-05-08 16:24:45 +08:00
|
|
|
|
auto srcIP = inet_addr(get_peer_ip().data());
|
|
|
|
|
auto onUdpData = [weakSelf,srcIP](const Buffer::Ptr &pBuf, struct sockaddr *pPeerAddr,int intervaled){
|
2018-12-14 17:10:24 +08:00
|
|
|
|
auto strongSelf=weakSelf.lock();
|
|
|
|
|
if(!strongSelf) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2019-05-28 18:46:52 +08:00
|
|
|
|
|
|
|
|
|
if (((struct sockaddr_in *) pPeerAddr)->sin_addr.s_addr != srcIP) {
|
|
|
|
|
WarnP(strongSelf.get()) << ((intervaled % 2 == 0) ? "收到其他地址的rtp数据:" : "收到其他地址的rtcp数据:")
|
|
|
|
|
<< inet_ntoa(((struct sockaddr_in *) pPeerAddr)->sin_addr);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-14 17:10:24 +08:00
|
|
|
|
struct sockaddr addr=*pPeerAddr;
|
2019-05-08 14:23:18 +08:00
|
|
|
|
strongSelf->async([weakSelf,pBuf,addr,intervaled]() {
|
2018-12-14 17:10:24 +08:00
|
|
|
|
auto strongSelf=weakSelf.lock();
|
|
|
|
|
if(!strongSelf) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-05-08 14:23:18 +08:00
|
|
|
|
strongSelf->onRcvPeerUdpData(intervaled,pBuf,addr);
|
2018-12-14 17:10:24 +08:00
|
|
|
|
});
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
switch (_rtpType){
|
2019-03-27 18:41:52 +08:00
|
|
|
|
case Rtsp::RTP_MULTICAST:{
|
2018-12-14 17:10:24 +08:00
|
|
|
|
//组播使用的共享rtcp端口
|
|
|
|
|
UDPServer::Instance().listenPeer(get_peer_ip().data(), this, [onUdpData](
|
2019-05-08 14:23:18 +08:00
|
|
|
|
int intervaled, const Buffer::Ptr &pBuf, struct sockaddr *pPeerAddr) {
|
|
|
|
|
return onUdpData(pBuf,pPeerAddr,intervaled);
|
2017-04-01 16:35:56 +08:00
|
|
|
|
});
|
2018-12-14 17:10:24 +08:00
|
|
|
|
}
|
|
|
|
|
break;
|
2019-03-27 18:41:52 +08:00
|
|
|
|
case Rtsp::RTP_UDP:{
|
2019-05-08 14:23:18 +08:00
|
|
|
|
auto setEvent = [&](Socket::Ptr &sock,int intervaled){
|
2018-12-14 17:10:24 +08:00
|
|
|
|
if(!sock){
|
2019-05-28 18:46:52 +08:00
|
|
|
|
WarnP(this) << "udp端口为空:" << intervaled;
|
2018-12-14 17:10:24 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
2019-06-11 12:28:48 +08:00
|
|
|
|
sock->setOnRead([onUdpData,intervaled](const Buffer::Ptr &pBuf, struct sockaddr *pPeerAddr , int addr_len){
|
2019-05-08 14:23:18 +08:00
|
|
|
|
onUdpData(pBuf,pPeerAddr,intervaled);
|
2018-12-14 17:10:24 +08:00
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
setEvent(_apRtpSock[trackIdx], 2*trackIdx );
|
|
|
|
|
setEvent(_apRtcpSock[trackIdx], 2*trackIdx + 1 );
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-14 14:59:12 +08:00
|
|
|
|
static string dateStr(){
|
|
|
|
|
char buf[64];
|
|
|
|
|
time_t tt = time(NULL);
|
|
|
|
|
strftime(buf, sizeof buf, "%a, %b %d %Y %H:%M:%S GMT", gmtime(&tt));
|
|
|
|
|
return buf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool RtspSession::sendRtspResponse(const string &res_code,
|
|
|
|
|
const StrCaseMap &header_const,
|
|
|
|
|
const string &sdp,
|
|
|
|
|
const char *protocol){
|
|
|
|
|
auto header = header_const;
|
|
|
|
|
header.emplace("CSeq",StrPrinter << _iCseq);
|
|
|
|
|
if(!_strSession.empty()){
|
|
|
|
|
header.emplace("Session",_strSession);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
header.emplace("Server",SERVER_NAME "(build in " __DATE__ " " __TIME__ ")");
|
|
|
|
|
header.emplace("Date",dateStr());
|
|
|
|
|
|
|
|
|
|
if(!sdp.empty()){
|
|
|
|
|
header.emplace("Content-Length",StrPrinter << sdp.size());
|
|
|
|
|
header.emplace("Content-Type","application/sdp");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_StrPrinter printer;
|
|
|
|
|
printer << protocol << " " << res_code << "\r\n";
|
|
|
|
|
for (auto &pr : header){
|
|
|
|
|
printer << pr.first << ": " << pr.second << "\r\n";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
printer << "\r\n";
|
|
|
|
|
|
|
|
|
|
if(!sdp.empty()){
|
|
|
|
|
printer << sdp;
|
|
|
|
|
}
|
2019-05-28 18:46:52 +08:00
|
|
|
|
// DebugP(this) << printer;
|
2018-12-14 14:59:12 +08:00
|
|
|
|
return send(std::make_shared<BufferString>(printer)) > 0 ;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int RtspSession::send(const Buffer::Ptr &pkt){
|
2018-12-17 15:21:23 +08:00
|
|
|
|
// if(!_enableSendRtp){
|
2019-05-28 18:46:52 +08:00
|
|
|
|
// DebugP(this) << pkt->data();
|
2018-12-17 15:21:23 +08:00
|
|
|
|
// }
|
2018-12-14 14:59:12 +08:00
|
|
|
|
_ui64TotalBytes += pkt->size();
|
2018-12-17 13:14:49 +08:00
|
|
|
|
return TcpSession::send(pkt);
|
2018-12-14 14:59:12 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool RtspSession::sendRtspResponse(const string &res_code,
|
|
|
|
|
const std::initializer_list<string> &header,
|
|
|
|
|
const string &sdp,
|
|
|
|
|
const char *protocol) {
|
|
|
|
|
string key;
|
|
|
|
|
StrCaseMap header_map;
|
|
|
|
|
int i = 0;
|
|
|
|
|
for(auto &val : header){
|
|
|
|
|
if(++i % 2 == 0){
|
|
|
|
|
header_map.emplace(key,val);
|
|
|
|
|
}else{
|
|
|
|
|
key = val;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return sendRtspResponse(res_code,header_map,sdp,protocol);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline string RtspSession::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;
|
|
|
|
|
}
|
|
|
|
|
inline int RtspSession::getTrackIndexByTrackType(TrackType type) {
|
|
|
|
|
for (unsigned int i = 0; i < _aTrackInfo.size(); i++) {
|
|
|
|
|
if (type == _aTrackInfo[i]->_type) {
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
inline int RtspSession::getTrackIndexByControlSuffix(const string &controlSuffix) {
|
|
|
|
|
for (unsigned int i = 0; i < _aTrackInfo.size(); i++) {
|
|
|
|
|
if (controlSuffix == _aTrackInfo[i]->_control_surffix) {
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-12-21 17:12:26 +08:00
|
|
|
|
if(_aTrackInfo.size() == 1){
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2018-12-14 14:59:12 +08:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2018-12-17 15:21:23 +08:00
|
|
|
|
inline int RtspSession::getTrackIndexByInterleaved(int interleaved){
|
|
|
|
|
for (unsigned int i = 0; i < _aTrackInfo.size(); i++) {
|
|
|
|
|
if (_aTrackInfo[i]->_interleaved == interleaved) {
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-29 18:08:50 +08:00
|
|
|
|
bool RtspSession::close(MediaSource &sender,bool force) {
|
2019-05-31 15:40:55 +08:00
|
|
|
|
//此回调在其他线程触发
|
|
|
|
|
if(!_pushSrc || (!force && _pushSrc->readerCount() != 0)){
|
2019-05-27 18:39:43 +08:00
|
|
|
|
return false;
|
|
|
|
|
}
|
2019-05-29 18:08:50 +08:00
|
|
|
|
string err = StrPrinter << "close media:" << sender.getSchema() << "/" << sender.getVhost() << "/" << sender.getApp() << "/" << sender.getId() << " " << force;
|
|
|
|
|
safeShutdown(SockException(Err_shutdown,err));
|
2018-12-17 13:14:49 +08:00
|
|
|
|
return true;
|
|
|
|
|
}
|
2018-12-14 14:59:12 +08:00
|
|
|
|
|
2019-05-08 17:49:05 +08:00
|
|
|
|
|
2019-05-31 15:40:55 +08:00
|
|
|
|
void RtspSession::onNoneReader(MediaSource &sender){
|
|
|
|
|
//此回调在其他线程触发
|
|
|
|
|
if(!_pushSrc || _pushSrc->readerCount() != 0){
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
MediaSourceEvent::onNoneReader(sender);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-06-26 10:01:04 +08:00
|
|
|
|
void RtspSession::sendRtpPacket(const RtpPacket::Ptr & pkt) {
|
2019-05-28 18:46:52 +08:00
|
|
|
|
//InfoP(this) <<(int)pkt.Interleaved;
|
2019-05-08 17:49:05 +08:00
|
|
|
|
switch (_rtpType) {
|
|
|
|
|
case Rtsp::RTP_TCP: {
|
2019-06-11 18:31:34 +08:00
|
|
|
|
send(pkt);
|
2019-05-08 17:49:05 +08:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case Rtsp::RTP_UDP: {
|
|
|
|
|
int iTrackIndex = getTrackIndexByTrackType(pkt->type);
|
|
|
|
|
auto &pSock = _apRtpSock[iTrackIndex];
|
|
|
|
|
if (!pSock) {
|
2019-05-29 18:08:50 +08:00
|
|
|
|
shutdown(SockException(Err_shutdown,"udp sock not opened yet"));
|
2019-05-08 17:49:05 +08:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
BufferRtp::Ptr buffer(new BufferRtp(pkt,4));
|
|
|
|
|
_ui64TotalBytes += buffer->size();
|
|
|
|
|
pSock->send(buffer);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2019-06-11 18:31:34 +08:00
|
|
|
|
#if RTSP_SERVER_SEND_RTCP
|
2019-06-27 12:21:01 +08:00
|
|
|
|
int iTrackIndex = getTrackIndexByTrackType(pkt->type);
|
2019-05-08 17:49:05 +08:00
|
|
|
|
if(iTrackIndex == -1){
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
RtcpCounter &counter = _aRtcpCnt[iTrackIndex];
|
|
|
|
|
counter.pktCnt += 1;
|
|
|
|
|
counter.octCount += (pkt->length - pkt->offset);
|
|
|
|
|
auto &ticker = _aRtcpTicker[iTrackIndex];
|
|
|
|
|
if (ticker.elapsedTime() > 5 * 1000) {
|
|
|
|
|
//send rtcp every 5 second
|
|
|
|
|
ticker.resetTime();
|
|
|
|
|
//直接保存网络字节序
|
|
|
|
|
memcpy(&counter.timeStamp, pkt->payload + 8 , 4);
|
|
|
|
|
sendSenderReport(_rtpType == Rtsp::RTP_TCP,iTrackIndex);
|
|
|
|
|
}
|
2019-06-11 18:31:34 +08:00
|
|
|
|
#endif
|
2019-05-08 17:49:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-26 10:01:04 +08:00
|
|
|
|
void RtspSession::sendSenderReport(bool overTcp,int iTrackIndex) {
|
2019-05-09 13:35:54 +08:00
|
|
|
|
static const char s_cname[] = "ZLMediaKitRtsp";
|
|
|
|
|
uint8_t aui8Rtcp[4 + 28 + 10 + sizeof(s_cname) + 1] = {0};
|
2019-05-08 17:49:05 +08:00
|
|
|
|
uint8_t *pui8Rtcp_SR = aui8Rtcp + 4, *pui8Rtcp_SDES = pui8Rtcp_SR + 28;
|
|
|
|
|
auto &track = _aTrackInfo[iTrackIndex];
|
|
|
|
|
auto &counter = _aRtcpCnt[iTrackIndex];
|
|
|
|
|
|
|
|
|
|
aui8Rtcp[0] = '$';
|
|
|
|
|
aui8Rtcp[1] = track->_interleaved + 1;
|
2019-05-09 13:35:54 +08:00
|
|
|
|
aui8Rtcp[2] = (sizeof(aui8Rtcp) - 4) >> 8;
|
|
|
|
|
aui8Rtcp[3] = (sizeof(aui8Rtcp) - 4) & 0xFF;
|
2019-05-08 17:49:05 +08:00
|
|
|
|
|
|
|
|
|
pui8Rtcp_SR[0] = 0x80;
|
|
|
|
|
pui8Rtcp_SR[1] = 0xC8;
|
|
|
|
|
pui8Rtcp_SR[2] = 0x00;
|
|
|
|
|
pui8Rtcp_SR[3] = 0x06;
|
|
|
|
|
|
|
|
|
|
uint32_t ssrc=htonl(track->_ssrc);
|
|
|
|
|
memcpy(&pui8Rtcp_SR[4], &ssrc, 4);
|
|
|
|
|
|
|
|
|
|
uint64_t msw;
|
|
|
|
|
uint64_t lsw;
|
|
|
|
|
struct timeval tv;
|
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
|
msw = tv.tv_sec + 0x83AA7E80; /* 0x83AA7E80 is the number of seconds from 1900 to 1970 */
|
|
|
|
|
lsw = (uint32_t) ((double) tv.tv_usec * (double) (((uint64_t) 1) << 32) * 1.0e-6);
|
|
|
|
|
|
|
|
|
|
msw = htonl(msw);
|
|
|
|
|
memcpy(&pui8Rtcp_SR[8], &msw, 4);
|
|
|
|
|
|
|
|
|
|
lsw = htonl(lsw);
|
|
|
|
|
memcpy(&pui8Rtcp_SR[12], &lsw, 4);
|
|
|
|
|
//直接使用网络字节序
|
|
|
|
|
memcpy(&pui8Rtcp_SR[16], &counter.timeStamp, 4);
|
|
|
|
|
|
|
|
|
|
uint32_t pktCnt = htonl(counter.pktCnt);
|
|
|
|
|
memcpy(&pui8Rtcp_SR[20], &pktCnt, 4);
|
|
|
|
|
|
|
|
|
|
uint32_t octCount = htonl(counter.octCount);
|
|
|
|
|
memcpy(&pui8Rtcp_SR[24], &octCount, 4);
|
|
|
|
|
|
|
|
|
|
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;
|
2019-05-09 13:35:54 +08:00
|
|
|
|
memcpy(&pui8Rtcp_SDES[10], s_cname, sizeof(s_cname));
|
|
|
|
|
pui8Rtcp_SDES[10 + sizeof(s_cname)] = 0x00;
|
2019-05-08 17:49:05 +08:00
|
|
|
|
|
|
|
|
|
if(overTcp){
|
|
|
|
|
send(obtainBuffer((char *) aui8Rtcp, sizeof(aui8Rtcp)));
|
|
|
|
|
}else {
|
|
|
|
|
_apRtcpSock[iTrackIndex]->send((char *) aui8Rtcp + 4, sizeof(aui8Rtcp) - 4);
|
|
|
|
|
}
|
2017-04-01 16:35:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2018-10-24 17:17:55 +08:00
|
|
|
|
/* namespace mediakit */
|
2017-04-01 16:35:56 +08:00
|
|
|
|
|