Live555学习笔记(二)—— RTSP服务运作分析

时间:2022-01-06 20:05:46

Live555学习笔记(二)—— RTSP服务运作分析


知道了Live555 的基础知识,但我们还是不清楚RTSP,RTP,RTCP它们之间是如何运作的。我们首先分析RTSP服务运作,它的函数调用关系如上图所示。从上面的函数调用流程图我们知道,RTSP先是先创建一个TCP socket 来监听客户端的连接。如果客户端没有发起连接请求,那么RTSP就一直循环监听客户端的连接。如果客户端发起了连接,那么就创建一个客户会话,并把该会话加入到调度任务,该会话就会一直循环调度来查询客户端发送的RTSP命令(OPTIONSDESCRIBESETUPPLAYTEARDOWN

下面就按照函数的调用关系解析各函数的作用。

服务端的入口函数为mediaserver目录下live555MediaServer.cpp 文件的main函数。

int main(int argc, char** argv) {
  // Begin by setting up our usage environment:
  TaskScheduler* scheduler = BasicTaskScheduler::createNew();                    (1.0)
  UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);          (1.1)

  UserAuthenticationDatabase* authDB = NULL;
#ifdef ACCESS_CONTROL
  // To implement client access control to the RTSP server, do the following:
  authDB = new UserAuthenticationDatabase;
  authDB->addUserRecord("username1", "password1"); // replace these with real strings
  // Repeat the above with each <username>, <password> that you wish to allow
  // access to the server.
#endif

  // Create the RTSP server.  Try first with the default port number (554),
  // and then with the alternative port number (8554):
  RTSPServer* rtspServer;
  portNumBits rtspServerPortNum = 554;
  rtspServer = DynamicRTSPServer::createNew(*env, rtspServerPortNum, authDB);    (1.2)
  if (rtspServer == NULL) {
    rtspServerPortNum = 8554;
    rtspServer = DynamicRTSPServer::createNew(*env, rtspServerPortNum, authDB);  (1.3)
  }
  if (rtspServer == NULL) {
    *env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
    exit(1);
  }

  *env << "LIVE555 Media Server\n";
  *env << "\tversion " << MEDIA_SERVER_VERSION_STRING
       << " (LIVE555 Streaming Media library version "
       << LIVEMEDIA_LIBRARY_VERSION_STRING << ").\n";

  char* urlPrefix = rtspServer->rtspURLPrefix();
  *env << "Play streams from this server using the URL\n\t"
       << urlPrefix << "<filename>\nwhere <filename> is a file present in the current directory.\n";
  *env << "Each file's type is inferred from its name suffix:\n";
  *env << "\t\".264\" => a H.264 Video Elementary Stream file\n";
  *env << "\t\".265\" => a H.265 Video Elementary Stream file\n";
  *env << "\t\".aac\" => an AAC Audio (ADTS format) file\n";
  *env << "\t\".ac3\" => an AC-3 Audio file\n";
  *env << "\t\".amr\" => an AMR Audio file\n";
  *env << "\t\".dv\" => a DV Video file\n";
  *env << "\t\".m4e\" => a MPEG-4 Video Elementary Stream file\n";
  *env << "\t\".mkv\" => a Matroska audio+video+(optional)subtitles file\n";
  *env << "\t\".mp3\" => a MPEG-1 or 2 Audio file\n";
  *env << "\t\".mpg\" => a MPEG-1 or 2 Program Stream (audio+video) file\n";
  *env << "\t\".ogg\" or \".ogv\" or \".opus\" => an Ogg audio and/or video file\n";
  *env << "\t\".ts\" => a MPEG Transport Stream file\n";
  *env << "\t\t(a \".tsx\" index file - if present - provides server 'trick play' support)\n";
  *env << "\t\".vob\" => a VOB (MPEG-2 video with AC-3 audio) file\n";
  *env << "\t\".wav\" => a WAV Audio file\n";
  *env << "\t\".webm\" => a WebM audio(Vorbis)+video(VP8) file\n";
  *env << "See http://www.live555.com/mediaServer/ for additional documentation.\n";

  // Also, attempt to create a HTTP server for RTSP-over-HTTP tunneling.
  // Try first with the default HTTP port (80), and then with the alternative HTTP
  // port numbers (8000 and 8080).

  if (rtspServer->setUpTunnelingOverHTTP(80) || rtspServer->setUpTunnelingOverHTTP(8000) || rtspServer->setUpTunnelingOverHTTP(8080)) {
    *env << "(We use port " << rtspServer->httpServerPortNum() << " for optional RTSP-over-HTTP tunneling, or for HTTP live streaming (for indexed Transport Stream files only).)\n";
  } else {
    *env << "(RTSP-over-HTTP tunneling is not available.)\n";
  }

  env->taskScheduler().doEventLoop(); // does not return                         (1.4)

  return 0; // only to prevent compiler warning
}
(1.0)创建任务调度器

(1.1)创建交互环境

(1.2)使用554端口创建RTSP服务器

(1.3)如果554端口已经被占用,则使用8554端口创建RTSP服务器

(1.4)进入任务调度

我们进入(1.2)中的创建函数,

DynamicRTSPServer*
DynamicRTSPServer::createNew(UsageEnvironment& env, Port ourPort,
			     UserAuthenticationDatabase* authDatabase,
			     unsigned reclamationTestSeconds) {
  int ourSocket = setUpOurSocket(env, ourPort);                                                        (2.0)
  if (ourSocket == -1) return NULL;

  return new DynamicRTSPServer(env, ourSocket, ourPort, authDatabase, reclamationTestSeconds);         (2.1)
}
(2.0)从它里面调用的setupStreamSocket ->createSocket-> socket(AF_INET, type, 0)我们可以知道这里创建的是一个TCP 类型socket,它是用来帧听客户端的连接。

