ZLMediaKit源码分析(五)代理服务

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

继承关系

PlayerProxy
Socket
以下是代理源码分析

WebServer 接收任务

server/WebApi.cpp
/**
 * 安装api接口
 * 所有api都支持GET和POST两种方式
 * POST方式参数支持application/json和application/x-www-form-urlencoded方式
 */
void installWebApi() {
    addHttpListener();
    GET_CONFIG(string,api_secret,API::kSecret);
    ......
    //动态添加rtsp/rtmp拉流代理
    //测试url http://127.0.0.1/index/api/addStreamProxy?vhost=__defaultVhost__&app=proxy&enable_rtsp=1&enable_rtmp=1&stream=0&url=rtmp://127.0.0.1/live/obs
    api_regist("/index/api/addStreamProxy",[](API_ARGS_MAP_ASYNC){
        CHECK_SECRET();
        CHECK_ARGS("vhost","app","stream","url");
        addStreamProxy(allArgs["vhost"],
                       allArgs["app"],
                       allArgs["stream"],
                       allArgs["url"],
                       allArgs["retry_count"],
                       allArgs["enable_hls"],/* 是否hls转发 */
                       allArgs["enable_mp4"],/* 是否MP4录制 */
                       allArgs["rtp_type"],
                       allArgs["timeout_sec"],
                       [invoker,val,headerOut](const SockException &ex,const string &key) mutable{
                           if (ex) {
                               val["code"] = API::OtherFailed;
                               val["msg"] = ex.what();
                           } else {
                               val["data"]["key"] = key;
                           }
                           invoker(200, headerOut, val.toStyledString());
                       });
    });
    ......
}

该步主要创建 player,并且调用 player->play(url); 拉流。

server/WebApi.cpp
void addStreamProxy(const string &vhost, const string &app, const string &stream, const string &url, int retry_count,
                    const ProtocolOption &option, int rtp_type, float timeout_sec,
                    const function<void(const SockException &ex, const string &key)> &cb) {
    auto key = getProxyKey(vhost, app, stream);

    lock_guard<recursive_mutex> lck(s_proxyMapMtx);
    if (s_proxyMap.find(key) != s_proxyMap.end()) {
        //已经在拉流了
        cb(SockException(Err_success), key);
        return;
    }
    //添加拉流代理
    auto player = std::make_shared<PlayerProxy>(vhost, app, stream, option, retry_count);
    s_proxyMap[key] = player;

    //指定RTP over TCP(播放rtsp时有效)
    (*player)[Client::kRtpType] = rtp_type;

    if (timeout_sec > 0.1) {
        //播放握手超时时间
        (*player)[Client::kTimeoutMS] = timeout_sec * 1000;
    }

    //开始播放,如果播放失败或者播放中止,将会自动重试若干次,默认一直重试
    player->setPlayCallbackOnce([cb, key](const SockException &ex) {
        if (ex) {
            lock_guard<recursive_mutex> lck(s_proxyMapMtx);
            s_proxyMap.erase(key);
        }
        cb(ex, key);
    });

    //被主动关闭拉流
    player->setOnClose([key](const SockException &ex) {
        lock_guard<recursive_mutex> lck(s_proxyMapMtx);
        s_proxyMap.erase(key);
    });
    // 重要的是这个函数,实际调用PlayerProxy::paly()
    player->play(url);
};
src/Player/PlayerProxy.cpp
PlayerProxy::PlayerProxy(const string &vhost, const string &app, const string &stream_id, const ProtocolOption &option,
                         int retry_count, const EventPoller::Ptr &poller) : MediaPlayer(poller) , _option(option) {
    _vhost = vhost;
    _app = app;
    _stream_id = stream_id;
    _retry_count = retry_count;
    _on_close = [](const SockException &) {};
    (*this)[Client::kWaitTrackReady] = false;
}

PlayerProxy::play() 创建拉流连接

src/Player/PlayerProxy.cpp
void PlayerProxy::play(const string &strUrlTmp) {
    weak_ptr<PlayerProxy> weakSelf = shared_from_this();
	std::shared_ptr<int> piFailedCnt(new int(0)); //连续播放失败次数
	// PlayerProxy 继承自 MediaPlayer 继承自PlayerImp<PlayerBase, PlayerBase>,
	// 实际为 PlayerImp::setOnPlayResult()
	// 1. 注册创建总封装回调
    setOnPlayResult([weakSelf, strUrlTmp, piFailedCnt](const SockException &err) {
        auto strongSelf = weakSelf.lock();
        if (!strongSelf) {
            return;
        }

        if (strongSelf->_on_play) {
            strongSelf->_on_play(err);
            strongSelf->_on_play = nullptr;
        }

        if (!err) {
            // 取消定时器,避免hls拉流索引文件因为网络波动失败重连成功后出现循环重试的情况
           strongSelf->_timer.reset();
            // 播放成功
            *piFailedCnt = 0;//连续播放失败次数清0
            strongSelf->onPlaySuccess();
        } else if (*piFailedCnt < strongSelf->_retry_count || strongSelf->_retry_count < 0) {
            // 播放失败,延时重试播放
            strongSelf->rePlay(strUrlTmp, (*piFailedCnt)++);
        } else {
            //达到了最大重试次数,回调关闭
            strongSelf->_on_close(err);
        }
	});
	// PlayerProxy 继承自 MediaPlayer 继承自PlayerImp<PlayerBase, PlayerBase>,
	// 实际为 PlayerImp::setOnShutdown()
	// 2. 注册结束回调
    setOnShutdown([weakSelf, strUrlTmp, piFailedCnt](const SockException &err) {
        auto strongSelf = weakSelf.lock();
        if (!strongSelf) {
            return;
        }

        //注销直接拉流代理产生的流:#532
        strongSelf->setMediaSource(nullptr);

        if (strongSelf->_muxer) {
            auto tracks = strongSelf->MediaPlayer::getTracks(false);
            for (auto &track : tracks) {
                track->delDelegate(strongSelf->_muxer.get());
            }

            GET_CONFIG(bool, reset_when_replay, General::kResetWhenRePlay);
            if (reset_when_replay) {
                strongSelf->_muxer.reset();
            } else {
                strongSelf->_muxer->resetTracks();
            }
        }
        //播放异常中断,延时重试播放
        if (*piFailedCnt < strongSelf->_retry_count || strongSelf->_retry_count < 0) {
            strongSelf->rePlay(strUrlTmp, (*piFailedCnt)++);
        } else {
            //达到了最大重试次数,回调关闭
            strongSelf->_on_close(err);
        }
	});
	// 3. 创建拉流结构体,创建socket连接,触发Rtsp协议发送,并注册数据回调
    MediaPlayer::play(strUrlTmp);
	_pull_url = strUrlTmp;
	// 4. 创建直接代理结构体
    setDirectProxy();
}

PlayerImp::setOnPlayResult() 注册创建总封装回调

src/Player/PlayerBase.h
template<typename Parent, typename Delegate>
class PlayerImp : public Parent {
    ......
    void setOnShutdown(const std::function<void(const toolkit::SockException &)> &cb) override {
        if (_delegate) {
            // no run
            _delegate->setOnShutdown(cb);
        }
        _on_shutdown = cb;
    }
	
