ZLMediaKit源码分析(二)推流创建

本文详细介绍了ZLMediaKit中RtspSession的源码分析,包括服务启动、推流创建、拉流创建、数据处理等关键步骤。重点讲解了RtspSession如何接收和处理RTSP、RTP包,以及如何创建和管理MediaSource进行推流和拉流操作。
摘要由CSDN通过智能技术生成

ZLMediaKit源码分析(一)服务启动
ZLMediaKit源码分析(二)推流创建
ZLMediaKit源码分析(三)拉流创建
ZLMediaKit源码分析(四)重封装
ZLMediaKit源码分析(五)代理服务
ZLMediaKit源码分析(六)http 点播

ZLMediaKit推流结构体创建是在RtspSession::onRecv()之后。如何触发onRecv()请参考上一篇文章ZLMediaKit源码分析(一)服务启动这里不在赘述。

继承关系

RtspSession
RtspMediaSourceImp

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

  1. 创建RtspMediaSource::RingType::Ptr _ring,管理新加入的流。
  2. 添加全局四维数组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源码分析(三)拉流创建

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值