用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

相关文章

Windows Server 2025 搭建NPS-Radius服务器的步骤

《WindowsServer2025搭建NPS-Radius服务器的步骤》本文主要介绍了通过微软的NPS角色实现一个Radius服务器,身份验证和证书使用微软ADCS、ADDS,具有一定的参考价... 目录简介示意图什么是 802.1X?核心作用802.1X的组成角色工作流程简述802.1X常见应用802.

使用Nginx配置文件服务器方式

《使用Nginx配置文件服务器方式》:本文主要介绍使用Nginx配置文件服务器方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 为什么选择 Nginx 作为文件服务器?2. 环境准备3. 配置 Nginx 文件服务器4. 将文件放入服务器目录5. 启动 N

Go语言开发实现查询IP信息的MCP服务器

《Go语言开发实现查询IP信息的MCP服务器》随着MCP的快速普及和广泛应用,MCP服务器也层出不穷,本文将详细介绍如何在Go语言中使用go-mcp库来开发一个查询IP信息的MCP... 目录前言mcp-ip-geo 服务器目录结构说明查询 IP 信息功能实现工具实现工具管理查询单个 IP 信息工具的实现服

springboot上传zip包并解压至服务器nginx目录方式

《springboot上传zip包并解压至服务器nginx目录方式》:本文主要介绍springboot上传zip包并解压至服务器nginx目录方式,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录springboot上传zip包并解压至服务器nginx目录1.首先需要引入zip相关jar包2.然

将Java项目提交到云服务器的流程步骤

《将Java项目提交到云服务器的流程步骤》所谓将项目提交到云服务器即将你的项目打成一个jar包然后提交到云服务器即可,因此我们需要准备服务器环境为:Linux+JDK+MariDB(MySQL)+Gi... 目录1. 安装 jdk1.1 查看 jdk 版本1.2 下载 jdk2. 安装 mariadb(my

在Android平台上实现消息推送功能

《在Android平台上实现消息推送功能》随着移动互联网应用的飞速发展,消息推送已成为移动应用中不可或缺的功能,在Android平台上,实现消息推送涉及到服务端的消息发送、客户端的消息接收、通知渠道(... 目录一、项目概述二、相关知识介绍2.1 消息推送的基本原理2.2 Firebase Cloud Me

基于Python和MoviePy实现照片管理和视频合成工具

《基于Python和MoviePy实现照片管理和视频合成工具》在这篇博客中,我们将详细剖析一个基于Python的图形界面应用程序,该程序使用wxPython构建用户界面,并结合MoviePy、Pill... 目录引言项目概述代码结构分析1. 导入和依赖2. 主类:PhotoManager初始化方法:__in

基于Python打造一个可视化FTP服务器

《基于Python打造一个可视化FTP服务器》在日常办公和团队协作中,文件共享是一个不可或缺的需求,所以本文将使用Python+Tkinter+pyftpdlib开发一款可视化FTP服务器,有需要的小... 目录1. 概述2. 功能介绍3. 如何使用4. 代码解析5. 运行效果6.相关源码7. 总结与展望1

使用Python开发一个简单的本地图片服务器

《使用Python开发一个简单的本地图片服务器》本文介绍了如何结合wxPython构建的图形用户界面GUI和Python内建的Web服务器功能,在本地网络中搭建一个私人的,即开即用的网页相册,文中的示... 目录项目目标核心技术栈代码深度解析完整代码工作流程主要功能与优势潜在改进与思考运行结果总结你是否曾经

使用Python实现快速搭建本地HTTP服务器

《使用Python实现快速搭建本地HTTP服务器》:本文主要介绍如何使用Python快速搭建本地HTTP服务器,轻松实现一键HTTP文件共享,同时结合二维码技术,让访问更简单,感兴趣的小伙伴可以了... 目录1. 概述2. 快速搭建 HTTP 文件共享服务2.1 核心思路2.2 代码实现2.3 代码解读3.