(2.1)这是对RTSPServerSupportingHTTPStreaming函数封装的一个接口,进入该函数

RTSPServerSupportingHTTPStreaming*
RTSPServerSupportingHTTPStreaming::createNew(UsageEnvironment& env, Port rtspPort,
					     UserAuthenticationDatabase* authDatabase, unsigned reclamationTestSeconds) {
  int ourSocket = setUpOurSocket(env, rtspPort);                                                       (3.0)
  if (ourSocket == -1) return NULL;

  return new RTSPServerSupportingHTTPStreaming(env, ourSocket, rtspPort, authDatabase, reclamationTestSeconds);(3.1)
}
 (3.0)这里与(2.0)处相同,不知道它在这里还要再创建一个TCP socket 的作用是什么,求高人指点。这里需要明白一点的就是它们的继承关系是:DynamicRTSPServer -->RTSPServerSupportingHTTPStreaming --->  RTSPServer

(3.1)这里进过几层封装,最后会调用到RTSPServer 函数

RTSPServer::RTSPServer(UsageEnvironment& env,
		       int ourSocket, Port ourPort,
		       UserAuthenticationDatabase* authDatabase,
		       unsigned reclamationSeconds)
  : GenericMediaServer(env, ourSocket, ourPort, reclamationSeconds),                                    (4.0)
    fHTTPServerSocket(-1), fHTTPServerPort(0),
    fClientConnectionsForHTTPTunneling(NULL), // will get created if needed
    fTCPStreamingDatabase(HashTable::create(ONE_WORD_HASH_KEYS)),
    fPendingRegisterRequests(HashTable::create(ONE_WORD_HASH_KEYS)), fRegisterRequestCounter(0),
    fAuthDB(authDatabase), fAllowStreamingRTPOverTCP(True) {
}
(4.0)处初始化函数GenericMediaServer,进入函数里面有
GenericMediaServer
::GenericMediaServer(UsageEnvironment& env, int ourSocket, Port ourPort,
		     unsigned reclamationSeconds)
  : Medium(env),
    fServerSocket(ourSocket), fServerPort(ourPort), fReclamationSeconds(reclamationSeconds),
    fServerMediaSessions(HashTable::create(STRING_HASH_KEYS)),
    fClientConnections(HashTable::create(ONE_WORD_HASH_KEYS)),
    fClientSessions(HashTable::create(STRING_HASH_KEYS)) {
  ignoreSigPipeOnSocket(fServerSocket); // so that clients on the same host that are killed don't also kill us
  
  // Arrange to handle connections from others:
  env.taskScheduler().turnOnBackgroundReadHandling(fServerSocket, incomingConnectionHandler, this);    (5.0)
}
(5.0)这里需要注意两个地方turnOnBackgroundReadHandling 函数和incomingConnectionHandler 函数

turnOnBackgroundReadHandling  函数里面调用的是setBackgroundHandling(socketNum, SOCKET_READABLE, handlerProc, clientData);,它设置socket函数可读,并且使handlerProc 函数周期读取数据到clientData。

incomingConnectionHandler 这个函数就是任务调度中每次调度都会执行的函数。该函数的调用关系是incomingConnectionHandler ---> server->incomingConnectionHandler ---> incomingConnectionHandlerOnSocket,进入该函数

void GenericMediaServer::incomingConnectionHandlerOnSocket(int serverSocket) {
  struct sockaddr_in clientAddr;
  SOCKLEN_T clientAddrLen = sizeof clientAddr;
  int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrLen);              (6.0)
  if (clientSocket < 0) {
    int err = envir().getErrno();
    if (err != EWOULDBLOCK) {
      envir().setResultErrMsg("accept() failed: ");
    }
    return;
  }
  ignoreSigPipeOnSocket(clientSocket); // so that clients on the same host that are killed don't also kill us
  makeSocketNonBlocking(clientSocket);
  increaseSendBufferTo(envir(), clientSocket, 50*1024);
  
#ifdef DEBUG
  envir() << "accept()ed connection from " << AddressString(clientAddr).val() << "\n";
#endif
  
  // Create a new object for handling this connection:
  (void)createNewClientConnection(clientSocket, clientAddr);                                          (6.1)
}
(6.0)接收客户端的连接,如果没有设备连接,直接返回。如果有设备连接进入(6.1)创建客户会话createNewClientConnection ---> RTSPServer::RTSPClientConnection
::RTSPClientConnection ---> GenericMediaServer::ClientConnection::ClientConnection   进入该函数

GenericMediaServer::ClientConnection
::ClientConnection(GenericMediaServer& ourServer, int clientSocket, struct sockaddr_in clientAddr)
  : fOurServer(ourServer), fOurSocket(clientSocket), fClientAddr(clientAddr) {
  // Add ourself to our 'client connections' table:
  fOurServer.fClientConnections->Add((char const*)this, this);
  
  // Arrange to handle incoming requests:
  resetRequestBuffer();
  envir().taskScheduler()
    .setBackgroundHandling(fOurSocket, SOCKET_READABLE|SOCKET_EXCEPTION, incomingRequestHandler, this);(7.0)
}
(7.0)把incomingRequestHandler 添加到任务调度中,也就是Live555 会周期调用该函数查询客户端发送过来的RTSP命令( OPTIONS DESCRIBE SETUP PLAY TEARDOWN