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是通过DynamicRTSPServer
的lookupServerMediaSession
函数创建的,这里有兴趣的童鞋可以看下,不太复杂。 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的愿望估计大家很难实现了,不过这些可以给那些想读,可是苦于毫无头绪的人一些启发。
- live555之DESCRIBE
- live555代码解读之二:DESCRIBE请求消息处理过程
- live555代码解读之二:DESCRIBE请求消息处理过程
- live555代码解读之二:DESCRIBE请求消息处理过程
- live555代码解读之二:DESCRIBE请求消息处理过程
- live555 DESCRIBE处理详解
- live555源码分析---- DESCRIBE命令处理
- live555源码分析---- DESCRIBE命令处理
- live555源码分析---- DESCRIBE命令处理
- live555源码分析---- DESCRIBE命令处理
- live555源码分析---- DESCRIBE命令处理
- live555源码分析---- DESCRIBE命令处理
- live555 源码分析: DESCRIBE 的处理
- 1.linux编译live555与分析DESCRIBE命令流程
- DESCRIBE
- MySQL语句之describe,explain,help,use
- LIVE555研究之三:LIVE555基础
- LIVE555研究之三:LIVE555基础
- webview与js的交互
- 写区块链技术文章阵地 简书,因简书有打赏
- 上课时间安排
- BZOJ 3670 动物园 (KMP)
- TP5开发前篇---缓存(Cache)
- live555之DESCRIBE
- java多线程
- ActiveMQ系列—ActiveMQ集群方案(上)(高性能方案)
- udp客户端与服务端相连时,握手情况的处理
- JVM菜鸟进阶高手之路十二(jdk9、JVM方面变化, 蹭热度)
- 什么?!不用敲键盘也能get到excel代码啦?
- 剑指offer-链表中倒数第K个结点
- LeetCode3. Longest Substring Without Repeating Characters
- Hadoop 解除 "Name node is in safe mode"