用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

相关文章

ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法

《ElasticSearch+Kibana通过Docker部署到Linux服务器中操作方法》本文介绍了Elasticsearch的基本概念,包括文档和字段、索引和映射,还详细描述了如何通过Docker... 目录1、ElasticSearch概念2、ElasticSearch、Kibana和IK分词器部署

部署Vue项目到服务器后404错误的原因及解决方案

《部署Vue项目到服务器后404错误的原因及解决方案》文章介绍了Vue项目部署步骤以及404错误的解决方案,部署步骤包括构建项目、上传文件、配置Web服务器、重启Nginx和访问域名,404错误通常是... 目录一、vue项目部署步骤二、404错误原因及解决方案错误场景原因分析解决方案一、Vue项目部署步骤

Linux流媒体服务器部署流程

《Linux流媒体服务器部署流程》文章详细介绍了流媒体服务器的部署步骤,包括更新系统、安装依赖组件、编译安装Nginx和RTMP模块、配置Nginx和FFmpeg,以及测试流媒体服务器的搭建... 目录流媒体服务器部署部署安装1.更新系统2.安装依赖组件3.解压4.编译安装(添加RTMP和openssl模块

JavaWeb-WebSocket浏览器服务器双向通信方式

《JavaWeb-WebSocket浏览器服务器双向通信方式》文章介绍了WebSocket协议的工作原理和应用场景,包括与HTTP的对比,接着,详细介绍了如何在Java中使用WebSocket,包括配... 目录一、概述二、入门2.1 POM依赖2.2 编写配置类2.3 编写WebSocket服务2.4 浏

Java如何获取视频文件的视频时长

《Java如何获取视频文件的视频时长》文章介绍了如何使用Java获取视频文件的视频时长,包括导入maven依赖和代码案例,同时,也讨论了在运行过程中遇到的SLF4J加载问题,并给出了解决方案... 目录Java获取视频文件的视频时长1、导入maven依赖2、代码案例3、SLF4J: Failed to lo

查询SQL Server数据库服务器IP地址的多种有效方法

《查询SQLServer数据库服务器IP地址的多种有效方法》作为数据库管理员或开发人员,了解如何查询SQLServer数据库服务器的IP地址是一项重要技能,本文将介绍几种简单而有效的方法,帮助你轻松... 目录使用T-SQL查询方法1:使用系统函数方法2:使用系统视图使用SQL Server Configu

Python实现多路视频多窗口播放功能

《Python实现多路视频多窗口播放功能》这篇文章主要为大家详细介绍了Python实现多路视频多窗口播放功能的相关知识,文中的示例代码讲解详细,有需要的小伙伴可以跟随小编一起学习一下... 目录一、python实现多路视频播放功能二、代码实现三、打包代码实现总结一、python实现多路视频播放功能服务端开

Python实现视频转换为音频的方法详解

《Python实现视频转换为音频的方法详解》这篇文章主要为大家详细Python如何将视频转换为音频并将音频文件保存到特定文件夹下,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. python需求的任务2. Python代码的实现3. 代码修改的位置4. 运行结果5. 注意事项

nginx-rtmp-module构建流媒体直播服务器实战指南

《nginx-rtmp-module构建流媒体直播服务器实战指南》本文主要介绍了nginx-rtmp-module构建流媒体直播服务器实战指南,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有... 目录1. RTMP协议介绍与应用RTMP协议的原理RTMP协议的应用RTMP与现代流媒体技术的关系2

mysqld_multi在Linux服务器上运行多个MySQL实例

《mysqld_multi在Linux服务器上运行多个MySQL实例》在Linux系统上使用mysqld_multi来启动和管理多个MySQL实例是一种常见的做法,这种方式允许你在同一台机器上运行多个... 目录1. 安装mysql2. 配置文件示例配置文件3. 创建数据目录4. 启动和管理实例启动所有实例