近期非常多人问,怎样将内网的摄像机流媒体数据公布到公网,假设用公网与局域网间的port映射方式太过麻烦,一个摄像机要做一组映射,并且不是每个局域网都是有固定ip地址,即使外网主机配置好了每个摄像机的映射地址,也有可能会由于宽带公网ip地址变动而导致配置无效。

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

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

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

/*
功能描写叙述: 一个简单的RTSP/RTP对接功能,从RTSP源通过主要的RTSPClient流程,获取到RTP流媒体数据
再通过标准RTSP推送过程(ANNOUNCE/SETUP/PLAY),将获取到RTP数据推送给Darwin流媒体
分发server。
此Demo仅仅演示了单个源的转换、推送功能! Author: sunpany@qq.com
时间: 2014/06/25
*/ #include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
#include "RTSPCommon.hh" char* server = "www.easydss.com";//RTSP流媒体转发server地址,<请改动为自己搭建的流媒体server地址>
int port = 8554; //RTSP流媒体转发server端口,<请改动为自己搭建的流媒体server端口>
char* streamName = "live.sdp"; //流名称,推送到Darwin的流名称必须以.sdp结尾
char* src = "rtsp://218.204.223.237:554/live/1/66251FC11353191F/e7ooqwcfbqjoo80j.sdp";//源端URL UsageEnvironment* env = NULL; //live555 global environment
TaskScheduler* scheduler = NULL;
char eventLoopWatchVariable = 0; DarwinInjector* injector = NULL; //DarwinInjector
FramedSource* vSource = NULL; //Video Source
FramedSource* aSource = NULL; //Audio Source RTPSink* vSink = NULL; //Video Sink
RTPSink* aSink = NULL; //Audio Sink Groupsock* rtpGroupsockVideo = NULL;//Video Socket
Groupsock* rtpGroupsockAudio = NULL;//Audio Socket ProxyServerMediaSession* sms = NULL;//proxy session // 流转发过程
bool RedirectStream(char const* ip, unsigned port); // 流转发结束后处理回调
void afterPlaying(void* clientData); // 实现等待功能
void sleep(void* clientSession)
{
char* var = (char*)clientSession;
*var = ~0;
} // Main
int main(int argc, char** argv)
{
// 初始化主要的live555环境
scheduler = BasicTaskScheduler::createNew();
env = BasicUsageEnvironment::createNew(*scheduler); // 新建转发SESSION
sms = ProxyServerMediaSession::createNew(*env, NULL, src); // 循环等待转接程序与源端连接成功
while(sms->numSubsessions() <= 0 )
{
char fWatchVariable = 0;
env->taskScheduler().scheduleDelayedTask(2*1000000,(TaskFunc*)sleep,&fWatchVariable);
env->taskScheduler().doEventLoop(&fWatchVariable);
} // 開始转发流程
RedirectStream(server, port); env->taskScheduler().doEventLoop(&eventLoopWatchVariable); return 0;
} // 推送视频到流媒体server
bool RedirectStream(char const* ip, unsigned port)
{
// 转发SESSION必须保证存在
if( sms == NULL) return false; // 推断sms是否已经连接上源端
if( sms->numSubsessions() <= 0 )
{
*env << "sms numSubsessions() == 0\n";
return false;
} // DarwinInjector主要用于向Darwin推送RTSP/RTP数据
injector = DarwinInjector::createNew(*env); struct in_addr dummyDestAddress;
dummyDestAddress.s_addr = 0;
rtpGroupsockVideo = new Groupsock(*env, dummyDestAddress, 0, 0); struct in_addr dummyDestAddressAudio;
dummyDestAddressAudio.s_addr = 0;
rtpGroupsockAudio = new Groupsock(*env, dummyDestAddressAudio, 0, 0); ServerMediaSubsession* subsession = NULL;
ServerMediaSubsessionIterator iter(*sms);
while ((subsession = iter.next()) != NULL)
{
ProxyServerMediaSubsession* proxySubsession = (ProxyServerMediaSubsession*)subsession; unsigned streamBitrate;
FramedSource* source = proxySubsession->createNewStreamSource(1, streamBitrate); if (strcmp(proxySubsession->mediumName(), "video") == 0)
{
// 用ProxyServerMediaSubsession建立Video的RTPSource
vSource = source;
unsigned char rtpPayloadType = proxySubsession->rtpPayloadFormat();
// 建立Video的RTPSink
vSink = proxySubsession->createNewRTPSink(rtpGroupsockVideo,rtpPayloadType,source);
// 将Video的RTPSink赋值给DarwinInjector,推送视频RTP给Darwin
injector->addStream(vSink,NULL);
}
else
{
// 用ProxyServerMediaSubsession建立Audio的RTPSource
aSource = source;
unsigned char rtpPayloadType = proxySubsession->rtpPayloadFormat();
// 建立Audio的RTPSink
aSink = proxySubsession->createNewRTPSink(rtpGroupsockVideo,rtpPayloadType,source);
// 将Audio的RTPSink赋值给DarwinInjector,推送音频RTP给Darwin
injector->addStream(aSink,NULL);
}
} // RTSP ANNOUNCE/SETUP/PLAY推送过程
if (!injector->setDestination(ip, streamName, "live555", "LIVE555", port))
{
*env << "injector->setDestination() failed: " << env->getResultMsg() << "\n";
return false;
} // 開始转发视频RTP数据
if((vSink != NULL) && (vSource != NULL))
vSink->startPlaying(*vSource,afterPlaying,vSink); // 開始转发音频RTP数据
if((aSink != NULL) && (aSource != NULL))
aSink->startPlaying(*aSource,afterPlaying,aSink); *env << "\nBeginning to get camera video...\n";
return true;
} // 停止推送,释放全部变量
void afterPlaying(void* clientData)
{
if( clientData == NULL ) return; if(vSink != NULL)
vSink->stopPlaying(); if(aSink != NULL)
aSink->stopPlaying(); if(injector != NULL)
{
Medium::close(*env, injector->name());
injector == NULL;
} ServerMediaSubsession* subsession = NULL;
ServerMediaSubsessionIterator iter(*sms);
while ((subsession = iter.next()) != NULL)
{
ProxyServerMediaSubsession* proxySubsession = (ProxyServerMediaSubsession*)subsession;
if (strcmp(proxySubsession->mediumName(), "video") == 0)
proxySubsession->closeStreamSource(vSource); else
proxySubsession->closeStreamSource(aSource);
} if(vSink != NULL)
Medium::close(vSink); 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

用live555将内网摄像机视频推送到外网server,附源代码的更多相关文章

  1. 用live555将内网摄像机视频推送到外网服务器,附源码

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

  2. 把iPad上的视频推送到大麦盒子去

    把iPad上的视频推送到大麦盒子去   最近因为升级家里的宽带,服务商送了一个大麦盒子给我.   大麦盒子,就是一个网络机顶盒,用它可以通过互联网收看电视剧.电影.电视节目.音乐等等.除了它自身带的一 ...

  3. iOS直播-基于RTMP的视频推送

    iOS直播-基于RTMP的视频推送 所谓的视频推送就是把摄像头和麦克风捕获到视频和音频推送到直播服务器上.我们这里使用推送协议是RTMP协议. 扩展:腾讯直播平台,阿里直播平台,百度直播平台提供均为R ...

  4. TP-LINK路由器设置内网的一台电脑在外网可以远程操控

    1.[IP和MAC绑定]--[静态ARP绑定设置]对MAC和IP进行绑定 2.[转发规则]--[DMZ主机],选择启用并把刚才设置的内网IP填入 3.直接访问路由器的外网IP就可以直接访问绑定的MAC ...

  5. WinServer 之 内网发布网站后端口映射外网访问

    内网IP只能在内网局域网访问连接,在外网是不能认识内网IP不能访问的.如有路由权限,且路由有固定公网IP,可以通过路由的端口映射,实现外网访问内网.如无路由,或路由无公网IP,需要用到第三方开放的花生 ...

  6. 请问 内网的 dns服务器 为什么和 外网的dns服务器 一样??

    公司内的内网使用192.169.X.X的内网地址,但是在DNS段填写的是210.34.X.X,显然这是一个公网固定IP,我不明白的是:为什么内部网客户端使用的DNS服务器是公网上的IP呢?内网客户端能 ...

  7. 实现外网訪问局域网内的SVN——花生壳+visiualSVN实现外网訪问局域网内的SVN(三)

    经过前两篇文章.到眼下为止,我们已经获取了外网域名而且搭建好了SVN server.接下来,我们就总结一下怎样实践实现一下訪问局域网. 1.安装VisiualSVN Server(可见:http:// ...

  8. 内网服务器通过Squid代理访问外网

    环境说明 项目整体需部署Zabbix监控并配置微信报警,而Zabbix Server并不能访问外网,故运维小哥找了台能访问外网的服务器做Suqid代理,Zabbix Server服务器通过代理服务器访 ...

  9. 网云穿-SpringBoot项目映射外网

    网云穿-最简单易用的内网穿透软件,最简洁教程一键穿透网站.数据库.远程桌面 网云穿,致力于打造最便捷的「内网穿透」应用 https://xiaomy.net/index.html  网云穿是一款可以在 ...

随机推荐

  1. [Python] List & Object spread in Python

    def myfunc(x, y, z): print(x, y, z) tuple_vec = (, , ) dict_vec = {, , } >>> myfunc(*tuple_ ...

  2. golang sync.Cond

    package main import ( "fmt" "sync" "time" ) func main() { wait := sync ...

  3. windows7下安装Office2010提示需要安装MSXML6.10.1129

    平台:Windows 7 问题:刚刚下载的ghost Win 7,安装过程一切顺利,进入系统后把集成的软件全部卸载,清理完垃圾,安装了VC库,在安装Office2010时提示需要安装MSXML6.10 ...

  4. BZOJ2754: [SCOI2012]喵星球上的点名(AC自动机/后缀自动机)

    Description a180285幸运地被选做了地球到喵星球的留学生.他发现喵星人在上课前的点名现象非常有趣.   假设课堂上有N个喵星人,每个喵星人的名字由姓和名构成.喵星球上的老师会选择M个串 ...

  5. Spring学习总结(9)——Spring AOP总结

    spring IOC和AOP是Spring框架的两大核心基石,本文将对Spring AOP做一个系统的总结. 什么是AOP AOP(Aspect-Oriented Programming,面向切面编程 ...

  6. 上传excel数据到数据库中

    上传excel表格数据到数据库 导入固定路径下的excel数据到数据库 <form id="disposeFlightDataForm" action="../up ...

  7. 决策树2 -- CART算法

    声明: 1,本篇为个人对<2012.李航.统计学习方法.pdf>的学习总结.不得用作商用,欢迎转载,但请注明出处(即:本帖地址). 2,因为本人在学习初始时有非常多数学知识都已忘记.所以为 ...

  8. 在 Android* 商务应用中实施地图和地理围栏特性

    摘要 本案例研究讨论了怎样将地图和地理定位特性构建到 Android* 商务应用中.包含在 Google Maps* 上覆盖商店位置,以及在设备进入商店地理围栏邻近区域时借助地理围栏通知用户. 文件夹 ...

  9. amazeui学习笔记一(开始使用4)--Web App 相关

    amazeui学习笔记一(开始使用4)--Web App 相关 一.总结 1.桌面图标(Touch icon)解决方案:终极方案:link标签的rel和href属性: <link rel=&qu ...

  10. background-size在PC端和移动端使用媒体查询的不同

    1.PC端background-size:100%:是展现原图的大小. 2.使用媒体查询的移动端的background-size:100%:是根据内容的高度自动拉伸高度的.