ZLMediaKit中rtmp流推拉的整体过程

7 篇文章 0 订阅
4 篇文章 0 订阅

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()) {
            //未获取到所有Track后,或者开启转协议,那么需要解复用rtmp
            _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) {
                //注册后收到config帧更新到各播放器
                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);
            };
            
            //GOP默认缓冲512组RTMP包,每组RTMP包时间戳相同(如果开启合并写了,那么每组为合并写时间内的RTMP包),
            //每次遇到关键帧第一个RTMP包,则会清空GOP缓存(因为有新的关键帧了,同样可以实现秒开)
            _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) {

        //InfoL<<"RingBuffer _delegate "<<_delegate;
        if (_delegate) {
            //InfoL<<"RingBuffer _delegate _delegate->onWrite "<<_delegate;
            _delegate->onWrite(std::move(in), is_key);
            return;
        }

        //InfoL<<"RingBuffer write read map size "<<_dispatcher_map.size();
        LOCK_GUARD(_mtx_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);
        }

        _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())) {
            //如果是关键帧,则flush掉前面的数据,确保关键帧为该组数据的第一帧,确保GOP缓存有效
            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的回调函数
  • 根据上面的函数,最后调用的是RingReaderDispatcherwrite,根据最后一个函数,就是调用了_ring_readeronRead函数,就是下述设置的回调,其实最终调用的就是strongSelf->onSendMedia(rtmp);,就是播放端的 onSendMedia
//找到了要播放的流 回复消息
void RtmpSession::sendPlayResponse(const string &err,const RtmpMediaSource::Ptr &src){
    bool auth_success = err.empty();
    //如果当前的shared_ptr是空 返回false 否则返回true
    bool ok = (src.operator bool() && auth_success);
    if (ok) {
        //stream begin
        sendUserControl(CONTROL_STREAM_BEGIN, STREAM_MEDIA);
    }
    // onStatus(NetStream.Play.Reset)
    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;
    }

    // onStatus(NetStream.Play.Start)
    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);

    // |RtmpSampleAccess(true, true)
    AMFEncoder invoke;
    invoke << "|RtmpSampleAccess" << true << true;
    sendResponse(MSG_DATA, invoke.data());

    //onStatus(NetStream.Data.Start)
    invoke.clear();
    AMFValue obj(AMF_OBJECT);
    obj.set("code", "NetStream.Data.Start");
    invoke << "onStatus" << obj;
    sendResponse(MSG_DATA, invoke.data());

    //onStatus(NetStream.Play.PublishNotify)
    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){
        //在有metadata的情况下才发送metadata
        //其实metadata没什么用,有些推流器不产生metadata
        // onMetaData
        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);
    });

    //音频同步于视频
    //将stamp0 中的 stamp指针赋值为 stamp1 的值
    _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);
            }
            //InfoL<<"_ring_reader->setReadCB strongSelf->onSendMedi";
            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);
    }
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值