zoukankan      html  css  js  c++  java
  • WebRTC VoiceEngine综合应用示例(二)——音频通话的基本流程(转)

    下面将以实现一个音频通话功能为示例详细介绍VoiceEngine的使用,在文末将附上相应源码的下载地址。这里参考的是voiceenginevoe_cmd_test。

    第一步是创建VoiceEngine和相关的sub-apis

    // Create VoiceEngine related instance
    webrtc::VoiceEngine* ptrVoE = NULL;
    ptrVoE = webrtc::VoiceEngine::Create();
    
    webrtc::VoEBase* ptrVoEBase = NULL;
    ptrVoEBase = webrtc::VoEBase::GetInterface(ptrVoE);
    
    webrtc::VoECodec* ptrVoECodec = NULL;
    ptrVoECodec = webrtc::VoECodec::GetInterface(ptrVoE);
    
    webrtc::VoEAudioProcessing* ptrVoEAp = NULL;
    ptrVoEAp = webrtc::VoEAudioProcessing::GetInterface(ptrVoE);
    
    webrtc::VoEVolumeControl* ptrVoEVolume = NULL;
    ptrVoEVolume = webrtc::VoEVolumeControl::GetInterface(ptrVoE);
    
    webrtc::VoENetwork* ptrVoENetwork = NULL;
    ptrVoENetwork = webrtc::VoENetwork::GetInterface(ptrVoE);
    
    webrtc::VoEFile* ptrVoEFile = NULL;
    ptrVoEFile = webrtc::VoEFile::GetInterface(ptrVoE);
    
    webrtc::VoEHardware* ptrVoEHardware = NULL;
    ptrVoEHardware = webrtc::VoEHardware::GetInterface(ptrVoE);

    然后可以选择设置tracefile的路径,这里我们还会对麦克风以及回放的声音做一个录制,所以也一并指明路径。

    //Set Trace File and Record File
    const std::string trace_filename = "webrtc_trace.txt";
    VoiceEngine::SetTraceFilter(kTraceAll);
    error = VoiceEngine::SetTraceFile(trace_filename.c_str());
    if (error != 0)
    {
        printf("ERROR in VoiceEngine::SetTraceFile
    ");
        return error;
    }
    error = VoiceEngine::SetTraceCallback(NULL);
    if (error != 0)
    {
        printf("ERROR in VoiceEngine::SetTraceCallback
    ");
        return error;
    }
    const std::string play_filename = "recorded_playout.wav";
    const std::string mic_filename = "recorded_mic.wav";

    接下来是初始化,获取VoiceEngine的版本号

    //Init
    error = ptrVoEBase->Init();
    if (error != 0)
    {
        printf("ERROR in VoEBase::Init
    ");
        return error;
    }
    error = ptrVoEBase->RegisterVoiceEngineObserver(my_observer);
    if (error != 0)
    {
        printf("ERROR in VoEBase:;RegisterVoiceEngineObserver
    ");
        return error;
    }
    printf("Version
    ");
    char tmp[1024];
    error = ptrVoEBase->GetVersion(tmp);
    if (error != 0)
    {
        printf("ERROR in VoEBase::GetVersion
    ");
        return error;
    }
    printf("%s
    ", tmp);

    这里同时还注册了一个VoiceEngineObserver对象,可以根据相应的error code输出信息,比如当检测到键盘敲击的噪音时会给出提示。这个类的定义如下:

    class MyObserver : public VoiceEngineObserver
    {
    public:
        virtual void CallbackOnError(int channel, int err_code);
    };
    
    void MyObserver::CallbackOnError(int channel, int err_code)
    {
        // Add printf for other error codes here
        if (err_code == VE_TYPING_NOISE_WARNING)
        {
            printf("  TYPING NOISE DETECTED 
    ");
        }
        else if (err_code == VE_TYPING_NOISE_OFF_WARNING)
        {
            printf("  TYPING NOISE OFF DETECTED 
    ");
        }
        else if (err_code == VE_RECEIVE_PACKET_TIMEOUT)
        {
            printf("  RECEIVE PACKET TIMEOUT 
    ");
        }
        else if (err_code == VE_PACKET_RECEIPT_RESTARTED)
        {
            printf("  PACKET RECEIPT RESTARTED 
    ");
        }
        else if (err_code == VE_RUNTIME_PLAY_WARNING)
        {
            printf("  RUNTIME PLAY WARNING 
    ");
        }
        else if (err_code == VE_RUNTIME_REC_WARNING)
        {
            printf("  RUNTIME RECORD WARNING 
    ");
        }
        else if (err_code == VE_SATURATION_WARNING)
        {
            printf("  SATURATION WARNING 
    ");
        }
        else if (err_code == VE_RUNTIME_PLAY_ERROR)
        {
            printf("  RUNTIME PLAY ERROR 
    ");
        }
        else if (err_code == VE_RUNTIME_REC_ERROR)
        {
            printf("  RUNTIME RECORD ERROR 
    ");
        }
        else if (err_code == VE_REC_DEVICE_REMOVED)
        {
            printf("  RECORD DEVICE REMOVED 
    ");
        }
    }

    以上完成了前期准备的工作,下面首先开始对网络的设置。如果是在本机上进行测试的话,ip地址直接写127.0.0.1即可,同时要注意的是,remote port和local port要一致。

    //Network Settings
    int audiochannel;
    audiochannel = ptrVoEBase->CreateChannel();
    if (audiochannel < 0)
    {
        printf("ERROR in VoEBase::CreateChannel
    ");
        return audiochannel;
    }
    VoiceChannelTransport* voice_channel_transport = new VoiceChannelTransport(ptrVoENetwork, audiochannel);
    char ip[64] = "127.0.0.1";
    int rPort = 800;//remote port
    int lPort = 800;//local port
    error = voice_channel_transport->SetSendDestination(ip, rPort);
    if (error != 0)
    {
        printf("ERROR in set send ip and port
    ");
        return error;
    }
    error = voice_channel_transport->SetLocalReceiver(lPort);
    if (error != 0)
    {
        printf("ERROR in set receiver and port
    ");
        return error;
    }

    上面出现的VoiceChannelTransport类的定义如下

    // Helper class for VoiceEngine tests.
    class VoiceChannelTransport : public webrtc::test::UdpTransportData
    {
    public:
        VoiceChannelTransport(VoENetwork* voe_network, int channel);
    
        virtual ~VoiceChannelTransport();
    
        // Start implementation of UdpTransportData.
        void IncomingRTPPacket(const int8_t* incoming_rtp_packet,
            const size_t packet_length,
            const char* /*from_ip*/,
            const uint16_t /*from_port*/) override;
    
        void IncomingRTCPPacket(const int8_t* incoming_rtcp_packet,
            const size_t packet_length,
            const char* /*from_ip*/,
            const uint16_t /*from_port*/) override;
        // End implementation of UdpTransportData.
    
        // Specifies the ports to receive RTP packets on.
        int SetLocalReceiver(uint16_t rtp_port);
    
        // Specifies the destination port and IP address for a specified channel.
        int SetSendDestination(const char* ip_address, uint16_t rtp_port);
    
    private:
        int channel_;
        VoENetwork* voe_network_;
        webrtc::test::UdpTransport* socket_transport_;
    };
    
    
    VoiceChannelTransport::VoiceChannelTransport(VoENetwork* voe_network,
        int channel)
        : channel_(channel),
        voe_network_(voe_network)
    {
        uint8_t socket_threads = 1;
        socket_transport_ = webrtc::test::UdpTransport::Create(channel, socket_threads);
        int registered = voe_network_->RegisterExternalTransport(channel, *socket_transport_);
    #if !defined(WEBRTC_ANDROID) && !defined(WEBRTC_IOS)
        if (registered != 0)
            return;
    #else
        assert(registered == 0);
    #endif
    }
    
    VoiceChannelTransport::~VoiceChannelTransport()
    {
        voe_network_->DeRegisterExternalTransport(channel_);
        webrtc::test::UdpTransport::Destroy(socket_transport_);
    }
    
    void VoiceChannelTransport::IncomingRTPPacket(
        const int8_t* incoming_rtp_packet,
        const size_t packet_length,
        const char* /*from_ip*/,
        const uint16_t /*from_port*/)
    {
        voe_network_->ReceivedRTPPacket(channel_, incoming_rtp_packet, packet_length, PacketTime());
    }
    
    void VoiceChannelTransport::IncomingRTCPPacket(
        const int8_t* incoming_rtcp_packet,
        const size_t packet_length,
        const char* /*from_ip*/,
        const uint16_t /*from_port*/)
    {
        voe_network_->ReceivedRTCPPacket(channel_, incoming_rtcp_packet, packet_length);
    }
    
    int VoiceChannelTransport::SetLocalReceiver(uint16_t rtp_port)
    {
        static const int kNumReceiveSocketBuffers = 500;
        int return_value = socket_transport_->InitializeReceiveSockets(this, rtp_port);
        if (return_value == 0)
        {
            return socket_transport_->StartReceiving(kNumReceiveSocketBuffers);
        }
        return return_value;
    }
    
    int VoiceChannelTransport::SetSendDestination(const char* ip_address, uint16_t rtp_port)
    {
        return socket_transport_->InitializeSendSockets(ip_address, rtp_port);
    }

    完成了网络的设置后,进行编解码器的设置。这里简单的由用户选择使用哪一个编码器,当然还可以进一步对编码器的参数进行设置

    //Setup Codecs
    CodecInst codec_params;
    CodecInst cinst;
    for (int i = 0; i < ptrVoECodec->NumOfCodecs(); ++i)
    {
        int error = ptrVoECodec->GetCodec(i, codec_params);
        if (error != 0)
        {
            printf("ERROR in VoECodec::GetCodec
    ");
            return error;
        }
        printf("%2d. %3d  %s/%d/%d 
    ", i, codec_params.pltype, codec_params.plname, codec_params.plfreq, codec_params.channels);
    }
    printf("Select send codec: ");
    int codec_selection;
    scanf("%i", &codec_selection);
    ptrVoECodec->GetCodec(codec_selection, cinst);
    error = ptrVoECodec->SetSendCodec(audiochannel, cinst);
    if (error != 0)
    {
        printf("ERROR in VoECodec::SetSendCodec
    ");
        return error;
    }

    接下来进行录制设备和播放设备的设置

    //Setup Devices
    int rd(-1), pd(-1);
    error = ptrVoEHardware->GetNumOfRecordingDevices(rd);
    if (error != 0)
    {
        printf("ERROR in VoEHardware::GetNumOfRecordingDevices
    ");
        return error;
    }
    error = ptrVoEHardware->GetNumOfPlayoutDevices(pd);
    if (error != 0)
    {
        printf("ERROR in VoEHardware::GetNumOfPlayoutDevices
    ");
        return error;
    }
    
    char dn[128] = { 0 };
    char guid[128] = { 0 };
    printf("
    Playout devices (%d): 
    ", pd);
    for (int j = 0; j < pd; ++j)
    {
        error = ptrVoEHardware->GetPlayoutDeviceName(j, dn, guid);
        if (error != 0)
        {
            printf("ERROR in VoEHardware::GetPlayoutDeviceName
    ");
            return error;
        }
        printf("  %d: %s 
    ", j, dn);
    }
    
    printf("Recording devices (%d): 
    ", rd);
    for (int j = 0; j < rd; ++j)
    {
        error = ptrVoEHardware->GetRecordingDeviceName(j, dn, guid);
        if (error != 0)
        {
            printf("ERROR in VoEHardware::GetRecordingDeviceName
    ");
            return error;
        }
        printf("  %d: %s 
    ", j, dn);
    }
    
    printf("Select playout device: ");
    scanf("%d", &pd);
    error = ptrVoEHardware->SetPlayoutDevice(pd);
    if (error != 0)
    {
        printf("ERROR in VoEHardware::SetPlayoutDevice
    ");
        return error;
    }
    printf("Select recording device: ");
    scanf("%d", &rd);
    getchar();
    error = ptrVoEHardware->SetRecordingDevice(rd);
    if (error != 0)
    {
        printf("ERROR in VoEHardware::SetRecordingDevice
    ");
        return error;
    }

    然后对音频预处理功能进行设置,这里作为示例,把各种预处理功能都enable了

    //Audio Processing
    error = ptrVoECodec->SetVADStatus(0, 1);//FIX:why not use audio channel
    if (error != 0)
    {
        printf("ERROR in VoECodec::SetVADStatus
    ");
        return error;
    }
    error = ptrVoEAp->SetAgcStatus(1);
    if (error != 0)
    {
        printf("ERROR in VoEAudioProcess::SetAgcStatus
    ");
        return error;
    }
    error = ptrVoEAp->SetEcStatus(1);
    if (error != 0)
    {
        printf("ERROR in VoEAudioProcess::SetEcStatus
    ");
        return error;
    }
    error = ptrVoEAp->SetNsStatus(1);
    if (error != 0)
    {
        printf("ERROR in VoEAudioProcess::SetNsStatus
    ");
        return error;
    }
    error = ptrVoEAp->SetRxAgcStatus(audiochannel, 1);
    if (error != 0)
    {
        printf("ERROR in VoEAudioProcess::SetRxAgcStatus
    ");
        return error;
    }
    error = ptrVoEAp->SetRxNsStatus(audiochannel, 1);
    if (error != 0)
    {
        printf("ERROR in VoEAudioProcess::SetRxNsStatus
    ");
        return error;
    }

    至此,就可以开始发送、接收、录制了

    //Start Receive
    error = ptrVoEBase->StartReceive(audiochannel);
    if (error != 0)
    {
        printf("ERROR in VoEBase::StartReceive
    ");
        return error;
    }
    //Start Playout
    error = ptrVoEBase->StartPlayout(audiochannel);
    if (error != 0)
    {
        printf("ERROR in VoEBase::StartPlayout
    ");
        return error;
    }
    //Start Send
    error = ptrVoEBase->StartSend(audiochannel);
    if (error != 0)
    {
        printf("ERROR in VoEBase::StartSend
    ");
        return error;
    }
    //Start Record
    error = ptrVoEFile->StartRecordingMicrophone(mic_filename.c_str());
    if (error != 0)
    {
        printf("ERROR in VoEFile::StartRecordingMicrophone
    ");
        return error;
    }
    error = ptrVoEFile->StartRecordingPlayout(audiochannel, play_filename.c_str());
    if (error != 0)
    {
        printf("ERROR in VoEFile::StartRecordingPlayout
    ");
        return error;
    }

    在通话结束之后,还需要进行相应的stop elease

    //Stop Record
    error = ptrVoEFile->StopRecordingMicrophone();
    if (error != 0)
    {
        printf("ERROR in VoEFile::StopRecordingMicrophone
    ");
        return error;
    }
    error = ptrVoEFile->StopRecordingPlayout(audiochannel);
    if (error != 0)
    {
        printf("ERROR in VoEFile::StopRecordingPlayout
    ");
        return error;
    }
    //Stop Receive
    error = ptrVoEBase->StopReceive(audiochannel);
    if (error != 0)
    {
        printf("ERROR in VoEBase::StopReceive
    ");
        return error;
    }
    //Stop Send
    error = ptrVoEBase->StopSend(audiochannel);
    if (error != 0)
    {
        printf("ERROR in VoEBase::StopSend
    ");
        return error;
    }
    //Stop Playout
    error = ptrVoEBase->StopPlayout(audiochannel);
    if (error != 0)
    {
        printf("ERROR in VoEBase::StopPlayout
    ");
        return error;
    }
    //Delete Channel
    error = ptrVoEBase->DeleteChannel(audiochannel);
    if (error != 0)
    {
        printf("ERROR in VoEBase::DeleteChannel
    ");
        return error;
    }
    
    delete voice_channel_transport;
    
    ptrVoEBase->DeRegisterVoiceEngineObserver();
    error = ptrVoEBase->Terminate();
    if (error != 0)
    {
        printf("ERROR in VoEBase::Terminate
    ");
        return error;
    }
    
    int remainingInterfaces = 0;
    remainingInterfaces += ptrVoEBase->Release();
    remainingInterfaces = ptrVoECodec->Release();
    remainingInterfaces += ptrVoEVolume->Release();
    remainingInterfaces += ptrVoEFile->Release();
    remainingInterfaces += ptrVoEAp->Release();
    remainingInterfaces += ptrVoEHardware->Release();
    remainingInterfaces += ptrVoENetwork->Release();
    
    
    /*if (remainingInterfaces > 0)
    {
    printf("ERROR: Could not release all interfaces
    ");
    return -1;
    }*/
    
    bool deleted = webrtc::VoiceEngine::Delete(ptrVoE);
    if (deleted == false)
    {
        printf("ERROR in VoiceEngine::Delete
    ");
        return -1;
    }

    需要注意的是,这里remainingInterfaces最后不会为0,因为我们没有用到VoiceEngine的全部sub-apis。 

    至此,就实现了一个音频通话的功能。本项目源代码下载地址。github地址

    原文转自 http://blog.csdn.net/nonmarking/article/details/50577860

  • 相关阅读:
    小白自动化测试指南
    分布式性能测试框架用例方案设想(二)
    高QPS下的固定QPS模型
    测试自动化最佳实践【译】
    moco框架接口命中率统计实践
    基于docker的分布式性能测试框架功能验证(一)
    编写高质量代码:Web前端开发修炼之道(一)
    JavaScript中点操作符和中括号操作符区别
    Vue脚手架生成及配置
    Npm设置淘宝镜像
  • 原文地址:https://www.cnblogs.com/happykoukou/p/5765506.html
Copyright © 2011-2022 走看看