用Darwin开发RTSP级联服务器(拉模式转发)(附源码)
来源:互联网 发布:淘宝旺铺首页全屏装修 编辑:程序博客网 时间:2024/05/17 04:45
源码下载地址:https://github.com/EasyDarwin orwww.easydarwin.org
在博客 在Darwin进行实时视频转发的两种模式 中,我们描述了流媒体服务器对源端音视频转发的两种模式,其中一种#拉模式# 转发,在我们通常的项目中经常会用到,比如在传统视频监控行业,IP摄像机部署在监控内网的各个地点,我们需要将他们进行集中式的管理,并且对外发布,这时候我们就需要用到一台流媒体服务器,能够拉取所需的摄像机的音视频流,并做转化(如RTMP、HTTP等),作为监控内网与公网的中转,提供转发服务。
#转发模块设计
拉模式转发中,转发服务器一方面作为RTSP客户端的角色,向源端摄像机获取音视频数据,另一方面作为服务器的角色,将拉取到的音视频数据,重新作为数据源,分发给正在请求的客户端。这样,我们在设计中需要考虑到以下几点:
- 源端数据流到服务器的数据流能够复用,也就是一路进多路出。
- 服务器端维护所有正在分发的摄像机源列表。
- 服务器与源端在空闲状态下无连接,只有在有需要的情况下才发起连接过程。
- 当所有客户端结束对某个源端请求时,服务器停止从源端获取数据,断开连接。
Darwin系统已经具有了我们所需的一定条件:RTSPClient客户端实现、RTP分发流程(ReflectorSession),我们需要实现:Darwin拉模式转发模块,我们定义此模块名称为QTSSOnDemandRelayModule,意为只有在有需要的时候,才会转发;Darwin与源端用于交互、保存信息、接收数据的ClientSession,为了不影响Darwin原有的架构,我们没有直接在RTSPClient类中修改,而是自定义类:RTSPClientSession,实例化RTSPClient对象为其成员变量:
在RTSPClientSession中,所有RTSP流程都由fClient(RTSPClient对象)完成,RTSPClientSession负责进行变量存储(如服务器地址fAddr、端口fPort、用户名fName、密码fPassword)、收到数据包统计(fStates、fNumPacketReceived)、RTSPClient控制(SETUP发送fNumSetups、RTSP断开fTeardownImmediately)、以及在非客户端断开情况下,服务器与摄像机间的重连。
#转发模块实现
我们命名拉模式转发模块名称为:QTSSOnDemandRelayModule,需要分别实现对RTSP和RTP的转发和处理,如此,我们会分别处理QTSS_RTSPPreProcessor_Role(RTSP消息处理)、QTSS_RTSPRelayingData_Role(拉取的RTP数据处理)、QTSS_ClientSessionClosing_Role(客户端或RTSPClientSession断开处理)。
*QTSS_RTSPPreProcessor_Role(RTSP消息处理)
我们设计的拉模式转发为名称与地址映射的方式,映射列表配置在xml文件中,在QTSSOnDemandRelayModule初始化时,我们就会将配置映射表加载到模块中,当然!我们也可以修改为读取数据库的方式:
例如,RTSP摄像机地址为:rtsp://218.204.223.237:554/live/1/66251FC11353191F/e7ooqwcfbqjoo80j.sdp,RTSP转发服务器地址为:8.8.8.8,端口为:554,那么客户端请求:rtsp://8.8.8.8:554/ipC1,转发服务器就会向rtsp://218.204.223.237:554/live/1/66251FC11353191F/e7ooqwcfbqjoo80j.sdp 请求摄像机数据,获取后转发给客户端列表。映射查找我们在DoDescribe中进行:
QTSS_Error DoDescribe(QTSS_StandardRTSP_Params* inParams){char* theUriStr = NULL; QTSS_Error err = QTSS_GetValueAsString(inParams->inRTSPRequest, qtssRTSPReqFileName, 0, &theUriStr); Assert(err == QTSS_NoErr); if(err != QTSS_NoErr)return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientBadRequest, 0); QTSSCharArrayDeleter theUriStrDeleter(theUriStr);// 查找配置表,获取摄像机信息结构体DeviceInfo* pDeviceInfo = parseDevice->GetDeviceInfoByIdName(theUriStr);if(pDeviceInfo == NULL){// 映射表中没有查到相关信息,返回,RTSP请求交给其他模块处理return QTSS_RequestFailed;}// 映射信息存在rtsp://218.204.223.237:554/live/1/66251FC11353191F/e7ooqwcfbqjoo80j.sdpRTSPClientSession* clientSes = NULL;// 首先查找RTSPClientSession Hash表是否已经建立了对应摄像机的RTSPClientSessionStrPtrLen streamName(theUriStr);OSRef* clientSesRef = sClientSessionMap->Resolve(&streamName);if(clientSesRef != NULL){clientSes = (RTSPClientSession*)clientSesRef->GetObject();}else{// 初次建立服务器与摄像机间的交互RTSPClientSessionclientSes = NEW RTSPClientSession(SocketUtils::ConvertStringToAddr(pDeviceInfo->m_szIP),pDeviceInfo->m_nPort,pDeviceInfo->m_szSourceUrl,1,rtcpInterval,0,theReadInterval,sockRcvBuf,speed,packetPlayHeader,overbufferwindowInK,sendOptions,pDeviceInfo->m_szUser,pDeviceInfo->m_szPassword,theUriStr);// 向摄像机源端发送Describe请求OS_Error theErr = clientSes->SendDescribe();if(theErr == QTSS_NoErr){// 将成功建立的RTSPClientSession注册到sClientSessionMap表中OS_Error theErr = sClientSessionMap->Register(clientSes->GetRef());Assert(theErr == QTSS_NoErr);}else{clientSes->Signal(Task::kKillEvent);return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientNotFound, 0); }//增加一次对RTSPClientSession的无效引用,后面会统一释放OSRef* debug = sClientSessionMap->Resolve(&streamName);Assert(debug == clientSes->GetRef());}// 建立转发所用的ReflectorSession,后续流程与QTSSReflectorModule类似ReflectorSession* theSession = SetupProxySession(inParams, clientSes); if (theSession == NULL){sClientSessionMap->Release(clientSes->GetRef()); return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssServerNotImplemented, 0);}}
这里我们用到了两个Hash Map,一个是存储RTSPClientSession的sClientSessionMap、一个存储ReflectorSession的sSessionMap。
*QTSS_RTSPRelayingData_Role(拉取的RTP数据处理)
当RTSPClientSession获取到一个RTP包时,我们就会调用QTSS_RTSPRelayingData_Role,将RTP包Push给ReflectorSession进行分发,分发过程与QTSSReflectorModule处理方式是一样的,调用方法也同理:
*QTSS_ClientSessionClosing_Role(客户端和RTSPClientSession断开处理)
ReflectorSession客户端引用数统计、客户端端断开流程、RTSPClientSession断开流程,基本与RTSPSession(客户端与设备推送)方法一样:
void RemoveOutput(ReflectorOutput* inOutput, ReflectorSession* inSession, Bool16 killClients){ // 从ReflectorSession中移除RTSPSession Assert(inSession); if (inSession != NULL){ if (inOutput != NULL)inSession->RemoveOutput(inOutput,true);OSMutexLocker locker (sSessionMap->GetMutex()); OSRef* theSessionRef = inSession->GetRef(); if (theSessionRef != NULL) { if (theSessionRef->GetRefCount() == 0) { // 当引用客户端数量为0的时候,通知RTSPClientSession断开与摄像机的连接RTSPClientSession* proxySession = (RTSPClientSession*)inSession->GetRelaySession();if(proxySession != NULL){proxySession->SetReflectorSession(NULL);sClientSessionMap->UnRegister(proxySession->GetRef());proxySession->Signal(Task::kKillEvent);}inSession->SetRelaySession(NULL);sSessionMap->UnRegister(theSessionRef);delete inSession; } else {qtss_printf("QTSSReflector.cpp:RemoveOutput Release SESSION=%lu RefCount=%d\n",(UInt32)inSession,theSessionRef->GetRefCount()); sSessionMap->Release(theSessionRef); } } } delete inOutput;}
#演示效果
#下载
程序下载:http://pan.baidu.com/s/1c09vY6k ,运行start.bat,具体用法请看ReadMe.txt !
------------------------------------------------------------
1 0
- 用Darwin开发RTSP级联服务器(拉模式转发)(附源码)
- 用Darwin开发RTSP级联服务器(拉模式转发)
- 用Darwin实现流媒体转发程序(附源码)
- EasyDarwin流媒体服务器RTSP拉模式流媒体转发模块设计
- EasyDarwin流媒体服务器RTSP拉模式流媒体转发模块设计
- RTSP流媒体转发服务器源码
- 基于EasyIPCamera实现的RTSP跨平台拉模式转发流媒体服务器
- darwin拉模式设计详解
- Darwin Streaming Server搭建RTSP服务器
- 用Darwin开发分布式流媒体服务器
- 用Darwin开发分布式流媒体服务器
- 用Darwin开发分布式流媒体服务器
- 关于 darwin 转发服务器收集的资料
- Darwin流媒体服务器源码分析
- rtsp 移动流媒体服务器Darwin Streaming Server安装和配置
- rtsp 移动流媒体服务器Darwin Streaming Server安装和配置
- rtsp移动流媒体服务器Darwin Streaming Server安装和配置
- rtsp 移动流媒体服务器Darwin Streaming Server安装和配置
- 牛顿法及拟牛顿法(L-BFGS)
- Android PackageManagerService启动过程
- Android应用程序实现自动更新功能4_实现的整体代码
- java正则表达式替换空格和换行符
- 几点睡复合肥电视柜
- 用Darwin开发RTSP级联服务器(拉模式转发)(附源码)
- 如何做到每天写代码?
- Linux 卸载Openjdk,安装JDK7
- libcurl教程(转)
- [全文索引]MySQL 全文搜索支持, mysql 5.6.4支持Innodb的全文检索和类memcache的nosql支持。
- 74岁二次创业成为千万富豪
- Eclipse设置软tab(用4个空格字符代替)及默认utf-8文件编码(unix)
- ComboBox 组合框
- HDU 2180 时钟 题解