RTSP服务运作
基础基本搞明白了,那么RTSP,RTP等这些协议又是如何利用这些基础机制运作的呢?
首先来看RTSP.
RTSP首先需建立TCP侦听socket。可见于此函数:
- DynamicRTSPServer*DynamicRTSPServer::createNew(UsageEnvironment&env,PortourPort,
- UserAuthenticationDatabase*authDatabase,
- unsignedreclamationTestSeconds){
- intourSocket=setUpOurSocket(env,ourPort);//建立TCPsocket
- if(ourSocket==-1)
- returnNULL;
- returnnewDynamicRTSPServer(env,ourSocket,ourPort,authDatabase,
- reclamationTestSeconds);
- }
要帧听客户端的连接,就需要利用任务调度机制了,所以需添加一个socket handler。可见于此函数:
- RTSPServer::RTSPServer(UsageEnvironment&env,
- intourSocket,
- PortourPort,
- UserAuthenticationDatabase*authDatabase,
- unsignedreclamationTestSeconds):
- Medium(env),
- fRTSPServerSocket(ourSocket),
- fRTSPServerPort(ourPort),
- fHTTPServerSocket(-1),
- fHTTPServerPort(0),
- fClientSessionsForHTTPTunneling(NULL),
- fAuthDB(authDatabase),
- fReclamationTestSeconds(reclamationTestSeconds),
- fServerMediaSessions(HashTable::create(STRING_HASH_KEYS))
- {
- #ifdefUSE_SIGNALS
- //IgnoretheSIGPIPEsignal,sothatclientsonthesamehostthatarekilled
- //don'talsokillus:
- signal(SIGPIPE,SIG_IGN);
- #endif
- //Arrangetohandleconnectionsfromothers:
- env.taskScheduler().turnOnBackgroundReadHandling(
- fRTSPServerSocket,
- (TaskScheduler::BackgroundHandlerProc*)&incomingConnectionHandlerRTSP,
- this);
- }
当收到客户的连接时需保存下代表客户端的新socket,以后用这个socket与这个客户通讯。每个客户将来会对应一个rtp会话,而且各客户的RTSP请求只控制自己的rtp会话,那么最好建立一个会话类,代表各客户的rtsp会话。于是类RTSPServer::RTSPClientSession产生,它保存的代表客户的socket。下为RTSPClientSession的创建过程
- voidRTSPServer::incomingConnectionHandler(intserverSocket)
- {
- structsockaddr_inclientAddr;
- SOCKLEN_TclientAddrLen=sizeofclientAddr;
- //接受连接
- intclientSocket=accept(serverSocket,
- (structsockaddr*)&clientAddr,
- &clientAddrLen);
- if(clientSocket<0){
- interr=envir().getErrno();
- if(err!=EWOULDBLOCK){
- envir().setResultErrMsg("accept()failed:");
- }
- return;
- }
- //设置socket的参数
- makeSocketNonBlocking(clientSocket);
- increaseSendBufferTo(envir(),clientSocket,50*1024);
- #ifdefDEBUG
- envir()<<"accept()edconnectionfrom"<<our_inet_ntoa(clientAddr.sin_addr)<<"\n";
- #endif
- //产生一个sessonid
- //CreateanewobjectforthisRTSPsession.
- //(Choosearandom32-bitintegerforthesessionid(itwillbeencodedasa8-digithexnumber).Wedon'tbothercheckingfor
- //acollision;theprobabilityoftwoconcurrentsessionsgettingthesamesessionidisverylow.)
- //(Wedo,however,avoidchoosingsessionid0,becausethathasaspecialuse(by"OnDemandServerMediaSubsession").)
- unsignedsessionId;
- do{
- sessionId=(unsigned)our_random();
- }while(sessionId==0);
- //创建RTSPClientSession,注意传入的参数
- (void)createNewClientSession(sessionId,clientSocket,clientAddr);
- }
RTSPClientSession要提供什么功能呢?可以想象:需要监听客户端的rtsp请求并回应它,需要在DESCRIBE请求中返回所请求的流的信息,需要在SETUP请求中建立起RTP会话,需要在TEARDOWN请求中关闭RTP会话,等等...
RTSPClientSession要侦听客户端的请求,就需把自己的socket handler加入计划任务。证据如下:
- RTSPServer::RTSPClientSession::RTSPClientSession(
- RTSPServer&ourServer,
- unsignedsessionId,
- intclientSocket,
- structsockaddr_inclientAddr):
- fOurServer(ourServer),
- fOurSessionId(sessionId),
- fOurServerMediaSession(NULL),
- fClientInputSocket(clientSocket),
- fClientOutputSocket(clientSocket),
- fClientAddr(clientAddr),
- fSessionCookie(NULL),
- fLivenessCheckTask(NULL),
- fIsMulticast(False),
- fSessionIsActive(True),
- fStreamAfterSETUP(False),
- fTCPStreamIdCount(0),
- fNumStreamStates(0),
- fStreamStates(NULL),
- fRecursionCount(0)
- {
- //Arrangetohandleincomingrequests:
- resetRequestBuffer();
- envir().taskScheduler().turnOnBackgroundReadHandling(fClientInputSocket,
- (TaskScheduler::BackgroundHandlerProc*)&incomingRequestHandler,
- this);
- noteLiveness();
- }
下面重点讲一下下RTSPClientSession响应DESCRIBE请求的过程:
- voidRTSPServer::RTSPClientSession::handleCmd_DESCRIBE(
- charconst*cseq,
- charconst*urlPreSuffix,
- charconst*urlSuffix,
- charconst*fullRequestStr)
- {
- char*sdpDescription=NULL;
- char*rtspURL=NULL;
- do{
- //整理一下下RTSP地址
- charurlTotalSuffix[RTSP_PARAM_STRING_MAX];
- if(strlen(urlPreSuffix)+strlen(urlSuffix)+2
- >sizeofurlTotalSuffix){
- handleCmd_bad(cseq);
- break;
- }
- urlTotalSuffix[0]='\0';
- if(urlPreSuffix[0]!='\0'){
- strcat(urlTotalSuffix,urlPreSuffix);
- strcat(urlTotalSuffix,"/");
- }
- strcat(urlTotalSuffix,urlSuffix);
- //验证帐户和密码
- if(!authenticationOK("DESCRIBE",cseq,urlTotalSuffix,fullRequestStr))
- break;
- //Weshouldreallycheckthattherequestcontainsan"Accept:"#####
- //for"application/sdp",becausethat'swhatwe'resendingback#####
- //Beginbylookingupthe"ServerMediaSession"objectforthespecified"urlTotalSuffix":
- //跟据流的名字查找ServerMediaSession,如果找不到,会创建一个。每个ServerMediaSession中至少要包含一个
- //ServerMediaSubsession。一个ServerMediaSession对应一个媒体,可以认为是Server上的一个文件,或一个实时获取设备。其包含的每个ServerMediaSubSession代表媒体中的一个Track。所以一个ServerMediaSession对应一个媒体,如果客户请求的媒体名相同,就使用已存在的ServerMediaSession,如果不同,就创建一个新的。一个流对应一个StreamState,StreamState与ServerMediaSubsession相关,但代表的是动态的,而ServerMediaSubsession代表静态的。
- ServerMediaSession*session=fOurServer.lookupServerMediaSession(urlTotalSuffix);
- if(session==NULL){
- handleCmd_notFound(cseq);
- break;
- }
- //Then,assembleaSDPdescriptionforthissession:
- //获取SDP字符串,在函数内会依次获取每个ServerMediaSubSession的字符串然连接起来。
- sdpDescription=session->generateSDPDescription();
- if(sdpDescription==NULL){
- //Thisusuallymeansthatafilenamethatwasspecifiedfora
- //"ServerMediaSubsession"doesnotexist.
- snprintf((char*)fResponseBuffer,sizeoffResponseBuffer,
- "RTSP/1.0404FileNotFound,OrInIncorrectFormat\r\n"
- "CSeq:%s\r\n"
- "%s\r\n",cseq,dateHeader());
- break;
- }
- unsignedsdpDescriptionSize=strlen(sdpDescription);
- //Also,generateourRTSPURL,forthe"Content-Base:"header
- //(whichisnecessarytoensurethatthecorrectURLgetsusedin
- //subsequent"SETUP"requests).
- rtspURL=fOurServer.rtspURL(session,fClientInputSocket);
- //形成响应DESCRIBE请求的RTSP字符串。
- snprintf((char*)fResponseBuffer,sizeoffResponseBuffer,
- "RTSP/1.0200OK\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",cseq,dateHeader(),rtspURL,sdpDescriptionSize,
- sdpDescription);
- }while(0);
- delete[]sdpDescription;
- delete[]rtspURL;
- //返回后会被立即发送(没有把socketwrite操作放入计划任务中)。
- }
fOurServer.lookupServerMediaSession(urlTotalSuffix)中会在找不到同名ServerMediaSession时新建一个,代表一个RTP流的ServerMediaSession们是被RTSPServer管理的,而不是被RTSPClientSession拥有。为什么呢?因为ServerMediaSession代表的是一个静态的流,也就是可以从它里面获取一个流的各种信息,但不能获取传输状态。不同客户可能连接到同一个流,所以ServerMediaSession应被RTSPServer所拥有。创建一个ServerMediaSession过程值得一观:
- staticServerMediaSession*createNewSMS(UsageEnvironment&env,charconst*fileName,FILE*/*fid*/)
- {
- //Usethefilenameextensiontodeterminethetypeof"ServerMediaSession":
- charconst*extension=strrchr(fileName,'.');
- if(extension==NULL)
- returnNULL;
- ServerMediaSession*sms=NULL;
- BooleanconstreuseSource=False;
- if(strcmp(extension,".aac")==0){
- //AssumedtobeanAACAudio(ADTSformat)file:
- NEW_SMS("AACAudio");
- sms->addSubsession(
- ADTSAudioFileServerMediaSubsession::createNew(env,fileName,
- reuseSource));
- }elseif(strcmp(extension,".amr")==0){
- //AssumedtobeanAMRAudiofile:
- NEW_SMS("AMRAudio");
- sms->addSubsession(
- AMRAudioFileServerMediaSubsession::createNew(env,fileName,
- reuseSource));
- }elseif(strcmp(extension,".ac3")==0){
- //AssumedtobeanAC-3Audiofile:
- NEW_SMS("AC-3Audio");
- sms->addSubsession(
- AC3AudioFileServerMediaSubsession::createNew(env,fileName,
- reuseSource));
- }elseif(strcmp(extension,".m4e")==0){
- //AssumedtobeaMPEG-4VideoElementaryStreamfile:
- NEW_SMS("MPEG-4Video");
- sms->addSubsession(
- MPEG4VideoFileServerMediaSubsession::createNew(env,fileName,
- reuseSource));
- }elseif(strcmp(extension,".264")==0){
- //AssumedtobeaH.264VideoElementaryStreamfile:
- NEW_SMS("H.264Video");
- OutPacketBuffer::maxSize=100000;//allowforsomepossiblylargeH.264frames
- sms->addSubsession(
- H264VideoFileServerMediaSubsession::createNew(env,fileName,
- reuseSource));
- }elseif(strcmp(extension,".mp3")==0){
- //AssumedtobeaMPEG-1or2Audiofile:
- NEW_SMS("MPEG-1or2Audio");
- //Tostreamusing'ADUs'ratherthanrawMP3frames,uncommentthefollowing:
- //#defineSTREAM_USING_ADUS1
- //ToalsoreorderADUsbeforestreaming,uncommentthefollowing:
- //#defineINTERLEAVE_ADUS1
- //(FormoreinformationaboutADUsandinterleaving,
- //see<http://www.live555.com/rtp-mp3/>)
- BooleanuseADUs=False;
- Interleaving*interleaving=NULL;
- #ifdefSTREAM_USING_ADUS
- useADUs=True;
- #ifdefINTERLEAVE_ADUS
- unsignedcharinterleaveCycle[]={0,2,1,3};//orchooseyourown...
- unsignedconstinterleaveCycleSize
- =(sizeofinterleaveCycle)/(sizeof(unsignedchar));
- interleaving=newInterleaving(interleaveCycleSize,interleaveCycle);
- #endif
- #endif
- sms->addSubsession(
- MP3AudioFileServerMediaSubsession::createNew(env,fileName,
- reuseSource,useADUs,interleaving));
- }elseif(strcmp(extension,".mpg")==0){
- //AssumedtobeaMPEG-1or2ProgramStream(audio+video)file:
- NEW_SMS("MPEG-1or2ProgramStream");
- MPEG1or2FileServerDemux*demux=MPEG1or2FileServerDemux::createNew(env,
- fileName,reuseSource);
- sms->addSubsession(demux->newVideoServerMediaSubsession());
- sms->addSubsession(demux->newAudioServerMediaSubsession());
- }elseif(strcmp(extension,".ts")==0){
- //AssumedtobeaMPEGTransportStreamfile:
- //Useanindexfilenamethat'sthesameastheTSfilename,exceptwith".tsx":
- unsignedindexFileNameLen=strlen(fileName)+2;//allowfortrailing"x\0"
- char*indexFileName=newchar[indexFileNameLen];
- sprintf(indexFileName,"%sx",fileName);
- NEW_SMS("MPEGTransportStream");
- sms->addSubsession(
- MPEG2TransportFileServerMediaSubsession::createNew(env,
- fileName,indexFileName,reuseSource));
- delete[]indexFileName;
- }elseif(strcmp(extension,".wav")==0){
- //AssumedtobeaWAVAudiofile:
- NEW_SMS("WAVAudioStream");
- //Toconvert16-bitPCMdatato8-bitu-law,priortostreaming,
- //changethefollowingtoTrue:
- BooleanconvertToULaw=False;
- sms->addSubsession(
- WAVAudioFileServerMediaSubsession::createNew(env,fileName,
- reuseSource,convertToULaw));
- }elseif(strcmp(extension,".dv")==0){
- //AssumedtobeaDVVideofile
- //First,makesurethattheRTPSinks'bufferswillbelargeenoughtohandlethehugesizeofDVframes(asbigas288000).
- OutPacketBuffer::maxSize=300000;
- NEW_SMS("DVVideo");
- sms->addSubsession(
- DVVideoFileServerMediaSubsession::createNew(env,fileName,
- reuseSource));
- }elseif(strcmp(extension,".mkv")==0){
- //AssumedtobeaMatroskafile
- NEW_SMS("Matroskavideo+audio+(optional)subtitles");
- //CreateaMatroskafileserverdemultiplexorforthespecifiedfile.(Weentertheeventlooptowaitforthistocomplete.)
- newMatroskaDemuxWatchVariable=0;
- MatroskaFileServerDemux::createNew(env,fileName,
- onMatroskaDemuxCreation,NULL);
- env.taskScheduler().doEventLoop(&newMatroskaDemuxWatchVariable);
- ServerMediaSubsession*smss;
- while((smss=demux->newServerMediaSubsession())!=NULL){
- sms->addSubsession(smss);
- }
- }
- returnsms;
- }
可以看到NEW_SMS("AMR Audio")会创建新的ServerMediaSession,之后马上调用sms->addSubsession()为这个ServerMediaSession添加一个 ServerMediaSubSession 。看起来ServerMediaSession应该可以添加多个ServerMediaSubSession,但这里并没有这样做。如果可以添加多个 ServerMediaSubsession 那么ServerMediaSession与流名字所指定与文件是没有关系的,也就是说它不会操作文件,而文件的操作是放在 ServerMediaSubsession中的。具体应改是在ServerMediaSubsession的sdpLines()函数中打开。
原文地址:http://blog.csdn.net/niu_gao/article/details/6911130
live555源代码(VC6):http://download.csdn.net/detail/leixiaohua1020/6374387
相关推荐
live555-rtsp-live-v4l2-master(ARM)
live555_rtsp_live_v4l2-master(ububtu平台)
gst-rtsp-server编译测试 分类: 流媒体 Linux c/c++ 2013-01-24 19:28 2550人阅读 评论(1) 收藏 举报 gst-rtsp gstreamer v4l2 最近在做全志A80平台的4K相机(RK3688的八核处理器只能够支持2K相机),调好之后就...
rtsp-live555 介绍 这是一个包装程序,可让您从IPC获取RTSP流并以FLV流导出。 安装 NPM npm install rtsp-live555安装最新的稳定版本 npm install godka/node-rtsp-live555从github安装最新版本 从Github克隆最新...
ffmpeg直接采集屏幕;VLC的x264库进行压缩编码;live555作为服务器,侦听554端口,当有连接时,开始录制屏幕并发送。
1、使用FFMPEG进行编码(ffmpeg版本为ffmpeg-5.0) 2、使用LIVE555建立RTSP服务器(live555版本为live.2022.02.07) 实现实时桌面视频流,Visual Studio为Visual Studio 2022版本
node-rtsp-stream, 通过 jsmpeg ( https ) 将任何RTSP流和输出流到 web socket node-rtsp-stream将任何RTSP流和输出输出到 web socket,以使用 jsmpeg 。...使用方法:$ npm install node-rtsp-stream
rtsp播放器,同时支持OSD显示,最大支持64个画面同时显示
live555 c++版本rtsp服务器,从开源网站上down的源码文件,自己编译通过。可以直接运行
安卓直播推流 rtmp方式,android studio3.5以上版本使用,服务器需要自己搭建
EasyPlayer-RTSP- iOS源码,喜欢的欢迎下载。EasyPlayer RTSP是一款精炼、高效、稳定的RTSP流媒体播放器,视频支持H.264/H.265,音频支持G.711/G.726/AAC,支持RTP over UDP/TCP两种模式!
基于live555库的mjpeg流传输c++代码(多播方式) 博客地址:https://blog.csdn.net/Di_Wong/article/details/107284635 【使用说明】 1、在http://www.live555.com/ 官网下载live555库 2、将该文件下内容替换到live...
EasyPlayer-RTSP-Win-master.rar的源码,可以看下直播方案,还能客户端播放rtsp流进行测试,支持多个画面
EasyPlayer-RTSP工具,支持大华、海康等nvr设备的直播查看。
EasyPlayer-RTSP-Android-master源码,EasyPlayer是一款精炼、高效、稳定的流媒体播放器,分为RTSP版和Pro版本,支持各种各样的流媒体音视频播放!
happytime rtsp h264 push example.
支持跨平台编译rtsp服务器自己需要跨平台编译dlib,live555,ffmpeg,ffmepg-live555编解码发布直播,不依赖第三放库(ngix等)做直播。支持抓取本地视频,视频摄像头,桌面,可用vlc等播放器观看测试。
EasyPlayer-RTSP-Win-v3.0.19.0415;EasyPlayer-RTSP-Win-v3.0.19.0415
直接从IP摄像头获取数据(H265数据需稍微改动),然后作为服务器转发,支持多个摄像头同时连接。摄像头的地址和用户名密码在代码中更改即可。下载后可留言交流。