    void setOnPlayResult(const std::function<void(const toolkit::SockException &ex)> &cb) override {
        if (_delegate) {
            // no run
            _delegate->setOnPlayResult(cb);
        }
        _on_play_result = cb;
    }
    ......
};

PlayerProxy::onPlaySuccess() 创建总封装

关闭了 rtsp 子封装和 rtmp 子封装,也就是总封装接收到数据,不继续分发给子封装,不向下执行。
其他子封装,继续执行。

src/Player/PlayerProxy.cpp
void PlayerProxy::onPlaySuccess() {
    GET_CONFIG(bool, reset_when_replay, General::kResetWhenRePlay);
    if (dynamic_pointer_cast<RtspMediaSource>(_media_src)) {
        //rtsp拉流代理
        if (reset_when_replay || !_muxer) {
            // 关闭 rtsp子封装
            _option.enable_rtsp = false;
            _muxer = std::make_shared<MultiMediaSourceMuxer>(_vhost, _app, _stream_id, getDuration(), _option);
        }
    } else if (dynamic_pointer_cast<RtmpMediaSource>(_media_src)) {
        //rtmp拉流代理
        if (reset_when_replay || !_muxer) {
            // 关闭 rtmp子封装。
            _option.enable_rtmp = false;
            _muxer = std::make_shared<MultiMediaSourceMuxer>(_vhost, _app, _stream_id, getDuration(), _option);
        }
    } else {
        //其他拉流代理
        if (reset_when_replay || !_muxer) {
            _muxer = std::make_shared<MultiMediaSourceMuxer>(_vhost, _app, _stream_id, getDuration(), _option);
        }
    }
    _muxer->setMediaListener(shared_from_this());

    auto videoTrack = getTrack(TrackVideo, false);
    if (videoTrack) {
        //添加视频
        _muxer->addTrack(videoTrack);
        //视频数据写入_mediaMuxer
        videoTrack->addDelegate(_muxer);
    }

    auto audioTrack = getTrack(TrackAudio, false);
    if (audioTrack) {
        //添加音频
        _muxer->addTrack(audioTrack);
        //音频数据写入_mediaMuxer
        audioTrack->addDelegate(_muxer);
    }

    //添加完毕所有track,防止单track情况下最大等待3秒
    _muxer->addTrackCompleted();

    if (_media_src) {
        //让_muxer对象拦截一部分事件(比如说录像相关事件)
        _media_src->setListener(_muxer);
    }
}

PlayerImp::setOnShutdown()

参考 PlayerImp::setOnPlayResult(),具体执行暂不分析。

MediaPlayer::play() Rtsp 远程拉流

src/Player/MediaPlayer.cpp
void MediaPlayer::play(const string &url) {
	// createPlayer 返回 RtspPlayerImp
	// 1. 创建拉流结构体
    _delegate = PlayerBase::createPlayer(_poller, url);
	assert(_delegate);
	// 2. 注册拉流结构体,创建socket连接 回调
    setOnCreateSocket_l(_delegate, _on_create_socket);
    // 参考PlayerImp::setOnShutdown()
    _delegate->setOnShutdown(_on_shutdown);
    // 参考PlayerImp::setOnPlayResult()
    _delegate->setOnPlayResult(_on_play_result);
	_delegate->setOnResume(_on_resume);
	// MediaPlayer : public PlayerImp<PlayerBase, PlayerBase>
	// MediaSource::Ptr PlayerImp::_media_src;
	//打印 _media_src 为空
	// 3. 注册转推结构体,这里为空,没有生效。在拉流有数据时regist()
    _delegate->setMediaSource(_media_src);
	_delegate->mINI::operator=(*this);
	// RtspPlayer继承自PlayerBase
	// 实际调用RtspPlayer::play()
	// 4. 下行拉流 播放
    _delegate->play(url);
}

PlayerBase::createPlayer() 创建拉流结构体 RtspPlayerImp。

src/Player/PlayerBase.cpp
PlayerBase::Ptr PlayerBase::createPlayer(const EventPoller::Ptr &poller, const string &url_in) {
    static auto releasePlayer = [](PlayerBase *ptr) {
        onceToken token(nullptr, [&]() {
            delete ptr;
        });
        ptr->teardown();
    };
    string url = url_in;
    string prefix = FindField(url.data(), NULL, "://");
    auto pos = url.find('?');
    if (pos != string::npos) {
        //去除?后面的字符串
        url = url.substr(0, pos);
    }

    if (strcasecmp("rtsps", prefix.data()) == 0) {
        return PlayerBase::Ptr(new TcpClientWithSSL<RtspPlayerImp>(poller), releasePlayer);
    }
	// 以 rtsp 拉流为例
    if (strcasecmp("rtsp", prefix.data()) == 0) {
        return PlayerBase::Ptr(new RtspPlayerImp(poller), releasePlayer);
    }

    if (strcasecmp("rtmps", prefix.data()) == 0) {
        return PlayerBase::Ptr(new TcpClientWithSSL<RtmpPlayerImp>(poller), releasePlayer);
    }

    if (strcasecmp("rtmp", prefix.data()) == 0) {
        return PlayerBase::Ptr(new RtmpPlayerImp(poller), releasePlayer);
    }
    if ((strcasecmp("http", prefix.data()) == 0 || strcasecmp("https", prefix.data()) == 0)) {
        if (end_with(url, ".m3u8") || end_with(url_in, ".m3u8")) {
            return PlayerBase::Ptr(new HlsPlayerImp(poller), releasePlayer);
        } else if (end_with(url, ".ts") || end_with(url_in, ".ts")) {
            return PlayerBase::Ptr(new TsPlayerImp(poller), releasePlayer);
        }
    }

    throw std::invalid_argument("not supported play schema:" + url_in);
}

setOnCreateSocket_l() 注册创建 socket 回调

静态函数。

src/Player/MediaPlayer.cpp
// 逻辑上cb 为null
// delegate 为 RtspPlayerImp
// RtspPlayerImp 继承自 RtspPlayer继承自toolkit::TcpClient 继承自 SocketHelper
static void setOnCreateSocket_l(const std::shared_ptr<PlayerBase> &delegate, const Socket::onCreateSocket &cb){
    auto helper = dynamic_pointer_cast<SocketHelper>(delegate);
    if (helper) {
        if (cb) {
            helper->setOnCreateSocket(cb);
        } else {
             // run
            //客户端,确保开启互斥锁
            helper->setOnCreateSocket([](const EventPoller::Ptr &poller) {
                return Socket::createSocket(poller, true);
            });
        }
    }
}
3rdpart/ZLToolKit/src/Network/Socket.cpp
void SocketHelper::setOnCreateSocket(Socket::onCreateSocket cb){
    if (cb) {
    	// run
        _on_create_socket = std::move(cb);
    } else {
        _on_create_socket = [](const EventPoller::Ptr &poller) {
            return Socket::createSocket(poller, false);
        };
    }
}

PlayerImp::setMediaSource() 注册转推结构体

空执行 setMediaSource。实际操作在下面 PlayerProxy::setDirectProxy()。

