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

扫描二维码关注公众号,回复: 2640772 查看本文章

最后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