·您当前的位置:首页 > 技术教程 > Rtsp技术 >

[live555]live555将内网摄像机视频推送到外网服务器

时间:2015-06-16 13:57博客园
[live555]live555将内网摄像机视频推送到外网服务器

最近很多人问,如何将内网的摄像机流媒体数据发布到公网,如果用公网与局域网间的端口映射方式太过麻烦,一个摄像机要做一组映射,而且不是每一个局域网都是有固定ip地址,即使外网主机配置好了每一个摄像机的映射地址,也有可能会因为宽带公网ip地址变动而导致配置无效。

再换一个应用场景,当我们的所有IP摄像机都部署在一个没有有线网络的环境里面,所有的流媒体数据都要通过3G/4G网络发布出去。那么就必须有这么一个服务单元,能够通过先拉后推的方式,将内网的流媒体数据,推送并发布到外网的流媒体服务器上去:

在 实现先拉后推式转发之前,我们先熟悉下live555的运转模式,live555主要运转的是一个source与sink的循环,sink想要数据,就调 用source的getNextFrame,source获取到数据后,再调用afterGettingFrame回调,返回给sink数据,sink处 理完后,再调用source的getNextFrame,如此循环。那么我们这里要实现从摄像机获取数据,那么我们的source就是一个 RTPSource,我们又需要将数据以RTP的方式发送给流媒体服务器,那么我们的sink就是一个RTPSink,我们需要打通的就是一个 RTPSource到一个RTPSink的过程。

