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

2024-02-22 03:58

本文主要是介绍用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);<pre name="code" class="html">  
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

非常感谢感谢6楼 Boris_Cao_2015 5天前 的回复,是这样的!

[plain]  view plain copy
在CODE上查看代码片 派生到我的代码片
  1. “按着这个代码不同时支持音视频,要修改LIVE555里面DarwinInjector源码, stream channel id记得加1,因为RTCP instance不存在,所以RTP流的stream channel id必须自动加1, 否则跟RTCP的stream channel id重合,这就是原因。跟楼主和大家分享。嘻嘻!”  

------------------------------------------------------------

这篇关于用live555将内网摄像机视频推送到外网服务器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/734057

相关文章

Apache Tomcat服务器版本号隐藏的几种方法

《ApacheTomcat服务器版本号隐藏的几种方法》本文主要介绍了ApacheTomcat服务器版本号隐藏的几种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需... 目录1. 隐藏HTTP响应头中的Server信息编辑 server.XML 文件2. 修China编程改错误

如何在一台服务器上使用docker运行kafka集群

《如何在一台服务器上使用docker运行kafka集群》文章详细介绍了如何在一台服务器上使用Docker运行Kafka集群,包括拉取镜像、创建网络、启动Kafka容器、检查运行状态、编写启动和关闭脚本... 目录1.拉取镜像2.创建集群之间通信的网络3.将zookeeper加入到网络中4.启动kafka集群

Python如何实现 HTTP echo 服务器

《Python如何实现HTTPecho服务器》本文介绍了如何使用Python实现一个简单的HTTPecho服务器,该服务器支持GET和POST请求,并返回JSON格式的响应,GET请求返回请求路... 一个用来做测试的简单的 HTTP echo 服务器。from http.server import HT

如何安装 Ubuntu 24.04 LTS 桌面版或服务器? Ubuntu安装指南

《如何安装Ubuntu24.04LTS桌面版或服务器?Ubuntu安装指南》对于我们程序员来说,有一个好用的操作系统、好的编程环境也是很重要,如何安装Ubuntu24.04LTS桌面... Ubuntu 24.04 LTS,代号 Noble NumBAT,于 2024 年 4 月 25 日正式发布,引入了众

如何提高Redis服务器的最大打开文件数限制

《如何提高Redis服务器的最大打开文件数限制》文章讨论了如何提高Redis服务器的最大打开文件数限制,以支持高并发服务,本文给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录如何提高Redis服务器的最大打开文件数限制问题诊断解决步骤1. 修改系统级别的限制2. 为Redis进程特别设置限制

流媒体平台/视频监控/安防视频汇聚EasyCVR播放暂停后视频画面黑屏是什么原因?

视频智能分析/视频监控/安防监控综合管理系统EasyCVR视频汇聚融合平台,是TSINGSEE青犀视频垂直深耕音视频流媒体技术、AI智能技术领域的杰出成果。该平台以其强大的视频处理、汇聚与融合能力,在构建全栈视频监控系统中展现出了独特的优势。视频监控管理系统EasyCVR平台内置了强大的视频解码、转码、压缩等技术,能够处理多种视频流格式,并以多种格式(RTMP、RTSP、HTTP-FLV、WebS

服务器集群同步时间手记

1.时间服务器配置(必须root用户) (1)检查ntp是否安装 [root@node1 桌面]# rpm -qa|grep ntpntp-4.2.6p5-10.el6.centos.x86_64fontpackages-filesystem-1.41-1.1.el6.noarchntpdate-4.2.6p5-10.el6.centos.x86_64 (2)修改ntp配置文件 [r

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

Linux服务器Java启动脚本

Linux服务器Java启动脚本 1、初版2、优化版本3、常用脚本仓库 本文章介绍了如何在Linux服务器上执行Java并启动jar包, 通常我们会使用nohup直接启动,但是还是需要手动停止然后再次启动, 那如何更优雅的在服务器上启动jar包呢,让我们一起探讨一下吧。 1、初版 第一个版本是常用的做法,直接使用nohup后台启动jar包, 并将日志输出到当前文件夹n

《x86汇编语言:从实模式到保护模式》视频来了

《x86汇编语言:从实模式到保护模式》视频来了 很多朋友留言,说我的专栏《x86汇编语言:从实模式到保护模式》写得很详细,还有的朋友希望我能写得更细,最好是覆盖全书的所有章节。 毕竟我不是作者,只有作者的解读才是最权威的。 当初我学习这本书的时候,只能靠自己摸索,网上搜不到什么好资源。 如果你正在学这本书或者汇编语言,那你有福气了。 本书作者李忠老师,以此书为蓝本,录制了全套视频。 试