本文由EasyDarwin开源团队成员Alex贡献:http://blog.csdn.net/cai6811376/article/details/52006958

EasyDarwin云平台一直在稳步的升级迭代中,近日,EasyDarwin云平台实现了语音对讲的功能。对讲功能的加入,标志着EasyDarwin云平台进一步的完善。

  • 流程设计

    1. 客户端使用POST的方式在body中携带协议报文向云平台发送开始对讲命令;

    2. 云平台组织协议报文向指定的设备发送;

    3. 设备执行开始对讲命令后向云平台返回相应报文;

    4. 云平台将响应报文返回给客户端;

    5. 客户端请求开始对讲成功后,开始向云平台发送采集的音频数据;

    6. 云平台将音频数据转发给设备;

    7. 设备将音频数据播放出来;

    8. 客户端需要停止对讲时,向云平台发送停止对讲命令;

    9. 云平台转发给设备;

    10. 设备执行停止对讲后返回执行结果给云平台;

    11. 云平台将结果返回给客户端。

  • 协议设计
//客户端向云平台发送对讲报文
{
"EasyDarwin": {
"Body": {
"Channel": "0",
"Command": "START",
"AudioType": "G711A",
"Protocol": "ONVIF",
"Reserve": "1",
"Serial": "001001000058",
"Preset": "3",
"AudioData": "BASE64DATA",
"Pts": "20"
},
"Header": {
"CSeq": "1",
"MessageType": "MSG_CS_TALKBACK_CONTROL_REQ",
"Version": "1.0"
}
}
}

格式说明:

Serial:设备序列号;

Channel:摄像机通道号;

Protocol:指定预置位控制方式,ONVIF协议或者设备SDK;

Command:对讲控制命令,包括启动对讲、停止对讲、发送对讲数据(START/STOP/SENDDATA);

AudioType:音频数据类型,包括G711A/G726;

AudioData:音频数据,将音频数据通过Base64编码发送;

Pts:音频时间戳,开始为0,每20ms增加20;

Reserve:预留。

//云平台响应客户端请求
{
"EasyDarwin": {
"Body": {
"Channel": "0",
"Protocol": "ONVIF",
"Reserve": "1",
"Serial": "001001000058"
},
"Header": {
"CSeq": "1",
"ErrorNum": "200",
"ErrorString": "Success OK",
"MessageType": "MSG_SC_TALKBACK_CONTROL_ACK",
"Version": "1.0"
}
}
}
//云平台向设备发送对讲命令
{
"EasyDarwin": {
"Body": {
"Channel": "0",
"Command": "SENDDATA",
"AudioType": "G711A",
"AudioData": "BASE64DATA",
"Pts": "20",
"From": "f6a221eec46b47dea8ae1a2bd11f8d02",
"Protocol": "ONVIF",
"Reserve": "1",
"Serial": "001001000058",
"To": "245d6ec33cd247b7b7524219552db4d8",
"Via": "27823d2e8b6b4032b453d435a16b7be8"
},
"Header": {
"CSeq": "1",
"MessageType": "MSG_SD_CONTROL_TALKBACK_REQ",
"Version": "1.0"
}
}
}

格式说明:

From: EasyCMS接收Client访问的SessionID;

To: EasyCMS向Device发送报文的SessionID;

Via: EasyCMS的ServiceID;

//设备向云平台响应对讲命令
{
"EasyDarwin": {
"Body": {
"Channel": "0",
"From": "245d6ec33cd247b7b7524219552db4d8",
"Protocol": "ONVIF",
"Reserve": "1",
"Serial": "001001000058",
"To": "f6a221eec46b47dea8ae1a2bd11f8d02",
"Via": "27823d2e8b6b4032b453d435a16b7be8"
},
"Header": {
"CSeq": "1",
"ErrorNum": "200",
"ErrorString": "Success OK",
"MessageType": "MSG_DS_CONTROL_TALKBACK_ACK",
"Version": "1.0"
}
}
}
  • 实现
