live555 rtspserver端 创建并且接收客户端数据

前言

本文分析是基于已经搭建好的live 555 环境, media server 能正常跑起来

准备知识

因为 文章中socket 进行通信,所以最好补习下linux下最基本的socket 通信基本流程,其他涉及知识点会补上

其实最核心的思想就是socket 三次握手,如下图
socket

media server 构建一个rtsp 服务器

直接分析源码 live555MediaServer.cpp

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

  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);
  env->taskScheduler().doEventLoop(); // does not return

  return 0; 
}

下面的代码逻辑围绕live555MediaServer展开

初始化环境

  TaskScheduler* scheduler = BasicTaskScheduler::createNew();
  UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);

上面在初始化任务调度和使用环境对象

创建 rtsp server

  rtspServer = DynamicRTSPServer::createNew(*env, rtspServerPortNum, authDB);

在创建 RTSPServer的时候已经将 发送rtsp 消息的socket 创建了,详细见下面的流程图
DynamicRTSPServer

在创建 DynamicRTSPServer对象的时候,就开始了setUpOurSocket

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

创建socket

这里setUpOurSocket 具体流程不展开,详细的流程图如下
socket

流程图中看出 传入 初始环境对象 和端口号 ,通过socket 编程 sock_stream 创建tcp传输需要的socket,并根据socket 通信过程设置 listen socket状态,最后返回给ourSocket变量

传递ourSocket 和使用ourSocket 发送消息

在new DynamicRTSPServer的时候,一层一层初始化父类对象,这里查看源码知道
DynamicRTSPServer 继承 RTSPServerSupportingHTTPStreaming 继承 RTSPServer 继承 GenericMediaServer ,最后继承 Medium

最后ourSocket 保存在 GenericMediaServer 变量fServerSocket中

然后 GenericMediaServer 通过 TaskScheduler 调用turnOnBackgroundReadHandling 打开后台处理 准备 回调incomingConnectionHandler 对连接的处理函数,参数this 指的是 DynamicRTSPServer 实例,因为 当前new 的是DynamicRTSPServer

具体 函数处理 在 BasicTaskScheduler 中的 setBackgroundHandling处理

    fHandlers->assignHandler(socketNum, conditionSet, handlerProc, clientData);
    if (socketNum+1 > fMaxNumSockets) {
      fMaxNumSockets = socketNum+1;
    }
    if (conditionSet&SOCKET_READABLE) FD_SET((unsigned)socketNum, &fReadSet);
    if (conditionSet&SOCKET_WRITABLE) FD_SET((unsigned)socketNum, &fWriteSet);
    if (conditionSet&SOCKET_EXCEPTION) FD_SET((unsigned)socketNum, &fExceptionSet);

fHandlers 就是handlerset 实例,保存了 socketid 条件集合 回调函数 以及代表DynamicRTSPServer 实例的clientData
通过FD_SET 分别将socket 放在 fReadSet fWriteSet fExceptionSet 集合中供select 函数使用

select 函数是socket 变成的一个方法,提高效率用的

fHandlers->assignHandler

这个函数的理解 看HandlerSet 的分析

== 开始回调 incomingConnectionHandler 函数===

doEventLoop

env->taskScheduler().doEventLoop();

上面的代码 开始事件轮询

void BasicTaskScheduler::SingleStep(unsigned maxDelayTime) {
    int selectResult = select(fMaxNumSockets, &readSet, &writeSet, &exceptionSet, &tv_timeToDelay);
    //回调函数 incomingConnectionHandler
    (*handler->handlerProc)(handler->clientData, resultConditionSet);
}

select 函数 根据 读数据流的集合和写数据流的集合 判断 哪个socket 可读可写

handlerProc 就是incomingConnectionHandler直接回调

void GenericMediaServer::incomingConnectionHandlerOnSocket(int serverSocket) {
  int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrLen);
  increaseSendBufferTo(envir(), clientSocket, 50*1024);
  // Create a new object for handling this connection:
  (void)createNewClientConnection(clientSocket, clientAddr);
}

处理了socket accept 函数,准备等待客户端的connect,连接后 根据 产生 新的 clientSocket ,创建 连接createNewClientConnection对象

GenericMediaServer::ClientConnection* RTSPServer::createNewClientConnection(int clientSocket, struct sockaddr_in clientAddr) {
  return new RTSPClientConnection(*this, clientSocket, clientAddr);
}

上面的this 是DynamicRTSPServer 的实例(抱歉之前写错了写成GenericMediaServer 和RTSPServer),很重要

在 初始化 创建connection对象的时候,将父类对象GenericMediaServer::ClientConnection::ClientConnection 内部类也创建了 ,并且保存在client connections’ table 中,进行 设置回调函数incomingRequestHandler ,事件doEvent轮询

////////// GenericMediaServer::ClientConnection implementation //////////

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:
  envir().taskScheduler().setBackgroundHandling(fOurSocket, SOCKET_READABLE|SOCKET_EXCEPTION, incomingRequestHandler, this);
}

触发 incomingRequestHandler 函数执行

void GenericMediaServer::ClientConnection::incomingRequestHandler() {
  struct sockaddr_in dummy; // 'from' address, meaningless in this case
  int bytesRead = readSocket(envir(), fOurSocket, &fRequestBuffer[fRequestBytesAlreadySeen], fRequestBufferBytesLeft, dummy);
  handleRequestBytes(bytesRead);
}

readSocket 通过recvfrom socket 函数接受数据 保存在fRequestBuffer[fRequestBytesAlreadySeen],并且在handleRequestBytes 函数中处理接收数据

这里才开始rtsp 协议

开始接收客户端发过来sdp

首先通过 parseRTSPRequestString 解析请求
然后 根据解析的数据 创建clientSession
最后处理每一种请求 handleCmd_OPTIONS handleCmd_GET_PARAMETER handleCmd_SET_PARAMETER handleCmd_DESCRIBE handleCmd_SETUP 这里的handleCmd_SETUP 很关键是创建 udp socket 传输rtp消息的地方,最后 将处理好的消息send 客户端

这里的详细流程,下一篇讲述


猜你喜欢

转载自blog.csdn.net/engineer_james/article/details/81514758