live555之DESCRIBE

来源:互联网 发布:知天命什么意思 编辑:程序博客网 时间:2024/06/05 23:42

前言

DESCRIBE.请求直接返回一些服务器可用参数,这些其实也就是RTSP支持的一些通讯协议,

正文

第一次OPTION指令基本没做啥事情,也就是创建一个mediaSession,我们上一篇也介绍了一下,这里不再全部介绍。直接开始DESCRIBE请求相应流程。

void RTSPServer::RTSPClientConnection::handleRequestBytes(int newBytesRead) {    Boolean parseSucceeded = parseRTSPRequestString((char*)fRequestBuffer, fLastCRLF + 2 - fRequestBuffer,    cmdName, sizeof cmdName,    urlPreSuffix, sizeof urlPreSuffix,    urlSuffix, sizeof urlSuffix,    cseq, sizeof cseq,    sessionIdStr, sizeof sessionIdStr,    contentLength);    ......    handleCmd_DESCRIBE(urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);    ......    send(fClientOutputSocket, (char const*)fResponseBuffer, strlen((char*)fResponseBuffer), 0);}

这里总共只有三个操作,解析请求,处理请求,和返回相应,解析处理流程比较无聊,这里也不在详细阅读, 请求指令只这些,解析后如下:

DESCRIBE rtsp://192.168.1.100:8554/test.mkv RTSP/1.0CSeq: 3User-Agent: LibVLC/2.2.6 (LIVE555 Streaming Media v2016.02.22)Accept: application/sdp//解析后:cmdName "DESCRIBE", urlPreSuffix "", urlSuffix "test.mkv", CSeq "3", Content-Length 0,

处理的话,我们直接追踪下,因为这里有读取文件,等等比较关键的操作。

void RTSPServer::RTSPClientConnection::handleCmd_DESCRIBE(char const* urlPreSuffix, char const* urlSuffix, char const* fullRequestStr) {    ......        session = fOurServer.lookupServerMediaSession(urlTotalSuffix);    ......        sdpDescription = session->generateSDPDescription();    ......}

session是通过DynamicRTSPServerlookupServerMediaSession函数创建的,这里有兴趣的童鞋可以看下,不太复杂。
handleCmd_DESCRIBE->generateSDPDescription,这是读取文件的参数的函数

char* ServerMediaSession::generateSDPDescription() {    ......        char const* sdpLines = subsession->sdpLines();        ......}

这是开始读取文件,得到必要编码规则,然后生成视频信息发给客户端。可是这里比较复杂,暂时停下来,吧整个发送的代码部分整理清楚,这里得到视频的一些信息,最后和必要的通讯头结合形成一个完整的通讯信息,形式如下

RTSP/1.0 200 OKCSeq: 3Date: Sun, Sep 03 2017 10:44:36 GMTContent-Base: rtsp://192.168.1.100:8554/test.mkv/Content-Type: application/sdpContent-Length: 665v=0o=- 1504435476491988 1 IN IP4 192.168.1.100s=Matroska video+audio+(optional)subtitles, streamed by the LIVE555 Media Serveri=test.mkvt=0 0a=tool:LIVE555 Streaming Media v2017.07.18a=type:broadcasta=control:*a=range:npt=0-a=x-qt-text-nam:Matroska video+audio+(optional)subtitles, streamed by the LIVE555 Media Servera=x-qt-text-inf:test.mkvm=video 0 RTP/AVP 96c=IN IP4 0.0.0.0b=AS:500a=rtpmap:96 H264/90000a=fmtp:96 packetization-mode=1;profile-level-id=640029;sprop-parameter-sets=Z2QAKazZAFAFuwEQAGXTsBMS0Ajxgxlg,aOrssiw=a=control:track1m=audio 0 RTP/AVP 97c=IN IP4 0.0.0.0b=AS:48a=rtpmap:97 AC3/48000a=control:track2

这些信息到底如何读取,我这里就不一一介绍,如果谁想完全搞懂,可以慢慢顺着这个思路一一整理,最关键的视频信息的读取,我们这里开始完整介绍

之前我们读到subsession->sdpLines();可以读取文件的部分信息,我们这里慢慢开始读这部分代码。对于h264文件,subsession,类型是H264VideoFileServerMediaSubsession

OnDemandServerMediaSubsession::sdpLines() {    FramedSource* inputSource = createNewStreamSource(0, estBitrate);    Groupsock* dummyGroupsock = createGroupsock(dummyAddr, 0);    RTPSink* dummyRTPSink = createNewRTPSink(dummyGroupsock, rtpPayloadType, inputSource);  return fSDPLines;  }

这里创建了一个FramedSource。,类型是H264VideoStreamFramer,然后通过createNewRTPSink调用开始读取,dummyRTPSink类型为H264VideoRTPSink

