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

  • 相关阅读:
    菜根谭#245
    菜根谭#244
    菜根谭#243
    菜根谭#242
    菜根谭#241
    菜根谭#240
    菜根谭#239
    菜根谭#238
    菜根谭#237
    [转载]Linux 内核list_head 学习(一)
  • 原文地址:https://www.cnblogs.com/babosa/p/9217908.html
Copyright © 2011-2022 走看看