I have a pretty basic live555 RTSP server and client to stream a h264 stream written in c++.
Here’s the code I have for the client (adapted from testProgs/testRTSPClient.cpp, bundled with live555)
client->scheduler = BasicTaskScheduler::createNew(); client->env = BasicUsageEnvironment::createNew(*client->scheduler); client->rtspClient = NULL; RTSP_CLIENT::eventLoopWatchVariable = 0; openURL(client, *client->env, string(string("rtsp://") + ip_address + ":" + to_string(BASE_RTSP_PORT + iris_id) + "/iris").c_str()); client->env->taskScheduler().doEventLoop(&RTSP_CLIENT::eventLoopWatchVariable); void openURL(RTSP_CLIENT* client, UsageEnvironment& env, char const* rtspURL) { // Begin by creating a "RTSPClient" object. Note that there is a separate "RTSPClient" object for each stream that we wish // to receive (even if more than stream uses the same "rtsp://" URL). while (!client->rtspClient) { client->rtspClient = ourRTSPClient::createNew(env, rtspURL, RTSP_CLIENT_VERBOSITY_LEVEL, "main"); } // Next, send a RTSP "DESCRIBE" command, to get a SDP description for the stream. // Note that this command - like all RTSP commands - is sent asynchronously; we do not block, waiting for a response. // Instead, the following function call returns immediately, and we handle the RTSP response later, from within the event loop: client->rtspClient->sendDescribeCommand(continueAfterDESCRIBE); } void continueAfterDESCRIBE(RTSPClient* rtspClient, int resultCode, char* resultString) { do { UsageEnvironment& env = rtspClient->envir(); // alias StreamClientState& scs = ((ourRTSPClient*)rtspClient)->scs; // alias if (resultCode != 0) { env << *rtspClient << "Failed to get a SDP description: " << resultString << "n"; delete[] resultString; break; } char* const sdpDescription = resultString; env << *rtspClient << "Got a SDP description:n" << sdpDescription << "n"; // Create a media session object from this SDP description: scs.session = MediaSession::createNew(env, sdpDescription); delete[] sdpDescription; // because we don't need it anymore if (scs.session == NULL) { env << *rtspClient << "Failed to create a MediaSession object from the SDP description: " << env.getResultMsg() << "n"; break; } else if (!scs.session->hasSubsessions()) { env << *rtspClient << "This session has no media subsessions (i.e., no "m=" lines)n"; break; } // Then, create and set up our data source objects for the session. We do this by iterating over the session's 'subsessions', // calling "MediaSubsession::initiate()", and then sending a RTSP "SETUP" command, on each one. // (Each 'subsession' will have its own data source.) scs.iter = new MediaSubsessionIterator(*scs.session); setupNextSubsession(rtspClient); return; } while (0); // An unrecoverable error occurred with this stream. shutdownStream(rtspClient); }
Here’s the code I have for the server (adapted from testProgs/testOnDemandRTSPServer.cpp, bundled with live555)
rtsp_server->taskSchedular = BasicTaskScheduler::createNew(); rtsp_server->usageEnvironment = BasicUsageEnvironment::createNew(*rtsp_server->taskSchedular); rtsp_server->rtspServer = RTSPServer::createNew(*rtsp_server->usageEnvironment, BASE_RTSP_PORT + iris_id, NULL); rtsp_server->eventLoopWatchVariable = 0; if(rtsp_server->rtspServer == NULL) { *rtsp_server->usageEnvironment << "Failed to create rtsp server ::" << rtsp_server->usageEnvironment->getResultMsg() <<"n"; return false; } rtsp_server->sms = ServerMediaSession::createNew(*rtsp_server->usageEnvironment, "iris", "iris", "stream"); rtsp_server->liveSubSession = H264LiveServerMediaSession::createNew(*rtsp_server->usageEnvironment, true); rtsp_server->sms->addSubsession(rtsp_server->liveSubSession); rtsp_server->rtspServer->addServerMediaSession(rtsp_server->sms); rtsp_server->taskSchedular->doEventLoop(&rtsp_server->eventLoopWatchVariable);
I was under the assumption that live555 by default used UDP to transport data to the client from the server, which is what I wanted for it’s latency benefits over TCP. However while running the server client I happened to check netstat and I found this:
~# netstat | grep 8554 tcp 0 0 x.x.x.x:8554 wsip-x-x-x-x:39224 ESTABLISHED
It is however showing that the communications are going through TCP not UDP. I am a bit confused here, am I mis-interpreting netstat here?
Is there anything I need to tune in my c++ code to force the communication to go through UDP not TCP?
Advertisement
Answer
Okay so I figured out the answer. To help anyone else who is curious about this, the code is actually all correct. There is also no mis-interpretation of netstat. RTSP does indeed run over TCP not UDP. However the transport method of the A/V data runs on RTP, a connection that RTSP simply negotiates and instantiates. RTP almost always will run over UDP. To figure out what port and protocol the A/V data stream is going over you will need to sniff the packets sent out via RTSP. In my case the A/V data stream was indeed still going over UDP.