src/Player/PlayerImp.h
template<typename Parent, typename Delegate>
class PlayerImp : public Parent {
public:
	......
    // 打印 _delegate 为空 _media_src为空
    void setMediaSource(const MediaSource::Ptr &src) override {
        if (_delegate) {
            _delegate->setMediaSource(src);
        }
        _media_src = src;
    }
	......
protected:
	......
    MediaSource::Ptr _media_src;
    std::shared_ptr<Delegate> _delegate;
};

RtspPlayer::play() 拉流连接创建

连接远端服务器拉流,并注册两个重要回调。RtspPlayer::onConnect()连接创建成功,Rtsp::onRecv() 接收数据处理。

src/Rtsp/RtspPlayer.cpp
void RtspPlayer::play(const string &strUrl){
    RtspUrl url;
    try {
        url.parse(strUrl);
    } catch (std::exception &ex) {
        onPlayResult_l(SockException(Err_other, StrPrinter << "illegal rtsp url:" << ex.what()), false);
        return;
    }

    teardown();

    if (url._user.size()) {
        (*this)[Client::kRtspUser] = url._user;
    }
    if (url._passwd.size()) {
        (*this)[Client::kRtspPwd] = url._passwd;
        (*this)[Client::kRtspPwdIsMD5] = false;
    }

    _play_url = url._url;
    _rtp_type = (Rtsp::eRtpType)(int)(*this)[Client::kRtpType];
    DebugL << url._url << " " << (url._user.size() ? url._user : "null") << " " << (url._passwd.size() ? url._passwd : "null") << " " << _rtp_type;

    weak_ptr<RtspPlayer> weakSelf = dynamic_pointer_cast<RtspPlayer>(shared_from_this());
    float playTimeOutSec = (*this)[Client::kTimeoutMS].as<int>() / 1000.0f;
    _play_check_timer.reset(new Timer(playTimeOutSec, [weakSelf]() {
        auto strongSelf=weakSelf.lock();
        if(!strongSelf) {
            return false;
        }
        strongSelf->onPlayResult_l(SockException(Err_timeout,"play rtsp timeout"),false);
        return false;
    }, getPoller()));

    if(!(*this)[Client::kNetAdapter].empty()){
        setNetAdapter((*this)[Client::kNetAdapter]);
	}
	// 继承自toolkit::TcpClient
    startConnect(url._host, url._port, playTimeOutSec);
}
3rdpart/ZLToolKit/src/Network/TcpClient.cpp
void TcpClient::startConnect(const string &url, uint16_t port, float timeout_sec, uint16_t local_port) {
    weak_ptr<TcpClient> weak_self = shared_from_this();

    _timer = std::make_shared<Timer>(2.0f, [weak_self]() {
        auto strong_self = weak_self.lock();
        if (!strong_self) {
            return false;
        }
        strong_self->onManager();
        return true;
    }, getPoller());

	// RtspPlayer 继承自 toolkit::TcpClient 继承自SocketHelper
	// setSock() 实际为 SocketHelper::setSock()
	// createSocket() 实际为SocketHelper::createSocket()
    setSock(createSocket());

    auto sock_ptr = getSock().get();
    sock_ptr->setOnErr([weak_self, sock_ptr](const SockException &ex) {
        auto strong_self = weak_self.lock();
        if (!strong_self) {
            return;
        }
        if (sock_ptr != strong_self->getSock().get()) {
            //已经重连socket,上次的socket的事件忽略掉
            return;
        }
        strong_self->_timer.reset();
        strong_self->onErr(ex);
    });

    sock_ptr->connect(url, port, [weak_self](const SockException &err) {
        auto strong_self = weak_self.lock();
        if (strong_self) {
            strong_self->onSockConnect(err);
        }
    }, timeout_sec, _net_adapter, local_port);
}
3rdpart/ZLToolKit/src/Network/Socket.cpp
void SocketHelper::setSock(const Socket::Ptr &sock) {
    _peer_port = 0;
    _local_port = 0;
    _peer_ip.clear();
    _local_ip.clear();
    _sock = sock;
    if (_sock) {
        _poller = _sock->getPoller();
    }
}
3rdpart/ZLToolKit/src/Network/Socket.cpp
Socket::Ptr SocketHelper::createSocket(){
    return _on_create_socket(_poller);
}

connect 远程。

3rdpart/ZLToolKit/src/Network/Socket.cpp
void Socket::connect(const string &url, uint16_t port, const onErrCB &con_cb_in, float timeout_sec, const string &local_ip, uint16_t local_port) {
    weak_ptr<Socket> weak_self = shared_from_this();
    _poller->async([=] {
        auto strong_self = weak_self.lock();
        if (!strong_self) {
            return;
        }
        strong_self->connect_l(url, port, con_cb_in, timeout_sec, local_ip, local_port);
     });
}

注册 RtspPlayer::onRecv() 并调用 onConnect() 发送RTSP OPTIONS

3rdpart/ZLToolKit/src/Network/TcpClient.cpp
void TcpClient::onSockConnect(const SockException &ex) {
    if (ex) {
        //连接失败
        _timer.reset();
        onConnect(ex);
        return;
    }

    auto sock_ptr = getSock().get();
    weak_ptr<TcpClient> weak_self = shared_from_this();

    sock_ptr->setOnFlush([weak_self, sock_ptr]() {
        auto strong_self = weak_self.lock();
        if (!strong_self) {
            return false;
        }
        if (sock_ptr != strong_self->getSock().get()) {
            //已经重连socket,上传socket的事件忽略掉
            return false;
        }
        strong_self->onFlush();
        return true;
    });

    // 注册接收数据回调
    sock_ptr->setOnRead([weak_self, sock_ptr](const Buffer::Ptr &pBuf, struct sockaddr *, int) {
        auto strong_self = weak_self.lock();
        if (!strong_self) {
            return;
        }
        if (sock_ptr != strong_self->getSock().get()) {
            //已经重连socket,上传socket的事件忽略掉
            return;
        }
        try {
             // RtspPlayer::onRecv()
            strong_self->onRecv(pBuf);
        } catch (std::exception &ex) {
            strong_self->shutdown(SockException(Err_other, ex.what()));
        }
    });

    // 开始发送 RTSP 协议
    // 实际调用 RtspPlayer::onConnect()
    onConnect(ex);
}

PlayerProxy::setDirectProxy() 创建直接代理转发

读取配置文件,获取directProxy。

src/Player/PlayerProxy.cpp
void PlayerProxy::setDirectProxy() {
    MediaSource::Ptr mediaSource;
    if (dynamic_pointer_cast<RtspPlayer>(_delegate)) {
        //rtsp拉流
        GET_CONFIG(bool, directProxy, Rtsp::kDirectProxy);
        if (directProxy) {
            mediaSource = std::make_shared<RtspMediaSource>(_vhost, _app, _stream_id);
        }
    } else if (dynamic_pointer_cast<RtmpPlayer>(_delegate)) {
        //rtmp拉流,rtmp强制直接代理
        mediaSource = std::make_shared<RtmpMediaSource>(_vhost, _app, _stream_id);
	}

	// PlayerProxy 继承自 MediaPlayer : public PlayerImp<PlayerBase, PlayerBase>
    if (mediaSource) {
        setMediaSource(mediaSource);
    }
}
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>;

    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) {}
	......
