zoukankan      html  css  js  c++  java
  • EasyDarwin开源流媒体云平台之语音对讲功能设计与实现

    本文由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. 云平台将结果返回给客户端。

    tb

    • 协议设计
    //客户端向云平台发送对讲报文
    {
       "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

  • 相关阅读:
    《ASP.NET Core跨平台开发从入门到实战》Web API自定义格式化protobuf
    .NET Core中文分词组件jieba.NET Core
    .NET Core 2.0及.NET Standard 2.0
    Visual Studio 2017 通过SSH 调试Linux 上.NET Core
    Visual Studio 2017 ASP.NET Core开发
    Visual Studio 2017正式版离线安装及介绍
    在.NET Core 上运行的 WordPress
    IT人员如何开好站立会议
    puppeteer(二)操作实例——新Web自动化工具更轻巧更简单
    puppeteer(一)环境搭建——新Web自动化工具(同selenium)
  • 原文地址:https://www.cnblogs.com/babosa/p/5904622.html
Copyright © 2011-2022 走看看