源码下载地址: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中进行:

  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(客户端与设备推送)方法一样:
  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

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

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


用Darwin开发RTSP级联服务器(拉模式转发)(附源码)的更多相关文章

  1. 用Darwin开发RTSP级联server(拉模式转发)(附源代码)

    源代码下载地址:https://github.com/EasyDarwin orwww.easydarwin.org 在博客 在Darwin进行实时视频转发的两种模式 中,我们描写叙述了流媒体serv ...

  2. C#/ASP.NET MVC微信公众号接口开发之从零开发(四) 微信自定义菜单(附源码)

    C#/ASP.NET MVC微信接口开发文章目录: 1.C#/ASP.NET MVC微信公众号接口开发之从零开发(一) 接入微信公众平台 2.C#/ASP.NET MVC微信公众号接口开发之从零开发( ...

  3. Asp.net MVC - 使用PRG模式(附源码)

    阅读目录: 一. 传统的Asp.net页面问题 二.Asp.net MVC中也存在同样的问题 三.使用PRG模式 四.PRG模式在MVC上的实现 一. 传统的Asp.net页面问题 一个传统的Asp. ...

  4. C#/ASP.NET MVC微信公众号接口开发之从零开发(三)回复消息 (附源码)

    C#/ASP.NET MVC微信接口开发文章目录: 1.C#/ASP.NET MVC微信公众号接口开发之从零开发(一) 接入微信公众平台 2.C#/ASP.NET MVC微信公众号接口开发之从零开发( ...

  5. openlayers4 入门开发系列结合 echarts4 实现散点图(附源码下载)

    前言 openlayers4 官网的 api 文档介绍地址 openlayers4 api,里面详细的介绍 openlayers4 各个类的介绍,还有就是在线例子:openlayers4 官网在线例子 ...

  6. cesium 入门开发系列矢量瓦片加载展示(附源码下载)

    前言 cesium 入门开发系列环境知识点了解:cesium api文档介绍,详细介绍 cesium 每个类的函数以及属性等等cesium 在线例子 内容概览 cesium 实现矢量瓦片加载效果 源代 ...

  7. leaflet-webpack 入门开发系列三地图分屏对比(附源码下载)

    前言 leaflet-webpack 入门开发系列环境知识点了解: node 安装包下载webpack 打包管理工具需要依赖 node 环境,所以 node 安装包必须安装,上面链接是官网下载地址 w ...

  8. IM即时通讯设计 高并发聊天服务:服务器 + qt客户端(附源码)

    来源:微信公众号「编程学习基地」 目录 IM即时通信程序设计 IM即时通讯 设计一款高并发聊天服务需要注意什么 如何设计可靠的消息处理服务 什么是粘包 什么是半包 解决粘包和半包 IM通信协议 应用层 ...

  9. openlayers4 入门开发系列之地图工具栏篇(附源码下载)

    前言 openlayers4 官网的 api 文档介绍地址 openlayers4 api,里面详细的介绍 openlayers4 各个类的介绍,还有就是在线例子:openlayers4 官网在线例子 ...

随机推荐

  1. POJ3539 Elevator

    Time Limit: 4000MS   Memory Limit: 65536KB   64bit IO Format: %lld & %llu Description Edward wor ...

  2. js 函数arguments一种用法

    无意改同事的代码发现的 function toggle(){ var _arguments=arguments; var count=0; $("#more").click(fun ...

  3. 【Visual Studio】工程类型选择win32还是x64 ?

    1. 如果将Visual Studio的工程类型设置为win32,那么我们开发的程序既能在32位操作系统上运行,也能在64位操作系统上运行. 2. 如果将Visual Studio的工程类型设置为x6 ...

  4. ajax 分页(jquery分页插件pagination) 小例3

    <#macro ajaxPaginte url > <script type="text/javascript"> var PageSize = 10;// ...

  5. PHP基础知识练习

    1 . PHP 指的是?(C ) A.Private Home Page B.Personal Hypertext Processor C.PHP: Hypertext Preprocessor D. ...

  6. linux下网络监控神器"iptraf-ng"

    优点:监控的网络信息很全面,安装和使用方便   #centos安装: #yum 源使用centos自带的base源即可. yum install -y iptraf-ng   #运行 iptraf-n ...

  7. HDU 6206 Apple

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6206 判断给定一点是否在三角形外接圆内. 给定三角形三个顶点的坐标,如何求三角形的外心的坐标呢? 知乎 ...

  8. 微信小程序踩坑之一【weui-wxss-master单选按钮图标修改思路】

    小程序原生所带的weui框架做小程序UI实在太方便了,但是他的一些细微变化也是让开发中碰到不少头疼的问题 一直以来单选多选的美化都是设计师重点表达的地方之一 而weui-wxss-master中的单选 ...

  9. [原创][SW]一些实用软件的小tips(长期更新)

    0. 简介 生活中我们经常使用许多的小工具或软件,来提高我们的工作效率,比如UltraEdit.Notepad++等.本文主要做一些记录,目的呢就是防止自己遗忘或者是快速的查询,来源是自己的摸索和网络 ...

  10. FZU 2224 An exciting GCD problem(GCD种类预处理+树状数组维护)同hdu5869

    题目链接:http://acm.fzu.edu.cn/problem.php?pid=2224 同hdu5869 //#pragma comment(linker, "/STACK:1024 ...