1.linux编译live555与分析DESCRIBE命令流程

来源:互联网 发布:淘宝宝贝权重怎么看 编辑:程序博客网 时间:2024/05/20 04:10
1.使用的版本是live555-latest.tar.gz
2.环境linux
现在按如下步骤编译:
1从live555官网上下载源码,执行tar -zxvflive555-latest.tar.gz,解压到linux目录下。
2到live555目录下,发现有很多类似config.xx-xx的文件名,这是针对不同平台的配置文件。执行./genMakefiles linux-gdb。
3执行make。到此编译完成,在4个子目录下可以找到生成的.a文件。
3.测试一下:
在live555主目录下
mkfifo test.264
./mediaServer/live555MediaServer
cat test.h264 >> test.264
这时在pc电脑上用vlc播放 :rtsp://172.16.200.43:8554/test.264 即可看到转发流

4.参照网上大神的博客自己走了一遍代码,版本不同有些不同的区别,所以决定记录一下:http://blog.csdn.net/u013898698/article/details/56284534?locationNum=12&fps=1
从live555官方自带的demo为例:在testProgs的 testOnDemandRTSPServer.cpp为流程,
这个程序官方介绍是这样的:
// Copyright (c) 1996-2017, Live Networks, Inc. All rights reserved
// A test program that demonstrates how to stream - via unicast RTP
// - various kinds of file on demand, using a built-in RTSP server.
就是说这个程序演示了如何利用RTSPServer这个类来对媒体文件进行单播,OnDemand的意思是收到RTSP客户端请求时才进行流化和单播。main函数:
其中 TaskScheduler* scheduler = BasicTaskScheduler::createNew();官方解释是:Begin by setting up our usage environment,也就是TaskScheduler提供了任务调度功能,是整个程序的运行发动机就是它,通过他来调度任务和执行任务。
env = BasicUsageEnvironment::createNew(*scheduler);是用于整个系统的基础功能类。UsageEnvironment代表了整个系统运行的环境,它提供了错误记录和错误报告的功能,无论哪一个类要输出错误,就需要保存UsageEnvironment的指针。
UserAuthenticationDatabase就不累赘解释了,这个是用户数据库,有需要的话把ACCESS_CONTROL定义了,通过addUserRecord来添加用户RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554, authDB);创建一个rtsp服务器端口号8554,如果不指定则默认为554
我们就仔细看看创建RTSPServer,创建ServerMediaSession以及ServerMediaSubsession这部分的代码,看这部分代码之前,我们需要先对RTSP协议的建立连接过程有个大概的了解,在此我就不再详述,网上有很多讲解这个过程的博文,在此推荐一个:http://www.cnblogs.com/qq78292959/archive/2010/08/12/2077039.html
RTSPServer类即表示一个流媒体服务器实例,RTSPServer::createNew是一个简单工厂函数,使用指定的端口(8554)创建一个TCP的socket用于等待客户端的连接,然后new一个RTSPServer实例。

RTSPServer继承了GenericMediaServer这个类 ,createNew的时候调用了GenericMediaServer里面的setUpOurSocket:

以看出来是创建一个套接字进行连接,最大同时可连接数是20,接下将调用:

这里我们看到继承了GenericMediaServer,追踪进去:

ignoreSigPipeOnSocket 里面是这样子:
#ifdef USE_SIGNALS
#ifdef SO_NOSIGPIPE
int set_option = 1;
setsockopt(socketNum, SOL_SOCKET, SO_NOSIGPIPE, &set_option, sizeof set_option);
#else
signal(SIGPIPE, SIG_IGN);
#endif
#endif
so that clients on the same host that are killed don't also kill us,忽略掉SIGPIPE信号,连接同一个socket的客户端将不会被杀掉,重要的是接下来调用的函数:
env.taskScheduler().turnOnBackgroundReadHandling(fServerSocket, incomingConnectionHandler, this);
这个turnOnBackgroundReadHandling在类UsageEnvironment中:TaskScheduler& taskScheduler() const {return fScheduler;}而在类TaskScheduler中:
所以找到:

->

这个函数的作用是将某个socket加入参见select集中,并指定相应的处理函数
incomingConnectionHandler:

void GenericMediaServer::incomingConnectionHandler(void* instance, int /*mask*/)
{GenericMediaServer* server = (GenericMediaServer*)instance;server->incomingConnectionHandler();     } 


void GenericMediaServer::incomingConnectionHandler()     {incomingConnectionHandlerOnSocket(fServerSocket);    } 

