live555之DESCRIBE

前言

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.0
CSeq: 3
User-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 OK
CSeq: 3
Date: Sun, Sep 03 2017 10:44:36 GMT
Content-Base: rtsp://192.168.1.100:8554/test.mkv/
Content-Type: application/sdp
Content-Length: 665

v=0
o=- 1504435476491988 1 IN IP4 192.168.1.100
s=Matroska video+audio+(optional)subtitles, streamed by the LIVE555 Media Server
i=test.mkv
t=0 0
a=tool:LIVE555 Streaming Media v2017.07.18
a=type:broadcast
a=control:*
a=range:npt=0-
a=x-qt-text-nam:Matroska video+audio+(optional)subtitles, streamed by the LIVE555 Media Server
a=x-qt-text-inf:test.mkv
m=video 0 RTP/AVP 96
c=IN IP4 0.0.0.0
b=AS:500
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1;profile-level-id=640029;sprop-parameter-sets=Z2QAKazZAFAFuwEQAGXTsBMS0Ajxgxlg,aOrssiw=
a=control:track1
m=audio 0 RTP/AVP 97
c=IN IP4 0.0.0.0
b=AS:48
a=rtpmap:97 AC3/48000
a=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的愿望估计大家很难实现了,不过这些可以给那些想读,可是苦于毫无头绪的人一些启发。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值