1. 程式人生 > >EasyDarwin開源流媒體雲平臺之語音對講功能設計與實現

EasyDarwin開源流媒體雲平臺之語音對講功能設計與實現

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;
}

獲取更多資訊

QQ交流群:288214068

Copyright © EasyDarwin.org 2012-2016

EasyDarwin