h.264rtsprtplive555

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


I have to implement an RTSP Client which connects to an existing RTSP session without being able to send commands to the RTSP Server (recvonly).

To simulate such an environment, I have recorded a RTSP/RTP stream between testH264VideoStreamer and testRTSPClient examples from Live555 with Wireshark, and played it back using tcpreplay while trying to receive stream data with a modified version of testRTSPClient.

I've also stored the SDP information provided by the testH264VideoStreamer as an SDP file.

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
^@

I've modified the testRTSPClient example so that it connects to the RTP stream only by using the data from the SDP File.

Here are two functions which I use to initialise.

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

Everything initialises without errors, but DummySink receives no data. Any ideas?


Solution

  • I've found out that although wireshark was showing me the incoming packets with valid checksums, udp port received no packets.

    I've tried following commands (as sudo) to avoid kernel discarding the packets but they simply don't help on 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
    

    Basically I've ended up streaming the pcap file from another computer, now I'm able to receive NALUs.