ZLMediaKitのソースコード解析 (1) サービス起動
ZLMediaKitのソースコード解析 (2) プッシュストリームの作成
ZLMediaKitのソースコード解析 (3) プルストリームの作成
ZLMediaKit ストリーミング構造は、RtspSession::onRecv() の後に作成されます。
onRecv()のトリガー方法については、ZLMediaKitのソースコード解析(1)サービス起動を参照してください。ここでは詳しく説明しません。
RtspSession::onRecv() のデータ処理については、「ZLMediaKit ソース コード分析 (2) プッシュ ストリームの作成」を参照してください。ここでは繰り返しません。
RtspSession::onRecv()
RtspSession:: onWholeRtspPacket()
RtspSession::handleReq_Options()
RtspSession::handleReq_ANNOUNCE() アップリンク リクエスト
RtspSession::handleReq_SETUP() は接続を確立します
RtspSession::handleReq_RECORD() アップストリーム プッシュ
RtspSession::handleReq_Describe() ダウンリンク リクエスト
src/Rtsp/RtspSession.cpp
void RtspSession::handleReq_Describe(const Parser &parser) {
//该请求中的认证信息
auto authorization = parser["Authorization"];
weak_ptr<RtspSession> weak_self = dynamic_pointer_cast<RtspSession>(shared_from_this());
//rtsp专属鉴权是否开启事件回调
onGetRealm invoker = [weak_self, authorization](const string &realm) {
auto strong_self = weak_self.lock();
if (!strong_self) {
//本对象已经销毁
return;
}
//切换到自己的线程然后执行
strong_self->async([weak_self, realm, authorization]() {
auto strong_self = weak_self.lock();
if (!strong_self) {
//本对象已经销毁
return;
}
if (realm.empty()) {
//无需rtsp专属认证, 那么继续url通用鉴权认证(on_play)
strong_self->emitOnPlay();
return;
}
//该流需要rtsp专属认证,开启rtsp专属认证后,将不再触发url通用鉴权认证(on_play)
strong_self->_rtsp_realm = realm;
strong_self->onAuthUser(realm, authorization);
});
};
if(_rtsp_realm.empty()){
//广播是否需要rtsp专属认证事件
if (!NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastOnGetRtspRealm, _media_info, invoker, static_cast<SockInfo &>(*this))) {
//无人监听此事件,说明无需认证
invoker("");
}
}else{
invoker(_rtsp_realm);
}
}
src/Rtsp/RtspSession.cpp
void RtspSession::emitOnPlay(){
weak_ptr<RtspSession> weak_self = dynamic_pointer_cast<RtspSession>(shared_from_this());
//url鉴权回调
auto onRes = [weak_self](const string &err) {
auto strong_self = weak_self.lock();
if (!strong_self) {
return;
}
if (!err.empty()) {
//播放url鉴权失败
strong_self->sendRtspResponse("401 Unauthorized", {
"Content-Type", "text/plain"}, err);
strong_self->shutdown(SockException(Err_shutdown, StrPrinter << "401 Unauthorized:" << err));
return;
}
strong_self->onAuthSuccess();
};
Broadcast::AuthInvoker invoker = [weak_self, onRes](const string &err) {
auto strong_self = weak_self.lock();
if (!strong_self) {
return;
}
strong_self->async([onRes, err, weak_self]() {
onRes(err);
});
};
//广播通用播放url鉴权事件
auto flag = _emit_on_play ? false : NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastMediaPlayed, _media_info, invoker, static_cast<SockInfo &>(*this));
if (!flag) {
//该事件无人监听,默认不鉴权
onRes("");
}
//已经鉴权过了
_emit_on_play = true;
}
プッシュ ストリーム構造 RtspMediaSource の作成
src/Rtsp/RtspSession.cpp
void RtspSession::onAuthSuccess() {
TraceP(this);
weak_ptr<RtspSession> weak_self = dynamic_pointer_cast<RtspSession>(shared_from_this());
// 查找流是否存在
// 返回MediaSource::Ptr src
MediaSource::findAsync(_media_info, weak_self.lock(), [weak_self](const MediaSource::Ptr &src){
// RtspSession strong_self
auto strong_self = weak_self.lock();
if(!strong_self){
return;
}
// 转换为派生类的指针
// 派生出RtspMediaSource??
// 打印显示 rtsp_src依然是src的指针内容
auto rtsp_src = dynamic_pointer_cast<RtspMediaSource>(src);
if (!rtsp_src) {
//未找到相应的MediaSource
string err = StrPrinter << "no such stream:" << strong_self->_media_info.shortUrl();
strong_self->send_StreamNotFound();
strong_self->shutdown(SockException(Err_shutdown,err));
return;
}
//找到了相应的rtsp流
// rtsp_src->getSdp() 返回字符串
strong_self->_sdp_track = SdpParser(rtsp_src->getSdp()).getAvailableTrack();
if (strong_self->_sdp_track.empty()) {
//该流无效
WarnL << "sdp中无有效track,该流无效:" << rtsp_src->getSdp();
strong_self->send_StreamNotFound();
strong_self->shutdown(SockException(Err_shutdown,"can not find any available track in sdp"));
return;
}
strong_self->_rtcp_context.clear();
for (auto &track : strong_self->_sdp_track) {
strong_self->_rtcp_context.emplace_back(std::make_shared<RtcpContextForSend>());
}
// 更新sessionid
strong_self->_sessionid = makeRandStr(12);
strong_self->_play_src = rtsp_src;
for(auto &track : strong_self->_sdp_track){
track->_ssrc = rtsp_src->getSsrc(track->_type);
track->_seq = rtsp_src->getSeqence(track->_type);
track->_time_stamp = rtsp_src->getTimeStamp(track->_type);
}
// 返回数据包含sessionid???
// sendRtspResponse 返回时会主动加上 sessionid 具体参考函数实现
// if(!_sessionid.empty()){
// header.emplace("Session", _sessionid);
// }
strong_self->sendRtspResponse("200 OK",
{
"Content-Base", strong_self->_content_base + "/",
"x-Accept-Retransmit","our-retransmit",
"x-Accept-Dynamic-Rate","1"
},rtsp_src->getSdp());
});
}
RtspSession::handleReq_Play() ダウンストリーム再生
src/Rtsp/RtspSession.cpp
void RtspSession::handleReq_Play(const Parser &parser) {
// parser中包含sessionid, 是的。
if (_sdp_track.empty() || parser["Session"] != _sessionid) {
send_SessionNotFound();
throw SockException(Err_shutdown, _sdp_track.empty() ? "can not find any available track when play" : "session not found when play");
}
//直播源读取器
// RtspMediaSource::RingType::RingReader::Ptr _play_reader;
//rtsp播放器绑定的直播源
// std::weak_ptr<RtspMediaSource> _play_src;
// _play_src 创建应该在handleReq_Describe()
auto play_src = _play_src.lock();
if(!play_src){
send_StreamNotFound();
shutdown(SockException(Err_shutdown,"rtsp stream released"));
return;
}
bool use_gop = true;
auto &strScale = parser["Scale"];
auto &strRange = parser["Range"];
StrCaseMap res_header;
if (!strScale.empty()) {
//这是设置播放速度
res_header.emplace("Scale", strScale);
auto speed = atof(strScale.data());
play_src->speed(speed);
InfoP(this) << "rtsp set play speed:" << speed;
}
if (!strRange.empty()) {
//这是seek操作
res_header.emplace("Range", strRange);
auto strStart = FindField(strRange.data(), "npt=", "-");
if (strStart == "now") {
strStart = "0";
}
auto iStartTime = 1000 * (float) atof(strStart.data());
use_gop = !play_src->seekTo((uint32_t) iStartTime);
InfoP(this) << "rtsp seekTo(ms):" << iStartTime;
}
vector<TrackType> inited_tracks;
_StrPrinter rtp_info;
for (auto &track : _sdp_track) {
if (track->_inited == false) {
//为支持播放器播放单一track, 不校验没有发setup的track
continue;
}
inited_tracks.emplace_back(track->_type);
track->_ssrc = play_src->getSsrc(track->_type);
track->_seq = play_src->getSeqence(track->_type);
track->_time_stamp = play_src->getTimeStamp(track->_type);
rtp_info << "url=" << track->getControlUrl(_content_base) << ";"
<< "seq=" << track->_seq << ";"
<< "rtptime=" << (int) (track->_time_stamp * (track->_samplerate / 1000)) << ",";
}
rtp_info.pop_back();
res_header.emplace("RTP-Info", rtp_info);
//已存在Range时不覆盖
res_header.emplace("Range", StrPrinter << "npt=" << setiosflags(ios::fixed) << setprecision(2) << play_src->getTimeStamp(TrackInvalid) / 1000.0);
sendRtspResponse("200 OK", res_header);
//设置播放track
if (inited_tracks.size() == 1) {
_target_play_track = inited_tracks[0];
InfoP(this) << "指定播放track:" << _target_play_track;
}
//在回复rtsp信令后再恢复播放
play_src->pause(false);
setSocketFlags();
//直播源读取器
// RtspMediaSource::RingType::RingReader::Ptr _play_reader;
if (!_play_reader && _rtp_type != Rtsp::RTP_MULTICAST) {
weak_ptr<RtspSession> weak_self = dynamic_pointer_cast<RtspSession>(shared_from_this());
//直播源读取器
// RtspMediaSource::RingType::RingReader::Ptr _play_reader;
//rtsp播放器绑定的直播源
// std::weak_ptr<RtspMediaSource> _play_src;
// RtspMediaSource::getRing()返回 (RingType::Ptr)RtspMediaSource::_ring;
_play_reader = play_src->getRing()->attach(getPoller(), use_gop);
_play_reader->setGetInfoCB([weak_self]() {
return weak_self.lock(); });
_play_reader->setDetachCB([weak_self]() {
auto strong_self = weak_self.lock();
if (!strong_self) {
return;
}
strong_self->shutdown(SockException(Err_shutdown, "rtsp ring buffer detached"));
});
// weak_ptr<RtspSession> weak_self = dynamic_pointer_cast<RtspSession>(shared_from_this());
_play_reader->setReadCB([weak_self](const RtspMediaSource::RingDataType &pack) {
auto strong_self = weak_self.lock();
if (!strong_self) {
return;
}
strong_self->sendRtpPacket(pack);
});
}
}
RtspSession::_ring追加RingReaderDispatcher
RingBuffer::_dispatcher_map のタイプ (1 対多の関係) を見てください。
std::unowned_map<EventPoller::Ptr、タイプ名 RingReaderDispatcher::Ptr、HashOfPtr> RingBuffer::_dispatcher_map;
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>;
......
const RingType::Ptr &getRing() const {
return _ring;
}
private:
......
RingType::Ptr _ring;
};
ポーラーに対応する RingReaderDispatcher が存在しない場合は、RingReaderDispatcher::Ptr ディスパッチャ、new RingReaderDispatcher() を構築します;
存在する場合は、RingReaderDispatcher に RingReader を追加します、dispatcher->attach(poller, use_cache);
3rdpart/ZLToolKit/src/Util/RingBuffer.h
template <typename T>
class RingBuffer : public std::enable_shared_from_this<RingBuffer<T>> {
public:
......
using RingReaderDispatcher = _RingReaderDispatcher<T>;
......
std::shared_ptr<RingReader> attach(const EventPoller::Ptr &poller, bool use_cache = true) {
typename RingReaderDispatcher::Ptr dispatcher;
{
LOCK_GUARD(_mtx_map);
// 数组变量中增加一项。针对poller只会初始化一次
auto &ref = _dispatcher_map[poller];
// 如果不存在则创建RingReaderDispatcher
if (!ref) {
std::weak_ptr<RingBuffer> weak_self = this->shared_from_this();
// 定义回调函数,最终调用RingReader的onSizeChanged()
auto onSizeChanged = [weak_self, poller](int size, bool add_flag) {
if (auto strong_self = weak_self.lock()) {
strong_self->onSizeChanged(poller, size, add_flag);
}
};
auto onDealloc = [poller](RingReaderDispatcher *ptr) {
poller->async([ptr]() {
delete ptr; }); };
// 初始化
ref.reset(new RingReaderDispatcher(_storage->clone(), std::move(onSizeChanged)), std::move(onDealloc));
}
dispatcher = ref;
}
// 返回ringReader
return dispatcher->attach(poller, use_cache);
}
private:
......
std::unordered_map<EventPoller::Ptr, typename RingReaderDispatcher::Ptr, HashOfPtr> _dispatcher_map;
};
RingReaderDispatcher は RingReader を追加します
RingReaderDispatcher::_reader_map のタイプを見ると、RingReaderDispatcher は複数の RingReader に対応しています。
std::unowned_map<void *, std::weak_ptr> _reader_map;
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)>;
......
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);
}
......
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
_reader_map[reader.get()] = reader;
++_reader_size;
onSizeChanged(true);
return reader;
}
......
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;
};
RingReader の初期化、新しい RingReader()。
3rdpart/ZLToolKit/src/Util/RingBuffer.h
template <typename T>
class _RingReader {
public:
using Ptr = std::shared_ptr<_RingReader>;
friend class _RingReaderDispatcher<T>;
_RingReader(std::shared_ptr<_RingStorage<T>> storage) {
_storage = std::move(storage); }
~_RingReader() = default;
void setReadCB(std::function<void(const T &)> cb) {
if (!cb) {
_read_cb = [](const T &) {
};
} else {
_read_cb = std::move(cb);
flushGop();
}
}
void setDetachCB(std::function<void()> cb) {
_detach_cb = cb ? std::move(cb) : []() {
};
}
void setGetInfoCB(std::function<ReaderInfo()> cb) {
_get_info = cb ? std::move(cb) : []() {
return ReaderInfo(); };
}
private:
void onRead(const T &data, bool /*is_key*/) {
_read_cb(data); }
void onDetach() const {
_detach_cb(); }
void flushGop() {
if (!_storage) {
return;
}
_storage->getCache().for_each([this](const List<std::pair<bool, T>> &lst) {
lst.for_each([this](const std::pair<bool, T> &pr) {
onRead(pr.second, pr.first); });
});
}
ReaderInfo getInfo() {
return _get_info(); }
private:
std::shared_ptr<_RingStorage<T>> _storage;
std::function<void(void)> _detach_cb = []() {
};
std::function<void(const T &)> _read_cb = [](const T &) {
};
std::function<ReaderInfo()> _get_info = []() {
return ReaderInfo(); };
};
RingReader::setGetInfoCB() を登録する
参考:RingReader::setReadCB()。
RingReader::setDetachCB() を登録する
参考:RingReader::setReadCB()。
RingReader::setReadCB() を登録する
実装リファレンス: RingReaderDispatcher は RingReader を追加します。
移行:
src/Rtsp/RtspSession.cpp
void RtspSession::handleReq_Play(const Parser &parser) {
......
auto play_src = _play_src.lock();
......
//直播源读取器
// RtspMediaSource::RingType::RingReader::Ptr _play_reader;
if (!_play_reader && _rtp_type != Rtsp::RTP_MULTICAST) {
......
_play_reader = play_src->getRing()->attach(getPoller(), use_gop);
_play_reader->setGetInfoCB([weak_self]() {
return weak_self.lock(); });
_play_reader->setDetachCB([weak_self]() {
auto strong_self = weak_self.lock();
if (!strong_self) {
return;
}
strong_self->shutdown(SockException(Err_shutdown, "rtsp ring buffer detached"));
});
// weak_ptr<RtspSession> weak_self = dynamic_pointer_cast<RtspSession>(shared_from_this());
_play_reader->setReadCB([weak_self](const RtspMediaSource::RingDataType &pack) {
auto strong_self = weak_self.lock();
if (!strong_self) {
return;
}
strong_self->sendRtpPacket(pack);
});
}
}
RingReader::setReadCB() を呼び出し、最後に登録された関数は RtspSession::sendRtpPacket() です。再び頂上へ投げ上げられた。
src/Rtsp/RtspSession.cpp
void RtspSession::sendRtpPacket(const RtspMediaSource::RingDataType &pkt) {
switch (_rtp_type) {
case Rtsp::RTP_TCP: {
setSendFlushFlag(false);
pkt->for_each([&](const RtpPacket::Ptr &rtp) {
if (_target_play_track == TrackInvalid || _target_play_track == rtp->type) {
updateRtcpContext(rtp);
send(rtp);
}
});
flushAll();
setSendFlushFlag(true);
}
break;
case Rtsp::RTP_UDP: {
//下标0表示视频,1表示音频
Socket::Ptr rtp_socks[2];
rtp_socks[TrackVideo] = _rtp_socks[getTrackIndexByTrackType(TrackVideo)];
rtp_socks[TrackAudio] = _rtp_socks[getTrackIndexByTrackType(TrackAudio)];
pkt->for_each([&](const RtpPacket::Ptr &rtp) {
if (_target_play_track == TrackInvalid || _target_play_track == rtp->type) {
updateRtcpContext(rtp);
auto &sock = rtp_socks[rtp->type];
if (!sock) {
shutdown(SockException(Err_shutdown, "udp sock not opened yet"));
return;
}
_bytes_usage += rtp->size() - RtpPacket::kRtpTcpHeaderSize;
sock->send(std::make_shared<BufferRtp>(rtp, RtpPacket::kRtpTcpHeaderSize), nullptr, 0, false);
}
});
for (auto &sock : rtp_socks) {
if (sock) {
sock->flushAll();
}
}
}
break;
default:
break;
}
}
RtspSession::handleReq_TEARDOWN() リンクを切断する
RtspSession::onRtpPacket() データ入力
主にストリーミングで呼び出されます
RtspSession::onRtpSorted() データのソート後
データ配布については「ZLMediaKit ソースコード解析(2) プッシュストリーム作成」
を参照してください。ここでは詳しく説明しません。