private:
	......
	int _ring_size;
	......
};
src/Common/MediaSource.cpp
MediaSource::MediaSource(const string &schema, const string &vhost, const string &app, const string &stream_id){
    GET_CONFIG(bool, enableVhost, General::kEnableVhost);
    if (!enableVhost) {
        _vhost = DEFAULT_VHOST;
    } else {
        _vhost = vhost.empty() ? DEFAULT_VHOST : vhost;
    }
    _schema = schema;
    _app = app;
    _stream_id = stream_id;
    _create_stamp = time(NULL);
}
src/Player/PlayerBase.h
template<typename Parent, typename Delegate>
class PlayerImp : public Parent {
public:
    using Ptr = std::shared_ptr<PlayerImp>;

    template<typename ...ArgsType>
    PlayerImp(ArgsType &&...args) : Parent(std::forward<ArgsType>(args)...) {}
    ~PlayerImp() override = default;
    ......
    void setMediaSource(const MediaSource::Ptr &src) override {
        if (_delegate) {
            // _delegate 为 PlayerBase,但是PlayImp(也就是本类)继承自PlayerBase,所以再执行一次setMediaSource,但是第二次_delegate为空
            _delegate->setMediaSource(src);
        }
        _media_src = src;
    }
    ......
protected:
    ......
    MediaSource::Ptr _media_src;
    std::shared_ptr<Delegate> _delegate;
};
src/Player/PlayerBase.h
class PlayerBase : public TrackSource, public toolkit::mINI {
public:
    ......

    /**
     * 设置一个MediaSource,直接生产rtsp/rtmp代理
     */
    virtual void setMediaSource(const MediaSource::Ptr &src) = 0;

    /**
     * 设置异常中断回调
     */
    virtual void setOnShutdown(const Event &cb) = 0;

    /**
     * 设置播放结果回调
     */
    virtual void setOnPlayResult(const Event &cb) = 0;

    /**
     * 设置播放恢复回调
     */
    virtual void setOnResume(const std::function<void()> &cb) = 0;

protected:
    virtual void onResume() = 0;
    virtual void onShutdown(const toolkit::SockException &ex) = 0;
    virtual void onPlayResult(const toolkit::SockException &ex) = 0;
};

RtspPlayer::onConnect() 连接创建成功

拉流连接创建成功,开始发送RTSP数据。

src/Rtsp/RtspPlayer.cpp
void RtspPlayer::onConnect(const SockException &err){
    if(err.getErrCode() != Err_success) {
        onPlayResult_l(err,false);
        return;
    }
    sendOptions();
}

发送 RTSP OPTIONS

src/Rtsp/RtspPlayer.cpp
void RtspPlayer::sendOptions(){
    _on_response = [this](const Parser& parser){
        if (!handleResponse("OPTIONS", parser)) {
            return;
        }
        //获取服务器支持的命令
        _supported_cmd.clear();
        auto public_val = split(parser["Public"],",");
        for(auto &cmd : public_val){
            trim(cmd);
            _supported_cmd.emplace(cmd);
        }
        //发送Describe请求,获取sdp
        sendDescribe();
    };
    // 具体发送函数
    sendRtspRequest("OPTIONS", _play_url);
}

RtspPlayer::_on_response 注册 OPTIONS 返回回调函数 RtspPlayer::sendDescribe()。

src/Rtsp/RtspPlayer.cpp
void RtspPlayer::sendRtspRequest(const string &cmd, const string &url,const StrCaseMap &header_const) {
    auto header = header_const;
    header.emplace("CSeq", StrPrinter << _cseq_send++);
    header.emplace("User-Agent", kServerName);

    if (!_session_id.empty()) {
        header.emplace("Session", _session_id);
    }

    if (!_realm.empty() && !(*this)[Client::kRtspUser].empty()) {
        if (!_md5_nonce.empty()) {
            // MD5认证
            /*
            response计算方法如下:
            RTSP客户端应该使用username + password并计算response如下:
            (1)当password为MD5编码,则
                response = md5( password:nonce:md5(public_method:url)  );
            (2)当password为ANSI字符串,则
                response= md5( md5(username:realm:password):nonce:md5(public_method:url) );
             */
            string encrypted_pwd = (*this)[Client::kRtspPwd];
            if (!(*this)[Client::kRtspPwdIsMD5].as<bool>()) {
                encrypted_pwd = MD5((*this)[Client::kRtspUser] + ":" + _realm + ":" + encrypted_pwd).hexdigest();
            }
            auto response = MD5(encrypted_pwd + ":" + _md5_nonce + ":" + MD5(cmd + ":" + url).hexdigest()).hexdigest();
            _StrPrinter printer;
            printer << "Digest ";
            printer << "username=\"" << (*this)[Client::kRtspUser] << "\", ";
            printer << "realm=\"" << _realm << "\", ";
            printer << "nonce=\"" << _md5_nonce << "\", ";
            printer << "uri=\"" << url << "\", ";
            printer << "response=\"" << response << "\"";
            header.emplace("Authorization", printer);
        } else if (!(*this)[Client::kRtspPwdIsMD5].as<bool>()) {
            // base64认证
            auto authStrBase64 = encodeBase64((*this)[Client::kRtspUser] + ":" + (*this)[Client::kRtspPwd]);
            header.emplace("Authorization", StrPrinter << "Basic " << authStrBase64);
        }
    }

    _StrPrinter printer;
    printer << cmd << " " << url << " RTSP/1.0\r\n";
    for (auto &pr : header){
        printer << pr.first << ": " << pr.second << "\r\n";
    }
    printer << "\r\n";
    SockSender::send(std::move(printer));
}
3rdpart/ZLToolKit/src/Network/Socket.cpp
ssize_t SockSender::send(string buf) {
    // ScoketHepler 继承自 SockSender
    return send(std::make_shared<BufferString>(std::move(buf)));
}
3rdpart/ZLToolKit/src/Network/Socket.cpp
ssize_t SocketHelper::send(Buffer::Ptr buf) {
    if (!_sock) {
        return -1;
    }
    return _sock->send(std::move(buf), nullptr, 0, _try_flush);
}

RtspPlayer::onRecv() 数据回调总入口