ok,live555 已经帮我们实现了大部分的功能,我们只需要将已有的部分组合起来就行了,这里我们主要用到的就是live555的 ProxyServerMediaSession类和DarwinInjector类,我们用ProxyServerMediaSession从摄像机获 取流媒体,再用DarwinInjector推送到Darwin Streaming Server,主要实现流程在下面代码注释中:

  1. /* 
  2.     功能描述:   一个简单的RTSP/RTP对接功能,从RTSP源通过基本的RTSPClient流程,获取到RTP流媒体数据 
  3.                 再通过标准RTSP推送过程(ANNOUNCE/SETUP/PLAY),将获取到RTP数据推送给Darwin流媒体 
  4.                 分发服务器。 
  5.                 此Demo只演示了单个源的转换、推送功能! 
  6.                  
  7.     Author: sunpany@qq.com 
  8.     时间:     2014/06/25 
  9. */ 
  10.  
  11. #include "liveMedia.hh" 
  12. #include "BasicUsageEnvironment.hh" 
  13. #include "RTSPCommon.hh" 
  14.  
  15. char* server = "www.easydss.com";//RTSP流媒体转发服务器地址,<请修改为自己搭建的流媒体服务器地址> 
  16. int port = 8554;                //RTSP流媒体转发服务器端口,<请修改为自己搭建的流媒体服务器端口> 
  17. char* streamName = "live.sdp";      //流名称,推送到Darwin的流名称必须以.sdp结尾 
  18. char* src = "rtsp://218.204.223.237:554/live/1/66251FC11353191F/e7ooqwcfbqjoo80j.sdp";//源端URL 
  19.  
  20. UsageEnvironment* env = NULL;       //live555 global environment 
  21. TaskScheduler* scheduler = NULL
  22. char eventLoopWatchVariable = 0
  23.  
  24. DarwinInjector* injector = NULL;    //DarwinInjector 
  25. FramedSource* vSource = NULL;       //Video Source 
  26. FramedSource* aSource = NULL;       //Audio Source 
  27.  
  28. RTPSink* vSink = NULL;              //Video Sink 
  29. RTPSink* aSink = NULL;              //Audio Sink 
  30.  
  31. Groupsock* rtpGroupsockVideo = NULL;//Video Socket 
  32. Groupsock* rtpGroupsockAudio = NULL;//Audio Socket 
  33.  
  34. ProxyServerMediaSession* sms = NULL;//proxy session 
  35.  
  36. // 流转发过程 
  37. bool RedirectStream(char const* ip, unsigned port); 
  38.  
  39. // 流转发结束后处理回调 
  40. void afterPlaying(void* clientData); 
  41.  
  42. // 实现等待功能 
  43. void sleep(void* clientSession)   
  44.     char* var = (char*)clientSession; 
  45.     *var = ~0;  
  46. }   
  47.  
  48. // Main 
  49. int main(int argc, char** argv)  
  50.     // 初始化基本的live555环境 
  51.     scheduler = BasicTaskScheduler::createNew(); 
  52.     env = BasicUsageEnvironment::createNew(*scheduler); 
  53.  
  54.     // 新建转发SESSION 
  55.     sms = ProxyServerMediaSession::createNew(*env, NULL, src); 
  56.      
  57.     // 循环等待转接程序与源端连接成功 
  58.     while(sms->numSubsessions() <= 0 ) 
  59.     { 
  60.         char fWatchVariable  = 0;   
  61.         env->taskScheduler().scheduleDelayedTask(2*1000000,(TaskFunc*)sleep,&fWatchVariable);   
  62.         env->taskScheduler().doEventLoop(&fWatchVariable);   
  63.     } 
  64.      
  65.     // 开始转发流程 
  66.     RedirectStream(server, port); 
  67.  
  68.     env->taskScheduler().doEventLoop(&eventLoopWatchVariable); 
  69.  
  70.     return 0; 
  71.  
  72.  
  73. // 推送视频到流媒体服务器 
  74. bool RedirectStream(char const* ip, unsigned port) 
  75.     // 转发SESSION必须保证存在 
  76.     if( sms == NULL) return false; 
  77.  
  78.     // 判断sms是否已经连接上源端 
  79.     if( sms->numSubsessions() <= 0 )  
  80.     { 
  81.         *env << "sms numSubsessions() == 0\n"; 
  82.         return false; 
  83.     } 
  84.  
  85.     // DarwinInjector主要用于向Darwin推送RTSP/RTP数据 
  86.     injector = DarwinInjector::createNew(*env); 
  87.  
  88.     struct in_addr dummyDestAddress; 
  89.     dummyDestAddress.s_addr = 0
  90.     rtpGroupsockVideo = new Groupsock(*env, dummyDestAddress, 0, 0); 
  91.  
  92.     struct in_addr dummyDestAddressAudio; 
  93.     dummyDestAddressAudio.s_addr = 0
  94.     rtpGroupsockAudio = new Groupsock(*env, dummyDestAddressAudio, 0, 0); 
  95.  
  96.     ServerMediaSubsession* subsession = NULL
  97.     ServerMediaSubsessionIterator iter(*sms); 
  98.     while ((subsession = iter.next()) != NULL) 
  99.     { 
  100.         ProxyServerMediaSubsession* proxySubsession = (ProxyServerMediaSubsession*)subsession; 
  101.                          
  102.         unsigned streamBitrate; 
  103.         FramedSource* source = proxySubsession->createNewStreamSource(1, streamBitrate); 
  104.          
  105.         if (strcmp(proxySubsession->mediumName(), "video") == 0) 
  106.         { 
  107.             // 用ProxyServerMediaSubsession建立Video的RTPSource 
  108.             vSource = source
  109.             unsigned char rtpPayloadType = proxySubsession->rtpPayloadFormat(); 
  110.             // 建立Video的RTPSink 
  111.             vSink = proxySubsession->createNewRTPSink(rtpGroupsockVideo,rtpPayloadType,source); 
  112.             // 将Video的RTPSink赋值给DarwinInjector,推送视频RTP给Darwin 
  113.             injector->addStream(vSink,NULL); 
  114.         } 
  115.         else 
  116.         { 
  117.             // 用ProxyServerMediaSubsession建立Audio的RTPSource 
  118.             aSource = source
  119.             unsigned char rtpPayloadType = proxySubsession->rtpPayloadFormat(); 
  120.             // 建立Audio的RTPSink 
  121.             aSink = proxySubsession->createNewRTPSink(rtpGroupsockVideo,rtpPayloadType,source); 
  122.             // 将Audio的RTPSink赋值给DarwinInjector,推送音频RTP给Darwin 
  123.             injector->addStream(aSink,NULL); 
  124.         } 
  125.     } 
  126.  
  127.     // RTSP ANNOUNCE/SETUP/PLAY推送过程 
  128.     if (!injector->setDestination(ip, streamName, "live555", "LIVE555", port))  
  129.     { 
  130.         *env << "injector->setDestination() failed: " << env->getResultMsg() << "\n"; 
  131.         return false; 
  132.     } 
  133.  
  134.     // 开始转发视频RTP数据 
  135.     if((vSink != NULL) && (vSource != NULL)) 
  136.         vSink->startPlaying(*vSource,afterPlaying,vSink); 
  137.  
  138.     // 开始转发音频RTP数据 
  139.     if((aSink != NULL) && (aSource != NULL)) 
  140.         aSink->startPlaying(*aSource,afterPlaying,aSink); 
  141.  
  142.     *env << "\nBeginning to get camera video...\n"; 
  143.     return true; 
  144.  
  145.  
  146. // 停止推送,释放所有变量 
  147. void afterPlaying(void* clientData)  
  148.     if( clientData == NULL ) return; 
  149.  
  150.     if(vSink != NULL) 
  151.         vSink->stopPlaying(); 
  152.  
  153.     if(aSink != NULL) 
  154.         aSink->stopPlaying(); 
  155.  
  156.     if(injector != NULL) 
  157.     { 
  158.         Medium::close(*env, injector->name()); 
  159.         injector == NULL; 
  160.     } 
  161.  
  162.     ServerMediaSubsession* subsession = NULL
  163.     ServerMediaSubsessionIterator iter(*sms); 
  164.     while ((subsession = iter.next()) != NULL) 
  165.     { 
  166.         ProxyServerMediaSubsession* proxySubsession = (ProxyServerMediaSubsession*)subsession; 
  167.         if (strcmp(proxySubsession->mediumName(), "video") == 0) 
  168.             proxySubsession->closeStreamSource(vSource); 
  169.  
  170.         else 
  171.             proxySubsession->closeStreamSource(aSource); 
  172.     } 
  173.  
  174.     if(vSink != NULL) 
  175.         Medium::close(vSink); 
  176.  
  177.     if(aSink != NULL) 
  178.         Medium::close(aSink); 
  179.  
  180.     if(vSource != NULL) 
  181.         Medium::close(vSource); 
  182.  
  183.     if(aSource != NULL) 
  184.         Medium::close(aSource); 
  185.  
  186.     delete rtpGroupsockVideo; 
  187.     rtpGroupsockVideo = NULL
  188.     delete rtpGroupsockAudio; 
  189.     rtpGroupsockAudio = NULL

程序还有许多要完善的地方,只是一个简单的实现。

热门文章推荐

请稍候...

保利威视云平台-轻松实现点播直播视频应用

酷播云数据统计分析跨平台播放器