码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2015-01-21 11:32:31      阅读:1354      评论:0      收藏:0      [点我收藏+]

标签:

近期非常多人问,怎样将内网的摄像机流媒体数据公布到公网,假设用公网与局域网间的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,附源代码

标签:

原文地址:http://www.cnblogs.com/gcczhongduan/p/4238366.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!