src/Rtsp/RtspPlayer.cpp
void RtspPlayer::onRecv(const Buffer::Ptr& buf) {
    if(_benchmark_mode && !_play_check_timer){
        //在性能测试模式下,如果rtsp握手完毕后,不再解析rtp包
        _rtp_recv_ticker.resetTime();
        return;
    }
	try {
        // RtspPlayer继承自RtspSplitter继承自HttpRequestSplitter
        input(buf->data(), buf->size());
    } catch (exception &e) {
        SockException ex(Err_other, e.what());
        onPlayResult_l(ex, !_play_check_timer);
    }
}
Http/HttpRequestSplitter.cpp
void HttpRequestSplitter::input(const char *data,size_t len) {
    {
        auto size = remainDataSize();
        if (size > kMaxCacheSize) {
            //缓存太多数据无法处理则上抛异常
            reset();
            throw std::out_of_range("remain data size is too huge, now cleared:" + to_string(size));
        }
    }
    const char *ptr = data;
    if(!_remain_data.empty()){
        _remain_data.append(data,len);
        data = ptr = _remain_data.data();
        len = _remain_data.size();
    }

    splitPacket:

    /*确保ptr最后一个字节是0,防止strstr越界
     *由于ZLToolKit确保内存最后一个字节是保留未使用字节并置0,
     *所以此处可以不用再次置0
     *但是上层数据可能来自其他渠道,保险起见还是置0
     */

    char &tail_ref = ((char *) ptr)[len];
    char tail_tmp = tail_ref;
    tail_ref = 0;

    //数据按照请求头处理
    const char *index = nullptr;
    _remain_data_size = len;
    while (_content_len == 0 && _remain_data_size > 0 && (index = onSearchPacketTail(ptr,_remain_data_size)) != nullptr) {
        if (index == ptr) {
            break;
        }
        if (index < ptr || index > ptr + _remain_data_size) {
            throw std::out_of_range("上层分包逻辑异常");
        }
        //_content_len == 0,这是请求头
        const char *header_ptr = ptr;
        ssize_t header_size = index - ptr;
        ptr = index;
        _remain_data_size = len - (ptr - data);
        _content_len = onRecvHeader(header_ptr, header_size);
    }

    if(_remain_data_size <= 0){
        //没有剩余数据,清空缓存
        _remain_data.clear();
        return;
    }

    /*
     * 恢复末尾字节
     * 移动到这来,目的是防止HttpRequestSplitter::reset()导致内存失效
     */
    tail_ref = tail_tmp;

    if(_content_len == 0){
        //尚未找到http头,缓存定位到剩余数据部分
        _remain_data.assign(ptr,_remain_data_size);
        return;
    }

    //已经找到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);

        _remain_data_size -= _content_len;
        ptr += _content_len;
        //content处理完毕,后面数据当做请求头处理
        _content_len = 0;

        if(_remain_data_size > 0){
            //还有数据没有处理完毕
            _remain_data.assign(ptr,_remain_data_size);
            data = ptr = (char *)_remain_data.data();
            len = _remain_data.size();
            goto splitPacket;
        }
        _remain_data.clear();
        return;
    }

    //_content_len < 0;数据按照不固定长度content处理
    onRecvContent(ptr,_remain_data_size);//消费掉所有剩余数据
    _remain_data.clear();
}
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;
}

RtspPlayer::onWholeRtspPacket() 处理 Rtsp 数据

src/Rtsp/RtspPlayer.cpp
void RtspPlayer::onWholeRtspPacket(Parser &parser) {
	try {
        // 在RtspPlayer::sendOptions() 中注册,实际为RtspPlayer::sendDescribe()
        decltype(_on_response) func;
        _on_response.swap(func);
        if(func){
            func(parser);
        }
        parser.Clear();
    } catch (std::exception &err) {
        //定时器_pPlayTimer为空后表明握手结束了
        onPlayResult_l(SockException(Err_other, err.what()),!_play_check_timer);
    }
}

发送 RTSP DESCRIBE

src/Rtsp/RtspPlayer.cpp
void RtspPlayer::sendDescribe() {
    //发送DESCRIBE命令后处理函数:handleResDESCRIBE
    _on_response = std::bind(&RtspPlayer::handleResDESCRIBE, this, placeholders::_1);
    sendRtspRequest("DESCRIBE", _play_url, {"Accept", "application/sdp"});
}

RtspPlayer::_on_response注册DESCRIBE回调。实际为RtspPlayer::handleResDESCRIBE()。

解析 SDP,创建解封装

src/Rtsp/RtspPlayer.cpp
void RtspPlayer::handleResDESCRIBE(const Parser& parser) {
    if (!handleResponse("DESCRIBE", parser)) {
        return;
    }
    _content_base = parser["Content-Base"];
    if(_content_base.empty()){
        _content_base = _play_url;
    }
    if (_content_base.back() == '/') {
        _content_base.pop_back();
    }

    //解析sdp
    SdpParser sdpParser(parser.Content());

    string sdp;
    auto play_track = (TrackType)((int)(*this)[Client::kPlayTrack] - 1);
    if (play_track != TrackInvalid) {
        auto track = sdpParser.getTrack(play_track);
        _sdp_track.emplace_back(track);
        sdp = track->toString();
    } else {
        _sdp_track = sdpParser.getAvailableTrack();
        sdp = sdpParser.toString();
    }

    if (_sdp_track.empty()) {
        throw std::runtime_error("无有效的Sdp Track");
    }
    if (!onCheckSDP(sdp)) {
        throw std::runtime_error("onCheckSDP faied");
    }
    _rtcp_context.clear();
    for (auto &track : _sdp_track) {
        if(track->_pt != 0xff){
            setPayloadType(_rtcp_context.size(),track->_pt);
        }
        _rtcp_context.emplace_back(std::make_shared<RtcpContextForRecv>());
    }
    sendSetup(0);
}

创建解封装,初始化 Rtsp 代理结构体。

src/Rtsp/RtspPlayer.cpp
bool RtspPlayerImp::onCheckSDP(const std::string &sdp)
{
	// RtspPlayerImp 继承自 PlayerImp,PlayerImp::_media_src。
	// PlayerImp::_media_src 在setDirectProxy() 中初始化
	// MediaSource::Ptr _media_src;
	// RtspMediaSource::Ptr _rtsp_media_src;
    _rtsp_media_src = std::dynamic_pointer_cast<RtspMediaSource>(_media_src);
    if (_rtsp_media_src) {
        _rtsp_media_src->setSdp(sdp);
    }
    _demuxer = std::make_shared<RtspDemuxer>();
    _demuxer->setTrackListener(this, (*this)[Client::kWaitTrackReady].as<bool>());
    _demuxer->loadSdp(sdp);
    return true;
}
src/Rtsp/RtspMediaSourceImp.cpp
void RtspMediaSource::setSdp(const std::string &sdp) {
	SdpParser sdp_parser(sdp);
	// SdpTrack::Ptr _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();
    }
}

解封装创建,_demuxer->loadSdp(sdp);有复杂的流程,参考 ZLMediaKit源码分析(四)重封装

发送 RTSP SETUP

src/Rtsp/RtspPlayer.cpp
//发送SETUP命令
void RtspPlayer::sendSetup(unsigned int track_idx) {
    _on_response = std::bind(&RtspPlayer::handleResSETUP, this, placeholders::_1, track_idx);
    auto &track = _sdp_track[track_idx];
    auto control_url = track->getControlUrl(_content_base);
    switch (_rtp_type) {
        case Rtsp::RTP_TCP: {
            sendRtspRequest("SETUP", control_url, {"Transport", StrPrinter << "RTP/AVP/TCP;unicast;interleaved=" << track->_type * 2 << "-" << track->_type * 2 + 1});
        }
            break;
        case Rtsp::RTP_MULTICAST: {
            sendRtspRequest("SETUP", control_url, {"Transport", "RTP/AVP;multicast"});
        }
            break;
        case Rtsp::RTP_UDP: {
            createUdpSockIfNecessary(track_idx);
            sendRtspRequest("SETUP", control_url, {"Transport",
                                                   StrPrinter << "RTP/AVP;unicast;client_port="
                                                          << _rtp_sock[track_idx]->get_local_port() << "-"
                                                          << _rtcp_sock[track_idx]->get_local_port()});
        }
            break;
        default:
            break;
    }
}

