ZLMediaKit源码分析(一)服务启动
ZLMediaKit源码分析(二)推流创建
ZLMediaKit源码分析(三)拉流创建
ZLMediaKit源码分析(四)重封装
ZLMediaKit源码分析(五)代理服务
ZLMediaKit源码分析(六)http 点播
ZLMediaKit推流结构体创建是在RtspSession::onRecv()之后。如何触发onRecv()请参考上一篇文章ZLMediaKit源码分析(一)服务启动这里不在赘述。
继承关系
RtspSession::onRecv()
接收数据入口。
src/Rtsp/RtspSession.cpp
void RtspSession::onRecv(const Buffer::Ptr &buf) {
_alive_ticker.resetTime();
_bytes_usage += buf->size();
if (_on_recv) {
//http poster的请求数据转发给http getter处理
_on_recv(buf);
} else {
input(buf->data(), buf->size());
}
}
RtspSession 继承自 RtspSplitter 继承自 HttpRequestSplitter
HttpRequestSplitter::input() 调用onRecvHeader(),onRecvContent(),但在类HttpRequestSplitter中,这两个函数是虚函数,实际调用 RtspSplitter::onRecvHeader()和RtspSplitter::onRecvContent()。
Http/HttpRequestSplitter.cpp
void HttpRequestSplitter::input(const char *data,size_t len) {
......
//数据按照请求头处理
const char *index = nullptr;
_remain_data_size = len;
while (_content_len == 0 && _remain_data_size > 0 && (index = onSearchPacketTail(ptr,_remain_data_size)) != nullptr) {
......
_content_len = onRecvHeader(header_ptr, header_size);
}
......
//已经找到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);
......
return;
}
//_content_len < 0;数据按照不固定长度content处理
onRecvContent(ptr,_remain_data_size);//消费掉所有剩余数据
_remain_data.clear();
}
RtspSession 继承自 RtspSplitter 继承自 HttpRequestSplitter(暂时不涉及该类)
RtspSplitter::onRecvHeader() 调用两个重要的纯虚函数RtspSplitter::onWholeRtspPacket(),RtspSplitter::onRtpPacket()。
RtspSplitter::onWholeRtspPacket()实际调用RtspSession:: onWholeRtspPacket(),处理rtsp包,包括sdp等content数据。
RtspSplitter::onRtpPacket()实际调用RtspSession::onRtpPacket() 处理rtp包。
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;
}
RtspSession:: onWholeRtspPacket()
src/Rtsp/RtspSession.cpp
void RtspSession::onWholeRtspPacket(Parser &parser) {
string method = parser.Method(); //提取出请求命令字
_cseq = atoi(parser["CSeq"].data());
if (_content_base.empty() && method != "GET") {
_content_base = parser.Url();
_media_info.parse(parser.FullUrl());
_media_info._schema = RTSP_SCHEMA;
}
using rtsp_request_handler = void (RtspSession::*)(const Parser &parser);
static unordered_map<string, rtsp_request_handler> s_cmd_functions;
static onceToken token([]() {
s_cmd_functions.emplace("OPTIONS", &RtspSession::handleReq_Options);
s_cmd_functions.emplace("DESCRIBE", &RtspSession::handleReq_Describe);
s_cmd_functions.emplace("ANNOUNCE", &RtspSession::handleReq_ANNOUNCE);
s_cmd_functions.emplace("RECORD", &RtspSession::handleReq_RECORD);
s_cmd_functions.emplace("SETUP", &RtspSession::handleReq_Setup);
s_cmd_functions.emplace("PLAY", &RtspSession::handleReq_Play);
s_cmd_functions.emplace("PAUSE", &RtspSession::handleReq_Pause);
s_cmd_functions.emplace("TEARDOWN", &RtspSession::handleReq_Teardown);
s_cmd_functions.emplace("GET", &RtspSession::handleReq_Get);
s_cmd_functions.emplace("POST", &RtspSession::handleReq_Post);
s_cmd_functions.emplace("SET_PARAMETER", &RtspSession::handleReq_SET_PARAMETER);
s_cmd_functions.emplace("GET_PARAMETER", &RtspSession::handleReq_SET_PARAMETER);
});
auto it = s_cmd_functions.find(method);
if (it == s_cmd_functions.end()) {
sendRtspResponse("403 Forbidden");
throw SockException(Err_shutdown, StrPrinter << "403 Forbidden:" << method);
}
(this->*(it->second))(parser);
parser.Clear();
}
RtspSession::handleReq_Options()
src/Rtsp/RtspSession.cpp
void RtspSession::handleReq_Options(const Parser &parser) {
//支持这些命令
sendRtspResponse("200 OK",{"Public" , "OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, ANNOUNCE, RECORD, SET_PARAMETER, GET_PARAMETER"});
}
RtspSession::handleReq_ANNOUNCE()上行请求
src/Rtsp/RtspSession.cpp
void RtspSession::handleReq_ANNOUNCE(const Parser &parser) {
auto full_url = parser.FullUrl();
_content_base = full_url;
if (end_with(full_url, ".sdp")) {
//去除.sdp后缀,防止EasyDarwin推流器强制添加.sdp后缀
full_url = full_url.substr(0, full_url.length() - 4);
// url解析后保存的相关信息
// MediaInfo _media_info;
_media_info.parse(full_url);
}
if (_media_info._app.empty() || _media_info._streamid.empty()) {
//推流rtsp url必须最少两级(rtsp://host/app/stream_id),不允许莫名其妙的推流url
static constexpr auto err = "rtsp推流url非法,最少确保两级rtsp url";
sendRtspResponse("403 Forbidden", {"Content-Type", "text/plain"}, err);
throw SockException(Err_shutdown, StrPrinter << err << ":" << full_url);
}
auto onRes = [this, parser, full_url](const string &err, const ProtocolOption &option) {
if (!err.empty()) {
sendRtspResponse("401 Unauthorized", { "Content-Type", "text/plain" }, err);
shutdown(SockException(Err_shutdown, StrPrinter << "401 Unauthorized:" << err));
return;
}
// MediaSource::Ptr RtspSession::_push_src
assert(!_push_src);
auto src = MediaSource::find(RTSP_SCHEMA, _media_info._vhost, _media_info._app, _media_info._streamid);
auto push_failed = (bool)src;
// 初次推流src尚未创建,这里是不会执行的。
while (src) {
//尝试断连后继续推流
auto rtsp_src = dynamic_pointer_cast<RtspMediaSourceImp>(src);
if (!rtsp_src) {
//源不是rtsp推流产生的
break;
}
auto ownership = rtsp_src->getOwnership();
if (!ownership) {
//获取推流源所有权失败
break;
}
_push_src = std::move(rtsp_src);
_push_src_ownership = std::move(ownership);
push_failed = false;
break;
} // end while
if (push_failed) {
sendRtspResponse("406 Not Acceptable", { "Content-Type", "text/plain" }, "Already publishing.");
string err = StrPrinter << "ANNOUNCE: Already publishing:" << _media_info.shortUrl() << endl;
throw SockException(Err_shutdown, err);
}
SdpParser sdpParser(parser.Content());
// 获取sessionid。
_sessionid = makeRandStr(12);
//获取std::vector<SdpTrack::Ptr> RtspSession::_sdp_track;
_sdp_track = sdpParser.getAvailableTrack();
if (_sdp_track.empty()) {
// sdp无效
static constexpr auto err = "sdp中无有效track";
sendRtspResponse("403 Forbidden", { "Content-Type", "text/plain" }, err);
shutdown(SockException(Err_shutdown, StrPrinter << err << ":" << full_url));
return;
}
_rtcp_context.clear();
for (auto &track : _sdp_track) {
_rtcp_context.emplace_back(std::make_shared<RtcpContextForRecv>());
}
// MediaSource::Ptr RtspSession::_push_src
if (!_push_src) {
// RtspMediaSourceImp构造函数中 构造RtspMediaSource(vhost, app, id, ringSize)
// 然后又构造MediaSource(RTSP_SCHEMA, vhost, app, stream_id)
// 初始化 _vhost = vhost.empty() ? DEFAULT_VHOST : vhost; _schema = schema; _app = app; _stream_id = stream_id;
// 在数据到来之际 会调用 MediaSource::regist() 追加_push_src至四维map s_media_source_map
// auto &ref = s_media_source_map[_schema][_vhost][_app][_stream_id];
_push_src = std::make_shared<RtspMediaSourceImp>(_media_info._vhost, _media_info._app, _media_info._streamid);
//获取所有权
_push_src_ownership = _push_src->getOwnership();
_push_src->setProtocolOption(option);
_push_src->setSdp(parser.Content());
}
// MediaSource::Ptr RtspSession::_push_src
_push_src->setListener(dynamic_pointer_cast<MediaSourceEvent>(shared_from_this()));
_continue_push_ms = option.continue_push_ms;
sendRtspResponse("200 OK");
}; // end onRes 回掉函数
weak_ptr<RtspSession> weak_self = dynamic_pointer_cast<RtspSession>(shared_from_this());
Broadcast::PublishAuthInvoker invoker = [weak_self, onRes](const string &err, const ProtocolOption &option) {
auto strong_self = weak_self.lock();
if (!strong_self) {
return;
}
strong_self->async([weak_self, onRes, err, option]() {
auto strong_self = weak_self.lock();
if (!strong_self) {
return;
}
onRes(err, option);
});
};
//rtsp推流需要鉴权
auto flag = NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPublish, MediaOriginType::rtsp_push, _media_info, invoker, static_cast<SockInfo &>(*this));
if (!flag) {
//该事件无人监听,默认不鉴权
onRes("", ProtocolOption());
}
}
MediaSource::find()
在全局四维map中查找该流是否存在,存在则返回MediaSource::Ptr 指针。
RtspSession::_sdp_track
RtspSession::_rtcp_context
RtspSession::_sessionid
src/Rtsp/RtspSession.cpp
_sessionid = makeRandStr(12);
RtspSession::_push_src 创建上行
RtspMediaSourceImp 继承自 RtspMediaSource 继承自 MediaSource 。
调用MediaSource::regist() 在全局静态变量s_media_source_map中追加新的MediaSource::Ptr指针。
RtspMediaSourceImp构造函数中,构造RtspMediaSource(vhost, app, id, ringSize),然后又构造MediaSource(RTSP_SCHEMA, vhost, app, stream_id) 。
在rtp数据到来之际,会调用 MediaSource::regist() 追加四维map,全局静态变量s_media_source_map[_schema][_vhost][_app][_stream_id]。
RtspSession::_push_src
src/Rtsp/RtspSession.cpp
// RtspMediaSourceImp::Ptr RtspSession::_push_src;
if (!_push_src) {
// RtspMediaSourceImp构造函数中,构造RtspMediaSource(vhost, app, id, ringSize)
// 然后构造MediaSource(RTSP_SCHEMA, vhost, app, stream_id)
// 初始化 _vhost = vhost.empty() ? DEFAULT_VHOST : vhost; _schema = schema; _app = app; _stream_id = stream_id;
// 在数据到来之际 会调用 MediaSource::regist() 追加四维map s_media_source_map
// auto &ref = s_media_source_map[_schema][_vhost][_app][_stream_id];
_push_src = std::make_shared<RtspMediaSourceImp>(_media_info._vhost, _media_info._app, _media_info._streamid);
//获取所有权
_push_src_ownership = _push_src->getOwnership();
_push_src->setProtocolOption(option);
_push_src->setSdp(parser.Content());
}
_push_src->setListener(dynamic_pointer_cast<MediaSourceEvent>(shared_from_this()));
构造RtspMediaSourceImp,RtspMediaSource。
src/Rtsp/RtspMediaSourceImp.cpp
RtspMediaSourceImp::RtspMediaSourceImp(const std::string &vhost, const std::string &app, const std::string &id, int ringSize)
: RtspMediaSource(vhost, app, id, ringSize)
{
// MultiMediaSourceMuxer::Ptr _muxer
// RtspDemuxer::Ptr _demuxer
_demuxer = std::make_shared<RtspDemuxer>();
_demuxer->setTrackListener(this);
}
构造MediaSource,并且设置_ring_size。
src/Rtsp/RtspMediaSource.h
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) {}
make_shared()(RtspMediaSourceImp::_demuxer)没有显式构造函数。
src/Common/MediaSink.cpp
void Demuxer::setTrackListener(TrackListener *listener, bool wait_track_ready) {
if (wait_track_ready) {
auto sink = std::make_shared<MediaSinkDelegate>();
sink->setTrackListener(listener);
_sink = std::move(sink);
}
_listener = listener;
}
RtspMediaSourceImp::setProtocolOption()
这里应该是需要转码的时候走这里。
src/Rtsp/RtspMediaSourceImp.cpp
void RtspMediaSourceImp::setProtocolOption(const ProtocolOption &option)
{
GET_CONFIG(bool, direct_proxy, Rtsp::kDirectProxy);
//开启直接代理模式时,rtsp直接代理,不重复产生;但是有些rtsp推流端,由于sdp中已有sps pps,rtp中就不再包括sps pps,
//导致rtc无法播放,所以在rtsp推流rtc播放时,建议关闭直接代理模式
_option = option;
_option.enable_rtsp = !direct_proxy;
// MultiMediaSourceMuxer::Ptr _muxer
// RtspDemuxer::Ptr _demuxer
_muxer = std::make_shared<MultiMediaSourceMuxer>(getVhost(), getApp(), getId(), _demuxer->getDuration(), _option);
_muxer->setMediaListener(getListener());
_muxer->setTrackListener(std::static_pointer_cast<RtspMediaSourceImp>(shared_from_this()));
//让_muxer对象拦截一部分事件(比如说录像相关事件)
MediaSource::setListener(_muxer);
// 获取解封装的Tracks
for (auto &track : _demuxer->getTracks(false)) {
_muxer->addTrack(track);
// vector<Track::Ptr> 继承自 FrameDispatcher
track->addDelegate(_muxer);
}
}
RtspMediaSourceImp::setSdp()
创建解码轨道。
其父类函数virtual void RtspMediaSource::setSdp(const std::string &sdp)是虚函数。
src/Rtsp/RtspMediaSourceImp.cpp
void RtspMediaSourceImp::setSdp(const std::string &strSdp)
{
if (!getSdp().empty()) {
return;
}
// // RtspDemuxer::Ptr _demuxer
_demuxer->loadSdp(strSdp);
// 调用父类的函数 虚函数
RtspMediaSource::setSdp(strSdp);
}
src/Rtsp/RtspDemuxer.cpp
void RtspDemuxer::loadSdp(const SdpParser &attr) {
auto tracks = attr.getAvailableTrack();
for (auto &track : tracks) {
switch (track->_type) {
case TrackVideo: {
// 这里会调用 Demuxer::addTrack()
makeVideoTrack(track);
}
break;
case TrackAudio: {
makeAudioTrack(track);
}
break;
default:
break;
}
}
//rtsp能通过sdp立即知道有多少个track
addTrackCompleted();
auto titleTrack = attr.getTrack(TrackTitle);
if (titleTrack) {
_duration = titleTrack->_duration;
}
}
虚函数。
virtual void RtspMediaSource::setSdp(const std::string &sdp);
src/Rtsp/RtspMediaSourceImp.cpp
void RtspMediaSource::setSdp(const std::string &sdp) {
SdpParser sdp_parser(sdp);
// SdpTrack::Ptr RtspMediaSource::_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();
}
}
RtspMediaSourceImp::setListener()
不重编码的情况下,以下都没有执行。
Rtsp/RtspMediaSourceImp.h
/**
* 设置事件监听器
* @param listener 监听器
*/
void setListener(const std::weak_ptr<MediaSourceEvent> &listener) override{
// MultiMediaSourceMuxer::Ptr _muxer;
if (_muxer) {
// run
// 在函数RtspMediaSourceImp::setProtocolOption()中初始化
// MultiMediaSourceMuxer::Ptr _muxer
// _muxer = std::make_shared<MultiMediaSourceMuxer>();
//_muxer对象不能处理的事件再给listener处理
_muxer->setMediaListener(listener);
} else {
//未创建_muxer对象,事件全部给listener处理
MediaSource::setListener(listener);
}
}
src/Rtsp/MultiMediaSourceMuxer.cpp
void MultiMediaSourceMuxer::setMediaListener(const std::weak_ptr<MediaSourceEvent> &listener) {
setDelegate(listener);
auto self = shared_from_this();
//拦截事件
if (_rtmp) {
_rtmp->setListener(self);
}
if (_rtsp) {
_rtsp->setListener(self);
}
if (_ts) {
_ts->setListener(self);
}
#if defined(ENABLE_MP4)
if (_fmp4) {
_fmp4->setListener(self);
}
#endif
auto hls = _hls;
if (hls) {
hls->setListener(self);
}
}
RtspSession::handleReq_SETUP() 建立链接
RtspSession::handleReq_RECORD() 上行推流
src/Rtsp/RtspSession.cpp
void RtspSession::handleReq_RECORD(const Parser &parser){
if (_sdp_track.empty() || parser["Session"] != _sessionid) {
send_SessionNotFound();
throw SockException(Err_shutdown, _sdp_track.empty() ? "can not find any available track when record" : "session not found when record");
}
_StrPrinter rtp_info;
for (auto &track : _sdp_track) {
if (track->_inited == false) {
//还有track没有setup
shutdown(SockException(Err_shutdown, "track not setuped"));
return;
}
rtp_info << "url=" << track->getControlUrl(_content_base) << ",";
}
rtp_info.pop_back();
sendRtspResponse("200 OK", {"RTP-Info", rtp_info});
if (_rtp_type == Rtsp::RTP_TCP) {
//如果是rtsp推流服务器,并且是TCP推流,设置socket flags,,这样能提升接收性能
setSocketFlags();
}
}
RtspSession::handleReq_Describe() 下行请求
RtspSession::handleReq_Play() 下行播放
RtspSession::handleReq_TEARDOWN() 断开链接
RtspSession:: onRtpPacket()数据输入
src/Rtsp/RtspSession.cpp
void RtspSession::onRtpPacket(const char *data, size_t len) {
uint8_t interleaved = data[1];
if (interleaved % 2 == 0) {
auto track_idx = getTrackIndexByInterleaved(interleaved);
// 调用父类函数 RtpMultiReceiver::handleOneRtp()
// std::vector<SdpTrack::Ptr> RtspSession::_sdp_track;
handleOneRtp(track_idx, _sdp_track[track_idx]->_type, _sdp_track[track_idx]->_samplerate, (uint8_t *) data + RtpPacket::kRtpTcpHeaderSize, len - RtpPacket::kRtpTcpHeaderSize);
} else {
auto track_idx = getTrackIndexByInterleaved(interleaved - 1);
onRtcpPacket(track_idx, _sdp_track[track_idx], data + RtpPacket::kRtpTcpHeaderSize, len - RtpPacket::kRtpTcpHeaderSize);
}
}
RtspSession 继承自 RtpMultiReveiver。
RtspSession::onRtpPacket()调用handleOneRtp(),实际调用RtpMultiReceiver::handleOneRtp()。
src/Rtsp/RtpReceiver.h
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>;
RtpTrackImp继承自RtpTrack。
RtpMultiReveiver::handleOneRtp() 调用_track[index].inputRtp(),实际调用 RtpTrack::inputRtp()。
Rtsp/RtpReceiver.cpp
RtpPacket::Ptr RtpTrack::inputRtp(TrackType type, int sample_rate, uint8_t *ptr, size_t len) {
......
onBeforeRtpSorted(rtp);
sortPacket(rtp->getSeq(), rtp);
return rtp;
}
RtpTrackImp继承自RtpTrack继承自PacketSortor
PacketSortor::sortPacket()过程省略。这个地方还牵涉到音频轨和视频轨,具体不在分析。排好序之后,回调到最顶层RtspSession::onRtpSorted()。
RtspSession::onRtpSorted() 数据排序后
先看一下函数onRtpSorted()定义,_push_src声明。后面要用。
src/Rtsp/RtspSession.h
using BufferRtp = toolkit::BufferOffset<toolkit::Buffer::Ptr>;
class RtspSession : public toolkit::Session, public RtspSplitter, public RtpReceiver, public MediaSourceEvent {
public:
......
RtpReceiver override
void onRtpSorted(RtpPacket::Ptr rtp, int track_idx) override;
void onBeforeRtpSorted(const RtpPacket::Ptr &rtp, int track_index) override;
......
private:
//rtsp推流相关绑定的源
RtspMediaSourceImp::Ptr _push_src;
......
};
onRtpSorted()执行。
void RtspSession::onRtpSorted(RtpPacket::Ptr rtp, int track_idx) {
if (_push_src) {
// run
// 接收推流数据
//RtspMediaSourceImp::Ptr _push_src;
//RtspMediaSourceImp::onWrite [RtspMediaSourceImp.cpp]
_push_src->onWrite(std::move(rtp), false);
} else {
WarnL << "Not a rtsp push!";
}
}
src/Rtsp/RtspMediaSourceImp.cpp
void RtspMediaSourceImp::onWrite(RtpPacket::Ptr rtp, bool key_pos)
{
if (_all_track_ready && !_muxer->isEnabled()) {
// run
//获取到所有Track后,并且未开启转协议,那么不需要解复用rtp
//在关闭rtp解复用后,无法知道是否为关键帧,这样会导致无法秒开,或者开播花屏
key_pos = rtp->type == TrackVideo;
} else {
//需要解复用rtp
key_pos = _demuxer->inputRtp(rtp);
}
GET_CONFIG(bool, directProxy, Rtsp::kDirectProxy);
if (directProxy) {
// run
//直接代理模式才直接使用原始rtp
RtspMediaSource::onWrite(std::move(rtp), key_pos);
}
}
创建ring 和 regist MediaSource
- 创建RtspMediaSource::RingType::Ptr _ring,管理新加入的流。
- 添加全局四维数组s_media_source_map,添加新加入的流。
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;
_ring = std::make_shared<RingType>(_ring_size, std::move(lam));
if (!_sdp.empty()) {
regist();
}
}
bool is_video = rtp->type == TrackVideo;
// inputPacket() 函数内部会调用onFlush()
// onFlush()就是输出函数
PacketCache<RtpPacket>::inputPacket(stamp, is_video, std::move(rtp), keyPos);
}
RtspMediaSource::_ring创建
RtspMediaSource::_ring大小设置在RtspSession::handleReq_ANNOUNCE()。
该函数内初始化RtspMediaSourceImp::Ptr RtspSession::_push_src; 构造RtspMediaSourceImp,构造初始化RtspMediaSource。 然后构造初始化MediaSource,并且设置RtspMediaSource::_ring_size。
src/Rtsp/RtspMediaSourceImp.cpp
// RtspMediaSource::RingType::Ptr _ring;
_ring = std::make_shared<RingType>(_ring_size, std::move(lam));
初始化RingType实际上初始化的是RingBuffer。
Rtsp/RtspMediaSource.h
using RingType = toolkit::RingBuffer<RingDataType>;
3rdpart/ZLToolKit/src/Util/RingBuffer.h
template <typename T>
class RingBuffer : public std::enable_shared_from_this<RingBuffer<T>> {
public:
RingBuffer(size_t max_size = 1024, onReaderChanged cb = nullptr, size_t max_gop_size = 1) {
// 创建RingStorage
_storage = std::make_shared<RingStorage>(max_size, max_gop_size);
_on_reader_changed = cb ? std::move(cb) : [](int size) {};
//先触发无人观看
_on_reader_changed(0);
}
......
typename RingStorage::Ptr _storage;
};
MediaSource::regist()
记录MediaSource。
src/Common/MediaSource.cpp
void MediaSource::regist() {
{
//减小互斥锁临界区
lock_guard<recursive_mutex> lock(s_media_source_mtx);
auto &ref = s_media_source_map[_schema][_vhost][_app][_stream_id];
auto src = ref.lock();
if (src) {
if (src.get() == this) {
return;
}
//增加判断, 防止当前流已注册时再次注册
throw std::invalid_argument("media source already existed:" + getUrl());
} else {
}
ref = shared_from_this();
}
emitEvent(true);
}
数据输入与分发PacketCache
RtspMediaSourceImp继承自RtspMediaSource 继承自PacketCache。
RtspSession:: _push_src类型为RtspMediaSourceImp::Ptr。RtspMediaSourceImp::onWrite()调用RtspMediaSource::onWrite()然后调用inputPacket()。
PacketCache::inputPacket()输入数据。
PacketCache::flush()输出数据。
src/Common/PacketCache.h
template<typename packet, typename policy = FlushPolicy, typename packet_list = toolkit::List<std::shared_ptr<packet> > >
class PacketCache {
public:
PacketCache() { _cache = std::make_shared<packet_list>(); }
......
// 输入函数
void inputPacket(uint64_t stamp, bool is_video, std::shared_ptr<packet> pkt, bool key_pos) {
bool flag = flushImmediatelyWhenCloseMerge();
if (!flag && _policy.isFlushAble(is_video, key_pos, stamp, _cache->size())) {
// run
flush();
}
//追加数据到最后
_cache->emplace_back(std::move(pkt));
if (key_pos) {
_key_pos = key_pos;
}
if (flag) {
flush();
}
}
// 输出函数
void flush() {
if (_cache->empty()) {
return;
}
onFlush(std::move(_cache), _key_pos);
_cache = std::make_shared<packet_list>();
_key_pos = false;
}
......
virtual void onFlush(std::shared_ptr<packet_list>, bool key_pos) = 0;
......
};
PacketCache::onFlush()为虚函数,实际执行子类RtspMediaSource::onFlush()。
操作 RtspMediaSource::_ring->write() 进行数据分发。
Rtsp/RtspMediaSource.h
class RtspMediaSource : public MediaSource, public toolkit::RingDelegate<RtpPacket::Ptr>, private PacketCache<RtpPacket> {
......
private:
/**
* 批量flush rtp包时触发该函数
* @param rtp_list rtp包列表
* @param key_pos 是否包含关键帧
*/
// 继承自 PacketCache<RtpPacket> 输出函数
void onFlush(std::shared_ptr<toolkit::List<RtpPacket::Ptr> > rtp_list, bool key_pos) override {
//如果不存在视频,那么就没有存在GOP缓存的意义,所以is_key一直为true确保一直清空GOP缓存
_ring->write(std::move(rtp_list), _have_video ? key_pos : true);
}
......
private:
......
int _ring_size;
RingType::Ptr _ring;
SdpTrack::Ptr _tracks[TrackMax];
};
以下是RingBuffer::write()。
3rdpart/ZLToolKit/src/Util/RingBuffer.h
template <typename T>
class RingBuffer : public std::enable_shared_from_this<RingBuffer<T>> {
public:
......
// 分发 输出数据
void write(T in, bool is_key = true) {
// RingBuffer::_delegate 应该是重编码的时候用的,这里暂且不分析
if (_delegate) {
// RingDelegate::onWrite() 虚函数
// 最终调用RtspMediaSource::onWrite().
// 不过这里返回了
_delegate->onWrite(std::move(in), is_key);
return;
}
LOCK_GUARD(_mtx_map);
// std::unordered_map<EventPoller::Ptr, typename RingReaderDispatcher::Ptr, HashOfPtr> _dispatcher_map;
for (auto &pr : _dispatcher_map) {
auto &second = pr.second;
// 切换线程后触发onRead事件
pr.first->async([second, in, is_key](){
second->write(std::move(const_cast<T &>(in)), is_key);
}, false);
}
// 这里应该是存储,以备不时之需吧。
// RingBuffer:: _storage
_storage->write(std::move(in), is_key);
}
......
};
RingBuffer::_delegate 应该是重编码的时候用的,这里暂且不分析。
以下是RingReaderDispatcher::write()。
3rdpart/ZLToolKit/src/Util/RingBuffer.h
template <typename T>
class _RingReaderDispatcher : public std::enable_shared_from_this<_RingReaderDispatcher<T>> {
public:
using Ptr = std::shared_ptr<_RingReaderDispatcher>;
using RingReader = _RingReader<T>;
using RingStorage = _RingStorage<T>;
using onChangeInfoCB = std::function<ReaderInfo(ReaderInfo &&info)>;
friend class RingBuffer<T>;
......
private:
_RingReaderDispatcher(
const typename RingStorage::Ptr &storage, std::function<void(int, bool)> onSizeChanged) {
_reader_size = 0;
_storage = storage;
_on_size_changed = std::move(onSizeChanged);
assert(_on_size_changed);
}
// 分发数据
void write(T in, bool is_key = true) {
for (auto it = _reader_map.begin(); it != _reader_map.end();) {
auto reader = it->second.lock();
if (!reader) {
it = _reader_map.erase(it);
--_reader_size;
onSizeChanged(false);
continue;
}
reader->onRead(in, is_key);
++it;
}
_storage->write(std::move(in), is_key);
}
// 创建reader下行播放的时候会用。
std::shared_ptr<RingReader> attach(const EventPoller::Ptr &poller, bool use_cache) {
if (!poller->isCurrentThread()) {
throw std::runtime_error("You can attach RingBuffer only in it's poller thread");
}
std::weak_ptr<_RingReaderDispatcher> weak_self = this->shared_from_this();
auto on_dealloc = [weak_self, poller](RingReader *ptr) {
poller->async([weak_self, ptr]() {
auto strong_self = weak_self.lock();
if (strong_self && strong_self->_reader_map.erase(ptr)) {
--strong_self->_reader_size;
strong_self->onSizeChanged(false);
}
delete ptr;
});
};
std::shared_ptr<RingReader> reader(new RingReader(use_cache ? _storage : nullptr), on_dealloc);
_reader_map[reader.get()] = reader;
++_reader_size;
onSizeChanged(true);
return reader;
}
......
// 返回ReaderInfo list
std::list<ReaderInfo> getInfoList(const onChangeInfoCB &on_change) {
std::list<ReaderInfo> ret;
for (auto &pr : _reader_map) {
auto reader = pr.second.lock();
if (!reader) {
continue;
}
auto info = reader->getInfo();
if (!info) {
continue;
}
// on_change 返回一个json
ret.emplace_back(on_change(std::move(info)));
}
return ret;
}
private:
std::atomic_int _reader_size;
std::function<void(int, bool)> _on_size_changed;
typename RingStorage::Ptr _storage;
std::unordered_map<void *, std::weak_ptr<RingReader>> _reader_map;
};
reader->onRead()对应RingReader::onRead()。
该回调注册在RtspSession::handleReq_Play()中,调用RingReader::setReadCB(),最终注册函数为RtspSession::sendRtpPacket()。又抛到最上层了。
参考ZLMediaKit源码分析(三)拉流创建