Live555源码分析之1

本文深入探讨了Live555源代码的设计架构,详细分析了BasicUsageEnvironment类的作用及TaskScheduler的工作原理,介绍了如何通过Socket实现server,并讨论了读取文件的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 前言

Live555源代码,有一个很复杂的设计架构,反正对于我一个写应用的人来说,对于解耦之类的做的很差,简直无法理解。不愿意使用多线程,接收cline和解析数据,放到一起。这种设计思路,反正我这个写APP的人,无法理解。这里还是稍微整理下。

2. 正文

这里我们首先了解下一个类:
BasicUsageEnvironment 这个类,其实就类似于一个静态变量,可以在任何地方调用,里面保存了几个很关键的变量,和方法,也就是可以打打印log的地方,和一个最关键的变量—-·TaskScheduler& fScheduler;。TaskScheduler,主要用一个循环,实现了多任务的处理业务。

2.2 进程关管理

这是实现了整个工程任务调度的核心部分,里面处理两个任务,

  1. 读取socket内容,作为server端,相应服务器
  2. 读取文件,进行分发

最关键的一个函数,就是SingleStep,这个就是模拟了一个cpu的流水线,一直在循环的处理一些事情,最终实现server的相应,

void BasicTaskScheduler::SingleStep(unsigned maxDelayTime) {
  fd_set readSet = fReadSet; // make a copy for this select() call
  fd_set writeSet = fWriteSet; // ditto
  fd_set exceptionSet = fExceptionSet; // ditto

    ......

//读取socket内容,
  int selectResult = select(fMaxNumSockets, &readSet, &writeSet, &exceptionSet, &tv_timeToDelay);

    ......

 //处理socker内容。
      (*handler->handlerProc)(handler->clientData, resultConditionSet);

  //这是一个处理定时业务,大部分都是处理读取文件的内容,反复定时内容,实现逻辑
  fDelayQueue.handleAlarm();
}

2.2 server的实现

这里主要是实现了Socket,然后向TaskScheduler注册回调,首先是Socket的初始化。

DynamicRTSPServer*
DynamicRTSPServer::createNew(UsageEnvironment& env, Port ourPort,
                 UserAuthenticationDatabase* authDatabase,
                 unsigned reclamationTestSeconds) {
    //初始化出现一个socket的引用,类似于文件。
  int ourSocket = setUpOurSocket(env, ourPort);

}

这里初始化完成,然后要向TaskScheduler注册回调

GenericMediaServer
::GenericMediaServer(UsageEnvironment& env, int ourSocket, Port ourPort,
             unsigned reclamationSeconds)

    ......

  env.taskScheduler().turnOnBackgroundReadHandling(fServerSocket, incomingConnectionHandler, this);
}

这里传入的incomingConnectionHandler这是有Socket的传入参数,实现的具体逻辑,

void BasicTaskScheduler
  ::setBackgroundHandling(int socketNum, int conditionSet, BackgroundHandlerProc* handlerProc, void* clientData) {

    ......
    fHandlers->assignHandler(socketNum, conditionSet, handlerProc, clientData);
    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);
}

最终回调主要是

void GenericMediaServer::incomingConnectionHandlerOnSocket(int serverSocket) {

  ......

  // Create a new object for handling this connection:
  (void)createNewClientConnection(clientSocket, clientAddr);
}


GenericMediaServer::ClientConnection
::ClientConnection(GenericMediaServer& ourServer, int clientSocket, struct sockaddr_in clientAddr)
  : fOurServer(ourServer), fOurSocket(clientSocket), fClientAddr(clientAddr) {

  envir().taskScheduler()
    .setBackgroundHandling(fOurSocket, SOCKET_READABLE|SOCKET_EXCEPTION, incomingRequestHandler, this);
}

这是实现了第一次请求的全过程,然后等待第二次连接,才开始分发数据。我们接着看第二次连接,