//EasyCMS HTTPSession.cpp execNetMsgCSTalkbackControlReq
QTSS_Error HTTPSession::execNetMsgCSTalkbackControlReq(const char* json)
{
if (!fAuthenticated)//没有进行认证请求
return httpUnAuthorized; EasyProtocol req(json); string strDeviceSerial = req.GetBodyValue(EASY_TAG_SERIAL);
string strChannel = req.GetBodyValue(EASY_TAG_CHANNEL);
string strProtocol = req.GetBodyValue(EASY_TAG_PROTOCOL);
string strReserve = req.GetBodyValue(EASY_TAG_RESERVE);
string strCmd = req.GetBodyValue(EASY_TAG_CMD);
string strAudioType = req.GetBodyValue(EASY_TAG_AUDIO_TYPE);
string strAudioData = req.GetBodyValue(EASY_TAG_AUDIO_DATA);
string strPts = req.GetBodyValue(EASY_TAG_PTS); string strCSeq = req.GetHeaderValue(EASY_TAG_CSEQ);//这个是关键字 if (strChannel.empty())
strChannel = "0";
if (strReserve.empty())
strReserve = "1"; OSRefTableEx* deviceMap = QTSServerInterface::GetServer()->GetDeviceSessionMap();
OSRefTableEx::OSRefEx* theDevRef = deviceMap->Resolve(strDeviceSerial);
if (theDevRef == NULL)//找不到指定设备
return EASY_ERROR_DEVICE_NOT_FOUND; OSRefReleaserEx releaser(deviceMap, strDeviceSerial);
//走到这说明存在指定设备
HTTPSession* pDevSession = static_cast<HTTPSession *>(theDevRef->GetObjectPtr());//获得当前设备回话 string errNo, errString;
if (strCmd == "SENDDATA")
{
if (!pDevSession->GetTalkbackSession().empty() && pDevSession->GetTalkbackSession() == fSessionID)
{
EasyProtocolACK reqreq(MSG_SD_CONTROL_TALKBACK_REQ);
EasyJsonValue headerheader, bodybody; char chTemp[16] = { 0 };
UInt32 uDevCseq = pDevSession->GetCSeq();
sprintf(chTemp, "%d", uDevCseq);
headerheader[EASY_TAG_CSEQ] = string(chTemp);//注意这个地方不能直接将UINT32->int,因为会造成数据失真
headerheader[EASY_TAG_VERSION] = EASY_PROTOCOL_VERSION; bodybody[EASY_TAG_SERIAL] = strDeviceSerial;
bodybody[EASY_TAG_CHANNEL] = strChannel;
bodybody[EASY_TAG_PROTOCOL] = strProtocol;
bodybody[EASY_TAG_RESERVE] = strReserve;
bodybody[EASY_TAG_CMD] = strCmd;
bodybody[EASY_TAG_AUDIO_TYPE] = strAudioType;
bodybody[EASY_TAG_AUDIO_DATA] = strAudioData;
bodybody[EASY_TAG_PTS] = strPts;
bodybody[EASY_TAG_FROM] = fSessionID;
bodybody[EASY_TAG_TO] = pDevSession->GetValue(EasyHTTPSessionID)->GetAsCString();
bodybody[EASY_TAG_VIA] = QTSServerInterface::GetServer()->GetCloudServiceNodeID(); reqreq.SetHead(headerheader);
reqreq.SetBody(bodybody); string buffer = reqreq.GetMsg();
StrPtrLen theValue(const_cast<char*>(buffer.c_str()), buffer.size());
pDevSession->SendHTTPPacket(&theValue, false, false); errNo = EASY_ERROR_SUCCESS_OK;
errString = EasyProtocol::GetErrorString(EASY_ERROR_SUCCESS_OK);
}
else
{
errNo = EASY_ERROR_CLIENT_BAD_REQUEST;
errString = EasyProtocol::GetErrorString(EASY_ERROR_CLIENT_BAD_REQUEST);
goto ACK;
}
}
else
{
if (strCmd == "START")
{
if (pDevSession->GetTalkbackSession().empty())
{
pDevSession->SetTalkbackSession(fSessionID);
errNo = EASY_ERROR_SUCCESS_OK;
errString = EasyProtocol::GetErrorString(EASY_ERROR_SUCCESS_OK);
}
else
{
errNo = EASY_ERROR_CLIENT_BAD_REQUEST;
errString = EasyProtocol::GetErrorString(EASY_ERROR_CLIENT_BAD_REQUEST);
goto ACK;
}
}
else if (strCmd == "STOP")
{
if (pDevSession->GetTalkbackSession().empty() || pDevSession->GetTalkbackSession() != fSessionID)
{
errNo = EASY_ERROR_CLIENT_BAD_REQUEST;
errString = EasyProtocol::GetErrorString(EASY_ERROR_CLIENT_BAD_REQUEST);
goto ACK;
}
else
{
pDevSession->SetTalkbackSession("");
errNo = EASY_ERROR_SUCCESS_OK;
errString = EasyProtocol::GetErrorString(EASY_ERROR_SUCCESS_OK);
}
} EasyProtocolACK reqreq(MSG_SD_CONTROL_TALKBACK_REQ);
EasyJsonValue headerheader, bodybody; char chTemp[16] = { 0 };
UInt32 uDevCseq = pDevSession->GetCSeq();
sprintf(chTemp, "%d", uDevCseq);
headerheader[EASY_TAG_CSEQ] = string(chTemp);//注意这个地方不能直接将UINT32->int,因为会造成数据失真
headerheader[EASY_TAG_VERSION] = EASY_PROTOCOL_VERSION; bodybody[EASY_TAG_SERIAL] = strDeviceSerial;
bodybody[EASY_TAG_CHANNEL] = strChannel;
bodybody[EASY_TAG_PROTOCOL] = strProtocol;
bodybody[EASY_TAG_RESERVE] = strReserve;
bodybody[EASY_TAG_CMD] = strCmd;
bodybody[EASY_TAG_AUDIO_TYPE] = strAudioType;
bodybody[EASY_TAG_AUDIO_DATA] = strAudioData;
bodybody[EASY_TAG_PTS] = strPts;
bodybody[EASY_TAG_FROM] = fSessionID;
bodybody[EASY_TAG_TO] = pDevSession->GetValue(EasyHTTPSessionID)->GetAsCString();
bodybody[EASY_TAG_VIA] = QTSServerInterface::GetServer()->GetCloudServiceNodeID(); reqreq.SetHead(headerheader);
reqreq.SetBody(bodybody); string buffer = reqreq.GetMsg();
StrPtrLen theValue(const_cast<char*>(buffer.c_str()), buffer.size());
pDevSession->SendHTTPPacket(&theValue, false, false);
} ACK:
char chTemp[16] = { 0 };
UInt32 uDevCseq = pDevSession->GetCSeq();
sprintf(chTemp, "%d", uDevCseq); EasyProtocolACK rsp(MSG_SC_TALKBACK_CONTROL_ACK);
EasyJsonValue header, body;
body[EASY_TAG_SERIAL] = strDeviceSerial;
body[EASY_TAG_CHANNEL] = strChannel;
body[EASY_TAG_PROTOCOL] = strProtocol;
body[EASY_TAG_RESERVE] = strReserve; header[EASY_TAG_VERSION] = EASY_PROTOCOL_VERSION;
header[EASY_TAG_CSEQ] = string(chTemp);;
header[EASY_TAG_ERROR_NUM] = errNo;
header[EASY_TAG_ERROR_STRING] = errString; rsp.SetHead(header);
rsp.SetBody(body);
string msg = rsp.GetMsg();
StrPtrLen theValueAck(const_cast<char*>(msg.c_str()), msg.size());
this->SendHTTPPacket(&theValueAck, false, false); return QTSS_NoErr;
}
//EasyCamera EasyCameraSource.cpp ControlTalkback
QTSS_Error EasyCameraSource::ControlTalkback(Easy_CameraTalkback_Params* params)
{
QTSS_Error result = QTSS_RequestFailed; if (cameraLogin())
{
HI_S32 error = HI_SUCCESS;
switch (params->inCommand)
{
case EASY_TALKBACK_CMD_TYPE_START:
{
int type = 1;
if (params->inType == EASY_TALKBACK_AUDIO_TYPE_G711A)
type = 1;
else if (params->inType == EASY_TALKBACK_AUDIO_TYPE_G726)
type = 4;
error = HI_NET_DEV_StartVoice(m_u32Handle, type);
}
break;
case EASY_TALKBACK_CMD_TYPE_SENDDATA:
{
if (params->inBuff == NULL || params->inBuffLen == 0)
{
result = QTSS_BadArgument;
break;
}
int len = params->inBuffLen;
int offset = 0;
int pts = params->inPts;
while (len >= BUFFLEN)
{
AddHead(fTalkbackBuff, (char*)params->inBuff + offset, BUFFLEN);
error = HI_NET_DEV_SendVoiceData(m_u32Handle, fTalkbackBuff, HSBUFFLEN, pts);
offset += BUFFLEN;
len -= BUFFLEN;
pts += PTSPER;
}
}
break;
case EASY_TALKBACK_CMD_TYPE_STOP:
error = HI_NET_DEV_StopVoice(m_u32Handle);
break;
default:
result = QTSS_RequestFailed;
break;
} if (error == HI_SUCCESS)
{
result = QTSS_NoErr;
}
else
{
result = QTSS_RequestFailed;
}
} return result;
}