从上面步骤可以知道:从GenericMediaServer::incomingConnectionHandler函数开始,在GenericMediaServer::incomingConnectionHandler中又调用了incomingConnectionHandler,在incomingConnectionHandler中又调用了incomingConnectionHandlerOnSocket(= =!!)最后调用到incomingConnectionHandlerOnSocket!!照着字面上的意思是 ,在这个函数中accept客户端的TCP连接,忽略掉SIGFIFO,不阻塞,然后调用GenericMediaServer::createNewClientConnection函数创建一个RTSPClientConnection实例,该实例表示一个与客户端的RTSP连接。
GenericMediaServer::ClientConnection*RTSPServerSupportingHTTPStreaming::createNewClientConnection(int clientSocket, struct sockaddr_in clientAddr){return new RTSPClientConnectionSupportingHTTPStreaming(*this, clientSocket, clientAddr);}
GenericMediaServer::ClientConnection*RTSPServerSupportingHTTPStreaming::createNewClientConnection(int clientSocket, struct sockaddr_in clientAddr) {return new RTSPClientConnectionSupportingHTTPStreaming(*this, clientSocket, clientAddr);}
最后调用到

其中fOurServer.fClientConnections->Add((char const*)this, this);是把这个RTSPClientConnection实例添加到RTSPServer的列表中,在通过envir().taskScheduler().setBackgroundHandling(fOurSocket, SOCKET_READABLE|SOCKET_EXCEPTION, incomingRequestHandler, this);调用incomingRequestHandler将自己添加到RTSPServer的连接列表中,然后将客户端socket添加到SOCKET SET中,并且设置相应的回调处理函数incomingRequestHandler,然后就开始等待客户端发送命令。服务器端收到客户端的命令即回调GenericMediaServer::ClientConnection::incomingRequestHandler来处理。
GenericMediaServer::ClientConnection::incomingRequestHandler调用connection->incomingRequestHandler();
void GenericMediaServer::ClientConnection::incomingRequestHandler() 
{struct sockaddr_in dummy; // 'from' address, meaningless in this caseint bytesRead = readSocket(envir(), fOurSocket, &fRequestBuffer[fRequestBytesAlreadySeen], fRequestBufferBytesLeft, dummy);handleRequestBytes(bytesRead);}
在这里获取客户端传递过来的命令,存到fRequestBuffer数组中然后调用handleRequestBytes(bytesRead);进行解析,进入到 RTSPServer::RTSPClientConnection::handleRequestBytes中用

解析命令,得带cmdName,urlPreSuffix,urlSuffix,cseq,sessionIdStr和contentLength

copy大神的解释:在RTSPClientConnection::incomingRequestHandler函数中又调用 RTSPClientConnection::incomingRequestHandler1函数,在这个函数中,从客户端socket中读取数据,读取的数据存储在RTSPClientConnection::fRequestBuffer这个数组中,然后调RTSPClientConnection::handleRequestBytes函数处理刚才读到的数据。handleRequestBytes函数的内容(比较多)主要是分析读取的数据,提取出命令名等数据,然后根据不同的命令调用不同的函数去处理,将处理后的结果保存在fResponseBuffer这个数组中,然后发送给客户端。在此,我们假设客户端跳过OPTINS命令,直接发送DESCRIBE命令请求建立连接,则在handleRequestBytes函数中会调用RTSPClientConnection::handleCmd_DESCRIBE函数来处理,下面来看一下handleCmd_DESCRIBE函数。先说一下urlPreSuffix和urlSuffix吧,假设客户端请求媒体资源的RTSP地址是rtsp://127.0.0.1:8554/test1/test2/test.264,urlPreSuffix表示的是ip:port之后(不含紧跟的“/”)到最后一个“/”之前的部分,即test1/test2,urlSuffix表示的是最后一个“/”之后(不含紧跟的“/”)的内容,即test.264。
  关于testOnDemandRTSPServer.cpp就先介绍到这里,后面详细分析RTSP客户端与RTSPServer建立RTSP连接的详细过程。
接下来我们分析一下Live555中建立RTSP连接的详细过程,首先我们需要简单了解一下RTSP协议建立连接的过程:
  1.(可选) 
       RTSP客户端  —>   RTSP服务器端     OPTIONS命令             询问服务器端有哪些方法可使用
       RTSP服务器端 —>  RTSP客户端       回复OPTIONS命令        回复客户端服务器支持的方法
  2. (可选)
   RTSP客户端  —>  RTSP服务器端      DESCRIBE命令    请求对某个媒体资源(Live555中用ServerMediaSession表示)的描述信息
       RTSP服务器端 —>  RTSP客户端   回复DESCRIBE命令  回复客户端某个媒体资源的描述信息(即SDP)
      3. (必选)
       RTSP客户端  —>   RTSP服务器端  SETUP命令      请求建立对某个媒体资源的连接
   RTSP服务器端 —>  RTSP客户端   回复SETUP命令    回复建立连接的结果
      4. (必选)
       RTSP客户端  —>   RTSP服务器端  PLAY命令      请求播放媒体资源
   RTSP服务器端  —>  RTSP客户端  回复PLAY命令    回复播放的结果
      --------------------RTSP服务器端发送RTP包(封装了数据)给RTSP客户端-----------------
看到这里是不是感觉思路顺畅了很多,结合之前推荐看的http://www.cnblogs.com/qq78292959/archive/2010/08/12/2077039.html 中的rtsp请求协议就能够明白,现在感觉挺简单。接下来进入命令处理,假设直接跳过OPTINS命令直接发送DESCRIBE命令则进入handleCmd_DESCRIBE(urlPreSuffix, urlSuffix, (char const*)fRequestBuffer);这个处理函数,

在handleCmd_DESCRIBE函数中,主要调用了ServerMediaSession::generateSDPDescription函数产生SDP信息,ServerMediaSession的SDP信息由每个ServerMediaSubsession的SDP信息构成,然后将产生的SDP回复给客户端。
我们就来看一下generateSDPDescription函数。

generateSDPDescription这个程序太多截取重要的一部分,其实就是偏移偏移偏移,取值。看一下服务器端是如何获得SDP信息的:

我们再转到OnDemandServerMediaSubsession::setSDPLinesFromRTPSink函数,在这个函数中,我们通过创建的FramedSource对象和RTPSink对象将文件播放一段以便产生出sdp信息。在此,我要看一下Live555 RTSPServer播放媒体资源的一个大体流程:RTSPServer使用RTPSink获得和保存RTP包,RTPSink不断地向FramedSource请求帧数据,FramedSource取得帧数据后就调用回调函数把数据给RTPSink处理,RTPSink在回调函数中将数据发送给客户端(也可以保存在本地存成文件,即录像的功能)

在setSDPLinesFromRTPSink函数中通过RTPSink对象获得各种信息,最复杂的是获取auxSDPLine的过程,auxSDPLine中取得source 中保存的PPS,SPS等形成a=fmpt行。H264VideoFileServerMediaSubsession重写了getAuxSDPLine()!如果不重写,则说明auxSDPLine已经在前面分析文件时获得了,那么既然重写,就说明前面没有获取到,只能在这个函数中重写。这个函数在H264VideoFileServerMediaSubsession类中被重写了,由于我们现在分析的媒体资源是.264文件,所以我们来看一下H264VideoFileServerMediaSubsession::getAuxSDPLine函数,这个函数在首次调用时,会通过 RTPSink 启动对流媒体文件帧数据的读取,即调用 RTPSink 的 startPlaying() 函数。:

注释里面解释得很清楚,H264不能在文件头中取得PPS/SPS,必须在播放一下后(当然,它是一个原始流文件,没有文件头)才行。也就是说不能从rtpSink中取得了。为了保证在函数退出前能取得AuxSDP,把大循环搬到这里来了。afterPlayingDummy()是在播放结束也就是取得aux sdp之后执行。在大循环之前的checkForAuxSDPLine()做了什么呢?

它检查是否已取得Aux sdp,如果取得了,设置结束标志,直接返回。如果没有,就检查是否sink中已取得了aux sdp,如果是,也设置结束标志,返回。如果还没有取得,则把这个检查函数做为delay task加入计划任务中。每100毫秒检查一次,每检查一次主要就是调用一次fDummyRTPSink->auxSDPLine()。大循环在检测到fDoneFlag改变时停止,此时已取得了aux sdp。但是如果直到文件结束也没有得到aux sdp,则afterPlayingDummy()被执行,在其中停止掉这个大循环。然后在父Subsession类中关掉这些临时的source和sink。在直正播放时重新创建。

上面检查发现没有分析出sdp信息后,调用H264VideoRTPSink::auxSDPLine函数再次试图分析出sdp信息,看看auxSDPLine函数:

至此,RTSPServer成功地处理了客户端发送来的DESCRIBE命令,将SDP信息回复客户端。
OnDemandServerMediaSubsession::setSDPLinesFromRTPSink() 利用所有的信息,生成最终的子会话 SDP 行。最终的子会话 SDP 行通常像下面这样:
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=42802A;sprop-parameter-sets=Z0KAKtoBEA8eXlIKDAoNoUJq,aM4G4g==
a=control:track1
DESCRIBE DONE