live555源码分析(五)DESCRIBE请求的处理

news/2025/3/17 21:00:22/

live555源码分析系列

live555源码分析(一)live555初体验

live555源码分析(二)基本组件上

live555源码分析(三)基本组件下

live555源码分析(四)RTSPServer分析

live555源码分析(五)DESCRIBE请求的处理

live555源码分析(六)SETUP和PLAY请求的处理

live555源码分析(七)播放过程

live555源码分析(八)多播

live555源码分析(五)DESCRIBE请求的处理

文章目录

  • live555源码分析(五)DESCRIBE请求的处理
    • 一、源码分析
    • 二、总结

一、源码分析

本文续接上文,将分析DESCRIBE请求的处理过程

DESCRIBE的处理主要是返回sdp文件,接下来将重点讲解

handleCmd_DESCRIBE处理函数的内容大致如下

void RTSPServer::RTSPClientConnection
::handleCmd_DESCRIBE(char const* urlPreSuffix, char const* urlSuffix, char const* fullRequestStr) {ServerMediaSession* session = NULL;/* 找到会话 */session = fOurServer.lookupServerMediaSession(urlTotalSuffix);	/* 获取sdp信息 */sdpDescription = session->generateSDPDescription();/* 返回结果 */snprintf((char*)fResponseBuffer, sizeof fResponseBuffer,"RTSP/1.0 200 OK\r\nCSeq: %s\r\n""%s""Content-Base: %s/\r\n""Content-Type: application/sdp\r\n""Content-Length: %d\r\n\r\n""%s",fCurrentCSeq,dateHeader(),rtspURL,sdpDescriptionSize,sdpDescription);
}