代码与文档

EasyDarwin Github:https://github.com/easydarwin

EasyDarwin开源流媒体云平台协议文档:https://github.com/EasyDarwin/EasyDarwin/blob/master/Doc/EasyDarwin%20Protocol.pdf

获取更多信息

邮件:support@easydarwin.org

WEB:www.EasyDarwin.org

QQ交流群:496258327

Copyright © EasyDarwin.org 2012-2016

EasyDarwin开源流媒体云平台之语音对讲功能设计与实现的更多相关文章

  1. EasyCamera Android安卓移动视频监控单兵设备接入EasyDarwin开源流媒体云平台

    前言 随着Android系统的不断更新和发展,现在越来越多的硬件产品选择用安卓系统作为运行环境,电视机,机顶盒.门禁.行车记录仪.车载系统.单兵设备等等,Android系统底层还是Linux,但对上层 ...

  2. EasyDarwin开源流媒体云平台支持EasyCamera摄像机、EasyCamera手机直播监控、EasyNVR等多终端接入

    云平台架构 EasyDarwin开源流媒体云平台目前已经包括了EasyCMS中心管理服务.EasyDarwin流媒体服务.EasyCamera设备端(支持Arm_Linux.Android.PC).E ...

  3. EasyDarwin开源流媒体云平台设计与实现(分布式+负载均衡)

    前言 早在2013年我就设计了一套架构非常简单的分布式流媒体服务器平台<基于Darwin实现的分布式流媒体直播服务器系统>,当时的考虑如今看来有诸多的细节点没有考虑到:1.CMS是单点部署 ...

  4. EasyDarwin开源流媒体云平台之EasyRMS录播服务器功能设计

    需求背景 EasyDarwin开发团队维护EasyDarwin开源流媒体服务器也已经很多年了,之前也陆陆续续尝试过很多种服务端录像的方案,有:在EasyDarwin中直接解析收到的RTP包,重新组包录 ...

  5. EasyDarwin开源流媒体云平台之云台ptz控制设计与实现

    本文转自EasyDarwin开源团队成员Alex的博客:http://blog.csdn.net/cai6811376/article/details/51912692 近日,EasyDarwin云平 ...

  6. EasyDarwin开源流媒体云平台VS调试断点提示“还没有为该文档加载任何符号”的解决办法

    本文转自EasyDarwin开源团队成员Alex的博客:http://blog.csdn.net/cai6811376/article/details/52063666 近日,我们EasyDarwin ...

  7. EasyDarwin开源流媒体云平台中boost Base64编解码后与源长度不匹配的bug

    本文转自EasyDarwin团队Alex的博客:http://blog.csdn.net/cai6811376 EasyDarwin云平台中部分协议使用了Base64编码昨晚报文通信的载体.比如在对摄 ...

  8. 开源流媒体云视频平台EasyDarwin中EasyCMS服务是如何进行命令转发和消息路由的

    EasyCMS介绍 EasyCMS做为EasyDarwin开源流媒体云平台解决方案的一部分,主要进行的是设备的接入和Session(DeviceSession & ClientSession) ...

  9. EasyDarwin开源流媒体项目

    本文转自EasyDarwin CSDN官方博客:http://blog.csdn.net/easydarwin EasyDarwin是由国内开源流媒体团队维护和迭代的一整套开源流媒体视频平台框架,从2 ...

