用Darwin开发RTSP级联服务器(拉模式转发)

14 篇文章 0 订阅

原文地址:http://blog.csdn.net/xiejiashu/article/details/29580543  

在博客 在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中进行:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. QTSS_Error DoDescribe(QTSS_StandardRTSP_Params* inParams)  
  2. {  
  3.     char* theUriStr = NULL;  
  4.     QTSS_Error err = QTSS_GetValueAsString(inParams->inRTSPRequest, qtssRTSPReqFileName, 0, &theUriStr);  
  5.     Assert(err == QTSS_NoErr);  
  6.     if(err != QTSS_NoErr)  
  7.         return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientBadRequest, 0);  
  8.     QTSSCharArrayDeleter theUriStrDeleter(theUriStr);  
  9.   
  10.     // 查找配置表,获取摄像机信息结构体  
  11.     DeviceInfo* pDeviceInfo = parseDevice->GetDeviceInfoByIdName(theUriStr);  
  12.   
  13.     if(pDeviceInfo == NULL)  
  14.     {  
  15.         // 映射表中没有查到相关信息,返回,RTSP请求交给其他模块处理  
  16.         return QTSS_RequestFailed;  
  17.     }  
  18.   
  19.     // 映射信息存在rtsp://218.204.223.237:554/live/1/66251FC11353191F/e7ooqwcfbqjoo80j.sdp  
  20.     RTSPClientSession* clientSes = NULL;  
  21.     // 首先查找RTSPClientSession Hash表是否已经建立了对应摄像机的RTSPClientSession  
  22.     StrPtrLen streamName(theUriStr);  
  23.     OSRef* clientSesRef = sClientSessionMap->Resolve(&streamName);  
  24.     if(clientSesRef != NULL)  
  25.     {  
  26.         clientSes = (RTSPClientSession*)clientSesRef->GetObject();  
  27.     }  
  28.     else  
  29.     {  
  30.         // 初次建立服务器与摄像机间的交互RTSPClientSession  
  31.         clientSes = NEW RTSPClientSession(  
  32.                                     SocketUtils::ConvertStringToAddr(pDeviceInfo->m_szIP),  
  33.                                     pDeviceInfo->m_nPort,  
  34.                                     pDeviceInfo->m_szSourceUrl,  
  35.                                     1,  
  36.                                     rtcpInterval,  
  37.                                     0,  
  38.                                     theReadInterval,  
  39.                                     sockRcvBuf,  
  40.                                     speed,  
  41.                                     packetPlayHeader,  
  42.                                     overbufferwindowInK,  
  43.                                     sendOptions,  
  44.                                     pDeviceInfo->m_szUser,  
  45.                                     pDeviceInfo->m_szPassword,  
  46.                                     theUriStr);  
  47.   
  48.         // 向摄像机源端发送Describe请求  
  49.         OS_Error theErr = clientSes->SendDescribe();  
  50.   
  51.         if(theErr == QTSS_NoErr){  
  52.             // 将成功建立的RTSPClientSession注册到sClientSessionMap表中  
  53.             OS_Error theErr = sClientSessionMap->Register(clientSes->GetRef());  
  54.             Assert(theErr == QTSS_NoErr);  
  55.         }  
  56.         else{  
  57.             clientSes->Signal(Task::kKillEvent);  
  58.             return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssClientNotFound, 0);   
  59.         }  
  60.   
  61.         //增加一次对RTSPClientSession的无效引用,后面会统一释放  
  62.         OSRef* debug = sClientSessionMap->Resolve(&streamName);  
  63.         Assert(debug == clientSes->GetRef());  
  64.     }  
  65.   
  66.     // 建立转发所用的ReflectorSession,后续流程与QTSSReflectorModule类似  
  67.     ReflectorSession* theSession = SetupProxySession(inParams, clientSes);  
  68.       
  69.     if (theSession == NULL)  
  70.     {  
  71.         sClientSessionMap->Release(clientSes->GetRef());  
  72.         return QTSSModuleUtils::SendErrorResponse(inParams->inRTSPRequest, qtssServerNotImplemented, 0);  
  73.     }  
  74. }  

      这里我们用到了两个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(客户端与设备推送)方法一样:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void RemoveOutput(ReflectorOutput* inOutput, ReflectorSession* inSession, Bool16 killClients)  
  2. {  
  3.     // 从ReflectorSession中移除RTSPSession  
  4.     Assert(inSession);  
  5.     if (inSession != NULL)  
  6.     {  
  7.         if (inOutput != NULL)  
  8.             inSession->RemoveOutput(inOutput,true);  
  9.   
  10.         OSMutexLocker locker (sSessionMap->GetMutex());  
  11.           
  12.         OSRef* theSessionRef = inSession->GetRef();  
  13.         if (theSessionRef != NULL)   
  14.         {                 
  15.             if (theSessionRef->GetRefCount() == 0)  
  16.             {   
  17.                 // 当引用客户端数量为0的时候,通知RTSPClientSession断开与摄像机的连接  
  18.                 RTSPClientSession* proxySession = (RTSPClientSession*)inSession->GetRelaySession();  
  19.                 if(proxySession != NULL)  
  20.                 {  
  21.                     proxySession->SetReflectorSession(NULL);  
  22.                     sClientSessionMap->UnRegister(proxySession->GetRef());  
  23.                     proxySession->Signal(Task::kKillEvent);  
  24.                 }  
  25.   
  26.                 inSession->SetRelaySession(NULL);  
  27.                 sSessionMap->UnRegister(theSessionRef);  
  28.                 delete inSession;  
  29.             }  
  30.             else  
  31.             {  
  32.                 qtss_printf("QTSSReflector.cpp:RemoveOutput Release SESSION=%lu RefCount=%d\n",(UInt32)inSession,theSessionRef->GetRefCount());  
  33.                 sSessionMap->Release(theSessionRef);  
  34.             }  
  35.         }  
  36.     }  
  37.     delete inOutput;  
  38. }  

