最近很多人问,如何将内网的摄像机流媒体数据发布到公网,如果用公网与局域网间的端口映射方式太过麻烦,一个摄像机要做一组映射,而且不是每一个局域网都是有固定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,主要实现流程在下面代码注释中:

/*
功能描述: 一个简单的RTSP/RTP对接功能,从RTSP源通过基本的RTSPClient流程,获取到RTP流媒体数据
再通过标准RTSP推送过程(ANNOUNCE/SETUP/PLAY),将获取到RTP数据推送给Darwin流媒体
分发服务器。
此Demo只演示了单个源的转换、推送功能! Author: sunpany@qq.com
时间: 2014/06/25
*/ #include "liveMedia.hh"
#include "BasicUsageEnvironment.hh"
#include "RTSPCommon.hh" char* server = "www.easydss.com";//RTSP流媒体转发服务器地址,<请修改为自己搭建的流媒体服务器地址>
int port = 8554; //RTSP流媒体转发服务器端口,<请修改为自己搭建的流媒体服务器端口>
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;
} // 推送视频到流媒体服务器
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);<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天前 的回复,是这样的!

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

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

本文转自www.easydarwin.org,更多开源流媒体解决方案,请关注我们的微信:EasyDarwin 

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

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

    近期非常多人问,怎样将内网的摄像机流媒体数据公布到公网,假设用公网与局域网间的port映射方式太过麻烦,一个摄像机要做一组映射,并且不是每个局域网都是有固定ip地址,即使外网主机配置好了每个摄像机的映 ...

  2. Android 高仿 频道管理----网易、今日头条、腾讯视频 (可以拖动的GridView)附源码DEMO

    距离上次发布(android高仿系列)今日头条 --新闻阅读器 (二) 相关的内容已经半个月了,最近利用空闲时间,把今日头条客户端完善了下.完善的功能一个一个全部实现后,就放整个源码.开发的进度就是按 ...

  3. 安卓Android手机直播推送同步录像功能设计与实现源码

    本文转自:http://blog.csdn.net/jyt0551/article/details/58714595 EasyPusher是一款非常棒的推送客户端.稳定.高效.低延迟,音视频同步等都特 ...

  4. Android 音视频深入 二 AudioTrack播放pcm(附源码下载)

    本篇项目地址,名字是录音和播放PCM,求starhttps://github.com/979451341/Audio-and-video-learning-materials 1.AudioTrack ...

  5. struts2内置拦截器和自定义拦截器详解(附源码)

    一.Struts2内置拦截器 Struts2中内置类许多的拦截器,它们提供了许多Struts2的核心功能和可选的高级特 性.这些内置的拦截器在struts-default.xml中配置.只有配置了拦截 ...

  6. RTSP安防摄像机(海康大华宇视等)如何推送到RTMP流媒体服务器进行直播

    方案介绍 目前互联网直播的CDN和标准RTMP流媒体服务器通常只能接收RTMP格式的音视频推流.目前市场上有一些自带RTMP推流的摄像机和编码器,可以直接在其rtmp推流配置里面配置推送到RTMP流媒 ...

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

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

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

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

  9. C#调用EasyPusher推送到EasyDarwin流媒体服务器直播方案及示例代码整理

    博客一:转自:http://blog.csdn.net/u011039529/article/details/70832857 大家好,本人刚毕业程序猿一枚.受人所托,第一次写博客,如有错误之处敬请谅 ...

随机推荐

  1. 你如果知道这些css常用命名,绝对事半功倍!--摘抄

    对于布局,即用.g-作为前缀,通常有以下推荐的写法 对于模块,即.m-作为前缀.元件,.u-作为前缀,通常有下面推荐的写法. 对于功能,即以.f-为前缀,通常推荐如下: 对于颜色,即以.s-为前缀,通 ...

  2. 模仿锤子手机的bigbang效果

    <!DOCTYPE html> <html style="height: 100%"> <head> <meta http-equiv=& ...

  3. jsp文件放在WebRoot下还是WebInfo下

    观点一:(较为赞同) 安全性不是真正的原因,因为jsp是要解析后才显示到浏览器的,即使用户知道你jsp的路径,也不可能通过浏览器看到jsp源码的,而如果是通过其它手段入侵服务器的话,放在WEB-INF ...

  4. 【Visual Studio】error C2220: 警告被视为错误 - 没有生成“object”文件 (转)

    原文转自 http://www.cnblogs.com/kex1n/archive/2011/10/19/2217266.html [错误原因] 该文件的代码页为英文,而我们系统中的代码页为中文. [ ...

  5. Linux内核之网络

    应用层: 套接字将Unix一切都是内核的概念应用到网络连接中,内核跟用户空间套接字之间的接口实现在c的标准库中,使用了socketcall系统调用. socketcall充当了一个多路分解器,将各种任 ...

  6. LeetCode OJ--Permutations *

    https://oj.leetcode.com/problems/permutations/ 写出一列数的全排列 #include <iostream> #include <vect ...

  7. 洛谷—— P1873 砍树

    https://www.luogu.org/problemnew/show/P1873 题目描述 伐木工人米尔科需要砍倒M米长的木材.这是一个对米尔科来说很容易的工作,因为他有一个漂亮的新伐木机,可以 ...

  8. windows XP 下的DTRACE 跟踪 学习

    https://github.com/prash-wghats/DTrace-win32 1. dtrace_loader.exe -l //to load dtrace drivers 2. C:\ ...

  9. javascript对象初探 (五)--- 函数对象的属性

    与其他对象相同的是,函数对象中也有一个叫做constructor的属性,其引用就是Function()这个构造函数. function her(a){ return a; } console.log( ...

  10. Android设置TextView行间距(非行高)

    Android设置TextView行间距(非行高) Android系统中TextView默认显示中文时会比较紧凑,不是很美观. 为了让每行保持一定的行间距,可以设置属性android:lineSpac ...