簡體   English   中英

使用 Live555 從記錄的 RTSP session (pcap) 接收多播 RTP stream(包含多個子會話)

[英]Receiving multicast RTP stream (containing multiple subsessions) from a recorded RTSP session (pcap) using Live555

我必須實現一個 RTSP 客戶端,它連接到現有的 RTSP session 而不能向 RTSP 服務器發送命令(recvonly)。

為了模擬這樣的環境,我使用 Wireshark 在 Live555 的 testH264VideoStreamer 和 testRTSPClient 示例之間記錄了 RTSP/RTP stream,並使用 tcpreplay 播放它,同時嘗試接收 stream 數據和修改版本的 test。

我還將 testH264VideoStreamer 提供的 SDP 信息存儲為 SDP 文件。

v=0
o=- 1606317166144671 1 IN IP4 192.168.3.92
s=Session streamed by "testH264VideoStreamer"
i=test.264
t=0 0
a=tool:LIVE555 Streaming Media v2020.10.16
a=type:broadcast
a=control:*
a=source-filter: incl IN IP4 * 192.168.3.92
a=rtcp-unicast: reflection
a=range:npt=0-
a=x-qt-text-nam:Session streamed by "testH264VideoStreamer"
a=x-qt-text-inf:test.264
m=video 18888 RTP/AVP 96
c=IN IP4 232.42.39.62/255
b=AS:500
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1;profile-level-id=640028;sprop-$
a=control:track1
^@

我修改了 testRTSPClient 示例,使其僅通過使用 SDP 文件中的數據連接到 RTP stream。

這是我用來初始化的兩個函數。

void openSDP(UsageEnvironment& env, char const* sdpFile)
{
    const char * rtspURL = "rtsp://192.168.3.92:8554/testStream/";

    RTSPClient* rtspClient = ourRTSPClient::createNew(env, rtspURL, RTSP_CLIENT_VERBOSITY_LEVEL);

    if(rtspClient == NULL)
    {
        env << "Failed to create a RTSP client for URL \"" << rtspURL << "\": " << env.getResultMsg();
        return;
    }
    else
    {
        env << "Connecting to the stream at " << rtspURL;
    }

    StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias

    std::vector<char> sdpBuffer;

    std::ifstream file(sdpFile, std::ios_base::in | std::ios_base::binary);
    file.unsetf(std::ios::skipws);

    std::streampos fileSize;
    file.seekg(0, std::ios::end);
    fileSize = file.tellg();
    file.seekg(0, std::ios::beg);

    sdpBuffer.reserve(fileSize);
    sdpBuffer.insert(sdpBuffer.begin(),
                     std::istream_iterator<unsigned char>(file),
                     std::istream_iterator<unsigned char>());

    char* const sdpDescription = sdpBuffer.data();

    // Create a media session object from this SDP description:
    scs.session = MediaSession::createNew(env, sdpDescription);

    if(scs.session == NULL)
    {
        env << *rtspClient << "Failed to create a MediaSession object from the SDP description: " << env.getResultMsg() << "\n";
    }
    else
        if(!scs.session->hasSubsessions())
        {
            env << *rtspClient << "This session has no media subsessions (i.e., no \"m=\" lines)\n";
        }

    scs.iter = new MediaSubsessionIterator(*scs.session);
    setupNextSubsession(rtspClient);
    return;
}

void setupNextSubsession(RTSPClient* rtspClient)
{
    UsageEnvironment& env = rtspClient->envir(); // alias
    StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias

    scs.subsession = scs.iter->next();
    if(scs.subsession != NULL)
    {
        if(!scs.subsession->initiate())
        {
            env << "Failed to initiate the subsession: " << env.getResultMsg();
            setupNextSubsession(rtspClient); // give up on this subsession; go to the next one
        }
        else
        {
            env << "Initiated the subsession:";

            if(scs.subsession->rtcpIsMuxed())
            {
                env << "client port " << scs.subsession->clientPortNum();
            }
            else
            {
                env << "client ports " << scs.subsession->clientPortNum() << "-" << scs.subsession->clientPortNum()+1;
            }

            scs.subsession->sink = DummySink::createNew(env,
                                                       *scs.subsession,
                                                       rtspClient->url());

            // perhaps use your own custom "MediaSink" subclass instead
            if(scs.subsession->sink == NULL)
            {
                env << "Failed to create a data sink for the subsession: " << env.getResultMsg();
            }

            env << "Created a data sink for the subsession";

            scs.subsession->miscPtr = rtspClient; // a hack to let subsession handler functions get the "RTSPClient" from the subsession
            scs.subsession->sink->startPlaying(*(scs.subsession->readSource()),
                                               subsessionAfterPlaying, scs.subsession);
            // Also set a handler to be called if a RTCP "BYE" arrives for this subsession:
            if(scs.subsession->rtcpInstance() != NULL)
            {
                scs.subsession->rtcpInstance()->setByeWithReasonHandler(subsessionByeHandler, scs.subsession);
            }

            // Set up the next subsession, if any:
            setupNextSubsession(rtspClient);
        }
    }
}

一切初始化都沒有錯誤,但 DummySink 沒有收到任何數據。 有任何想法嗎?

我發現雖然wireshark 向我顯示了帶有有效校驗和的傳入數據包,但udp 端口沒有收到任何數據包。

我嘗試了以下命令(作為 sudo)來避免 kernel 丟棄數據包,但它們對 Debian Buster 沒有幫助。

sysctl net.ipv4.conf.eth0.rp_filter=0
sysctl net.ipv4.conf.all.rp_filter=0
echo 0 > /proc/sys/net/ipv4/conf/eth0/rp_filter
sysctl -a | grep "\.rp_filter" | awk '{print $1 "=0"}' | xargs sysctl

基本上我已經從另一台計算機流式傳輸 pcap 文件,現在我能夠接收 NALU。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM