ZLMedia中RTSP协议的处理简要分析(1)

1.RTSP协议的处理在 RtspSplitter::onRecvHeader中进行。

2.RTSP协议中SDP的处理在RtspSplitter::onRecvContent中进行。

3.收到的RTSP数据在Parser类中Parse函数解析后存到和相关的成员变量中,各个key和value的值通过函数FindField得到。主要是一个multimap类型的变量中。

void Parser::Parse(const char *buf) {
    //解析
    const char *start = buf;
    Clear();
    while (true) {
        auto line = FindField(start, NULL, "\r\n");
        if (line.size() == 0) {
            break;
        }
        if (start == buf) {
            _strMethod = FindField(line.data(), NnULL, " ");
            auto strFullUrl = FindField(line.data(), " ", " ");
            auto args_pos = strFullUrl.find('?');
            if (args_pos != string::npos) {
                _strUrl = strFullUrl.substr(0, args_pos);
                _params = strFullUrl.substr(args_pos + 1);
                _mapUrlArgs = parseArgs(_params);
            } else {
                _strUrl = strFullUrl;
            }
            _strTail = FindField(line.data(), (strFullUrl + " ").data(), NULL);
        } else {
            auto field = FindField(line.data(), NULL, ": ");
            auto value = FindField(line.data(), ": ", NULL);
            if (field.size() != 0) {
                _mapHeaders.emplace_force(field, value);
            }
        }
        start = start + line.size() + 2;
        if (strncmp(start, "\r\n", 2) == 0) { //协议解析完毕
            _strContent = FindField(start, "\r\n", NULL);
            break;
        }
    }
}
string FindField(const char* buf, const char* start, const char *end ,size_t bufSize) {
    if(bufSize <=0 ){
        bufSize = strlen(buf);
    }
    const char *msg_start = buf, *msg_end = buf + bufSize;
    size_t len = 0;
    if (start != NULL) {
        len = strlen(start);
        msg_start = strstr(buf, start);
    }
    if (msg_start == NULL) {
        return "";
    }
    msg_start += len;
    if (end != NULL) {
        msg_end = strstr(msg_start, end);
        if (msg_end == NULL) {
            return "";
        }
    }
    return string(msg_start, msg_end);//具体怎么用还有点不理解
}

 4.RSP的回复代码:

bool RtspSession::sendRtspResponse(const string &res_code, const StrCaseMap &header_const, const string &sdp, const char *protocol){
    auto header = header_const;
    header.emplace("CSeq",StrPrinter << _cseq);
    if(!_sessionid.empty()){
        header.emplace("Session", _sessionid);
    }

    header.emplace("Server",kServerName);
    header.emplace("Date",dateStr());

    if(!sdp.empty()){
        header.emplace("Content-Length",StrPrinter << sdp.size());
        header.emplace("Content-Type","application/sdp");
    }

    _StrPrinter printer;
    printer << protocol << " " << res_code << "\r\n";
    for (auto &pr : header){
        printer << pr.first << ": " << pr.second << "\r\n";
    }

    printer << "\r\n";

    if(!sdp.empty()){
        printer << sdp;
    }
//    DebugP(this) << printer;
    return send(std::make_shared<BufferString>(std::move(printer))) > 0 ;
}

ssize_t RtspSession::send(Buffer::Ptr pkt){
//    if(!_enableSendRtp){
//        DebugP(this) << pkt->data();
//    }
    _bytes_usage += pkt->size();
    return TcpSession::send(std::move(pkt));
}

 (1)代码中_cseq为全局变量,在void RtspSession::onWholeRtspPacket(Parser &parser)void RtspSession::onWholeRtspPacket(Parser &parser)中  _cseq = atoi(parser["CSeq"].data());得到。

 (2)_sessionid在此还为空。

  (3)kServerName:在\ZLMediaKit\src\Common\macros.cpp中

//请遵循MIT协议,勿修改服务器声明
#if !defined(ENABLE_VERSION)
const char kServerName[] = "ZLMediaKit-6.0(build in " __DATE__ " " __TIME__ ")";
#else
const char kServerName[] = "ZLMediaKit(git hash:" COMMIT_HASH ",branch:" BRANCH_NAME ",build time:" __DATE__ " " __TIME__ ")";
#endif

为什么这么写,还不清楚。

5.ANNOUNCE的回复