首先会根据url中指定的会话从服务器中找到会话(rtsp://ip:port/session中的session字段)

然后通过会话的generateSDPDescription函数获取sdp信息

接下来看generateSDPDescription函数的定义

char* ServerMediaSession::generateSDPDescription() {if (fIsSSM) { //多播?char const* const sourceFilterFmt ="a=source-filter: incl IN IP4 * %s\r\n""a=rtcp-unicast: reflection\r\n"; //反馈RTPC单播}char const* const sdpPrefixFmt ="v=0\r\n""o=- %ld%06ld %d IN IP4 %s\r\n""s=%s\r\n""i=%s\r\n""t=0 0\r\n""a=tool:%s%s\r\n""a=type:broadcast\r\n""a=control:*\r\n""%s""%s""a=x-qt-text-nam:%s\r\n""a=x-qt-text-inf:%s\r\n""%s";/* 遍历子会话 */for (subsession = fSubsessionsHead; subsession != NULL;subsession = subsession->fNext) {.../* 获取媒体级的sdp信息 */char const* sdpLines = subsession->sdpLines();...}
}

从上面可以看到generateSDPDescription会先描述好sdp的会话级信息,然后再调用每个子会话获取sdp的媒体级信息

我们在live555源码分析(四)RTSPServer分析的示例中,添加的子会话只有H264VideoFileServerMediaSubsession,所以下面基于它来分析

首先看如何获取媒体级的sdp信息

char const*
OnDemandServerMediaSubsession::sdpLines() {if (fSDPLines == NULL) {FramedSource* inputSource = createNewStreamSource(0, estBitrate);RTPSink* dummyRTPSink = createNewRTPSink(dummyGroupsock, rtpPayloadType, inputSource);setSDPLinesFromRTPSink(dummyRTPSink, inputSource, estBitrate);Medium::close(dummyRTPSink);closeStreamSource(inputSource);}return fSDPLines;
}

如果sdp信息为空,那么就创建一个临时的源和临时的消费者,然后再获取sdp信息,获取之后会将源和消费者删除

createNewStreamSourcecreateNewRTPSink都是虚函数,它们在H264VideoFileServerMediaSubsession中被重写,定义如下

FramedSource* H264VideoFileServerMediaSubsession::createNewStreamSource(unsigned /*clientSessionId*/, unsigned& estBitrate) {estBitrate = 500; // kbps, estimate/* 文件字节流 */ByteStreamFileSource* fileSource = ByteStreamFileSource::createNew(envir(), fFileName);if (fileSource == NULL) return NULL;fFileSize = fileSource->fileSize();/* 获取NALU */return H264VideoStreamFramer::createNew(envir(), fileSource);
}

这里用到的是装饰者模式,ByteStreamFileSource是获取字节流,将其传递给H264VideoStreamFramerH264VideoStreamFramer可以解析然后获取一帧一帧的NALU

RTPSink* H264VideoFileServerMediaSubsession
::createNewRTPSink(Groupsock* rtpGroupsock,unsigned char rtpPayloadTypeIfDynamic,FramedSource* /*inputSource*/) {return H264VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);
}

H264VideoRTPSink的作用是将H264VideoStreamFramer提供的NALU进行RTP打包

再回到OnDemandServerMediaSubsession::sdpLines()函数,看看setSDPLinesFromRTPSink函数是如何设置sdp信息的

void OnDemandServerMediaSubsession
::setSDPLinesFromRTPSink(RTPSink* rtpSink, FramedSource* inputSource, unsigned estBitrate) {char const* mediaType = rtpSink->sdpMediaType(); //video?audiochar* rtpmapLine = rtpSink->rtpmapLine(); //m=video 0 RTP/AVP 96char const* auxSDPLine = getAuxSDPLine(rtpSink, inputSource); //a=rtpmap:96 ...char const* const sdpFmt ="m=%s %u RTP/AVP %d\r\n""c=IN IP4 %s\r\n""b=AS:%u\r\n""%s""%s""%s""%s""a=control:%s\r\n";sprintf(...);
}

可以看到媒体信息都是从rtpSink中获取的,这里的rtpSInk对应上面生成的消费者H264VideoRTPSink

看一看rtpSink->sdpMediaType()的定义

char const* VideoRTPSink::sdpMediaType() const {return "video";
}

再看看rtpSink->rtpmapLine()的定义

char* RTPSink::rtpmapLine() const {char const* const rtpmapFmt = "a=rtpmap:%d %s/%d%s\r\n";sprintf(rtpmapLine, rtpmapFmt,rtpPayloadType(), rtpPayloadFormatName(),rtpTimestampFrequency(), encodingParamsPart);
}

接下来具体分析getAuxSDPLine,其定义如下

char const* H264VideoFileServerMediaSubsession::getAuxSDPLine(RTPSink* rtpSink, FramedSource* inputSource) {fDummyRTPSink->startPlaying(*inputSource, afterPlayingDummy, this);checkForAuxSDPLine(this);envir().taskScheduler().doEventLoop(&fDoneFlag);
}

首先让消费者播放,然后设置退出检查,接着就进入事件循环

这里有个疑问,为什么要在这里播放?

因为H.264的Sink中的媒体信息需要SPS和PPS来提供信息,而在没有播放前,Sink里面是没有缓存SPS和PPS的,只有播放一段时间后,才会有缓存SPS和PPS,这是才能获取到sdp信息

看一看checkForAuxSDPLine中如何检查退出条件

void H264VideoFileServerMediaSubsession::checkForAuxSDPLine1() {if (fDummyRTPSink != NULL && (dasl = fDummyRTPSink->auxSDPLine()) != NULL) {fAuxSDPLine = strDup(dasl);setDoneFlag();} else if (!fDoneFlag) {int uSecsToDelay = 100000;envir().taskScheduler().scheduleDelayedTask(uSecsToDelay,(TaskFunc*)checkForAuxSDPLine, this);}
}

如果可以通过fDummyRTPSink->auxSDPLine()获取sdp信息,那么就会将sdp信息拷贝下来,通过setDoneFlag来退出事件循环

否则,将再10ms后再次检查

看一下fDummyRTPSink->auxSDPLine()的实现

char const* H264VideoRTPSink::auxSDPLine() {/* 获取sps和pps */framerSource->getVPSandSPSandPPS(vpsDummy, vpsDummySize, sps, spsSize, pps, ppsSize);u_int32_t profileLevelId = (spsWEB[1]<<16) | (spsWEB[2]<<8) | spsWEB[3];char const* fmtpFmt ="a=fmtp:%d packetization-mode=1"";profile-level-id=%06X"";sprop-parameter-sets=%s,%s\r\n";sprintf(fmtp, fmtpFmt, ...);return fFmtpSDPLine;
}

可以看到H264VideoRTPSink::auxSDPLine()会获取sps和pps,然后生成sdp信息,再返回

整一个sdp文件的生成过程就是这样,下面来总结一下

二、总结

服务器对于DESCRIBE请求的处理主要是获取sdp文件,RTSPClientConnection会向ServerMediaSession获取sdp信息,ServerMediaSession会生成会话级的sdp信息,然后遍历所以子会话获取媒体级的sdp信息,子会话再向RTPSink(消费者)获取媒体信息来组成媒体级的sdp信息,RTPSink可能需要数据源来获取媒体信息


http://www.ppmy.cn/news/488377.html

相关文章

Live555实时视频流应用总结

1&#xff0c;linux 环境&#xff1a; 官网上下载&#xff0c;下载地址&#xff1a;http://www.live555.com/liveMedia/public/ live555 版本&#xff1a;“2018.12.14” 参考&#xff1a;http://www.live555.com/liveMedia/faq.html 这个FAQ要仔细阅读。 2,编译 根据不同的平台…

RedisLive安装指南

引言&#xff1a;Redis是目前最为流行的缓存服务&#xff0c;本文将详细介绍如何搭建RedisLive的监控环境。本文安装之后的功能仍不完整&#xff0c;无实时的数据显示。 1. 环境介绍 Centos 4.7.4, Python 2.6.6, 检查环境信息&#xff0c; 打开命令行&#xff1a; [abcdZC_VM_…

live2d内嵌html,为你的博客博客/网页添加Live2d二次元老婆的插件/手动方法

前言 好久没搞WP的教程了,想不想为你的博客加一个看板娘呢,反正我想2333,所以我便汇总了一下网上这些教程模型和机器人api。 插件版 Wordpress 1、Wikimoe大佬制作的插件(伊斯特瓦尔) 2、小白-白大佬制作的插件(22&33娘单服装版本) Github 3、小白-白大佬制作的插件(…

LIVE555学习5:testOnDemandRTSPServer例程解析

文章目录 1 主函数2 ServerMediaSession3 Source 和 Sink 关于testOnDemandRTSPServer例程分析&#xff0c;网上有很多文章&#xff0c;有些写的非常详细&#xff0c;将涉及到的每个函数以及类进行了详细讲解&#xff0c;看了之后很有收获&#xff0c;可是让自己写是如何也写不…

live555源码分析(八)多播

live555源码分析系列 live555源码分析&#xff08;一&#xff09;live555初体验 live555源码分析&#xff08;二&#xff09;基本组件上 live555源码分析&#xff08;三&#xff09;基本组件下 live555源码分析&#xff08;四&#xff09;RTSPServer分析 live555源码分析&…

LIVE555学习1:Linux下live555的编译及测试

以下为在linux下编译和测试live555的全部过程。 文章目录 1 源码下载2 编译3 测试 1 源码下载 官网地址&#xff1a;http://www.live555.com/liveMedia/public/ 打开后&#xff0c;选择live555-latest.tar.gz 2 编译 在主目录下创建文件config.3516c&#xff0c;内容如下&am…

live555读文件改为读内存

近期有一项工作涉及到视频流的传输&#xff0c;在RTSP服务器中实时转播国标服务器获取到的h264视频数据&#xff0c;而live555库只支持文件推流&#xff0c;经过几天的摸索&#xff0c;实现其功能&#xff0c;以此文档记录之。&#xff08;本次记录以live555库的mediaServer案例…

live555源码分析(七)播放过程

live555源码分析系列 live555源码分析&#xff08;一&#xff09;live555初体验 live555源码分析&#xff08;二&#xff09;基本组件上 live555源码分析&#xff08;三&#xff09;基本组件下 live555源码分析&#xff08;四&#xff09;RTSPServer分析 live555源码分析&…