[live555] 从testRTSPClient.cpp DESCRIBE 发起请求 一

从Meida Server 到live555客户端

之前零零碎碎写了
live555 MediaServer
以及关于RTSP 流程的处理
live555 处理 请求消息 一 “OPTIONS”
live555 处理 请求消息 二 “DESCRIBE”
live555 处理 请求消息 三 “SETUP”
live555 处理 请求消息 四 “PLAY”
live555 处理 请求消息 四 “PLAY” [续]
以及每个部分涉及到的知识,如何读取.mkv文件一帧,用NALU 封装,发送给客户端过程分析
当时只是关注细节,导致没有从架构分析,专门解剖live555 (也是对自己的live555总结)当然,live555 也有很多需要优化,一步一步来解决

这一章,从功能划分解剖 live555 RTSPClient.时至今日,我依然记得前辈讲过,有刨根问底的精神,当啃完后,就会豁然开朗

最后有彩蛋……

testRTSPClient

看 main入口,代码功能就划分为
1.初始化RTSPClient 环境变量
2.打开连接MediaServer RTSP服务器,自动触发RTSP 流程,以及传输RTP传输,甚至RTCP控制
3.第二步接收返回的消息,通过Scheduler 轮询处理 定时事件和socket,所以需要开启Scheduler轮询(单线程处理)

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

  // Open and start streaming :
  openURL(*env, "test", "rtsp://10.0.2.15/ss1.mkv");
  // All subsequent activity takes place within the event loop:
  env->taskScheduler().doEventLoop(&eventLoopWatchVariable);
  return 0;
}

首先wireshark抓一个rtsp 流程的包
rtsp

在步骤2 会触发 RTSP DESCRIPE 、SETUP、PLAY 请求,少了OPTION 请求,不过没关系,OPTION 只是为了确认服务器所支持的请求有哪一些

在SETUP会触发 RTP接收socket 建立 ,PLAY请求完成会接收RTP包

分析环境初始化

  1. 创建 scheduler 实例对象
TaskScheduler* scheduler = BasicTaskScheduler::createNew();

并且初始化变量,为轮询准备的变量

 maxSchedulerGranularity = 10000/*microseconds*/ //最大调度间隔
 fMaxNumSockets = 0 //最大socket 数目
 fDummySocketNum = -1 //dummysocket 的初始化值
 /*清除并初始化 保存socket 句柄的fd_set 结构体*/
  FD_ZERO(&fReadSet);
  FD_ZERO(&fWriteSet);
  FD_ZERO(&fExceptionSet);
typedef struct fd_set {
        u_int   fd_count;               /* how many are SET? */
        SOCKET  fd_array[FD_SETSIZE];   /* an array of SOCKETs */
} fd_set;

#define FD_SETSIZE      64

以及超时scheduler相关的初始化

然后初始化live555 使用环境

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

将*scheduler 放在env 实例中,方便轮询使用
初始化,使用轮寻需要相关参数

# define RESULT_MSG_BUFFER_MAX 1000 //消息的最大buffer
fCurBufferSize = 0;              //清除当前消息buffer大小
fResultMsgBuffer[fCurBufferSize] = '\0';   //清空 消息buffer

轮询 事件

char eventLoopWatchVariable = 0;
env->taskScheduler().doEventLoop(&eventLoopWatchVariable);
void BasicTaskScheduler0::doEventLoop(char volatile* watchVariable) {  
  // Repeatedly loop, handling readble sockets and timed events:
  while (1) {
    if (watchVariable != NULL && *watchVariable != 0) break;
    SingleStep();
  }
}

就是不断的处理 可读socket 传过来消息和定时事件

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
  //....
  // Call the handler function for one readable socket:
  HandlerIterator iter(*fHandlers);
  HandlerDescriptor* handler;
  //....
  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;
    }
  }

  //定时处理
}

关于HandlerIterator 迭代器原理 查看:live555 谈一谈 HandlerIterator \ HandlerDescriptor \HandlerSet 构成迭代器
最后,轮询处理接收socket 消息

(*handler->handlerProc)(handler->clientData, resultConditionSet);

openURL 触发RTSP流程

为当前RTSP客户端创建对象并且发送DESCRIBE请求

void openURL(UsageEnvironment& env, char const* progName, char const* rtspURL) {

  RTSPClient* rtspClient = ourRTSPClient::createNew(env, rtspURL, RTSP_CLIENT_VERBOSITY_LEVEL, progName);

  rtspClient->sendDescribeCommand(continueAfterDESCRIBE); 
}

