标签:
再换一个应用场景,当我们的全部IP摄像机都部署在一个没有有线网络的环境里面,全部的流媒体数据都要通过3G/4G网络公布出去。那么就必须有这么一个服务单元,可以通过先拉后推的方式,将内网的流媒体数据,推送并公布到外网的流媒体server上去:
在实现先拉后推式转发之前,我们先熟悉下live555的运转模式,live555主要运转的是一个source与sink的循环,sink想要数据,就调用source的getNextFrame,source获取到数据后,再调用afterGettingFrame回调,返回给sink数据,sink处理完后,再调用source的getNextFrame,如此循环。那么我们这里要实现从摄像机获取数据,那么我们的source就是一个RTPSource,我们又须要将数据以RTP的方式发送给流媒体server,那么我们的sink就是一个RTPSink,我们须要打通的就是一个RTPSource到一个RTPSink的过程。
ok,live555已经帮我们实现了大部分的功能,我们仅仅须要将已有的部分组合起来即可了,这里我们主要用到的就是live555的ProxyServerMediaSession类和DarwinInjector类,我们用ProxyServerMediaSession从摄像机获取流媒体,再用DarwinInjector推送到Darwin Streaming Server,主要实现流程在以下代码凝视中:
/* 功能描写叙述: 一个简单的RTSP/RTP对接功能,从RTSP源通过主要的RTSPClient流程,获取到RTP流媒体数据 再通过标准RTSP推送过程(ANNOUNCE/SETUP/PLAY),将获取到RTP数据推送给Darwin流媒体 分发server。 此Demo仅仅演示了单个源的转换、推送功能! Author: sunpany@qq.com 时间: 2014/06/25 */ #include "liveMedia.hh" #include "BasicUsageEnvironment.hh" #include "RTSPCommon.hh" char* server = "www.easydss.com";//RTSP流媒体转发server地址,<请改动为自己搭建的流媒体server地址> int port = 8554; //RTSP流媒体转发server端口,<请改动为自己搭建的流媒体server端口> char* streamName = "live.sdp"; //流名称,推送到Darwin的流名称必须以.sdp结尾 char* src = "rtsp://218.204.223.237:554/live/1/66251FC11353191F/e7ooqwcfbqjoo80j.sdp";//源端URL UsageEnvironment* env = NULL; //live555 global environment TaskScheduler* scheduler = NULL; char eventLoopWatchVariable = 0; DarwinInjector* injector = NULL; //DarwinInjector FramedSource* vSource = NULL; //Video Source FramedSource* aSource = NULL; //Audio Source RTPSink* vSink = NULL; //Video Sink RTPSink* aSink = NULL; //Audio Sink Groupsock* rtpGroupsockVideo = NULL;//Video Socket Groupsock* rtpGroupsockAudio = NULL;//Audio Socket ProxyServerMediaSession* sms = NULL;//proxy session // 流转发过程 bool RedirectStream(char const* ip, unsigned port); // 流转发结束后处理回调 void afterPlaying(void* clientData); // 实现等待功能 void sleep(void* clientSession) { char* var = (char*)clientSession; *var = ~0; } // Main int main(int argc, char** argv) { // 初始化主要的live555环境 scheduler = BasicTaskScheduler::createNew(); env = BasicUsageEnvironment::createNew(*scheduler); // 新建转发SESSION sms = ProxyServerMediaSession::createNew(*env, NULL, src); // 循环等待转接程序与源端连接成功 while(sms->numSubsessions() <= 0 ) { char fWatchVariable = 0; env->taskScheduler().scheduleDelayedTask(2*1000000,(TaskFunc*)sleep,&fWatchVariable); env->taskScheduler().doEventLoop(&fWatchVariable); } // 開始转发流程 RedirectStream(server, port); env->taskScheduler().doEventLoop(&eventLoopWatchVariable); return 0; } // 推送视频到流媒体server bool RedirectStream(char const* ip, unsigned port) { // 转发SESSION必须保证存在 if( sms == NULL) return false; // 推断sms是否已经连接上源端 if( sms->numSubsessions() <= 0 ) { *env << "sms numSubsessions() == 0\n"; return false; } // DarwinInjector主要用于向Darwin推送RTSP/RTP数据 injector = DarwinInjector::createNew(*env); struct in_addr dummyDestAddress; dummyDestAddress.s_addr = 0; rtpGroupsockVideo = new Groupsock(*env, dummyDestAddress, 0, 0); struct in_addr dummyDestAddressAudio; dummyDestAddressAudio.s_addr = 0; rtpGroupsockAudio = new Groupsock(*env, dummyDestAddressAudio, 0, 0); ServerMediaSubsession* subsession = NULL; ServerMediaSubsessionIterator iter(*sms); while ((subsession = iter.next()) != NULL) { ProxyServerMediaSubsession* proxySubsession = (ProxyServerMediaSubsession*)subsession; unsigned streamBitrate; FramedSource* source = proxySubsession->createNewStreamSource(1, streamBitrate); if (strcmp(proxySubsession->mediumName(), "video") == 0) { // 用ProxyServerMediaSubsession建立Video的RTPSource vSource = source; unsigned char rtpPayloadType = proxySubsession->rtpPayloadFormat(); // 建立Video的RTPSink vSink = proxySubsession->createNewRTPSink(rtpGroupsockVideo,rtpPayloadType,source); // 将Video的RTPSink赋值给DarwinInjector,推送视频RTP给Darwin injector->addStream(vSink,NULL); } else { // 用ProxyServerMediaSubsession建立Audio的RTPSource aSource = source; unsigned char rtpPayloadType = proxySubsession->rtpPayloadFormat(); // 建立Audio的RTPSink aSink = proxySubsession->createNewRTPSink(rtpGroupsockVideo,rtpPayloadType,source); // 将Audio的RTPSink赋值给DarwinInjector,推送音频RTP给Darwin injector->addStream(aSink,NULL); } } // RTSP ANNOUNCE/SETUP/PLAY推送过程 if (!injector->setDestination(ip, streamName, "live555", "LIVE555", port)) { *env << "injector->setDestination() failed: " << env->getResultMsg() << "\n"; return false; } // 開始转发视频RTP数据 if((vSink != NULL) && (vSource != NULL)) vSink->startPlaying(*vSource,afterPlaying,vSink); // 開始转发音频RTP数据 if((aSink != NULL) && (aSource != NULL)) aSink->startPlaying(*aSource,afterPlaying,aSink); *env << "\nBeginning to get camera video...\n"; return true; } // 停止推送,释放全部变量 void afterPlaying(void* clientData) { if( clientData == NULL ) return; if(vSink != NULL) vSink->stopPlaying(); if(aSink != NULL) aSink->stopPlaying(); if(injector != NULL) { Medium::close(*env, injector->name()); injector == NULL; } ServerMediaSubsession* subsession = NULL; ServerMediaSubsessionIterator iter(*sms); while ((subsession = iter.next()) != NULL) { ProxyServerMediaSubsession* proxySubsession = (ProxyServerMediaSubsession*)subsession; if (strcmp(proxySubsession->mediumName(), "video") == 0) proxySubsession->closeStreamSource(vSource); else proxySubsession->closeStreamSource(aSource); } if(vSink != NULL) Medium::close(vSink); if(aSink != NULL) Medium::close(aSink); if(vSource != NULL) Medium::close(vSource); if(aSource != NULL) Medium::close(aSource); delete rtpGroupsockVideo; rtpGroupsockVideo = NULL; delete rtpGroupsockAudio; rtpGroupsockAudio = NULL; }
程序还有很多要完好的地方,仅仅是一个简单的实现。
源代码下载:
http://pan.baidu.com/s/1sj6Ue4l
用live555将内网摄像机视频推送到外网server,附源代码
标签:
原文地址:http://www.cnblogs.com/gcczhongduan/p/4238366.html