在ssize_t RtspSplitter::onRecvHeader(const char *data, size_t len)和void RtspSplitter::onRecvContent(const char *data, size_t len)中处理。回复的代码统一在void RtspSession::handleReq_ANNOUNCE(const Parser &parser)中处理,代码如下:

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);
        _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;
        }

        assert(!_push_src);
        auto src = MediaSource::find(RTSP_SCHEMA, _media_info._vhost, _media_info._app, _media_info._streamid);
        auto push_failed = (bool)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;
        }

        if (push_failed) {
            sendRtspResponse("406 Not Acceptable", { "Content-Type", "text/plain" }, "Already publishing.");
            string err = StrPrinter << "ANNOUNCE:"
                                    << "Already publishing:" << _media_info._vhost << " " << _media_info._app << " "
                                    << _media_info._streamid << endl;
            throw SockException(Err_shutdown, err);
        }

        SdpParser sdpParser(parser.Content());
        _sessionid = makeRandStr(12);
        _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>());
        }

        if (!_push_src) {
            _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()));
        _continue_push_ms = option.continue_push_ms;
        sendRtspResponse("200 OK");
    };

    weak_ptr<RtspSession> weakSelf = dynamic_pointer_cast<RtspSession>(shared_from_this());
        Broadcast::PublishAuthInvoker invoker = [weakSelf, onRes](const string &err, const ProtocolOption &option) {
        auto strongSelf = weakSelf.lock();
        if (!strongSelf) {
            return;
        }
        strongSelf->async([weakSelf, onRes, err, option]() {
            auto strongSelf = weakSelf.lock();
            if (!strongSelf) {
                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());
    }
}
View Code

(1)full_url是推流的全连接。

(2)_media_info在onRecvContent中得到,主要存储连接信息:

 {_full_url = "rtsp://192.168.169.197:554/live/Camera_00001", _schema = "rtsp", _host = "192.168.169.197", _port = 554, _vhost = "__defaultVhost__", _app = "live", 

  _streamid = "Camera_00001", _param_strs = ""}

(3)SdpParser sdpParser(parser.Content());对sdp内容进行解析。通过 sdpParser.getAvailableTrack得到音视频的信息。

(4)_sessionid是一个1从字母和数字的数组中随机取出的12位数,不能保证完全不重复。

 (5)用ffmpeg推流rtsp收到的SDP如下:

 

 

经过SdpParser解析以后,存到std::vector<SdpTrack::Ptr> _sdp_track中,打印出的各变量为:

p { _sdp_track[0]->getName(), _sdp_track[1]->getName()} {"H264", "MPEG4-GENERIC"}
p { _sdp_track[0]->_pt, _sdp_track[1]->_pt} {96, 97}
p { _sdp_track[0]->_channel, _sdp_track[1]->_channel} {1, 1}
p { _sdp_track[0]->_samplerate, _sdp_track[1]->_samplerate} {90000, 16000}
p { _sdp_track[0]->_type, _sdp_track[1]->_type} {mediakit::TrackVideo, mediakit::TrackAudio}
p { _sdp_track[0]->_codec, _sdp_track[1]->_codec} {"H264", "MPEG4-GENERIC"}
p { _sdp_track[0]->_fmtp, _sdp_track[1]->_fmtp} {"packetization-mode=1; sprop-parameter-sets=Z00AKY2NQDwBE/LCAAAOEAACvyAI,aO44gA==; profile-level-id=4D0029", "profile-level-id=1;mode=AAC-
hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=1408"}
p { _sdp_track[0]->_control, _sdp_track[1]->_control} {"streamid=0", "streamid=1"}
p { _sdp_track[0]->_inited, _sdp_track[1]->_inited} {false, false}
p { _sdp_track[0]->_interleaved, _sdp_track[1]->_interleaved} $69 = "\000"
p { _sdp_track[0]->_seq, _sdp_track[1]->_seq} {0, 0}
p { _sdp_track[0]->_ssrc, _sdp_track[1]->_ssrc} {0, 0}
p { _sdp_track[0]->_time_stamp, _sdp_track[1]->_time_stamp}{0, 0}
p { _sdp_track[0]->_t, _sdp_track[1]->_t} {"", ""}
p { _sdp_track[0]->_b, _sdp_track[1]->_b} {"AS:2457", "AS:32"}
p { _sdp_track[0]->_port, _sdp_track[1]->_port} {0, 0}
p { _sdp_track[0]->_duration, _sdp_track[1]->_duration} {0, 0}
p { _sdp_track[0]->_start, _sdp_track[1]->_start} {0, 0}
p { _sdp_track[0]->_end, _sdp_track[1]->_end} {0, 0}
p { _sdp_track[0]->_other, _sdp_track[1]->_other}{std::map with 0 elements, std::map with 0 elements}
p { _sdp_track[0]->_attr, _sdp_track[1]->_attr}{std::multimap with 3 elements = {["control"] = "streamid=0", ["fmtp"] = "96 packetization-mode=1; sprop-parameter-sets=Z00AKY2NQDwBE/LCAAAOEAACvyAI,aO44gA==; profile-level-id=4D0029",
["rtpmap"] = "96 H264/90000"},
std::multimap with 3 elements = {["control"] = "streamid=1", ["fmtp"] = "97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=1408", ["rtpmap"] = "97 MPEG4-GENERIC/16000/1"}}

 

 

posted @ 2022-07-14 14:37  泽良_小涛  阅读(301)  评论(0编辑  收藏  举报