用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,主要实现流程在下面代码注释中:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  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. {  
  45.     char* var = (char*)clientSession;  
  46.     *var = ~0;   
  47. }    
  48.   
  49. // Main  
  50. int main(int argc, char** argv)   
  51. {  
  52.     // 初始化基本的live555环境  
  53.     scheduler = BasicTaskScheduler::createNew();  
  54.     env = BasicUsageEnvironment::createNew(*scheduler);  
  55.   
  56.     // 新建转发SESSION  
  57.     sms = ProxyServerMediaSession::createNew(*env, NULL, src);  
  58.       
  59.     // 循环等待转接程序与源端连接成功  
  60.     while(sms->numSubsessions() <= 0 )  
  61.     {  
  62.         char fWatchVariable  = 0;    
  63.         env->taskScheduler().scheduleDelayedTask(2*1000000,(TaskFunc*)sleep,&fWatchVariable);    
  64.         env->taskScheduler().doEventLoop(&fWatchVariable);    
  65.     }  
  66.       
  67.     // 开始转发流程  
  68.     RedirectStream(server, port);  
  69.   
  70.     env->taskScheduler().doEventLoop(&eventLoopWatchVariable);  
  71.   
  72.     return 0;  
  73. }  
  74.   
  75.   
  76. // 推送视频到流媒体服务器  
  77. bool RedirectStream(char const* ip, unsigned port)  
  78. {  
  79.     // 转发SESSION必须保证存在  
  80.     if( sms == NULL) return false;  
  81.   
  82.     // 判断sms是否已经连接上源端  
  83.     if( sms->numSubsessions() <= 0 )   
  84.     {  
  85.         *env << "sms numSubsessions() == 0\n";  
  86.         return false;  
  87.     }  
  88.   
  89.     // DarwinInjector主要用于向Darwin推送RTSP/RTP数据  
  90.     injector = DarwinInjector::createNew(*env);  
  91.   
  92.     struct in_addr dummyDestAddress;  
  93.     dummyDestAddress.s_addr = 0;  
  94.     rtpGroupsockVideo = new Groupsock(*env, dummyDestAddress, 0, 0);  
  95.   
  96.     struct in_addr dummyDestAddressAudio;  
  97.     dummyDestAddressAudio.s_addr = 0;  
  98.     rtpGroupsockAudio = new Groupsock(*env, dummyDestAddressAudio, 0, 0);  
  99.   
  100.     ServerMediaSubsession* subsession = NULL;  
  101.     ServerMediaSubsessionIterator iter(*sms);  
  102.     while ((subsession = iter.next()) != NULL)  
  103.     {  
  104.         ProxyServerMediaSubsession* proxySubsession = (ProxyServerMediaSubsession*)subsession;  
  105.                           
  106.         unsigned streamBitrate;  
  107.         FramedSource* source = proxySubsession->createNewStreamSource(1, streamBitrate);  
  108.           
  109.         if (strcmp(proxySubsession->mediumName(), "video") == 0)  
  110.         {  
  111.             // 用ProxyServerMediaSubsession建立Video的RTPSource  
  112.             vSource = source;  
  113.             unsigned char rtpPayloadType = proxySubsession->rtpPayloadFormat();  
  114.             // 建立Video的RTPSink  
  115.             vSink = proxySubsession->createNewRTPSink(rtpGroupsockVideo,rtpPayloadType,source);  
  116.             // 将Video的RTPSink赋值给DarwinInjector,推送视频RTP给Darwin  
  117.             injector->addStream(vSink,NULL);  
  118.         }  
  119.         else  
  120.         {  
  121.             // 用ProxyServerMediaSubsession建立Audio的RTPSource  
  122.             aSource = source;  
  123.             unsigned char rtpPayloadType = proxySubsession->rtpPayloadFormat();  
  124.             // 建立Audio的RTPSink  
  125.             aSink = proxySubsession->createNewRTPSink(rtpGroupsockVideo,rtpPayloadType,source);  
  126.             // 将Audio的RTPSink赋值给DarwinInjector,推送音频RTP给Darwin  
  127.             injector->addStream(aSink,NULL);  
  128.         }  
  129.     }  
  130.   
  131.     // RTSP ANNOUNCE/SETUP/PLAY推送过程  
  132.     if (!injector->setDestination(ip, streamName, "live555""LIVE555", port))   
  133.     {  
  134.         *env << "injector->setDestination() failed: " << env->getResultMsg() << "\n";  
  135.         return false;  
  136.     }  
  137.   
  138.     // 开始转发视频RTP数据  
  139.     if((vSink != NULL) && (vSource != NULL))  
  140.         vSink->startPlaying(*vSource,afterPlaying,vSink);  
  141.   
  142.     // 开始转发音频RTP数据  
  143.     if((aSink != NULL) && (aSource != NULL))  
  144.         aSink->startPlaying(*aSource,afterPlaying,aSink);  
  145.   
  146.     *env << "\nBeginning to get camera video...\n";  
  147.     return true;  
  148. }  
  149.   
  150.   
  151. // 停止推送,释放所有变量  
  152. void afterPlaying(void* clientData)   
  153. {  
  154.     if( clientData == NULL ) return;  
  155.   
  156.     if(vSink != NULL)  
  157.         vSink->stopPlaying();  
  158.   
  159.     if(aSink != NULL)  
  160.         aSink->stopPlaying();  
  161.   
  162.     if(injector != NULL)  
  163.     {  
  164.         Medium::close(*env, injector->name());  
  165.         injector == NULL;  
  166.     }  
  167.   
  168.     ServerMediaSubsession* subsession = NULL;  
  169.     ServerMediaSubsessionIterator iter(*sms);  
  170.     while ((subsession = iter.next()) != NULL)  
  171.     {  
  172.         ProxyServerMediaSubsession* proxySubsession = (ProxyServerMediaSubsession*)subsession;  
  173.         if (strcmp(proxySubsession->mediumName(), "video") == 0)  
  174.             proxySubsession->closeStreamSource(vSource);  
  175.   
  176.         else  
  177.             proxySubsession->closeStreamSource(aSource);  
  178.     }  
  179.   
  180.     if(vSink != NULL)  
  181.         Medium::close(vSink);  
  182.   
  183.     if(aSink != NULL)  
  184.         Medium::close(aSink);  
  185.   
  186.     if(vSource != NULL)  
  187.         Medium::close(vSource);  
  188.   
  189.     if(aSource != NULL)  
  190.         Medium::close(aSource);  
  191.   
  192.     delete rtpGroupsockVideo;  
  193.     rtpGroupsockVideo = NULL;  
  194.     delete rtpGroupsockAudio;  
  195.     rtpGroupsockAudio = NULL;  
  196. }  


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值