ZLMediaKit源码分析(一)服务启动
ZLMediaKit源码分析(二)推流创建
ZLMediaKit源码分析(三)拉流创建
ZLMediaKit源码分析(四)重封装
ZLMediaKit源码分析(五)代理服务
ZLMediaKit源码分析(六)http 点播
继承关系
以下是代理源码分析
WebServer 接收任务
server/WebApi.cpp
/**
* 安装api接口
* 所有api都支持GET和POST两种方式
* POST方式参数支持application/json和application/x-www-form-urlencoded方式
*/
void installWebApi() {
addHttpListener();
GET_CONFIG(string,api_secret,API::kSecret);
......
//动态添加rtsp/rtmp拉流代理
//测试url http://127.0.0.1/index/api/addStreamProxy?vhost=__defaultVhost__&app=proxy&enable_rtsp=1&enable_rtmp=1&stream=0&url=rtmp://127.0.0.1/live/obs
api_regist("/index/api/addStreamProxy",[](API_ARGS_MAP_ASYNC){
CHECK_SECRET();
CHECK_ARGS("vhost","app","stream","url");
addStreamProxy(allArgs["vhost"],
allArgs["app"],
allArgs["stream"],
allArgs["url"],
allArgs["retry_count"],
allArgs["enable_hls"],/* 是否hls转发 */
allArgs["enable_mp4"],/* 是否MP4录制 */
allArgs["rtp_type"],
allArgs["timeout_sec"],
[invoker,val,headerOut](const SockException &ex,const string &key) mutable{
if (ex) {
val["code"] = API::OtherFailed;
val["msg"] = ex.what();
} else {
val["data"]["key"] = key;
}
invoker(200, headerOut, val.toStyledString());
});
});
......
}
该步主要创建 player,并且调用 player->play(url); 拉流。
server/WebApi.cpp
void addStreamProxy(const string &vhost, const string &app, const string &stream, const string &url, int retry_count,
const ProtocolOption &option, int rtp_type, float timeout_sec,
const function<void(const SockException &ex, const string &key)> &cb) {
auto key = getProxyKey(vhost, app, stream);
lock_guard<recursive_mutex> lck(s_proxyMapMtx);
if (s_proxyMap.find(key) != s_proxyMap.end()) {
//已经在拉流了
cb(SockException(Err_success), key);
return;
}
//添加拉流代理
auto player = std::make_shared<PlayerProxy>(vhost, app, stream, option, retry_count);
s_proxyMap[key] = player;
//指定RTP over TCP(播放rtsp时有效)
(*player)[Client::kRtpType] = rtp_type;
if (timeout_sec > 0.1) {
//播放握手超时时间
(*player)[Client::kTimeoutMS] = timeout_sec * 1000;
}
//开始播放,如果播放失败或者播放中止,将会自动重试若干次,默认一直重试
player->setPlayCallbackOnce([cb, key](const SockException &ex) {
if (ex) {
lock_guard<recursive_mutex> lck(s_proxyMapMtx);
s_proxyMap.erase(key);
}
cb(ex, key);
});
//被主动关闭拉流
player->setOnClose([key](const SockException &ex) {
lock_guard<recursive_mutex> lck(s_proxyMapMtx);
s_proxyMap.erase(key);
});
// 重要的是这个函数,实际调用PlayerProxy::paly()
player->play(url);
};
src/Player/PlayerProxy.cpp
PlayerProxy::PlayerProxy(const string &vhost, const string &app, const string &stream_id, const ProtocolOption &option,
int retry_count, const EventPoller::Ptr &poller) : MediaPlayer(poller) , _option(option) {
_vhost = vhost;
_app = app;
_stream_id = stream_id;
_retry_count = retry_count;
_on_close = [](const SockException &) {};
(*this)[Client::kWaitTrackReady] = false;
}
PlayerProxy::play() 创建拉流连接
src/Player/PlayerProxy.cpp
void PlayerProxy::play(const string &strUrlTmp) {
weak_ptr<PlayerProxy> weakSelf = shared_from_this();
std::shared_ptr<int> piFailedCnt(new int(0)); //连续播放失败次数
// PlayerProxy 继承自 MediaPlayer 继承自PlayerImp<PlayerBase, PlayerBase>,
// 实际为 PlayerImp::setOnPlayResult()
// 1. 注册创建总封装回调
setOnPlayResult([weakSelf, strUrlTmp, piFailedCnt](const SockException &err) {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
return;
}
if (strongSelf->_on_play) {
strongSelf->_on_play(err);
strongSelf->_on_play = nullptr;
}
if (!err) {
// 取消定时器,避免hls拉流索引文件因为网络波动失败重连成功后出现循环重试的情况
strongSelf->_timer.reset();
// 播放成功
*piFailedCnt = 0;//连续播放失败次数清0
strongSelf->onPlaySuccess();
} else if (*piFailedCnt < strongSelf->_retry_count || strongSelf->_retry_count < 0) {
// 播放失败,延时重试播放
strongSelf->rePlay(strUrlTmp, (*piFailedCnt)++);
} else {
//达到了最大重试次数,回调关闭
strongSelf->_on_close(err);
}
});
// PlayerProxy 继承自 MediaPlayer 继承自PlayerImp<PlayerBase, PlayerBase>,
// 实际为 PlayerImp::setOnShutdown()
// 2. 注册结束回调
setOnShutdown([weakSelf, strUrlTmp, piFailedCnt](const SockException &err) {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
return;
}
//注销直接拉流代理产生的流:#532
strongSelf->setMediaSource(nullptr);
if (strongSelf->_muxer) {
auto tracks = strongSelf->MediaPlayer::getTracks(false);
for (auto &track : tracks) {
track->delDelegate(strongSelf->_muxer.get());
}
GET_CONFIG(bool, reset_when_replay, General::kResetWhenRePlay);
if (reset_when_replay) {
strongSelf->_muxer.reset();
} else {
strongSelf->_muxer->resetTracks();
}
}
//播放异常中断,延时重试播放
if (*piFailedCnt < strongSelf->_retry_count || strongSelf->_retry_count < 0) {
strongSelf->rePlay(strUrlTmp, (*piFailedCnt)++);
} else {
//达到了最大重试次数,回调关闭
strongSelf->_on_close(err);
}
});
// 3. 创建拉流结构体,创建socket连接,触发Rtsp协议发送,并注册数据回调
MediaPlayer::play(strUrlTmp);
_pull_url = strUrlTmp;
// 4. 创建直接代理结构体
setDirectProxy();
}
PlayerImp::setOnPlayResult() 注册创建总封装回调
src/Player/PlayerBase.h
template<typename Parent, typename Delegate>
class PlayerImp : public Parent {
......
void setOnShutdown(const std::function<void(const toolkit::SockException &)> &cb) override {
if (_delegate) {
// no run
_delegate->setOnShutdown(cb);
}
_on_shutdown = cb;
}
void setOnPlayResult(const std::function<void(const toolkit::SockException &ex)> &cb) override {
if (_delegate) {
// no run
_delegate->setOnPlayResult(cb);
}
_on_play_result = cb;
}
......
};
PlayerProxy::onPlaySuccess() 创建总封装
关闭了 rtsp 子封装和 rtmp 子封装,也就是总封装接收到数据,不继续分发给子封装,不向下执行。
其他子封装,继续执行。
src/Player/PlayerProxy.cpp
void PlayerProxy::onPlaySuccess() {
GET_CONFIG(bool, reset_when_replay, General::kResetWhenRePlay);
if (dynamic_pointer_cast<RtspMediaSource>(_media_src)) {
//rtsp拉流代理
if (reset_when_replay || !_muxer) {
// 关闭 rtsp子封装
_option.enable_rtsp = false;
_muxer = std::make_shared<MultiMediaSourceMuxer>(_vhost, _app, _stream_id, getDuration(), _option);
}
} else if (dynamic_pointer_cast<RtmpMediaSource>(_media_src)) {
//rtmp拉流代理
if (reset_when_replay || !_muxer) {
// 关闭 rtmp子封装。
_option.enable_rtmp = false;
_muxer = std::make_shared<MultiMediaSourceMuxer>(_vhost, _app, _stream_id, getDuration(), _option);
}
} else {
//其他拉流代理
if (reset_when_replay || !_muxer) {
_muxer = std::make_shared<MultiMediaSourceMuxer>(_vhost, _app, _stream_id, getDuration(), _option);
}
}
_muxer->setMediaListener(shared_from_this());
auto videoTrack = getTrack(TrackVideo, false);
if (videoTrack) {
//添加视频
_muxer->addTrack(videoTrack);
//视频数据写入_mediaMuxer
videoTrack->addDelegate(_muxer);
}
auto audioTrack = getTrack(TrackAudio, false);
if (audioTrack) {
//添加音频
_muxer->addTrack(audioTrack);
//音频数据写入_mediaMuxer
audioTrack->addDelegate(_muxer);
}
//添加完毕所有track,防止单track情况下最大等待3秒
_muxer->addTrackCompleted();
if (_media_src) {
//让_muxer对象拦截一部分事件(比如说录像相关事件)
_media_src->setListener(_muxer);
}
}
PlayerImp::setOnShutdown()
参考 PlayerImp::setOnPlayResult(),具体执行暂不分析。
MediaPlayer::play() Rtsp 远程拉流
src/Player/MediaPlayer.cpp
void MediaPlayer::play(const string &url) {
// createPlayer 返回 RtspPlayerImp
// 1. 创建拉流结构体
_delegate = PlayerBase::createPlayer(_poller, url);
assert(_delegate);
// 2. 注册拉流结构体,创建socket连接 回调
setOnCreateSocket_l(_delegate, _on_create_socket);
// 参考PlayerImp::setOnShutdown()
_delegate->setOnShutdown(_on_shutdown);
// 参考PlayerImp::setOnPlayResult()
_delegate->setOnPlayResult(_on_play_result);
_delegate->setOnResume(_on_resume);
// MediaPlayer : public PlayerImp<PlayerBase, PlayerBase>
// MediaSource::Ptr PlayerImp::_media_src;
//打印 _media_src 为空
// 3. 注册转推结构体,这里为空,没有生效。在拉流有数据时regist()
_delegate->setMediaSource(_media_src);
_delegate->mINI::operator=(*this);
// RtspPlayer继承自PlayerBase
// 实际调用RtspPlayer::play()
// 4. 下行拉流 播放
_delegate->play(url);
}
PlayerBase::createPlayer() 创建拉流结构体 RtspPlayerImp。
src/Player/PlayerBase.cpp
PlayerBase::Ptr PlayerBase::createPlayer(const EventPoller::Ptr &poller, const string &url_in) {
static auto releasePlayer = [](PlayerBase *ptr) {
onceToken token(nullptr, [&]() {
delete ptr;
});
ptr->teardown();
};
string url = url_in;
string prefix = FindField(url.data(), NULL, "://");
auto pos = url.find('?');
if (pos != string::npos) {
//去除?后面的字符串
url = url.substr(0, pos);
}
if (strcasecmp("rtsps", prefix.data()) == 0) {
return PlayerBase::Ptr(new TcpClientWithSSL<RtspPlayerImp>(poller), releasePlayer);
}
// 以 rtsp 拉流为例
if (strcasecmp("rtsp", prefix.data()) == 0) {
return PlayerBase::Ptr(new RtspPlayerImp(poller), releasePlayer);
}
if (strcasecmp("rtmps", prefix.data()) == 0) {
return PlayerBase::Ptr(new TcpClientWithSSL<RtmpPlayerImp>(poller), releasePlayer);
}
if (strcasecmp("rtmp", prefix.data()) == 0) {
return PlayerBase::Ptr(new RtmpPlayerImp(poller), releasePlayer);
}
if ((strcasecmp("http", prefix.data()) == 0 || strcasecmp("https", prefix.data()) == 0)) {
if (end_with(url, ".m3u8") || end_with(url_in, ".m3u8")) {
return PlayerBase::Ptr(new HlsPlayerImp(poller), releasePlayer);
} else if (end_with(url, ".ts") || end_with(url_in, ".ts")) {
return PlayerBase::Ptr(new TsPlayerImp(poller), releasePlayer);
}
}
throw std::invalid_argument("not supported play schema:" + url_in);
}
setOnCreateSocket_l() 注册创建 socket 回调
静态函数。
src/Player/MediaPlayer.cpp
// 逻辑上cb 为null
// delegate 为 RtspPlayerImp
// RtspPlayerImp 继承自 RtspPlayer继承自toolkit::TcpClient 继承自 SocketHelper
static void setOnCreateSocket_l(const std::shared_ptr<PlayerBase> &delegate, const Socket::onCreateSocket &cb){
auto helper = dynamic_pointer_cast<SocketHelper>(delegate);
if (helper) {
if (cb) {
helper->setOnCreateSocket(cb);
} else {
// run
//客户端,确保开启互斥锁
helper->setOnCreateSocket([](const EventPoller::Ptr &poller) {
return Socket::createSocket(poller, true);
});
}
}
}
3rdpart/ZLToolKit/src/Network/Socket.cpp
void SocketHelper::setOnCreateSocket(Socket::onCreateSocket cb){
if (cb) {
// run
_on_create_socket = std::move(cb);
} else {
_on_create_socket = [](const EventPoller::Ptr &poller) {
return Socket::createSocket(poller, false);
};
}
}
PlayerImp::setMediaSource() 注册转推结构体
空执行 setMediaSource。实际操作在下面 PlayerProxy::setDirectProxy()。
src/Player/PlayerImp.h
template<typename Parent, typename Delegate>
class PlayerImp : public Parent {
public:
......
// 打印 _delegate 为空 _media_src为空
void setMediaSource(const MediaSource::Ptr &src) override {
if (_delegate) {
_delegate->setMediaSource(src);
}
_media_src = src;
}
......
protected:
......
MediaSource::Ptr _media_src;
std::shared_ptr<Delegate> _delegate;
};
RtspPlayer::play() 拉流连接创建
连接远端服务器拉流,并注册两个重要回调。RtspPlayer::onConnect()连接创建成功,Rtsp::onRecv() 接收数据处理。
src/Rtsp/RtspPlayer.cpp
void RtspPlayer::play(const string &strUrl){
RtspUrl url;
try {
url.parse(strUrl);
} catch (std::exception &ex) {
onPlayResult_l(SockException(Err_other, StrPrinter << "illegal rtsp url:" << ex.what()), false);
return;
}
teardown();
if (url._user.size()) {
(*this)[Client::kRtspUser] = url._user;
}
if (url._passwd.size()) {
(*this)[Client::kRtspPwd] = url._passwd;
(*this)[Client::kRtspPwdIsMD5] = false;
}
_play_url = url._url;
_rtp_type = (Rtsp::eRtpType)(int)(*this)[Client::kRtpType];
DebugL << url._url << " " << (url._user.size() ? url._user : "null") << " " << (url._passwd.size() ? url._passwd : "null") << " " << _rtp_type;
weak_ptr<RtspPlayer> weakSelf = dynamic_pointer_cast<RtspPlayer>(shared_from_this());
float playTimeOutSec = (*this)[Client::kTimeoutMS].as<int>() / 1000.0f;
_play_check_timer.reset(new Timer(playTimeOutSec, [weakSelf]() {
auto strongSelf=weakSelf.lock();
if(!strongSelf) {
return false;
}
strongSelf->onPlayResult_l(SockException(Err_timeout,"play rtsp timeout"),false);
return false;
}, getPoller()));
if(!(*this)[Client::kNetAdapter].empty()){
setNetAdapter((*this)[Client::kNetAdapter]);
}
// 继承自toolkit::TcpClient
startConnect(url._host, url._port, playTimeOutSec);
}
3rdpart/ZLToolKit/src/Network/TcpClient.cpp
void TcpClient::startConnect(const string &url, uint16_t port, float timeout_sec, uint16_t local_port) {
weak_ptr<TcpClient> weak_self = shared_from_this();
_timer = std::make_shared<Timer>(2.0f, [weak_self]() {
auto strong_self = weak_self.lock();
if (!strong_self) {
return false;
}
strong_self->onManager();
return true;
}, getPoller());
// RtspPlayer 继承自 toolkit::TcpClient 继承自SocketHelper
// setSock() 实际为 SocketHelper::setSock()
// createSocket() 实际为SocketHelper::createSocket()
setSock(createSocket());
auto sock_ptr = getSock().get();
sock_ptr->setOnErr([weak_self, sock_ptr](const SockException &ex) {
auto strong_self = weak_self.lock();
if (!strong_self) {
return;
}
if (sock_ptr != strong_self->getSock().get()) {
//已经重连socket,上次的socket的事件忽略掉
return;
}
strong_self->_timer.reset();
strong_self->onErr(ex);
});
sock_ptr->connect(url, port, [weak_self](const SockException &err) {
auto strong_self = weak_self.lock();
if (strong_self) {
strong_self->onSockConnect(err);
}
}, timeout_sec, _net_adapter, local_port);
}
3rdpart/ZLToolKit/src/Network/Socket.cpp
void SocketHelper::setSock(const Socket::Ptr &sock) {
_peer_port = 0;
_local_port = 0;
_peer_ip.clear();
_local_ip.clear();
_sock = sock;
if (_sock) {
_poller = _sock->getPoller();
}
}
3rdpart/ZLToolKit/src/Network/Socket.cpp
Socket::Ptr SocketHelper::createSocket(){
return _on_create_socket(_poller);
}
connect 远程。
3rdpart/ZLToolKit/src/Network/Socket.cpp
void Socket::connect(const string &url, uint16_t port, const onErrCB &con_cb_in, float timeout_sec, const string &local_ip, uint16_t local_port) {
weak_ptr<Socket> weak_self = shared_from_this();
_poller->async([=] {
auto strong_self = weak_self.lock();
if (!strong_self) {
return;
}
strong_self->connect_l(url, port, con_cb_in, timeout_sec, local_ip, local_port);
});
}
注册 RtspPlayer::onRecv() 并调用 onConnect() 发送RTSP OPTIONS
3rdpart/ZLToolKit/src/Network/TcpClient.cpp
void TcpClient::onSockConnect(const SockException &ex) {
if (ex) {
//连接失败
_timer.reset();
onConnect(ex);
return;
}
auto sock_ptr = getSock().get();
weak_ptr<TcpClient> weak_self = shared_from_this();
sock_ptr->setOnFlush([weak_self, sock_ptr]() {
auto strong_self = weak_self.lock();
if (!strong_self) {
return false;
}
if (sock_ptr != strong_self->getSock().get()) {
//已经重连socket,上传socket的事件忽略掉
return false;
}
strong_self->onFlush();
return true;
});
// 注册接收数据回调
sock_ptr->setOnRead([weak_self, sock_ptr](const Buffer::Ptr &pBuf, struct sockaddr *, int) {
auto strong_self = weak_self.lock();
if (!strong_self) {
return;
}
if (sock_ptr != strong_self->getSock().get()) {
//已经重连socket,上传socket的事件忽略掉
return;
}
try {
// RtspPlayer::onRecv()
strong_self->onRecv(pBuf);
} catch (std::exception &ex) {
strong_self->shutdown(SockException(Err_other, ex.what()));
}
});
// 开始发送 RTSP 协议
// 实际调用 RtspPlayer::onConnect()
onConnect(ex);
}
PlayerProxy::setDirectProxy() 创建直接代理转发
读取配置文件,获取directProxy。
src/Player/PlayerProxy.cpp
void PlayerProxy::setDirectProxy() {
MediaSource::Ptr mediaSource;
if (dynamic_pointer_cast<RtspPlayer>(_delegate)) {
//rtsp拉流
GET_CONFIG(bool, directProxy, Rtsp::kDirectProxy);
if (directProxy) {
mediaSource = std::make_shared<RtspMediaSource>(_vhost, _app, _stream_id);
}
} else if (dynamic_pointer_cast<RtmpPlayer>(_delegate)) {
//rtmp拉流,rtmp强制直接代理
mediaSource = std::make_shared<RtmpMediaSource>(_vhost, _app, _stream_id);
}
// PlayerProxy 继承自 MediaPlayer : public PlayerImp<PlayerBase, PlayerBase>
if (mediaSource) {
setMediaSource(mediaSource);
}
}
src/Rtsp/RtspMediaSource.h
class RtspMediaSource : public MediaSource, public toolkit::RingDelegate<RtpPacket::Ptr>, private PacketCache<RtpPacket> {
public:
using Ptr = std::shared_ptr<RtspMediaSource>;
using RingDataType = std::shared_ptr<toolkit::List<RtpPacket::Ptr> >;
using RingType = toolkit::RingBuffer<RingDataType>;
RtspMediaSource(const std::string &vhost,
const std::string &app,
const std::string &stream_id,
int ring_size = RTP_GOP_SIZE) :
MediaSource(RTSP_SCHEMA, vhost, app, stream_id), _ring_size(ring_size) {}
......
private:
......
int _ring_size;
......
};
src/Common/MediaSource.cpp
MediaSource::MediaSource(const string &schema, const string &vhost, const string &app, const string &stream_id){
GET_CONFIG(bool, enableVhost, General::kEnableVhost);
if (!enableVhost) {
_vhost = DEFAULT_VHOST;
} else {
_vhost = vhost.empty() ? DEFAULT_VHOST : vhost;
}
_schema = schema;
_app = app;
_stream_id = stream_id;
_create_stamp = time(NULL);
}
src/Player/PlayerBase.h
template<typename Parent, typename Delegate>
class PlayerImp : public Parent {
public:
using Ptr = std::shared_ptr<PlayerImp>;
template<typename ...ArgsType>
PlayerImp(ArgsType &&...args) : Parent(std::forward<ArgsType>(args)...) {}
~PlayerImp() override = default;
......
void setMediaSource(const MediaSource::Ptr &src) override {
if (_delegate) {
// _delegate 为 PlayerBase,但是PlayImp(也就是本类)继承自PlayerBase,所以再执行一次setMediaSource,但是第二次_delegate为空
_delegate->setMediaSource(src);
}
_media_src = src;
}
......
protected:
......
MediaSource::Ptr _media_src;
std::shared_ptr<Delegate> _delegate;
};
src/Player/PlayerBase.h
class PlayerBase : public TrackSource, public toolkit::mINI {
public:
......
/**
* 设置一个MediaSource,直接生产rtsp/rtmp代理
*/
virtual void setMediaSource(const MediaSource::Ptr &src) = 0;
/**
* 设置异常中断回调
*/
virtual void setOnShutdown(const Event &cb) = 0;
/**
* 设置播放结果回调
*/
virtual void setOnPlayResult(const Event &cb) = 0;
/**
* 设置播放恢复回调
*/
virtual void setOnResume(const std::function<void()> &cb) = 0;
protected:
virtual void onResume() = 0;
virtual void onShutdown(const toolkit::SockException &ex) = 0;
virtual void onPlayResult(const toolkit::SockException &ex) = 0;
};
RtspPlayer::onConnect() 连接创建成功
拉流连接创建成功,开始发送RTSP数据。
src/Rtsp/RtspPlayer.cpp
void RtspPlayer::onConnect(const SockException &err){
if(err.getErrCode() != Err_success) {
onPlayResult_l(err,false);
return;
}
sendOptions();
}
发送 RTSP OPTIONS
src/Rtsp/RtspPlayer.cpp
void RtspPlayer::sendOptions(){
_on_response = [this](const Parser& parser){
if (!handleResponse("OPTIONS", parser)) {
return;
}
//获取服务器支持的命令
_supported_cmd.clear();
auto public_val = split(parser["Public"],",");
for(auto &cmd : public_val){
trim(cmd);
_supported_cmd.emplace(cmd);
}
//发送Describe请求,获取sdp
sendDescribe();
};
// 具体发送函数
sendRtspRequest("OPTIONS", _play_url);
}
RtspPlayer::_on_response 注册 OPTIONS 返回回调函数 RtspPlayer::sendDescribe()。
src/Rtsp/RtspPlayer.cpp
void RtspPlayer::sendRtspRequest(const string &cmd, const string &url,const StrCaseMap &header_const) {
auto header = header_const;
header.emplace("CSeq", StrPrinter << _cseq_send++);
header.emplace("User-Agent", kServerName);
if (!_session_id.empty()) {
header.emplace("Session", _session_id);
}
if (!_realm.empty() && !(*this)[Client::kRtspUser].empty()) {
if (!_md5_nonce.empty()) {
// MD5认证
/*
response计算方法如下:
RTSP客户端应该使用username + password并计算response如下:
(1)当password为MD5编码,则
response = md5( password:nonce:md5(public_method:url) );
(2)当password为ANSI字符串,则
response= md5( md5(username:realm:password):nonce:md5(public_method:url) );
*/
string encrypted_pwd = (*this)[Client::kRtspPwd];
if (!(*this)[Client::kRtspPwdIsMD5].as<bool>()) {
encrypted_pwd = MD5((*this)[Client::kRtspUser] + ":" + _realm + ":" + encrypted_pwd).hexdigest();
}
auto response = MD5(encrypted_pwd + ":" + _md5_nonce + ":" + MD5(cmd + ":" + url).hexdigest()).hexdigest();
_StrPrinter printer;
printer << "Digest ";
printer << "username=\"" << (*this)[Client::kRtspUser] << "\", ";
printer << "realm=\"" << _realm << "\", ";
printer << "nonce=\"" << _md5_nonce << "\", ";
printer << "uri=\"" << url << "\", ";
printer << "response=\"" << response << "\"";
header.emplace("Authorization", printer);
} else if (!(*this)[Client::kRtspPwdIsMD5].as<bool>()) {
// base64认证
auto authStrBase64 = encodeBase64((*this)[Client::kRtspUser] + ":" + (*this)[Client::kRtspPwd]);
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";
}
printer << "\r\n";
SockSender::send(std::move(printer));
}
3rdpart/ZLToolKit/src/Network/Socket.cpp
ssize_t SockSender::send(string buf) {
// ScoketHepler 继承自 SockSender
return send(std::make_shared<BufferString>(std::move(buf)));
}
3rdpart/ZLToolKit/src/Network/Socket.cpp
ssize_t SocketHelper::send(Buffer::Ptr buf) {
if (!_sock) {
return -1;
}
return _sock->send(std::move(buf), nullptr, 0, _try_flush);
}
RtspPlayer::onRecv() 数据回调总入口
src/Rtsp/RtspPlayer.cpp
void RtspPlayer::onRecv(const Buffer::Ptr& buf) {
if(_benchmark_mode && !_play_check_timer){
//在性能测试模式下,如果rtsp握手完毕后,不再解析rtp包
_rtp_recv_ticker.resetTime();
return;
}
try {
// RtspPlayer继承自RtspSplitter继承自HttpRequestSplitter
input(buf->data(), buf->size());
} catch (exception &e) {
SockException ex(Err_other, e.what());
onPlayResult_l(ex, !_play_check_timer);
}
}
Http/HttpRequestSplitter.cpp
void HttpRequestSplitter::input(const char *data,size_t len) {
{
auto size = remainDataSize();
if (size > kMaxCacheSize) {
//缓存太多数据无法处理则上抛异常
reset();
throw std::out_of_range("remain data size is too huge, now cleared:" + to_string(size));
}
}
const char *ptr = data;
if(!_remain_data.empty()){
_remain_data.append(data,len);
data = ptr = _remain_data.data();
len = _remain_data.size();
}
splitPacket:
/*确保ptr最后一个字节是0,防止strstr越界
*由于ZLToolKit确保内存最后一个字节是保留未使用字节并置0,
*所以此处可以不用再次置0
*但是上层数据可能来自其他渠道,保险起见还是置0
*/
char &tail_ref = ((char *) ptr)[len];
char tail_tmp = tail_ref;
tail_ref = 0;
//数据按照请求头处理
const char *index = nullptr;
_remain_data_size = len;
while (_content_len == 0 && _remain_data_size > 0 && (index = onSearchPacketTail(ptr,_remain_data_size)) != nullptr) {
if (index == ptr) {
break;
}
if (index < ptr || index > ptr + _remain_data_size) {
throw std::out_of_range("上层分包逻辑异常");
}
//_content_len == 0,这是请求头
const char *header_ptr = ptr;
ssize_t header_size = index - ptr;
ptr = index;
_remain_data_size = len - (ptr - data);
_content_len = onRecvHeader(header_ptr, header_size);
}
if(_remain_data_size <= 0){
//没有剩余数据,清空缓存
_remain_data.clear();
return;
}
/*
* 恢复末尾字节
* 移动到这来,目的是防止HttpRequestSplitter::reset()导致内存失效
*/
tail_ref = tail_tmp;
if(_content_len == 0){
//尚未找到http头,缓存定位到剩余数据部分
_remain_data.assign(ptr,_remain_data_size);
return;
}
//已经找到http头了
if(_content_len > 0){
//数据按照固定长度content处理
if(_remain_data_size < (size_t)_content_len){
//数据不够,缓存定位到剩余数据部分
_remain_data.assign(ptr, _remain_data_size);
return;
}
//收到content数据,并且接受content完毕
onRecvContent(ptr,_content_len);
_remain_data_size -= _content_len;
ptr += _content_len;
//content处理完毕,后面数据当做请求头处理
_content_len = 0;
if(_remain_data_size > 0){
//还有数据没有处理完毕
_remain_data.assign(ptr,_remain_data_size);
data = ptr = (char *)_remain_data.data();
len = _remain_data.size();
goto splitPacket;
}
_remain_data.clear();
return;
}
//_content_len < 0;数据按照不固定长度content处理
onRecvContent(ptr,_remain_data_size);//消费掉所有剩余数据
_remain_data.clear();
}
Rtsp/RtspSplitter.cpp
ssize_t RtspSplitter::onRecvHeader(const char *data, size_t len) {
if(_isRtpPacket){
//收到rtp包回调
onRtpPacket(data,len);
return 0;
}
_parser.Parse(data);
auto ret = getContentLength(_parser);
if(ret == 0){
//收到完整的rtsp包回调,包括sdp等content数据
onWholeRtspPacket(_parser);
_parser.Clear();
}
return ret;
}
RtspPlayer::onWholeRtspPacket() 处理 Rtsp 数据
src/Rtsp/RtspPlayer.cpp
void RtspPlayer::onWholeRtspPacket(Parser &parser) {
try {
// 在RtspPlayer::sendOptions() 中注册,实际为RtspPlayer::sendDescribe()
decltype(_on_response) func;
_on_response.swap(func);
if(func){
func(parser);
}
parser.Clear();
} catch (std::exception &err) {
//定时器_pPlayTimer为空后表明握手结束了
onPlayResult_l(SockException(Err_other, err.what()),!_play_check_timer);
}
}
发送 RTSP DESCRIBE
src/Rtsp/RtspPlayer.cpp
void RtspPlayer::sendDescribe() {
//发送DESCRIBE命令后处理函数:handleResDESCRIBE
_on_response = std::bind(&RtspPlayer::handleResDESCRIBE, this, placeholders::_1);
sendRtspRequest("DESCRIBE", _play_url, {"Accept", "application/sdp"});
}
RtspPlayer::_on_response注册DESCRIBE回调。实际为RtspPlayer::handleResDESCRIBE()。
解析 SDP,创建解封装
src/Rtsp/RtspPlayer.cpp
void RtspPlayer::handleResDESCRIBE(const Parser& parser) {
if (!handleResponse("DESCRIBE", parser)) {
return;
}
_content_base = parser["Content-Base"];
if(_content_base.empty()){
_content_base = _play_url;
}
if (_content_base.back() == '/') {
_content_base.pop_back();
}
//解析sdp
SdpParser sdpParser(parser.Content());
string sdp;
auto play_track = (TrackType)((int)(*this)[Client::kPlayTrack] - 1);
if (play_track != TrackInvalid) {
auto track = sdpParser.getTrack(play_track);
_sdp_track.emplace_back(track);
sdp = track->toString();
} else {
_sdp_track = sdpParser.getAvailableTrack();
sdp = sdpParser.toString();
}
if (_sdp_track.empty()) {
throw std::runtime_error("无有效的Sdp Track");
}
if (!onCheckSDP(sdp)) {
throw std::runtime_error("onCheckSDP faied");
}
_rtcp_context.clear();
for (auto &track : _sdp_track) {
if(track->_pt != 0xff){
setPayloadType(_rtcp_context.size(),track->_pt);
}
_rtcp_context.emplace_back(std::make_shared<RtcpContextForRecv>());
}
sendSetup(0);
}
创建解封装,初始化 Rtsp 代理结构体。
src/Rtsp/RtspPlayer.cpp
bool RtspPlayerImp::onCheckSDP(const std::string &sdp)
{
// RtspPlayerImp 继承自 PlayerImp,PlayerImp::_media_src。
// PlayerImp::_media_src 在setDirectProxy() 中初始化
// MediaSource::Ptr _media_src;
// RtspMediaSource::Ptr _rtsp_media_src;
_rtsp_media_src = std::dynamic_pointer_cast<RtspMediaSource>(_media_src);
if (_rtsp_media_src) {
_rtsp_media_src->setSdp(sdp);
}
_demuxer = std::make_shared<RtspDemuxer>();
_demuxer->setTrackListener(this, (*this)[Client::kWaitTrackReady].as<bool>());
_demuxer->loadSdp(sdp);
return true;
}
src/Rtsp/RtspMediaSourceImp.cpp
void RtspMediaSource::setSdp(const std::string &sdp) {
SdpParser sdp_parser(sdp);
// SdpTrack::Ptr _tracks[TrackMax];
_tracks[TrackVideo] = sdp_parser.getTrack(TrackVideo);
_tracks[TrackAudio] = sdp_parser.getTrack(TrackAudio);
_have_video = (bool)_tracks[TrackVideo];
_sdp = sdp_parser.toString();
// not run
if (_ring) {
regist();
}
}
解封装创建,_demuxer->loadSdp(sdp);有复杂的流程,参考 ZLMediaKit源码分析(四)重封装
发送 RTSP SETUP
src/Rtsp/RtspPlayer.cpp
//发送SETUP命令
void RtspPlayer::sendSetup(unsigned int track_idx) {
_on_response = std::bind(&RtspPlayer::handleResSETUP, this, placeholders::_1, track_idx);
auto &track = _sdp_track[track_idx];
auto control_url = track->getControlUrl(_content_base);
switch (_rtp_type) {
case Rtsp::RTP_TCP: {
sendRtspRequest("SETUP", control_url, {"Transport", StrPrinter << "RTP/AVP/TCP;unicast;interleaved=" << track->_type * 2 << "-" << track->_type * 2 + 1});
}
break;
case Rtsp::RTP_MULTICAST: {
sendRtspRequest("SETUP", control_url, {"Transport", "RTP/AVP;multicast"});
}
break;
case Rtsp::RTP_UDP: {
createUdpSockIfNecessary(track_idx);
sendRtspRequest("SETUP", control_url, {"Transport",
StrPrinter << "RTP/AVP;unicast;client_port="
<< _rtp_sock[track_idx]->get_local_port() << "-"
<< _rtcp_sock[track_idx]->get_local_port()});
}
break;
default:
break;
}
}
RtspPlayer::_on_response注册SETUP回调。实际为RtspPlayer::handleResSETUP()。
src/Rtsp/RtspPlayer.cpp
void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int track_idx) {
if (parser.Url() != "200") {
throw std::runtime_error(StrPrinter << "SETUP:" << parser.Url() << " " << parser.Tail() << endl);
}
if (track_idx == 0) {
_session_id = parser["Session"];
_session_id.append(";");
_session_id = FindField(_session_id.data(), nullptr, ";");
}
auto strTransport = parser["Transport"];
if (strTransport.find("TCP") != string::npos || strTransport.find("interleaved") != string::npos) {
_rtp_type = Rtsp::RTP_TCP;
} else if (strTransport.find("multicast") != string::npos) {
_rtp_type = Rtsp::RTP_MULTICAST;
} else {
_rtp_type = Rtsp::RTP_UDP;
}
auto transport_map = Parser::parseArgs(strTransport, ";", "=");
RtspSplitter::enableRecvRtp(_rtp_type == Rtsp::RTP_TCP);
string ssrc = transport_map["ssrc"];
if(!ssrc.empty()){
sscanf(ssrc.data(), "%x", &_sdp_track[track_idx]->_ssrc);
} else{
_sdp_track[track_idx]->_ssrc = 0;
}
if (_rtp_type == Rtsp::RTP_TCP) {
int interleaved_rtp, interleaved_rtcp;
sscanf(transport_map["interleaved"].data(), "%d-%d", &interleaved_rtp, &interleaved_rtcp);
_sdp_track[track_idx]->_interleaved = interleaved_rtp;
} else {
auto port_str = transport_map[(_rtp_type == Rtsp::RTP_MULTICAST ? "port" : "server_port")];
int rtp_port, rtcp_port;
sscanf(port_str.data(), "%d-%d", &rtp_port, &rtcp_port);
auto &pRtpSockRef = _rtp_sock[track_idx];
auto &pRtcpSockRef = _rtcp_sock[track_idx];
if (_rtp_type == Rtsp::RTP_MULTICAST) {
//udp组播
auto multiAddr = transport_map["destination"];
pRtpSockRef = createSocket();
//目前组播仅支持ipv4
if (!pRtpSockRef->bindUdpSock(rtp_port, "0.0.0.0")) {
pRtpSockRef.reset();
throw std::runtime_error("open udp sock err");
}
auto fd = pRtpSockRef->rawFD();
if (-1 == SockUtil::joinMultiAddrFilter(fd, multiAddr.data(), get_peer_ip().data(),get_local_ip().data())) {
SockUtil::joinMultiAddr(fd, multiAddr.data(),get_local_ip().data());
}
//设置rtcp发送端口
pRtcpSockRef = createSocket();
//目前组播仅支持ipv4
if (!pRtcpSockRef->bindUdpSock(0, "0.0.0.0")) {
//分配端口失败
throw runtime_error("open udp socket failed");
}
//设置发送地址和发送端口
auto dst = SockUtil::make_sockaddr(get_peer_ip().data(), rtcp_port);
pRtcpSockRef->bindPeerAddr((struct sockaddr *)&(dst));
} else {
createUdpSockIfNecessary(track_idx);
//udp单播
auto dst = SockUtil::make_sockaddr(get_peer_ip().data(), rtp_port);
pRtpSockRef->bindPeerAddr((struct sockaddr *)&(dst));
//发送rtp打洞包
pRtpSockRef->send("\xce\xfa\xed\xfe", 4);
dst = SockUtil::make_sockaddr(get_peer_ip().data(), rtcp_port);
//设置rtcp发送目标,为后续发送rtcp做准备
pRtcpSockRef->bindPeerAddr((struct sockaddr *)&(dst));
}
auto peer_ip = get_peer_ip();
weak_ptr<RtspPlayer> weakSelf = dynamic_pointer_cast<RtspPlayer>(shared_from_this());
//设置rtp over udp接收回调处理函数
pRtpSockRef->setOnRead([peer_ip, track_idx, weakSelf](const Buffer::Ptr &buf, struct sockaddr *addr , int addr_len) {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
return;
}
if (SockUtil::inet_ntoa(addr) != peer_ip) {
WarnL << "收到其他地址的rtp数据:" << SockUtil::inet_ntoa(addr);
return;
}
strongSelf->handleOneRtp(track_idx, strongSelf->_sdp_track[track_idx]->_type,
strongSelf->_sdp_track[track_idx]->_samplerate, (uint8_t *) buf->data(), buf->size());
});
if(pRtcpSockRef) {
//设置rtcp over udp接收回调处理函数
pRtcpSockRef->setOnRead([peer_ip, track_idx, weakSelf](const Buffer::Ptr &buf, struct sockaddr *addr , int addr_len) {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
return;
}
if (SockUtil::inet_ntoa(addr) != peer_ip) {
WarnL << "收到其他地址的rtcp数据:" << SockUtil::inet_ntoa(addr);
return;
}
strongSelf->onRtcpPacket(track_idx, strongSelf->_sdp_track[track_idx], (uint8_t *) buf->data(), buf->size());
});
}
}
if (track_idx < _sdp_track.size() - 1) {
//需要继续发送SETUP命令
sendSetup(track_idx + 1);
return;
}
//所有setup命令发送完毕
//发送play命令
sendPause(type_play, 0);
}
发送 RTSP PLAY
src/Rtsp/RtspPlayer.cpp
void RtspPlayer::sendPause(int type , uint32_t seekMS){
_on_response = std::bind(&RtspPlayer::handleResPAUSE, this, placeholders::_1, type);
//开启或暂停rtsp
switch (type){
case type_pause:
sendRtspRequest("PAUSE", _content_base);
break;
case type_play:
sendRtspRequest("PLAY", _content_base);
break;
case type_seek:
sendRtspRequest("PLAY", _content_base, {"Range",StrPrinter << "npt=" << setiosflags(ios::fixed) << setprecision(2) << seekMS / 1000.0 << "-"});
break;
default:
WarnL << "unknown type : " << type;
_on_response = nullptr;
break;
}
}
RtspPlayer::_on_response 注册 PLAY 回调。实际为 RtspPlayer::handleResPAUSE()。
创建总封装
src/Rtsp/RtspPlayer.cpp
void RtspPlayer::handleResPAUSE(const Parser& parser,int type) {
if (parser.Url() != "200") {
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;
onPlayResult_l(SockException(Err_shutdown, StrPrinter << "rtsp play failed:" << parser.Url() << " " << parser.Tail() ), !_play_check_timer);
break;
case type_seek:
WarnL << "Seek failed:" << parser.Url() << " " << parser.Tail() << endl;
break;
}
return;
}
if (type == type_pause) {
//暂停成功!
_rtp_check_timer.reset();
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 = (uint32_t)(1000 * atof(strStart.data()));
DebugL << "seekTo(ms):" << iSeekTo;
}
onPlayResult_l(SockException(Err_success, type == type_seek ? "resum rtsp success" : "rtsp play success"), !_play_check_timer);
}
src/Rtsp/RtspPlayer.cpp
void RtspPlayer::onPlayResult_l(const SockException &ex , bool handshake_done) {
if (ex.getErrCode() == Err_shutdown) {
//主动shutdown的,不触发回调
return;
}
WarnL << ex.getErrCode() << " " << ex.what();
if (!handshake_done) {
//开始播放阶段
_play_check_timer.reset();
onPlayResult(ex);
//是否为性能测试模式
_benchmark_mode = (*this)[Client::kBenchmarkMode].as<int>();
} else if (ex) {
//播放成功后异常断开回调
onShutdown(ex);
} else {
//恢复播放
onResume();
}
if (!ex) {
//播放成功,恢复rtp接收超时定时器
_rtp_recv_ticker.resetTime();
auto timeoutMS = (*this)[Client::kMediaTimeoutMS].as<uint64_t>();
weak_ptr<RtspPlayer> weakSelf = dynamic_pointer_cast<RtspPlayer>(shared_from_this());
auto lam = [weakSelf, timeoutMS]() {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
return false;
}
if (strongSelf->_rtp_recv_ticker.elapsedTime() > timeoutMS) {
//接收rtp媒体数据包超时
strongSelf->onPlayResult_l(SockException(Err_timeout, "receive rtp timeout"), true);
return false;
}
return true;
};
//创建rtp数据接收超时检测定时器
_rtp_check_timer = std::make_shared<Timer>(timeoutMS / 2000.0f, lam, getPoller());
} else {
sendTeardown();
}
}
src/Rtsp/RtspPlayer.cpp
void RtspPlayerImp::onPlayResult(const toolkit::SockException &ex) {
if (!(*this)[Client::kWaitTrackReady].as<bool>() || ex) {
// 实际为 PlayerImp::onPlayResult
// 在 PlayerImp::setOnPlayResult()中设置
Super::onPlayResult(ex);
return;
}
}
执行创建总封装回调。
在PlayerProxy::play()中调用PlayerImp::setOnPlayResult() 初始化。
src/Player/PlayerBase.h
template<typename Parent, typename Delegate>
class PlayerImp : public Parent {
public:
using Ptr = std::shared_ptr<PlayerImp>;
......
void setOnPlayResult(const std::function<void(const toolkit::SockException &ex)> &cb) override {
if (_delegate) {
_delegate->setOnPlayResult(cb);
}
_on_play_result = cb;
}
......
protected:
......
// 执行创建总封装回调。
void onPlayResult(const toolkit::SockException &ex) override {
if (_on_play_result) {
_on_play_result(ex);
_on_play_result = nullptr;
}
}
......
protected:
std::function<void()> _on_resume;
PlayerBase::Event _on_shutdown;
PlayerBase::Event _on_play_result;
MediaSource::Ptr _media_src;
std::shared_ptr<Delegate> _delegate;
};
RtspPlayer::onRtpPacket() 处理 Rtp 数据
src/Rtsp/RtspPlayer.cpp
void RtspPlayer::onRtpPacket(const char *data, size_t len) {
int trackIdx = -1;
uint8_t interleaved = data[1];
if(interleaved %2 == 0){
trackIdx = getTrackIndexByInterleaved(interleaved);
if (trackIdx == -1) {
return;
}
handleOneRtp(trackIdx, _sdp_track[trackIdx]->_type, _sdp_track[trackIdx]->_samplerate, (uint8_t *)data + RtpPacket::kRtpTcpHeaderSize, len - RtpPacket::kRtpTcpHeaderSize);
}else{
trackIdx = getTrackIndexByInterleaved(interleaved - 1);
if (trackIdx == -1) {
return;
}
onRtcpPacket(trackIdx, _sdp_track[trackIdx], (uint8_t *) data + RtpPacket::kRtpTcpHeaderSize, len - RtpPacket::kRtpTcpHeaderSize);
}
}
src/Rtsp/RtpReceiver.h
template<int kCount = 2>
class RtpMultiReceiver {
public:
......
bool handleOneRtp(int index, TrackType type, int sample_rate, uint8_t *ptr, size_t len) {
assert(index < kCount && index >= 0);
// RtspSession 继承自父类 RtpReceiver。就是包含两个元素的数组
return _track[index].inputRtp(type, sample_rate, ptr, len).operator bool();
}
......
private:
RtpTrackImp _track[kCount];
};
using RtpReceiver = RtpMultiReceiver<2>;
Rtsp/RtpReceiver.cpp
RtpPacket::Ptr RtpTrack::inputRtp(TrackType type, int sample_rate, uint8_t *ptr, size_t len) {
if (len < RtpPacket::kRtpHeaderSize) {
throw BadRtpException("rtp size less than 12");
}
GET_CONFIG(uint32_t, rtpMaxSize, Rtp::kRtpMaxSize);
if (len > 1024 * rtpMaxSize) {
WarnL << "超大的rtp包:" << len << " > " << 1024 * rtpMaxSize;
return nullptr;
}
if (!sample_rate) {
//无法把时间戳转换成毫秒
return nullptr;
}
RtpHeader *header = (RtpHeader *) ptr;
if (header->version != RtpPacket::kRtpVersion) {
throw BadRtpException("invalid rtp version");
}
if (header->getPayloadSize(len) < 0) {
//rtp有效负载小于0,非法
throw BadRtpException("invalid rtp payload size");
}
//比对缓存ssrc
auto ssrc = ntohl(header->ssrc);
if (_pt == 0xFF) {
_pt = header->pt;
} else if (header->pt != _pt) {
//TraceL << "rtp pt mismatch:" << (int) header->pt << " !=" << (int) _pt;
return nullptr;
}
if (!_ssrc) {
//记录并锁定ssrc
_ssrc = ssrc;
_ssrc_alive.resetTime();
} else if (_ssrc == ssrc) {
//ssrc匹配正确,刷新计时器
_ssrc_alive.resetTime();
} else {
//ssrc错误
if (_ssrc_alive.elapsedTime() < 3 * 1000) {
//接受正确ssrc的rtp在10秒内,那么我们认为存在多路rtp,忽略掉ssrc不匹配的rtp
WarnL << "ssrc mismatch, rtp dropped:" << ssrc << " != " << _ssrc;
return nullptr;
}
InfoL << "rtp ssrc changed:" << _ssrc << " -> " << ssrc;
_ssrc = ssrc;
_ssrc_alive.resetTime();
}
auto rtp = RtpPacket::create();
//需要添加4个字节的rtp over tcp头
rtp->setCapacity(RtpPacket::kRtpTcpHeaderSize + len);
rtp->setSize(RtpPacket::kRtpTcpHeaderSize + len);
rtp->sample_rate = sample_rate;
rtp->type = type;
//赋值4个字节的rtp over tcp头
uint8_t *data = (uint8_t *) rtp->data();
data[0] = '$';
data[1] = 2 * type;
data[2] = (len >> 8) & 0xFF;
data[3] = len & 0xFF;
//拷贝rtp
memcpy(&data[4], ptr, len);
if (_disable_ntp) {
//不支持ntp时间戳,例如国标推流,那么直接使用rtp时间戳
rtp->ntp_stamp = rtp->getStamp() * uint64_t(1000) / sample_rate;
} else {
//设置ntp时间戳
rtp->ntp_stamp = _ntp_stamp.getNtpStamp(rtp->getStamp(), sample_rate);
}
onBeforeRtpSorted(rtp);
sortPacket(rtp->getSeq(), rtp);
return rtp;
}
sortPacket() 省略很多
RtpTrackImp 继承自 RtpTrack 继承自 PacketSortor。
PacketSortor::sortPacket() 过程省略。排好序之后,回调到最顶层 RtspPlayer::onRtpSorted()。
RtspPlayer::onRtpSorted() rtp 数据排序之后
src/Rtsp/RtspPlayer.cpp
void RtspPlayer::onRtpSorted(RtpPacket::Ptr rtppt, int trackidx){
_stamp[trackidx] = rtppt->getStampMS();
_rtp_recv_ticker.resetTime();
onRecvRTP(std::move(rtppt), _sdp_track[trackidx]);
}
src/Rtsp/RtspPlayer.cpp
void RtspPlayerImp::onRecvRTP(RtpPacket::Ptr rtp, const SdpTrack::Ptr &track) {
// 这个是重封装使用
//rtp解复用时可以判断是否为关键帧起始位置
auto key_pos = _demuxer->inputRtp(rtp);
// 这个是代理转发使用
if (_rtsp_media_src) {
_rtsp_media_src->onWrite(std::move(rtp), key_pos);
}
}
RtspDemuxer::inputRtp()
重封装输入。
在 PlayerImp::setOnPlayResult()->PlayerProxy::onPlaySuccess() 创建总封装时,关闭了rtsp子封装和rtmp子封装,其他子封装没有关闭。 创建总封装时,关闭了 rtsp子 封装和 rtmp 子封装,其他子封装没有关闭。
首先解码,编码,delegate,走到总封装入口MultiMediaSourceMuxer::onTrackFrame()停止向下执行。参考ZLMediaKit源码分析(四)重封装。
src/Rtsp/RtspDemuxer.cpp
bool RtspDemuxer::inputRtp(const RtpPacket::Ptr &rtp) {
switch (rtp->type) {
case TrackVideo: {
// RtpCodec::Ptr _video_rtp_decoder;
if (_video_rtp_decoder) {
return _video_rtp_decoder->inputRtp(rtp, true);
}
return false;
}
case TrackAudio: {
if (_audio_rtp_decoder) {
_audio_rtp_decoder->inputRtp(rtp, false);
return false;
}
return false;
}
default: return false;
}
}
RtspMediaSource::onWrite()
代理转发输入。
参考: ZLMediaKit源码分析(二)推流创建 ,ZLMediaKit源码分析(三)拉流创建。
src/Rtsp/RtspMediaSourceImp.cpp
void RtspMediaSource::onWrite(RtpPacket::Ptr rtp, bool keyPos) {
_speed[rtp->type] += rtp->size();
assert(rtp->type >= 0 && rtp->type < TrackMax);
auto &track = _tracks[rtp->type];
auto stamp = rtp->getStampMS();
if (track) {
track->_seq = rtp->getSeq();
track->_time_stamp = rtp->getStamp() * uint64_t(1000) / rtp->sample_rate;
track->_ssrc = rtp->getSSRC();
}
// 如果_ring没有创建,则优先创建
// RtspMediaSource::_ring(RingType::Ptr)
if (!_ring) {
std::weak_ptr<RtspMediaSource> weakSelf = std::dynamic_pointer_cast<RtspMediaSource>(shared_from_this());
// 该回调函数用于统计 下行数量,设计好几层,RingBuffer RingReaderDispatcher,他们也有自定义的数量统计,具体参考其实现
auto lam = [weakSelf](int size) {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
return;
}
strongSelf->onReaderChanged(size);
};
//GOP默认缓冲512组RTP包,每组RTP包时间戳相同(如果开启合并写了,那么每组为合并写时间内的RTP包),
//每次遇到关键帧第一个RTP包,则会清空GOP缓存(因为有新的关键帧了,同样可以实现秒开)
// RtspMediaSource::RingType::Ptr _ring;
// 1. 创建RtspMediaSource::RingType::Ptr _ring,管理新加入的流。
_ring = std::make_shared<RingType>(_ring_size, std::move(lam));
if (!_sdp.empty()) {
// 2. 添加全局四维数组s_media_source_map,添加新加入的流。
regist();
}
}
bool is_video = rtp->type == TrackVideo;
// inputPacket() 函数内部会调用onFlush()
// onFlush()就是输出函数
// RtspMediaSource继承自PacketCache<RtpPacket>。
PacketCache<RtpPacket>::inputPacket(stamp, is_video, std::move(rtp), keyPos);
}
代理实例
添加代理流。
# 服务地址10.49.44.14
# app:为本地服务器创建的app
# stream:为本地服务器创建的stream
# url:为本地服务器拉流地址
# retry_count:为失败重试次数
http://10.49.44.14:805/index/api/addStreamProxy?secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc&vhost=__defaultVhost__&app=proxy&stream=hello_0&url=rtsp://10.49.44.16:5546/live/test_1&retry_count=1
删除代理流。
http://10.49.44.14:805/index/api/delStreamProxy?secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc&key=__defaultVhost__/proxy/hello_0
查看流信息。
# 查看推理信息
http://10.49.44.14:805/index/api/getMediaInfo?secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc&vhost=__defaultVhost__&app=proxy&stream=hello_0&schema=rtsp
# 查看拉流信息
http://10.49.44.14:805/index/api/getMediaPlayerList?secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc&vhost=__defaultVhost__&app=proxy&stream=hello_0&schema=rtsp
ZLMediaKit 写了六篇,暂告一段落。纪念一下。