随机推荐

  1. Office 365 切换语言设置

    The steps of changing the language: Click “Setting                         ”>”Office 365 Setting” ...

  2. 标准C程序设计七---32

    Linux应用             编程深入            语言编程 标准C程序设计七---经典C11程序设计    以下内容为阅读:    <标准C程序设计>(第7版) 作者 ...

  3. 图片点击放大并可点击旋转插件(1)-jquery.artZoom.js

    1.首先加入链接: <script type="text/javascript" src="js/jquery-1.6.1.min.js">< ...

  4. liteos事件(六)

    1. 概述 1.1 基本概念 事件是一种实现任务间通信的机制,可用于实现任务间的同步,但事件通信只能是事件类型的通信,无数据传输.一个任务可以等待多个事件的发生:可以是任意一个事件发生时唤醒任务进行事 ...

  5. 空扫描Idle Scanning

    空扫描Idle Scanning   空扫描Idle Scanning是一种借助第三方实施的端口扫描技术,可以很好的隐蔽扫描主机本身.它的实现基于以下两个TCP工作机制.   (1)在TCP三次握手阶 ...

  6. Windows下Python的虚拟环境

    前言 在开发python的应用程序的时候,有时候会遇到依赖包的版本问题,比如之前开发tensorflow应用的时候需要用到python3.5以下的版本(还是python3),但是日常做其它应用的时候用 ...

  7. 小W旅游railway

    对于一家铁路公司,我们可以首先使用 Floyd 算法求出任 意两点 x, y 间只经过属于该家铁路公司铁路的最短路,那么在新 图中我们在 x, y 间加一条 x 到 y 最短路对应的花费为边权的边. ...

  8. Utuntu 和 window共享文件

    由于自己想用服务器跑代码,数据集和模型一般都在本机电脑上,用实验室服务器需要拷贝数据或者,在服务器上重新下载数据很麻烦 都在局域网内可以实现文件共享,代码和数据都在本地,共享给服务器,只需要使用服务器 ...

  9. C# Http方式下载文件到本地

    下文代码是从网络(http://www.cnblogs.com/hayden/archive/2012/04/26/2472815.html)得来,亲测好用.我中修改了下格式和注释,版权属于原作者“舒 ...

  10. sql的一些知识_where

    简单相同查询,查询age=22的三列信息,并按照降序排列 ORDER BY weight DESC 此外,where还支持一下判断操作符 值得注意的是,如果比较的值为数值,则不需要加引号,如果是字符, ...