#演示效果



#下载

程序下载: http://pan.baidu.com/s/1c09vY6k ,运行start.bat,具体用法请看ReadMe.txt !
  1、支持国标GB28181平台、国标GB28181 IPC和国标GB28181 NVR设备同时接入 (支持GB28181-2011版本和GB28181-2016版本)     2、支持国标GB28181设备注册和注销,对所有设备进行管理,获取资源 对资源列表进行管理     3、支持国标GB28181的目录订阅,对接收的订阅通知进行处理     4、支持国标GB28181实时视频请求(支持UDP、TCP主动(tcpactive)、TCP被动(tcppassive))     5、支持国标GB28181 PTZ控制     6、支持国标GB28181 录像查询     7、支持国标GB28181 历史视频点播 (支持UDP、TCP主动(tcpactive)、TCP被动(tcppassive))     8、支持国标GB28181 历史视频下载 (支持UDP、TCP主动(tcpactive)、TCP被动(tcppassive))     9、支持对接收的国标实时视频码流和历史视频码流进行管理     10、支持将国标的PS码流转换成ES码流     11、支持丢包打印和断流打印     13、支持RTSP服务和RTSP会话管理     14、支持RTSP客户端 UDP传输和TCP传输     15、支持国标GB28181设备5000路左右的接入管理,支持国标请求视频在100路左右     16、支持国标28181设备和通道写入mysql数据库      17、支持设备的云台PTZ控制,控制类型:上"up",下"down",左"left",右"right",左上"leftup",左下"leftdown",右上"rightup",右下"rightdown",镜头近"zoomin",镜头远"zoomout", 焦距远"focusfar",焦距近"focusnear", 设置预置位"setpos",调预置位"callpos"     18、支持历史视频的查询和历史视频的点播控制     19、支持对国标设备的控制,"record":录像开启和停止-通道id "guard":布放和撤防-报警通道id "reboot":设备重启-设备id "keyfame":强制关键帧-通道id     20、支持对实时视频的图片截图,通过http直接访问图片    21、支持rtmp和hls会话一直保留    22、支持报警消息(设备上线、下线和设备端报警)通过httpclient方式主动通知    23、支持公网和局域网同时存在    24、支持httpserver,接口支持http+json    25、支持设备上线、下线和设备报警通过httpclient通知到指定的httpserver   26、支持http+json设置平台信息  27、支持http+json获取资源组、资源等信息  29、支持国标28181级联上级
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值