源代码下载地址:https://github.com/EasyDarwin orwww.easydarwin.org

在博客 在Darwin进行实时视频转发的两种模式 中,我们描写叙述了流媒体server对源端音视频转发的两种模式。当中一种#拉模式# 转发。在我们通常的项目中常常会用到。比方在传统视频监控行业,IP摄像机部署在监控内网的各个地点。我们须要将他们进行集中式的管理,而且对外公布,这时候我们就须要用到一台流媒体server,可以拉取所需的摄像机的音视频流,并做转化(如RTMP、HTTP等)。作为监控内网与公网的中转,提供转发服务。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveGllamlhc2h1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

#转发模块设计

拉模式转发中,转发server一方面作为RTSPclient的角色,向源端摄像机获取音视频数据。还有一方面作为server的角色,将拉取到的音视频数据。又一次作为数据源,分发给正在请求的client。这样,我们在设计中须要考虑到下面几点:

  • 源端数据流到server的数据流可以复用,也就是一路进多路出。
  • server端维护全部正在分发的摄像机源列表。
  • server与源端在空暇状态下无连接,仅仅有在有须要的情况下才发起连接过程。

  • 当全部client结束对某个源端请求时。server停止从源端获取数据。断开连接。
      Darwin系统已经具有了我们所需的一定条件:RTSPClientclient实现、RTP分发流程(ReflectorSession)。我们须要实现:Darwin拉模式转发模块,我们定义此模块名称为QTSSOnDemandRelayModule,意为仅仅有在有须要的时候,才会转发;Darwin与源端用于交互、保存信息、接收数据的ClientSession,为了不影响Darwin原有的架构,我们没有直接在RTSPClient类中改动。而是自己定义类:RTSPClientSession,实例化RTSPClient对象为其成员变量:



      在RTSPClientSession中。全部RTSP流程都由fClient(RTSPClient对象)完毕,RTSPClientSession负责进行变量存储(如server地址fAddr、portfPort、usernamefName、passwordfPassword)、收到数据包统计(fStates、fNumPacketReceived)、RTSPClient控制(SETUP发送fNumSetups、RTSP断开fTeardownImmediately)、以及在非client断开情况下。server与摄像机间的重连


#转发模块实现

我们命名拉模式转发模块名称为:QTSSOnDemandRelayModule。须要分别实现对RTSP和RTP的转发和处理。如此,我们会分别处理QTSS_RTSPPreProcessor_Role(RTSP消息处理)、QTSS_RTSPRelayingData_Role(拉取的RTP数据处理)、QTSS_ClientSessionClosing_Role(client或RTSPClientSession断开处理)。




*QTSS_RTSPPreProcessor_Role(RTSP消息处理)

我们设计的拉模式转发为名称与地址映射的方式,映射列表配置在xml文件里。在QTSSOnDemandRelayModule初始化时,我们就会将配置映射表载入到模块中,当然!我们也能够改动为读取数据库的方式:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveGllamlhc2h1/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" width="881" height="84" alt="">


比如。RTSP摄像机地址为:rtsp://218.204.223.237:554/live/1/66251FC11353191F/e7ooqwcfbqjoo80j.sdp,RTSP转发server地址为:8.8.8.8。port为:554。那么client请求:rtsp://8.8.8.8:554/ipC1,转发server就会向rtsp://218.204.223.237:554/live/1/66251FC11353191F/e7ooqwcfbqjoo80j.sdp
请求摄像机数据,获取后转发给client列表。

