2.live555源码分析----服务端doEventLoop()函数分析

本文详细解析了live555服务端的事件循环机制,包括doEventLoop函数的工作原理及其如何通过SingleStep函数不断轮询事件。重点介绍了select函数在事件监控中的作用及如何遍历socket集合来响应事件。

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

上一篇博客说道,live555服务端main函数做的最后一件事就是调用如下代码陷入死循环:

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

   那么这个doEventLoop是什么样的呢?如下:

复制代码
void BasicTaskScheduler0::doEventLoop(char* watchVariable) {
  // Repeatedly loop, handling readble sockets and timed events:
  while (1) {
    if (watchVariable != NULL && *watchVariable != 0) break;
    SingleStep();
  }
}
复制代码

   就是不停地调用SingleStep()这个函数,SingleStep函数中代码比较多,我下面仅截取关键代码,首先是使用selet陷入阻塞,等待事件发生:

int selectResult = select(fMaxNumSockets, &readSet, &writeSet, &exceptionSet, &tv_timeToDelay);

   返回值后之后会对所有的socket进行遍历,找到是哪个socket发生了事件:

复制代码
while ((handler = iter.next()) != NULL) {
    int sock = handler->socketNum; // alias
    int resultConditionSet = 0;
    if (FD_ISSET(sock, &readSet) && FD_ISSET(sock, &fReadSet)/*sanity check*/) resultConditionSet |= SOCKET_READABLE;
    if (FD_ISSET(sock, &writeSet) && FD_ISSET(sock, &fWriteSet)/*sanity check*/) resultConditionSet |= SOCKET_WRITABLE;
    if (FD_ISSET(sock, &exceptionSet) && FD_ISSET(sock, &fExceptionSet)/*sanity check*/) resultConditionSet |= SOCKET_EXCEPTION;
    if ((resultConditionSet&handler->conditionSet) != 0 && handler->handlerProc != NULL) {
      fLastHandledSocketNum = sock;
          // Note: we set "fLastHandledSocketNum" before calling the handler,
          // in case the handler calls "doEventLoop()" reentrantly.
      (*handler->handlerProc)(handler->clientData, resultConditionSet);
      break;
    }
  }
  if (handler == NULL && fLastHandledSocketNum >= 0) {
    // We didn't call a handler, but we didn't get to check all of them,
    // so try again from the beginning:
    iter.reset();
    while ((handler = iter.next()) != NULL) {
      int sock = handler->socketNum; // alias
      int resultConditionSet = 0;
      if (FD_ISSET(sock, &readSet) && FD_ISSET(sock, &fReadSet)/*sanity check*/) resultConditionSet |= SOCKET_READABLE;
      if (FD_ISSET(sock, &writeSet) && FD_ISSET(sock, &fWriteSet)/*sanity check*/) resultConditionSet |= SOCKET_WRITABLE;
      if (FD_ISSET(sock, &exceptionSet) && FD_ISSET(sock, &fExceptionSet)/*sanity check*/) resultConditionSet |= SOCKET_EXCEPTION;
      if ((resultConditionSet&handler->conditionSet) != 0 && handler->handlerProc != NULL) {
    fLastHandledSocketNum = sock;
        // Note: we set "fLastHandledSocketNum" before calling the handler,
            // in case the handler calls "doEventLoop()" reentrantly.
    (*handler->handlerProc)(handler->clientData, resultConditionSet);
    break;
      }
    }
    if (handler == NULL) fLastHandledSocketNum = -1;//because we didn't call a handler
  }
复制代码

    当找到相应的的socket的时候,调用的是(*handler->handlerProc)这个函数,这个函数是什么呢?在上一篇博客里讲过,其实这个函数就是我们用turnOnBackgroundReadHandling()和相应socket绑定的函数,那么相对于一个server socket,绑定的就是incomingConnectionHandlerRTSP,主要功能是接受连接,创建新会话。如果是一个处理单个客户端的socket,那它绑定的就是incomingRequestHandler(),也就是负责从socket里读出数据,然后使用函数handleRequstBytes()对数据进行处理。

  handler类型是HandlerDescriptor,这个类的定义是:

 