发送Describe请求

sendRequest(new RequestRecord(++fCSeq, "DESCRIBE", responseHandler));

在后面可以看出其他请求也是sendRequest
1. 打开创建socket 并且连接
2. send 发送组成的请求消息

unsigned RTSPClient::sendRequest(RequestRecord* request) {
  char* cmd = NULL;
  do {
    Boolean connectionIsPending = False;
    //....
    // we need to open a connection
    int connectResult = openConnection(); //success return 1

    // Construct and send the command:
    cmd = new char[cmdSize];
    sprintf(cmd, cmdFmt,
        request->commandName(), cmdURL, protocolStr,
        request->cseq(),
        authenticatorStr,
        fUserAgentHeaderStr,
            extraHeaders,
        contentLengthHeader,
        contentStr);
    }

    send(fOutputSocketNum, cmd, strlen(cmd), 0) 
    //......
  } while (0);


  return 0;
}

openConnection负责
1.创建socket
2.连接服务器
3.将socket 句柄对应信息,放入使用环境的scheduler 轮询,对接收消息进行回调处理

int RTSPClient::openConnection() {
  do {
    // Set up a connection to the server.  Begin by parsing the URL:
    // We don't yet have a TCP socket (or we used to have one, but it got closed).  Set it up now.
    fInputSocketNum = setupStreamSocket(envir(), 0);


    if (fOutputSocketNum < 0) fOutputSocketNum = fInputSocketNum;

    // Connect to the remote endpoint:
    int connectResult = connectToServer(fInputSocketNum, destPortNum);

    // The connection succeeded.  Arrange to handle responses to requests sent on it:
     envir().taskScheduler().setBackgroundHandling(
          fInputSocketNum
         ,SOCKET_READABLE|SOCKET_EXCEPTION
         ,(TaskScheduler::BackgroundHandlerProc*)&incomingDataHandler
         , this);
    }
    return connectResult;
  } while (0);
  return -1;
}

上面将socket conditionSet handlerProc丢给 Scheduler处理,等待轮询处理,前提通过HandlerSet 调用 assignHandler 封装给HandlerDescriptor 然后交由事件轮询处理

void BasicTaskScheduler::setBackgroundHandling(int socketNum, int conditionSet, BackgroundHandlerProc* handlerProc, void* clientData) {
    //....
    fHandlers->assignHandler(socketNum, conditionSet, handlerProc, clientData);
}

在轮询过程中才进行回调函数处理

(*handler->handlerProc)(handler->clientData, resultConditionSet);

说明了轮询处理过程,因此DESCRIPE请求 这里对应的

handlerProc->incomingDataHandler
clientData ->OurRTSPClient
resultConditionSet->SOCKET_READABLE|SOCKET_EXCEPTION
//以及socket句柄都存放在handler 对应是HandlerDescriptor

这里简单认识下HandlerDescriptor、HandlerSet、HandlerIterator
HandlerDescriptor 用来存放句柄相关信息,用双向链表查找
HandlerIterator 用来封装HandlerDescriptor ,遍历HandlerIterator其实就是查找HandlerDescriptor的节点
HandlerSet 用来将对应socket conditionSet 等处理回调函数信息封装给HandlerDescriptor

最后看一下回调函数incomingDataHandler 作用:
读取服务器返回消息,处理返回消息

void RTSPClient::incomingDataHandler1() {

  int bytesRead = readSocket(envir(), fInputSocketNum, (unsigned char*)&fResponseBuffer[fResponseBytesAlreadySeen], fResponseBufferBytesLeft, dummy);
  handleResponseBytes(bytesRead);
}

handleResponseBytes 后面会继续后续 SETUP PLAY等请求

总结 live555 事件RTSPClient 事件轮询过程

轮询

事件节点解释:
事件轮询 :SingleStep() 的过程
请求消息: 第一次是DESCRIBE 请求消息sendDescribeCommand ,后面依次发送其他请求
事件接收:通过envir().taskScheduler().setBackgroundHandling将事件传递给轮询
事件封装:在HandlerSet中通过assignHandler,将socket/conditionSet/handlerProc等信息封装在HandlerDescriptor 组成的双向链表中
回调:handlerProc函数回调,处理句柄所返回的消息,成功的话进行下一步请求


  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值