RtspPlayer::_on_response注册SETUP回调。实际为RtspPlayer::handleResSETUP()。

src/Rtsp/RtspPlayer.cpp
void RtspPlayer::handleResSETUP(const Parser &parser, unsigned int track_idx) {
    if (parser.Url() != "200") {
        throw std::runtime_error(StrPrinter << "SETUP:" << parser.Url() << " " << parser.Tail() << endl);
    }
    if (track_idx == 0) {
        _session_id = parser["Session"];
        _session_id.append(";");
        _session_id = FindField(_session_id.data(), nullptr, ";");
    }

    auto strTransport = parser["Transport"];
    if (strTransport.find("TCP") != string::npos || strTransport.find("interleaved") != string::npos) {
        _rtp_type = Rtsp::RTP_TCP;
    } else if (strTransport.find("multicast") != string::npos) {
        _rtp_type = Rtsp::RTP_MULTICAST;
    } else {
        _rtp_type = Rtsp::RTP_UDP;
    }
    auto transport_map = Parser::parseArgs(strTransport, ";", "=");
    RtspSplitter::enableRecvRtp(_rtp_type == Rtsp::RTP_TCP);
    string ssrc = transport_map["ssrc"];
    if(!ssrc.empty()){
        sscanf(ssrc.data(), "%x", &_sdp_track[track_idx]->_ssrc);
    } else{
        _sdp_track[track_idx]->_ssrc = 0;
    }

    if (_rtp_type == Rtsp::RTP_TCP) {
        int interleaved_rtp, interleaved_rtcp;
        sscanf(transport_map["interleaved"].data(), "%d-%d", &interleaved_rtp, &interleaved_rtcp);
        _sdp_track[track_idx]->_interleaved = interleaved_rtp;
    } else {
        auto port_str = transport_map[(_rtp_type == Rtsp::RTP_MULTICAST ? "port" : "server_port")];
        int rtp_port, rtcp_port;
        sscanf(port_str.data(), "%d-%d", &rtp_port, &rtcp_port);
        auto &pRtpSockRef = _rtp_sock[track_idx];
        auto &pRtcpSockRef = _rtcp_sock[track_idx];

        if (_rtp_type == Rtsp::RTP_MULTICAST) {
            //udp组播
            auto multiAddr =  transport_map["destination"];
            pRtpSockRef = createSocket();
            //目前组播仅支持ipv4
            if (!pRtpSockRef->bindUdpSock(rtp_port, "0.0.0.0")) {
                pRtpSockRef.reset();
                throw std::runtime_error("open udp sock err");
            }
            auto fd = pRtpSockRef->rawFD();
            if (-1 == SockUtil::joinMultiAddrFilter(fd, multiAddr.data(), get_peer_ip().data(),get_local_ip().data())) {
                SockUtil::joinMultiAddr(fd, multiAddr.data(),get_local_ip().data());
            }

            //设置rtcp发送端口
            pRtcpSockRef = createSocket();
            //目前组播仅支持ipv4
            if (!pRtcpSockRef->bindUdpSock(0, "0.0.0.0")) {
                //分配端口失败
                throw runtime_error("open udp socket failed");
            }

            //设置发送地址和发送端口
            auto dst = SockUtil::make_sockaddr(get_peer_ip().data(), rtcp_port);
            pRtcpSockRef->bindPeerAddr((struct sockaddr *)&(dst));
        } else {
            createUdpSockIfNecessary(track_idx);
            //udp单播
            auto dst = SockUtil::make_sockaddr(get_peer_ip().data(), rtp_port);
            pRtpSockRef->bindPeerAddr((struct sockaddr *)&(dst));
            //发送rtp打洞包
            pRtpSockRef->send("\xce\xfa\xed\xfe", 4);

            dst = SockUtil::make_sockaddr(get_peer_ip().data(), rtcp_port);
            //设置rtcp发送目标,为后续发送rtcp做准备
            pRtcpSockRef->bindPeerAddr((struct sockaddr *)&(dst));
        }

        auto peer_ip = get_peer_ip();
        weak_ptr<RtspPlayer> weakSelf = dynamic_pointer_cast<RtspPlayer>(shared_from_this());
        //设置rtp over udp接收回调处理函数
        pRtpSockRef->setOnRead([peer_ip, track_idx, weakSelf](const Buffer::Ptr &buf, struct sockaddr *addr , int addr_len) {
            auto strongSelf = weakSelf.lock();
            if (!strongSelf) {
                return;
            }
            if (SockUtil::inet_ntoa(addr) != peer_ip) {
                WarnL << "收到其他地址的rtp数据:" << SockUtil::inet_ntoa(addr);
                return;
            }
            strongSelf->handleOneRtp(track_idx, strongSelf->_sdp_track[track_idx]->_type,
                                     strongSelf->_sdp_track[track_idx]->_samplerate, (uint8_t *) buf->data(), buf->size());
        });

        if(pRtcpSockRef) {
            //设置rtcp over udp接收回调处理函数
            pRtcpSockRef->setOnRead([peer_ip, track_idx, weakSelf](const Buffer::Ptr &buf, struct sockaddr *addr , int addr_len) {
                auto strongSelf = weakSelf.lock();
                if (!strongSelf) {
                    return;
                }
                if (SockUtil::inet_ntoa(addr) != peer_ip) {
                    WarnL << "收到其他地址的rtcp数据:" << SockUtil::inet_ntoa(addr);
                    return;
                }
                strongSelf->onRtcpPacket(track_idx, strongSelf->_sdp_track[track_idx], (uint8_t *) buf->data(), buf->size());
            });
        }
    }

    if (track_idx < _sdp_track.size() - 1) {
        //需要继续发送SETUP命令
        sendSetup(track_idx + 1);
        return;
    }
    //所有setup命令发送完毕
    //发送play命令
    sendPause(type_play, 0);
}

发送 RTSP PLAY

src/Rtsp/RtspPlayer.cpp
void RtspPlayer::sendPause(int type , uint32_t seekMS){
    _on_response = std::bind(&RtspPlayer::handleResPAUSE, this, placeholders::_1, type);
    //开启或暂停rtsp
    switch (type){
        case type_pause:
            sendRtspRequest("PAUSE", _content_base);
            break;
        case type_play:
            sendRtspRequest("PLAY", _content_base);
            break;
        case type_seek:
            sendRtspRequest("PLAY", _content_base, {"Range",StrPrinter << "npt=" << setiosflags(ios::fixed) << setprecision(2) << seekMS / 1000.0 << "-"});
            break;
        default:
            WarnL << "unknown type : " << type;
            _on_response = nullptr;
            break;
    }
}

RtspPlayer::_on_response 注册 PLAY 回调。实际为 RtspPlayer::handleResPAUSE()。

创建总封装