void OnDemandServerMediaSubsession::setSDPLinesFromRTPSink(RTPSink* rtpSink, FramedSource* inputSource, unsigned estBitrate) {//一大堆用来获取吧RTPSink设置成我们需要的参数的方法,  char const* auxSDPLine = getAuxSDPLine(rtpSink, inputSource); //生成我们需要的参数}

注意这里通过vs,直接可以进入下面方法,可是这个方法调用是不对的,调用的是,子类H264VideoFileServerMediaSubsession对此方法的重写。

//没有调用这个方法char const* OnDemandServerMediaSubsession::getAuxSDPLine(RTPSink* rtpSink, FramedSource* /*inputSource*/) {  // Default implementation:  return rtpSink == NULL ? NULL : rtpSink->auxSDPLine();}//真正的是这个方法char const* H264VideoFileServerMediaSubsession::getAuxSDPLine(RTPSink* rtpSink, FramedSource* inputSource) {    fDummyRTPSink->startPlaying(*inputSource, afterPlayingDummy, this);    checkForAuxSDPLine(this);  }  envir().taskScheduler().doEventLoop(&fDoneFlag);  return fAuxSDPLine;}

看到这,废了这么大的劲,麻蛋还是云里雾里,并且雾气越来越多,那我们还是坚持,继续看,总有柳暗花明一天,不过看到注释,我想大家应该就可以放心一点了,我们继续

Boolean MediaSink::startPlaying(MediaSource& source,                afterPlayingFunc* afterFunc,                void* afterClientData) {    ......  fSource = (FramedSource*)&source;  fAfterFunc = afterFunc;  fAfterClientData = afterClientData;  return continuePlaying();}Boolean H264or5VideoRTPSink::continuePlaying() {    ......  return MultiFramedRTPSink::continuePlaying();}Boolean MultiFramedRTPSink::continuePlaying() {  // Send the first packet.  // (This will also schedule any future sends.)  buildAndSendPacket(True);  return True;  }Boolean MultiFramedRTPSink::continuePlaying() {  // Send the first packet.  // (This will also schedule any future sends.)  buildAndSendPacket(True);  return True;}void MultiFramedRTPSink::buildAndSendPacket(Boolean isFirstPacket) {  packFrame();}void MultiFramedRTPSink::packFrame() {    fSource->getNextFrame(fOutBuf->curPtr(), fOutBuf->totalBytesAvailable(),              afterGettingFrame, this, ourHandleClosure, this);  }void FramedSource::getNextFrame(unsigned char* to, unsigned maxSize,                afterGettingFunc* afterGettingFunc,                void* afterGettingClientData,                onCloseFunc* onCloseFunc,                void* onCloseClientData) {  doGetNextFrame();}void MPEGVideoStreamFramer::doGetNextFrame() {    ......  continueReadProcessing();}void MPEGVideoStreamFramer::continueReadProcessing() {  unsigned acquiredFrameSize = fParser->parse();  ......  //这些乌七八糟的是开始解析文件了。}

其实到这里,也就开始读文件了,读完以后开始解析,可是也许有人会说,parse()是读取了吗?我打开函数,觉得完全不像啊,这种变态的设计,我也是有些无语,我们还是慢慢一一读下代码吧。

unsigned H264or5VideoStreamParser::parse() {    ......    while ((first4Bytes = test4Bytes()) != 0x00000001) {        get1Byte();         setParseState(); // ensures that we progress over bad data    }    ......  u_int8_t get1Byte() { // byte-aligned    ensureValidBytes(1);  }void StreamParser::ensureValidBytes1(unsigned numBytesNeeded) {    ......  fInputSource->getNextFrame(&curBank()[fTotNumValidBytes],                 maxNumBytesToRead,                 afterGettingBytes, this,                 onInputClosure, this);  throw NO_MORE_BUFFERED_INPUT;}void FramedSource::getNextFrame(unsigned char* to, unsigned maxSize,                afterGettingFunc* afterGettingFunc,                void* afterGettingClientData,                onCloseFunc* onCloseFunc,                void* onCloseClientData) {  doGetNextFrame();}void ByteStreamFileSource::doGetNextFrame() {  doReadFromFile();}void ByteStreamFileSource::doReadFromFile() { //读文件,  fFrameSize = fread(fTo, 1, fMaxSize, fFid);  //通过定时,的重新调用我们的FramedSource::afterGetting这个函数,目的是为了让服务器长时间不响应其他服务吧,具体不了解。  nextTask() = envir().taskScheduler().scheduleDelayedTask(0,                (TaskFunc*)FramedSource::afterGetting, this);}

通过FramedSource::afterGetting,这是定时任务,被计时器触发,然后重新调用parse()开始解析数据,至于说如何解析h264。我也懒得再看。这里就不详细介绍。我们大概了解整个函数的流程了。
其实这里都是在看如何读取文件,但是最关键的,这些类的结构,和读到文件后,如何处理的,都没详细介绍,这里等我以后有空再整理一下吧,不过整个流程大概只有这些了。
这里写图片描述
这是主要的数据结构,在server创建RTSPClineConnect,然后RTSPClineConnect开始向进程调度器注册自己的特定socket端口和处理函数,可以处理来自特定socket的请求。在处理请求时候,一些文件的读写都是依靠serverMediaSession来实现的。而serverMediaSession,却仅仅是一个代理,每一种文件类型,都事通过自己的subsession来实现的,这里进行了一些封装。具体ServerMediaSession如何实现,以及集成关系,这里暂时不在详细介绍。

后记

废了一天功夫,终于整理出这篇博客,这里基本上想通过这篇博客看懂大概live555的愿望估计大家很难实现了,不过这些可以给那些想读,可是苦于毫无头绪的人一些启发。

原创粉丝点击