映射查找我们在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.sdp
RTSPClientSession* clientSes = NULL;
// 首先查找RTSPClientSession Hash表是否已经建立了相应摄像机的RTSPClientSession
StrPtrLen streamName(theUriStr);
OSRef* clientSesRef = sClientSessionMap->Resolve(&streamName);
if(clientSesRef != NULL)
{
clientSes = (RTSPClientSession*)clientSesRef->GetObject();
}
else
{
// 初次建立server与摄像机间的交互RTSPClientSession
clientSes = 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(client和RTSPClientSession断开处理)

ReflectorSessionclient引用数统计、client端断开流程、RTSPClientSession断开流程。基本与RTSPSession(client与设备推送)方法一样:
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)
{
// 当引用client数量为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


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

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


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

  1. 用Darwin开发RTSP级联服务器(拉模式转发)(附源码)

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

  2. EasyDarwin在做拉模式转发海康RTSP摄像机视频流的过程中出现花屏问题的解决方案

    问题描述 在3年前我当时基于EasyDarwin为用户开发了一款RTSP拉模式转发的程序,也发布了一篇博客<用Darwin开发RTSP级联服务器(拉模式转发)>,当时考虑的很简单,只要将R ...

  3. 基于EasyIPCamera实现的RTSP跨平台拉模式转发流媒体服务器

    本文转自博客:http://blog.csdn.net/xinlanbobo/article/details/53224445 上一篇博客<EasyIPCamera通过RTSP协议接入海康.大华 ...

  4. 【COCOS2D-HTML5 开发之三】演示样例项目附源代码及执行的GIF效果图

    本站文章均为李华明Himi原创,转载务必在明显处注明:(作者新浪微博:@李华明Himi) 转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/cocos2d- ...

  5. EasyDarwin流媒体服务器RTSP拉模式流媒体转发模块设计

    拉模式转发 拉模式转发,顾名思义就是服务器主动从源端(IPCamera.NVR.或者其他流媒体服务器)通过RTSP/RTP协议将流媒体音视频数据拉取到流媒体转发服务器,再通过内部分发调度机制,分发给请 ...

  6. js面向对象封装级联下拉菜单列表

    本实例开发的级联下拉菜单是根据已有json数据创建的DOM元素.点击文本框后,显示一级菜单.如果菜单中包含子菜单,菜单右侧会有指示箭头.点击菜单之后,会再显示下一级菜单,以此类推.当菜单下无子菜单时, ...

  7. 数据库开发基础-SQl Server 基础

    SQL Server 基础 1.什么是SQL Server SQL:Structured Query Language  结构化查询语言 SQL Server是一个以客户/服务器(c/s)模式访问.使 ...

  8. paip.java 开发中web server的选择jboss resin tomcat比较..

    paip.java 开发中web server的选择jboss resin tomcat比较.. 作者Attilax  艾龙, EMAIL:1466519819@qq.com 来源:attilax的专 ...

  9. JQuery和ASP.NET分别实现级联下拉框效果

    在学习JQuery之前知道下拉框的级联效果可以通过asp.net控件实现,现在学习了JQuery,知道了JQuery和select也能实现.我分别举两个小例子说明这两种方法如何实现. 1.用JQuer ...

随机推荐

  1. (19)python扩展

    当python程序遇到瓶颈时,可以考略扩展其他语言 例如:程序的某部分,需要高速度,或者与硬件交互时可以用到C语言.当其他语言有现成的程序,重新起来很麻烦时.有些功能用别的语言写更方便时 扩展语言有  ...

  2. HDU 多校1.11

  3. JavaScript代码放在head和body的区别(QRCode生成)

    1.在head中时,所代表的functions只加载而不执行,执行是在某一事件触发后才开始. 2.在body中时,直接加载并执行 典型的区别: 如果有不在函数中的执行语句,比如变量初始化,如果在hea ...

  4. Codeforces 863F - Almost Permutation

    863F - Almost Permutation 题意 给出每个位置可以放的数字的范围,定义 \(cost = \sum_{i=1}^{n}(cnt(i))^2\) ,其中 \(cnt(i)\) 为 ...

  5. 12、Flask实战第12天:子域名

    什么是子域名,我们的后台管理系统, 比如cms.heboan.com.配置子域名需要用到蓝图技术: 我现在buleprints下面创建一个cms.py 蓝图 from flask import Blu ...

  6. leetcode171 Excel Sheet Column Number

    题意: A -> 1 B -> 2 C -> 3 ... Z -> 26 AA -> 27 AB -> 28 思路:找规律,挺简单的···可是小地方错了一些搞了半天 ...

  7. 【分治】计算概论(A) / 函数递归练习(1)多边形游戏

    #include<cstdio> #include<algorithm> using namespace std; ],c[],s[]; int work(int L,int ...

  8. 8.2(java学习笔记)反射

    一.基础知识 对象是表示或封装一些数据,一个类被加载后JVM会创建一个对应该类的Class对象, 类的整个结构信息会被放在对应的对象中,通过这个对象我们可以获取改类的全部信息, 而这些操作称为反射. ...

  9. sql server博客

    SQLSERVER MSDN论坛 SQLSERVER 补丁博客 SQLSERVER中国研发中心 微软亚太区数据库技术支持组官方博客 PAUL的SQLSKILL网站 sqlsaturday网站 sqls ...

  10. IDEA中添加类的创建者信息

    创建方法: 1. 使用快捷键(ctrl + alt + s),在弹出框中左边侧选择 Editor -> File and Code Templates,左边侧相应会更新 右边侧选择 Class, ...