src/Rtsp/RtspPlayer.cpp
void RtspPlayer::handleResPAUSE(const Parser& parser,int type) {
    if (parser.Url() != "200") {
        switch (type) {
            case type_pause:
                WarnL << "Pause failed:" << parser.Url() << " " << parser.Tail() << endl;
                break;
            case type_play:
                WarnL << "Play failed:" << parser.Url() << " " << parser.Tail() << endl;
                onPlayResult_l(SockException(Err_shutdown, StrPrinter << "rtsp play failed:" << parser.Url() << " " << parser.Tail() ), !_play_check_timer);
                break;
            case type_seek:
                WarnL << "Seek failed:" << parser.Url() << " " << parser.Tail() << endl;
                break;
        }
        return;
    }

    if (type == type_pause) {
        //暂停成功!
        _rtp_check_timer.reset();
        return;
    }

    //play或seek成功
    uint32_t iSeekTo = 0;
    //修正时间轴
    auto strRange = parser["Range"];
    if (strRange.size()) {
        auto strStart = FindField(strRange.data(), "npt=", "-");
        if (strStart == "now") {
            strStart = "0";
        }
        iSeekTo = (uint32_t)(1000 * atof(strStart.data()));
        DebugL << "seekTo(ms):" << iSeekTo;
    }

    onPlayResult_l(SockException(Err_success, type == type_seek ? "resum rtsp success" : "rtsp play success"), !_play_check_timer);
}
src/Rtsp/RtspPlayer.cpp
void RtspPlayer::onPlayResult_l(const SockException &ex , bool handshake_done) {
    if (ex.getErrCode() == Err_shutdown) {
        //主动shutdown的,不触发回调
        return;
    }

    WarnL << ex.getErrCode() << " " << ex.what();
    if (!handshake_done) {
        //开始播放阶段
        _play_check_timer.reset();
        onPlayResult(ex);
        //是否为性能测试模式
        _benchmark_mode = (*this)[Client::kBenchmarkMode].as<int>();
    } else if (ex) {
        //播放成功后异常断开回调
        onShutdown(ex);
    } else {
        //恢复播放
        onResume();
    }

    if (!ex) {
        //播放成功,恢复rtp接收超时定时器
        _rtp_recv_ticker.resetTime();
        auto timeoutMS = (*this)[Client::kMediaTimeoutMS].as<uint64_t>();
        weak_ptr<RtspPlayer> weakSelf = dynamic_pointer_cast<RtspPlayer>(shared_from_this());
        auto lam = [weakSelf, timeoutMS]() {
            auto strongSelf = weakSelf.lock();
            if (!strongSelf) {
                return false;
            }
            if (strongSelf->_rtp_recv_ticker.elapsedTime() > timeoutMS) {
                //接收rtp媒体数据包超时
                strongSelf->onPlayResult_l(SockException(Err_timeout, "receive rtp timeout"), true);
                return false;
            }
            return true;
        };
        //创建rtp数据接收超时检测定时器
        _rtp_check_timer = std::make_shared<Timer>(timeoutMS / 2000.0f, lam, getPoller());
    } else {
        sendTeardown();
    }
}
src/Rtsp/RtspPlayer.cpp
void RtspPlayerImp::onPlayResult(const toolkit::SockException &ex) {
if (!(*this)[Client::kWaitTrackReady].as<bool>() || ex) {
        // 实际为 PlayerImp::onPlayResult
        // 在 PlayerImp::setOnPlayResult()中设置
        Super::onPlayResult(ex);
        return;
    }
}

执行创建总封装回调。
在PlayerProxy::play()中调用PlayerImp::setOnPlayResult() 初始化。

src/Player/PlayerBase.h
template<typename Parent, typename Delegate>
class PlayerImp : public Parent {
public:
    using Ptr = std::shared_ptr<PlayerImp>;
    ......
    void setOnPlayResult(const std::function<void(const toolkit::SockException &ex)> &cb) override {
        if (_delegate) {
            _delegate->setOnPlayResult(cb);
        }
        _on_play_result = cb;
    }
	......
protected:
    ......
    // 执行创建总封装回调。
    void onPlayResult(const toolkit::SockException &ex) override {
        if (_on_play_result) {
            _on_play_result(ex);
            _on_play_result = nullptr;
        }
    }
	......
protected:
    std::function<void()> _on_resume;
    PlayerBase::Event _on_shutdown;
    PlayerBase::Event _on_play_result;
    MediaSource::Ptr _media_src;
    std::shared_ptr<Delegate> _delegate;
};

RtspPlayer::onRtpPacket() 处理 Rtp 数据

src/Rtsp/RtspPlayer.cpp
void RtspPlayer::onRtpPacket(const char *data, size_t len) {
    int trackIdx = -1;
    uint8_t interleaved = data[1];
    if(interleaved %2 == 0){
        trackIdx = getTrackIndexByInterleaved(interleaved);
        if (trackIdx == -1) {
            return;
        }
        handleOneRtp(trackIdx, _sdp_track[trackIdx]->_type, _sdp_track[trackIdx]->_samplerate, (uint8_t *)data + RtpPacket::kRtpTcpHeaderSize, len - RtpPacket::kRtpTcpHeaderSize);
    }else{
        trackIdx = getTrackIndexByInterleaved(interleaved - 1);
        if (trackIdx == -1) {
            return;
        }
        onRtcpPacket(trackIdx, _sdp_track[trackIdx], (uint8_t *) data + RtpPacket::kRtpTcpHeaderSize, len - RtpPacket::kRtpTcpHeaderSize);
    }
}
src/Rtsp/RtpReceiver.h
template<int kCount = 2>
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>;
Rtsp/RtpReceiver.cpp
RtpPacket::Ptr RtpTrack::inputRtp(TrackType type, int sample_rate, uint8_t *ptr, size_t len) {
    if (len < RtpPacket::kRtpHeaderSize) {
        throw BadRtpException("rtp size less than 12");
    }
    GET_CONFIG(uint32_t, rtpMaxSize, Rtp::kRtpMaxSize);
    if (len > 1024 * rtpMaxSize) {
        WarnL << "超大的rtp包:" << len << " > " << 1024 * rtpMaxSize;
        return nullptr;
    }
    if (!sample_rate) {
        //无法把时间戳转换成毫秒
        return nullptr;
    }
    RtpHeader *header = (RtpHeader *) ptr;
    if (header->version != RtpPacket::kRtpVersion) {
        throw BadRtpException("invalid rtp version");
    }
    if (header->getPayloadSize(len) < 0) {
        //rtp有效负载小于0,非法
        throw BadRtpException("invalid rtp payload size");
    }

    //比对缓存ssrc
    auto ssrc = ntohl(header->ssrc);

    if (_pt == 0xFF) {
        _pt = header->pt;
    } else if (header->pt != _pt) {
        //TraceL << "rtp pt mismatch:" << (int) header->pt << " !=" << (int) _pt;
        return nullptr;
    }

    if (!_ssrc) {
        //记录并锁定ssrc
        _ssrc = ssrc;
        _ssrc_alive.resetTime();
    } else if (_ssrc == ssrc) {
        //ssrc匹配正确,刷新计时器
        _ssrc_alive.resetTime();
    } else {
        //ssrc错误
        if (_ssrc_alive.elapsedTime() < 3 * 1000) {
            //接受正确ssrc的rtp在10秒内,那么我们认为存在多路rtp,忽略掉ssrc不匹配的rtp
            WarnL << "ssrc mismatch, rtp dropped:" << ssrc << " != " << _ssrc;
            return nullptr;
        }
        InfoL << "rtp ssrc changed:" << _ssrc << " -> " << ssrc;
        _ssrc = ssrc;
        _ssrc_alive.resetTime();
    }

    auto rtp = RtpPacket::create();
    //需要添加4个字节的rtp over tcp头
    rtp->setCapacity(RtpPacket::kRtpTcpHeaderSize + len);
    rtp->setSize(RtpPacket::kRtpTcpHeaderSize + len);
    rtp->sample_rate = sample_rate;
    rtp->type = type;

    //赋值4个字节的rtp over tcp头
    uint8_t *data = (uint8_t *) rtp->data();
    data[0] = '$';
    data[1] = 2 * type;
    data[2] = (len >> 8) & 0xFF;
    data[3] = len & 0xFF;
    //拷贝rtp
    memcpy(&data[4], ptr, len);
    if (_disable_ntp) {
        //不支持ntp时间戳,例如国标推流,那么直接使用rtp时间戳
        rtp->ntp_stamp = rtp->getStamp() * uint64_t(1000) / sample_rate;
    } else {
        //设置ntp时间戳
        rtp->ntp_stamp = _ntp_stamp.getNtpStamp(rtp->getStamp(), sample_rate);
    }
    onBeforeRtpSorted(rtp);
    sortPacket(rtp->getSeq(), rtp);
    return rtp;
}

