ZLMediaKit中rtmp流推拉的整体过程
- 在最开始推流端连上来之后,建立一个 RtmpSession,接下来就是在函数
RtmpSession::onRtmpChunk
中调用_publisher_src->onWrite(std::move(packet));
首先进入_demuxer->inputRtmp(pkt)
将packet写入 其他的 rtsp hls ts等等,接下来调用RtmpMediaSource::onWrite(std::move(pkt))
void onWrite(RtmpPacket::Ptr pkt, bool = true) override {
if (!_all_track_ready || _muxer->isEnabled()) {
_demuxer->inputRtmp(pkt);
}
else
InfoL << "RtmpMediaSourceImp onWrite else";
RtmpMediaSource::onWrite(std::move(pkt));
}
- 后面其实就是调用
PacketCache<RtmpPacket>::inputPacket(stamp, is_video, std::move(pkt), key)
void onWrite(RtmpPacket::Ptr pkt, bool = true) override {
bool is_video = pkt->type_id == MSG_VIDEO;
_speed[is_video ? TrackVideo : TrackAudio] += pkt->size();
switch (pkt->type_id) {
case MSG_VIDEO : _track_stamps[TrackVideo] = pkt->time_stamp, _have_video = true; break;
case MSG_AUDIO : _track_stamps[TrackAudio] = pkt->time_stamp; break;
default : break;
}
if (pkt->isCfgFrame()) {
lock_guard<recursive_mutex> lock(_mtx);
_config_frame_map[pkt->type_id] = pkt;
if (!_ring) {
return;
}
}
if (!_ring) {
weak_ptr<RtmpMediaSource> weakSelf = dynamic_pointer_cast<RtmpMediaSource>(shared_from_this());
auto lam = [weakSelf](int size) {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
return;
}
strongSelf->onReaderChanged(size);
};
_ring = std::make_shared<RingType>(_ring_size,std::move(lam));
onReaderChanged(0);
if (_metadata)
{
regist();
}
}
bool key = pkt->isVideoKeyFrame();
auto stamp = pkt->time_stamp;
PacketCache<RtmpPacket>::inputPacket(stamp, is_video, std::move(pkt), key);
}
- 最后的write会进入下述函数,其中 _delegate 是0 不会进入第一个 if,最终就是调用
_dispatcher_map
的second的write函数
void write(T in, bool is_key = true) {
if (_delegate) {
_delegate->onWrite(std::move(in), is_key);
return;
}
LOCK_GUARD(_mtx_map);
for (auto &pr : _dispatcher_map) {
auto &second = pr.second;
pr.first->async([second, in, is_key]() {
second->write(std::move(const_cast<T &>(in)), is_key);
}, false);
}
_storage->write(std::move(in), is_key);
}
- 后面会进入
flushAll()
中 就是将 cache 中的数据放入到上述 _ring
中的_storage
中
void inputPacket(uint64_t stamp, bool is_video, std::shared_ptr<packet> pkt, bool key_pos) {
if (_policy.isFlushAble(is_video, key_pos, stamp, _cache->size())) {
flushAll();
}
_cache->emplace_back(std::move(pkt));
if (key_pos) {
_key_pos = key_pos;
}
}
- 对于拉流,其和推流建立关系是在下述函数中,其中主要在
_ring_reader = src->getRing()->attach(getPoller());
其将推流的源端的 _dispatcher_map
加入播放端的信息RingReaderDispatcher
,其中里面又有一个map unordered_map<void *, std::weak_ptr<RingReader> > _reader_map;
其中存了具体的read,我们最终返回的就是这个具体的 read:_ring_reader
,并为这个read创建了onRead的回调函数 - 根据上面的函数,最后调用的是
RingReaderDispatcher
的write,根据最后一个函数,就是调用了_ring_reader
的onRead函数,就是下述设置的回调,其实最终调用的就是strongSelf->onSendMedia(rtmp);
,就是播放端的 onSendMedia
void RtmpSession::sendPlayResponse(const string &err,const RtmpMediaSource::Ptr &src){
bool auth_success = err.empty();
bool ok = (src.operator bool() && auth_success);
if (ok) {
sendUserControl(CONTROL_STREAM_BEGIN, STREAM_MEDIA);
}
AMFValue status(AMF_OBJECT);
status.set("level", ok ? "status" : "error");
status.set("code", ok ? "NetStream.Play.Reset" : (auth_success ? "NetStream.Play.StreamNotFound" : "NetStream.Play.BadAuth"));
status.set("description", ok ? "Resetting and playing." : (auth_success ? "No such stream." : err.data()));
status.set("details", _media_info._streamid);
status.set("clientid", "0");
sendReply("onStatus", nullptr, status);
if (!ok) {
string err_msg = StrPrinter << (auth_success ? "no such stream:" : err.data()) << " "
<< _media_info._vhost << " "
<< _media_info._app << " "
<< _media_info._streamid;
shutdown(SockException(Err_shutdown, err_msg));
return;
}
status.clear();
status.set("level", "status");
status.set("code", "NetStream.Play.Start");
status.set("description", "Started playing.");
status.set("details", _media_info._streamid);
status.set("clientid", "0");
sendReply("onStatus", nullptr, status);
AMFEncoder invoke;
invoke << "|RtmpSampleAccess" << true << true;
sendResponse(MSG_DATA, invoke.data());
invoke.clear();
AMFValue obj(AMF_OBJECT);
obj.set("code", "NetStream.Data.Start");
invoke << "onStatus" << obj;
sendResponse(MSG_DATA, invoke.data());
status.clear();
status.set("level", "status");
status.set("code", "NetStream.Play.PublishNotify");
status.set("description", "Now published.");
status.set("details", _media_info._streamid);
status.set("clientid", "0");
sendReply("onStatus", nullptr, status);
auto &metadata = src->getMetaData();
if(metadata){
invoke.clear();
invoke << "onMetaData" << metadata;
sendResponse(MSG_DATA, invoke.data());
auto duration = metadata["duration"];
if(duration && duration.as_number() > 0){
_stamp[0].setPlayBack();
_stamp[1].setPlayBack();
}
}
src->getConfigFrame([&](const RtmpPacket::Ptr &pkt) {
DebugP(this)<<"send initial frame";
onSendMedia(pkt);
});
_stamp[0].syncTo(_stamp[1]);
_ring_reader = src->getRing()->attach(getPoller());
weak_ptr<RtmpSession> weakSelf = dynamic_pointer_cast<RtmpSession>(shared_from_this());
_ring_reader->setReadCB([weakSelf](const RtmpMediaSource::RingDataType &pkt) {
auto strongSelf = weakSelf.lock();
if (!strongSelf) {
return;
}
if(strongSelf->_paused){
return;
}
size_t i = 0;
auto size = pkt->size();
strongSelf->setSendFlushFlag(false);
pkt->for_each([&](const RtmpPacket::Ptr &rtmp){
if(++i == size){
strongSelf->setSendFlushFlag(true);
}
strongSelf->onSendMedia(rtmp);
});
});
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);
}