复制代码
class HandlerDescriptor {
  HandlerDescriptor(HandlerDescriptor* nextHandler);
  virtual ~HandlerDescriptor();

public:
  int socketNum;
  int conditionSet;
  TaskScheduler::BackgroundHandlerProc* handlerProc;
  void* clientData;

private:
  // Descriptors are linked together in a doubly-linked list:
  friend class HandlerSet;
  friend class HandlerIterator;
  HandlerDescriptor* fNextHandler;
  HandlerDescriptor* fPrevHandler;
};
复制代码

 

    这个类存储了select监控的socket的状况和相关参数。其中:

TaskScheduler::BackgroundHandlerProc* handlerProc;
就是当这个socket发生事件时所需要调用的函数。
<think>我们正在讨论如何使用live555发送AAC流媒体数据。根据引用内容,live555支持多种音频格式,其中包括AAC(ADTS格式)音频文件(文件名后缀为“.aac”)。因此,我们可以利用live555提供的功能来实现AAC流媒体的传输。步骤概述:1.准备AAC源:确保AAC文件是ADTS格式(通常以.aac为后缀)。2.使用live555提供的MediaServer:live555包含一个RTSP服务器实现,可以将文件作为流媒体源。3.配置服务器:将AAC文件作为媒体源,启动RTSP服务器。具体步骤:一、编译live555首先需要编译live555源码。在Windows上可以使用MSVC,Linux上使用g++。这里以Linux为例:```bash./genMakefileslinuxmake```二、准备AAC文件确保你的AAC文件是ADTS格式(可以使用工具转换,如ffmpeg)。三、运行RTSP服务器live555提供了一个示例程序`live555MediaServer`,它位于`mediaServer`目录下。运行该程序,并将AAC文件放在服务器可访问的目录中(默认是当前目录)。启动服务器:```bash./live555MediaServer```四、客户端访问客户端可以使用RTSPURL来访问AAC流。例如,如果服务器IP是192.168.1.100,AAC文件名为`test.aac`,则RTSPURL为:```rtsp://192.168.1.100:8554/test.aac```注意:live555MediaServer默认使用8554端口。五、自定义配置如果需要更灵活的控制(例如从其他来源获取AAC数据而不是文件),则需要编写自己的应用程序。可以参考live555提供的示例,如`testOnDemandRTSPServer`,并修改以支持AAC流。创建自定义服务器的大致步骤:1.创建一个继承自`OnDemandServerMediaSubsession`的类,用于处理AAC流。2.实现虚函数,创建`FramedSource`和`RTPSink`。3.在`FramedSource`中,你需要提供AAC数据(可以从文件、麦克风、网络等读取)。4.在`RTPSink`中,使用live555提供的`ADTSAudioFileSource`(如果是文件)或自定义源。示例代码片段(基于文件):```cpp#include<liveMedia.hh>#include<BasicUsageEnvironment.hh>//创建RTSP服务器RTSPServer*createRTSPServer(Portport){RTSPServer*rtspServer=RTSPServer::createNew(env,port);if(rtspServer==NULL){*env<<"FailedtocreateRTSPserver:"<<env->getResultMsg()<<"\n";exit(1);}returnrtspServer;}//添加AAC流会话voidaddAACStream(RTSPServer*rtspServer,charconst*streamName,charconst*inputFileName){ServerMediaSession*sms=ServerMediaSession::createNew(*env,streamName);//使用ADTS音频文件源sms->addSubsession(ADTSAudioFileServerMediaSubsession::createNew(*env,inputFileName));rtspServer->addServerMediaSession(sms);}intmain(){//初始化环境TaskScheduler*scheduler=BasicTaskScheduler::createNew();UsageEnvironment*env=BasicUsageEnvironment::createNew(*scheduler);//创建RTSP服务器,端口8554RTSPServer*rtspServer=createRTSPServer(8554);//添加AAC流,假设文件名为"test.aac"addAACStream(rtspServer,"aacTest","test.aac");//进入事件循环env->taskScheduler().doEventLoop();return0;}```注意:以上代码仅为示例,实际使用时需要处理错误和资源释放。六、传输中的配置AAC流通常使用RTP传输,live555会自动将AAC数据封装成RTP包。在SDP描述中,会包含AAC的配置信息(如采样率、声道数等),客户端根据这些信息进行解码。七、测试使用支持RTSP的播放器(如VLC)打开上述URL进行测试。相关问题:1.如何将其他格式的音频文件转换为ADTSAAC格式?2.live555如何处理实时采集的AAC数据(如来自麦克风)?3.live555在传输AAC流时,如何实现多客户端同时访问?4.live555的RTSP服务器支持哪些认证机制?5.如何优化live555以降低AAC流传输的延迟?</think>### 如何使用 Live555 发送 AAC 流媒体数据 以下是配置 Live555 传输 AAC 流媒体数据的完整步骤: #### 一、准备工作 1. **编译 Live555** 确保已正确编译 Live555(参考[编译指南](^3)): ```bash ./genMakefiles <平台> # 如 linux 或 win32 make ``` 2. **准备 AAC 源文件** 文件必须是 **ADTS 格式的 AAC 文件**(后缀 `.aac`)[^2]。若需转换其他格式: ```bash ffmpeg -i input.mp3 -c:a aac -b:a 128k -f adts output.aac ``` --- #### 二、配置 RTSP 服务器 1. **启动媒体服务器** 将 AAC 文件放入服务器工作目录,执行: ```bash ./live555MediaServer ``` 默认端口 `8554`,文件路径即流名称(如 `test.aac` 对应流地址 `rtsp://server:8554/test.aac`) 2. **自定义流名称(可选)** 创建符号链接指向 AAC 文件: ```bash ln -s test.aac my_stream.aac ``` 客户端访问:`rtsp://server:8554/my_stream.aac` --- #### 三、关键代码实现(自定义场景) 若需动态生成 AAC 流(非静态文件),需实现 **`FramedSource`** 子类: ```cpp #include <liveMedia.hh> class AACFrameSource : public FramedSource { public: static AACFrameSource* createNew(UsageEnvironment& env) { return new AACFrameSource(env); } protected: AACFrameSource(UsageEnvironment& env) : FramedSource(env) {} virtual void doGetNextFrame() override { // 在此填充 AAC 数据到 fTo getAACData(fTo, fMaxSize); // 自定义数据获取函数 // 设置帧属性(示例值) fFrameSize = 1024; // 实际数据大小 fNumTruncatedBytes = 0; // 无截断 fPresentationTime = ...; // 设置时间戳 // 通知数据就绪 FramedSource::afterGetting(this); } }; ``` 在媒体会话中注册: ```cpp ServerMediaSession* sms = ServerMediaSession::createNew(env, "liveAAC"); sms->addSubsession(PassiveServerMediaSubsession::createNew( ADTSAudioStreamDiscreteFramer::createNew(env, AACFrameSource::createNew(env)), NULL )); rtspServer->addServerMediaSession(sms); ``` --- #### 四、客户端拉流 使用 `openRTSP` 工具测试: ```bash ./openRTSP -v rtsp://192.168.1.100:8554/test.aac > output.aac ``` 或通过 VLC 播放: `媒体 → 打开网络串流 → 输入 rtsp://server:8554/test.aac` --- #### 五、调试与验证 1. **日志监控** 启动时添加 `-v` 参数查看详细日志: ```bash ./live555MediaServer -v ``` 2. **Wireshark 抓包** 过滤 `rtsp` 和 `rtp` 协议,确认: - RTSP 交互过程(OPTIONS/DESCRIBE/SETUP/PLAY) - RTP 负载类型是否为 `96`(动态 AAC) - 时间戳连续性 --- ### 常见问题解决 | 问题现象 | 解决方案 | |---------------------------|------------------------------------------| | 客户端报错 "415 Unsupported Media" | 检查 AAC 文件是否为 ADTS 格式[^2] | | 无音频输出 | 确认客户端支持 AAC-LC 解码 | | 高延迟 | 调整 `BUFFER_SIZE`(默认 200KB) |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值