sortPacket() 省略很多
RtpTrackImp 继承自 RtpTrack 继承自 PacketSortor。
PacketSortor::sortPacket() 过程省略。排好序之后,回调到最顶层 RtspPlayer::onRtpSorted()。

RtspPlayer::onRtpSorted() rtp 数据排序之后

src/Rtsp/RtspPlayer.cpp
void RtspPlayer::onRtpSorted(RtpPacket::Ptr rtppt, int trackidx){
    _stamp[trackidx] = rtppt->getStampMS();
    _rtp_recv_ticker.resetTime();
    onRecvRTP(std::move(rtppt), _sdp_track[trackidx]);
}
src/Rtsp/RtspPlayer.cpp
void RtspPlayerImp::onRecvRTP(RtpPacket::Ptr rtp, const SdpTrack::Ptr &track) {
    // 这个是重封装使用
    //rtp解复用时可以判断是否为关键帧起始位置
	auto key_pos = _demuxer->inputRtp(rtp);

	// 这个是代理转发使用
    if (_rtsp_media_src) {
        _rtsp_media_src->onWrite(std::move(rtp), key_pos);
    }
}

RtspDemuxer::inputRtp()

重封装输入。
在 PlayerImp::setOnPlayResult()->PlayerProxy::onPlaySuccess() 创建总封装时,关闭了rtsp子封装和rtmp子封装,其他子封装没有关闭。 创建总封装时,关闭了 rtsp子 封装和 rtmp 子封装,其他子封装没有关闭。
首先解码,编码,delegate,走到总封装入口MultiMediaSourceMuxer::onTrackFrame()停止向下执行。参考ZLMediaKit源码分析(四)重封装

src/Rtsp/RtspDemuxer.cpp
bool RtspDemuxer::inputRtp(const RtpPacket::Ptr &rtp) {
    switch (rtp->type) {
        case TrackVideo: {
             // RtpCodec::Ptr _video_rtp_decoder;
            if (_video_rtp_decoder) {
                return _video_rtp_decoder->inputRtp(rtp, true);
            }
            return false;
        }
        case TrackAudio: {
            if (_audio_rtp_decoder) {
                _audio_rtp_decoder->inputRtp(rtp, false);
                return false;
            }
            return false;
        }
        default: return false;
    }
}

RtspMediaSource::onWrite()

代理转发输入。
参考: ZLMediaKit源码分析(二)推流创建ZLMediaKit源码分析(三)拉流创建

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;
        // 1. 创建RtspMediaSource::RingType::Ptr _ring,管理新加入的流。
        _ring = std::make_shared<RingType>(_ring_size, std::move(lam));
        if (!_sdp.empty()) {
        	// 2. 添加全局四维数组s_media_source_map,添加新加入的流。
            regist();
        }
    }
	bool is_video = rtp->type == TrackVideo;
	// inputPacket() 函数内部会调用onFlush()
	// onFlush()就是输出函数
	// RtspMediaSource继承自PacketCache<RtpPacket>。
    PacketCache<RtpPacket>::inputPacket(stamp, is_video, std::move(rtp), keyPos);
}

代理实例

添加代理流。

# 服务地址10.49.44.14
# app:为本地服务器创建的app
# stream:为本地服务器创建的stream
# url:为本地服务器拉流地址
# retry_count:为失败重试次数
http://10.49.44.14:805/index/api/addStreamProxy?secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc&vhost=__defaultVhost__&app=proxy&stream=hello_0&url=rtsp://10.49.44.16:5546/live/test_1&retry_count=1

删除代理流。

http://10.49.44.14:805/index/api/delStreamProxy?secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc&key=__defaultVhost__/proxy/hello_0

查看流信息。

# 查看推理信息
http://10.49.44.14:805/index/api/getMediaInfo?secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc&vhost=__defaultVhost__&app=proxy&stream=hello_0&schema=rtsp
# 查看拉流信息
http://10.49.44.14:805/index/api/getMediaPlayerList?secret=035c73f7-bb6b-4889-a715-d9eb2d1925cc&vhost=__defaultVhost__&app=proxy&stream=hello_0&schema=rtsp

ZLMediaKit 写了六篇,暂告一段落。纪念一下。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
zlmediakit是一款开源的基于C++语言的流媒体服务器,其代码实现相对简单,源码分析后可以加深对流媒体服务器工作原理的理解。 首先,zlmediakit源码通过C++语言实现了一个基于TCP协议的流媒体服务器,可以用来实现音视频的实时传输和录像功能。源码中主要包含了底层网络通信模块、流媒体协议解析模块、音视频编解码模块和存储模块等功能。 在底层网络通信模块中,zlmediakit使用了epoll模型进行网络事件的监听与处理,通过TCP协议提供了稳定的连接,并支持同时处理多个客户端的请求。 在流媒体协议解析模块中,zlmediakit支持RTMP、RTSP、HLS等多种流媒体协议的解析和处理,可以接收来自客户端的音视频数据,并根据协议要求进行解析和分发。 在音视频编解码模块中,zlmediakit提供了基于FFmpeg的音视频编解码功能,支持常见的音视频编码格式,可以将输入的音视频数据进行解码或者编码,并通过网络传输给客户端。 在存储模块中,zlmediakit可以将音视频数据保存到本地文件或者直播服务器中,支持实时录像和回放功能。 总的来说,zlmediakit是一款功能强大且易于使用的流媒体服务器,其源码分析可以帮助我们了解流媒体服务器的工作原理,深入理解音视频传输与处理的过程,对于开发相关应用和解决流媒体相关问题具有一定的参考价值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值