void RTSPServer::RTSPClientConnection::handleRequestBytes(int newBytesRead) {

......
handleCmd_DESCRIBE(urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);
......

static ServerMediaSession* createNewSMS(UsageEnvironment& env,
                    char const* fileName, FILE* /*fid*/) {


    MatroskaFileServerDemux::createNew(env, fileName, onMatroskaDemuxCreation, &creationState);
    env.taskScheduler().doEventLoop(&creationState.watchVariable);
}

这里MatroskaFileServerDemux这是类似于一个server的东西,用来产生数据,真正的server的代码执行还是进程管理的工具执行。这里的代码部分的server执行是在接受部分已经完成。其他的相应部分这里没有详细介绍,这里我们都是实现了初始化的部分。

2.3 读取文件

这部分是相当复杂的东西,这里不是一对一的关系,主要流程是:

### Live555 源码结构与实现细节 Live555 是一个开源的多媒体流服务器框架,支持 RTSP 协议以及多种音视频编码格式。其源码设计模块化程度高,便于扩展和维护。以下是对其源码结构及其核心功能的详细分析。 #### 1. 源码主要组成部分 Live555 的源码被划分为多个独立的部分,每部分负责特定的功能域: - **UsageEnvironment**: 提供基础环境设置接口,包括事件循环机制、定时器管理等功能[^2]。 - **BasicUsageEnvironment**: 扩展 UsageEnvironment,提供更高级别的抽象层,主要用于处理输入输出操作。 - **Groupsock**: 实现组播套接字(Multicast Socket),用于网络数据传输中的多播通信。 - **liveMedia**: 核心媒体处理模块,封装了各种编解码器及相关协议的支持,如 RTP/RTCP 和 H.264/H.265 等[^2]。 - **testProgs**: 测试程序集合,包含一系列简单的客户端和服务端示例代码,帮助开发者快速上手。 - **mediaServer**: 媒体服务端逻辑实现,定义如何响应来自客户端的请求并发送媒体流[^2]。 #### 2. 关键类与函数解析 ##### (1) 播放控制流程 在处理 RTSP 请求时,Live555 使用了一系列方法来完成播放位置调整及实际播放过程: - `parseScaleHeader()` 和 `parseRangeHeader()` 方法分别解析缩放比例头信息和平滑范围头信息,从而确定播放的具体起始点与结束点[^3]。 - 遍历成员变量 `fNumStreamStates` 中存储的状态对象数组,逐一访问其中每个子会话实例 (`subsession`) 并调用 `OnDemandServerMediaSubsession::seekStream()` 来重新定位当前播放时间戳至指定位置。 - 同样通过迭代上述状态列表,在每次更新完成后再次逐项调用 `OnDemandServerMediaSubsession::startStream()` 函数启动对应的数据流传送任务[^3]。 ##### (2) 动态链接库生成 构建过程中会产生如下静态库文件: ```bash ./liveMedia/libliveMedia.a ./groupsock/libgroupsock.a ./UsageEnvironment/libUsageEnvironment.a ./BasicUsageEnvironment/libBasicUsageEnvironment.a ``` 这些库构成了整个系统的底层支撑体系,任何基于此平台的应用都需要依赖它们来进行初始化配置以及其他必要的交互操作[^4]。 #### 3. 示例代码片段 下面展示了一个简化版创建 RTSP 客户端连接的过程演示代码: ```cpp #include "liveMedia.hh" #include "BasicUsageEnvironment.hh" int main(int argc, char* argv[]) { TaskScheduler* scheduler = BasicTaskScheduler::createNew(); UsageEnvironment env(*scheduler); UserAuthenticationDatabase authDB; RTSPClient* rtspClient = RTSPClient::createNew(env, "rtsp://example.com/stream", &authDB); if (!rtspClient->sendDescribeCommand(*(new ResultHandler(rtspClient)))) { *env << "Failed to send DESCRIBE command\n"; } Medium::close(rtspClient); // Clean up resources. return 0; } ``` 以上代码展示了如何利用 Live555 库建立到远程 RTSP 服务器的一个基本连接,并尝试获取关于该资源的信息描述。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值