基于实时音视频技术的远程控制传输SDK的功能设计

一直以来从pc anywhere 到vnc 以及windows的自带远程工具,后来疫情加速了todesk的成功,占据了向日葵的远控市场,还有开源的rustdesk,硬件版的pikvm,以及久负盛名的云游戏sunshine moonlight。所有的远控工具都在追求极速,小码流,画质以及私密连接,这些努力在多年以后引来了巨大的行业技术的发展,特别是云游戏的现金诱惑,让这门技术变得越来越值钱。

经过多年的积累,在音视频的应用过程中,如何生成一套自己的sdk来适应这些新的快速变化的音视频应用场景,我做了一些工程化的尝试实践,并取得了一些成果。

流媒体的极速传输一定是非webrtc莫属,但是webrtc的传输又有很多Qos的设计,会影响画质和延迟,需要基于webrtc传输来实现自己的数据传输应用层协议,在延迟和质量间做取舍是一门工程学的艺术,比如工程实践中可以尽量采用单一IDR帧,在非必须情况下可以只传p帧以减少传输量,采集编码采用零拷贝以降低cpu消耗和延迟。当然这些实践需要耗费很多精力和时间去摸索,兴趣和正向激励都不可或缺,我也在路上不停的给自己打气。

我没有科班经历,所有的都是野路子,写的代码也不是那么的优雅,大部分都是我实践的一些经验,希望大佬们看到了不要笑话,在下会虚心学习各位的意见建议,下面是部分sdk的使用代码,可以实现信令,从远端拉流,本地发布并向远端传输流,支持多种流的协议,支持极速H265编码传输,可以快速的部署到windows, linux, android等平台设备,方便的集成sunshine的云游戏的生态,能够对等的进行音视频以及文件的互传互通,本地流管理方便的采用了订阅发布机制,可以将复杂的业务逻辑转换成简单的生产消费,包括级联也是可以的。在ipc监控,远程控制,群组通话等应用场景均可以实现简单的接入,甚至同时拥有这些功能,驻留后台服务既是应用也是边缘服务器,可以组网也可以p2p。下面的代码算是抛砖引玉吧,在大环境不好的当下,希望结识优秀的你共同学习,创造更多可能性

复制代码
//
// Created by xiang on 2023/7/14.
//

#include "gomediainterface.h"
#include "macro.h"
#include "Nalu.h"
extern "C" {
#include <libgomedia.h> // 根据实际情况包含生成的头文件
}

#include <boost/log/expressions.hpp>
#include "../logging.h"

static void *pullThread(void* self){
    gomediainterface* tself = (gomediainterface*)self;
    if(tself) {
        LOGD("pullThread %p", tself);
        LOGD("muri %s", tself->GetUri());
        //CallRtspClientStart(reinterpret_cast<void*>(tself->GetgoInstance()),tself->GetUri());
        CallRtspClientStart2(reinterpret_cast<void *>(tself->GetgoInstance()), tself->GetUri(),tself->GetChannel());
    }
    return 0;
}
static void *pullWebrtcThread(void *self){
    gomediainterface* tself = (gomediainterface*)self;
    if(tself) {
        tself->StartWebRTCPull();
    }
    return 0;
}
void OnSendPacket(void * callclass, uint8_t* data, int length,int mediatype,int channel,int inject,int duration) {
    if(callclass){
        gomediainterface *rtspclient=(gomediainterface*)callclass;
        if(data&&length>0) {
            rtspclient->onMediaDataReceive(data, length, mediatype, channel, inject, duration);
        }
    }
}

    extern int startkvmstream(char* displayname, int width, int height, int pixfmt, int fps, int gop, int ratekbps, int audiomuted, int playerid, int channel);
    extern void StopVideoStream(char* videopath, int playerid, int channel);
    extern void ReqIDR(int playerid, int channel);
    extern void AbsMouseMove(int playerid, int channel, int x, int y);
    extern void RelMouseMove(int playerid, int channel, int x, int y);
    extern void MouseUp(int playerid, int channel, int button);
    extern void MouseDown(int playerid, int channel, int button);
    extern void KeyUp(int playerid, int channel, int key,int modifiers);
    extern void KeyDown(int playerid, int channel, int key, int modifiers);
    extern void KeyTap(int playerid, int channel, int key,int modifiers);
    extern void MouseWheel(int playerid, int channel, int delta);
    extern void KeyProcess(int playerid, int channel, char* keystr, bool isKeyDown);
    extern void ClipBoardText(int playerid, int channel, char* textstr);
    extern void SwitchCursor(int playerid, int channel, int ndisplay);
    extern void *GetKVMVideoConfigInfo();
    // void OnKeyUp(int playerid, int channelid, int key, int modifiers);
void OnClipBoardText(int playerid, int channelid, char* textstr) {
        ClipBoardText(playerid, channelid,textstr);
}
void OnKeyProcess(int playerid, int channelid, char* keystr, int isKeyDown) {
        KeyProcess(playerid, channelid, keystr, isKeyDown==0?false:true);
}
void OnKeyUp(int playerid, int channelid, int key, int modifiers) {
        KeyUp(playerid, channelid, key, modifiers);
}
// void OnKeyDown(int playerid,int channelid,int key,int modifiers);
void OnKeyDown(int playerid, int channelid, int key, int modifiers) {
    KeyDown(playerid, channelid, key, modifiers);
}
void OnKeyTap(int playerid, int channelid, int key, int modifiers) {
    KeyTap(playerid, channelid, key, modifiers);
}
void OnAbsMouseMove(int playerid, int channel, int x, int y) {
    AbsMouseMove(playerid, channel, x, y);
}
void OnRelMouseMove(int playerid, int channel, int x, int y) {
    RelMouseMove(playerid, channel, x, y);
}
void OnMouseUp(int playerid, int channel, int button) {
    MouseUp(playerid, channel, button);
}
void OnMouseDown(int playerid, int channel, int button) {
    MouseDown(playerid, channel, button);
}
void OnMouseWheel(int playerid, int channelid, int delta) {
    MouseWheel(playerid, channelid, delta);
}
void OnSwitchCursor(int playerid, int channel,int ndisplay) {
    SwitchCursor(playerid, channel,ndisplay);
}
// void OnStartTransFile(char* filename) {
    
// }
void OnStartKVM(char* displayname, int width, int height, int pixfmt, int fps, int gop, int ratekbps, int audiomuted, int playerid, int channel) {
    LOGI("I go command from client to server start kvm %s %d/%d %s fps:%d gop:%d playerid:%d channelid:%d", displayname, width, height, (pixfmt == 0) ? "H264" : (pixfmt == 1) ? "H265" : "AV1",fps,gop, playerid, channel);
    startkvmstream(displayname, width, height, pixfmt, fps,gop,ratekbps, audiomuted,playerid, channel);
}
void OnStopKVM(char* displayname,int playerid, int channel) {
    LOGI("I go command from client to server stop kvm %s %d %d",displayname,playerid,channel);
    StopVideoStream(displayname,  playerid, channel);
}
void OnReqIDR(int playerid, int channel) {
    LOGI("I go command from client to server req idr  %d %d",playerid,channel);
    ReqIDR(  playerid, channel);
}
void AndroidLogD(char* msg) {
    // BOOST_LOG(info) << "gomedia msg "sv << msg;
    LOGD("go msg %s", msg);
}
//cgo回调获取rtsp音频参数
void OnAudioCodecConfig(void* callclass, unsigned char* data, int length,void * mpeg4audioconf, int mediatype,int channel) {
    LOGI("OnAudioCodecConfig %d %d %d", mediatype, length,channel);
//    dumphex("OnCodecConfig", data, length);
    MPEG4AudioConfig *mpeg4AudioConfig = (MPEG4AudioConfig *) mpeg4audioconf;
    LOGD("OnAudioCodecConfig SampleRate %d ChannelLayout %d ObjectType %d SampleRateIndex %d ChannelConfig %d ",
         mpeg4AudioConfig->SampleRate,
         mpeg4AudioConfig->ChannelLayout,
         mpeg4AudioConfig->ObjectType,
         mpeg4AudioConfig->SampleRateIndex,
         mpeg4AudioConfig->ChannelConfig);
    if(callclass){
        gomediainterface *rtspclient=(gomediainterface*)callclass;
        if(mediatype==av_AAC){ //aac
            rtspclient->SetConfBuf(data,length);
            rtspclient->SetAudioParameter(mpeg4AudioConfig->SampleRate,mpeg4AudioConfig->ChannelConfig);
        }

    }
}
// cgo回调获取rtsp视频参数
void OnVideoCodecConfig(void* callclass, void* configdata, int mediatype,int channel) {
    LOGI("OnVideoCodecConfig %d %d",mediatype,channel);
    VideoCodecData* codecData = (VideoCodecData*)configdata;
    LOGI("OnCodecConfig w/d %d:%d FPS %d ProfileIdc %d",codecData->Width,codecData->Height,codecData->FPS,codecData->ProfileIdc);
    if(callclass){
        gomediainterface *rtspclient=(gomediainterface*)callclass;
        if(mediatype==av_H264||mediatype==av_H265){
        // 将接收到的数据转换为兼容的结构体类型
            VideoCodecData* codecData = (VideoCodecData*)configdata;
            // 使用 codecData 中的字段进行处理
            rtspclient->SetVideoParameter(codecData->Width,codecData->Height,codecData->FPS,mediatype);
        }
    }
    // 在这里进行进一步处理...
}
void OnGoMediaStatus(void* callclass,int status,int channel) {
    LOGD("go msg OnGoMediaStatus status %d",status);
    if(callclass){
        gomediainterface *rtspclient=(gomediainterface*)callclass;
        auto statusset=rtspclient->GetMediaStatusCallBack();
        if(statusset){
            statusset(status,channel);
        }

    }

}
//内部断线超时关闭时回调,让上次做自动关闭动作
void OnVideoClose(void* callclass,int videoid){
    LOGD("go msg OnVideoClose videoid %d",videoid);
    if(callclass) {
        gomediainterface *rtspclient = (gomediainterface *) callclass;
        auto callb=rtspclient->GetOnVideoCloseCallBack();
        if(callb){
            callb(videoid);
        }
    }
}
void OnAudioClose(void* callclass,int cameraid){
    LOGD("go msg OnAudioClose videoid %d",cameraid);
    if(callclass) {
        gomediainterface *rtspclient = (gomediainterface *) callclass;
        auto callb=rtspclient->GetOnAudioCloseCallBack();
        if(callb){
            callb(cameraid);
        }
    }
}
std::mutex gomediamutex;
std::vector<gomediainterface*> g_GomediaInterfaces;
void _AddGlobleGoWebrtcInterface(gomediainterface *&go_interface){
    std::lock_guard<std::mutex> lock(gomediamutex);
    g_GomediaInterfaces.push_back(go_interface);
    LOGI("_AddGlobleGoWebrtcInterface  %d ",g_GomediaInterfaces.size());
}
void _ReleaseGlobleGoWebrtcInterface(){
    std::lock_guard<std::mutex> lock(gomediamutex);
    g_GomediaInterfaces.clear();

}
void OnFileDataReceived(void* callclass, int channel, int len, unsigned char* data){
        if (callclass){
            LOGD("on file data %d",len);
            //onFileData received        
        }
}
void OnHIDDataReceived(void* callclass, int channel, int len, unsigned char* data){
        if (callclass){
            LOGD("on hid data %d",len);
            //onHIDData received        
        }
}

//呼叫回调,做响铃提醒,并在app层调用流发布和应答开启对讲
void OnTalkCallIn(char *userid){

    LOGD("have %s call in \n",userid);
    const char *msg="just test";
    for (int i = 0; i < g_GomediaInterfaces.size(); i ++){
        auto interface=g_GomediaInterfaces[i];
        LOGD("g_GomediaInterfaces[%d]=%p",i,interface);
        if(interface){
        }
     }
//    char *oncallmsg="agree call in";
//    AgreeTalk((void*)oncallrtsp->GetgoInstance(),userid,2,1,oncallmsg);
}
gomediainterface::gomediainterface(int channel){
    goInstance = NewMediaImplementation();
    if(goInstance){
        LOGD("goInstance %lu",goInstance);
    }else{
        LOGE("NewMediaImplementation FAILED");
    }
    SetCallbackWrapper(reinterpret_cast<void*>(goInstance), reinterpret_cast<void*>(this),channel);
//    mChannel=channel;

}
gomediainterface::gomediainterface(const char *uri,int channel){
    if(mUri==nullptr){
        mUri=new char[strlen(uri)+1];
        if(mUri!=nullptr){
            strcpy(mUri,uri);
            LOGD("mUri %s",mUri);
        }

    }
    goInstance = NewMediaImplementation();
    if(goInstance){
        LOGD("goInstance %lu",goInstance);
    }else{
        LOGE("NewMediaImplementation FAILED");
    }
    SetCallbackWrapper(reinterpret_cast<void*>(goInstance), reinterpret_cast<void*>(this),channel);
    mChannel=channel;

}
//构造函数直接启动webrtc信令
gomediainterface::gomediainterface(const char *uri,int channel,char *mqttserver,char* sn,char *clientid,char * stunserver,char * turnserver ,char *turnservername,char * turnserverpassword,bool busewebrtc){
    if(mUri==nullptr){
        mUri=new char[strlen(uri)+1];
        if(mUri!=nullptr){
            strcpy(mUri,uri);
            LOGD("mUri %s",mUri);
        }

    }
    goInstance = NewMediaImplementation();
    if(goInstance)    LOGD("goInstance %lu",goInstance);
    SetCallbackWrapper(reinterpret_cast<void*>(goInstance), reinterpret_cast<void*>(this),channel);
    mChannel=channel;
    if(busewebrtc){
        SetWebRTCConfig(mqttserver, sn, clientid,
                        stunserver, turnserver, turnservername,
                        turnserverpassword);
    }
}
gomediainterface::~gomediainterface(){
    if(mUri!=nullptr){
        delete mUri;
        mUri = nullptr;
    }
    if(mServername) {
        delete mServername;
        mServername=nullptr;
    }
    if(mStreamname) {
        delete mStreamname;
        mStreamname=nullptr;
    }
    if(mTransmode) {
        delete mTransmode;
        mTransmode=nullptr;
    }
    if(opusdecoder){
        delete opusdecoder;
        opusdecoder=nullptr;
    }
    if(aacDecoder) {
        delete aacDecoder;
        aacDecoder = nullptr;
    }
#if USE_MIXER
    if(audiomixer) {
        delete audiomixer;
        audiomixer= nullptr;
    }
#endif

    if(goInstance){//销毁 goInstance
        RemoveMediaImplementation(reinterpret_cast<void*>(goInstance));
        goInstance=0;
    }
}
void gomediainterface::StartRtspClient()
{
    // pull_rtsp_client_thread=std::thread(&rtspClient::pullThread, this,this);

    pthread_create(&pull_rtsp_client_thread, 0, pullThread, (void*)this);
}
void gomediainterface::GetRtspConfig(int channel){
    LOGD("GetRtspConfig");
    GetMediaAudioExternData(reinterpret_cast<void*>(goInstance),channel);
}
void gomediainterface::StopRtspClient(int channel){
    LOGD("gomediainterface::StopRtspClient close ...");
    CallRtspClientStop(reinterpret_cast<void*>(goInstance),channel);
    if(pull_rtsp_client_thread){
        pthread_join(pull_rtsp_client_thread,NULL);
    }
    LOGD("gomediainterface::StopRtspClient closed");
    // pull_rtsp_client_thread.join();
}
int gomediainterface::PublishStream(char *streamname,int channel,int mediatype,int card,int device){
    int deviceid=-1;
    switch(mediatype) {
        case av_H264:
        case av_H265:
            deviceid = CallStartVideoStreamPublish((void *) (goInstance), streamname, channel);
            LOGD("\ngomediainterface PublishStream deviceid is %d\n", deviceid);
            return deviceid;
        case av_OPUS:
            deviceid=CallStartAudioStreamPublish((void*)(goInstance),streamname,card,device,channel);
            LOGD("\ngomediainterface PublishStream deviceid is %d\n",deviceid);
            return deviceid;
        default:
            break;
    }
    return deviceid;
//        mVideodeviceID=deviceid;
}
//启动从节点servername拉流streamname,生成级联拉流并生成本地流供本地处理和其他节点共享
// 采用transmode "webrtc_trans" 或者"datachannel_trans"传输模式 H265目前支持支datachannel_trans,
// bOnDataCall表示允许回调供渲染,不回调数据则只做数据级联生成本地流
void gomediainterface::SetWebRTCPullStream(char *servername, char *streamname,char * transmode,bool bIsH265,int bUseVideo,int bUseAudio,int bOnDataCall,int signalid,int channel){
    if(mServername) {
        delete mServername;
        mServername=nullptr;
    }
    if(mStreamname) {
        delete mStreamname;
        mStreamname=nullptr;
    }
    if(mTransmode) {
        delete mTransmode;
        mTransmode=nullptr;
    }
    mServername=new char(strlen(servername)+1);
    strcpy(mServername,servername);
    mStreamname=new char(strlen(streamname)+1);
    strcpy(mStreamname,streamname);
    mTransmode=new char(strlen(transmode)+1);
    strcpy(mTransmode,transmode);
    mSignalid=signalid;
    mbIsH265=bIsH265;
    mbUseVideo=bUseVideo;
    mbUseAudio=bUseAudio;
    mbOnDataCall=bOnDataCall;
    mChannel=channel;
}
void gomediainterface::StartWebRTCPullStream() {
    pthread_create(&pull_webrtc_client_thread, 0, pullWebrtcThread, (void*)this);
}
void gomediainterface::StartWebRTCPull() {
    LOGD("StartWebRTCPull %s %s %s %d %d %d %d %d %d\n",mServername,mStreamname,mTransmode,mbIsH265,mbUseVideo,mbUseAudio,mbOnDataCall,mChannel,mSignalid);
    if(mbIsH265) {
        CallStartVideoStreamPull((void *) (goInstance),mSignalid, mServername, mStreamname,"audio/opus", "video/H265", "datachannel_trans",mbUseVideo, mbUseAudio, mbOnDataCall,mChannel);
    }else{
        CallStartVideoStreamPull((void *) (goInstance),mSignalid, mServername, mStreamname, "audio/opus", "video/H264",mTransmode, mbUseVideo, mbUseAudio, mbOnDataCall,mChannel);
    }
}
//设置webrtc参数并启动mqtt信令代理
void gomediainterface::SetWebRTCConfig(char *mqttserver , char *sn , char *clientid, char *stunserver, char *turnserver, char *turnservername, char *turnserverpassword ,int signalid){
    goWebrtcConfig = GetMediaWebRTCConfig((void *) (goInstance),signalid);
    if(goWebrtcConfig) {

        LOGD("GetKVMVideoConfigInfo\r\n");
        auto config=(VideoConfigInfo*)GetKVMVideoConfigInfo();
        if(config){//设置kvm的视频采集参数,用于注册
         BOOST_LOG(info) << "videoconfig is width: "sv << config->width << "height :"sv <<config->height<< "framerate:"sv <<config->framerate << "videoformate:"sv <<config->videoFormat; 

  
          SetVideoConfigInfo((void *) goWebrtcConfig,config);   
        }
        SetWebrtcConfig((void *) goWebrtcConfig, mqttserver, sn , clientid , stunserver , turnserver , turnservername , turnserverpassword );
        CallStartWebrtc((void *) (goInstance),signalid);
    }
}
//停止信令
void gomediainterface::StopWebRTCSignal(int signalid){
    CallStopWebrtc((void *) (goInstance),signalid);
}
void gomediainterface::StopWebRTCPullStream(char *streamname){
    LOGD("gomediainterface::StopWebRTCPullStream  CallStopWebrtcStream close %s...",streamname);
    CallStopWebrtcStream(reinterpret_cast<void*>(goInstance),streamname);
}
void gomediainterface::StopWebRTCPullStream(int channel){
    LOGD("gomediainterface::StopWebRTCPullStream close %d...",channel);
//    CallStopWebrtcStream(reinterpret_cast<void*>(goInstance),channel);
}
void gomediainterface::StopWebRTCClient(){
    if(pull_webrtc_client_thread){
        pthread_join(pull_webrtc_client_thread,NULL);
    }
    LOGD("gomediainterface::StopWebRTCClient closed");
    // pull_rtsp_client_thread.join();
}
//此处回调只有rtsp拉流或者webrtc拉流,增加一个逻辑变量标识是否再注入
void gomediainterface::onMediaDataReceive( uint8_t* data, int length,int mediatype,int channel,int binject,int duration){
//    LOGD("onMediaDataReceive channel:%d mediatype:%d length %d",mChannel , mediatype ,length);
//    Nalu::dumphex("onMediaDataReceive", data, length);
    if(mMediacallfunc)   mMediacallfunc((void *) data, (size_t) length,mediatype,channel); //回调
    if(binject){//向webrtc 流中注入数据,内部会判断是否有虚拟摄像头或音频节点,只有本地的设备节点或者rtsp拉流到本地才发布生成内部虚拟节点,webrtc拉流不会生成虚拟节点,只有流管理,所以webrtc拉流出来注入式直接返回
        //目前还没有其他办法来判断过滤是否注入数据,注入音频数据需要转换成opus格式,如果回调的数据类型为opus类型则需要处理解码或者转aac编码
        if(mediatype==av_AAC){

        }else {
            tInfo videoinfo;
            videoinfo.a = data;
            // void* a;
            // unsigned long long  int timestamp;
            videoinfo.size = length;
            // int  pcmsize;
            videoinfo.mediatype = mediatype;
            videoinfo.devid = channel;
            CallSendData((void *) goInstance, &videoinfo);
        }
    }
}
void gomediainterface::SendToWebrtc(void * data,int length,int mediatype,int channel){
    if(mediatype==av_AAC){
     //transcode aac to opus
        trancodeAAC2OPUS(data, length,channel);
    }else {
        tInfo videoinfo;
        videoinfo.a = data;
        // void* a;
        // unsigned long long  int timestamp;
        videoinfo.size = length;
        // int  pcmsize;
        videoinfo.mediatype = mediatype;
        videoinfo.devid = channel;
        CallSendData((void *) goInstance, &videoinfo);
    }
}

void gomediainterface::trancodeAAC2OPUS(void * databuffer,int len,int channel){
    void *pData=databuffer;
    int outSize = len;
    int encsize=len;
    if(!aacDecoder){
        // GetMediaAudioExternData(reinterpret_cast<void*>(goInstance),channel);
        // char filename[256];
        // snprintf(filename,256,"%s.mp4",mSN);
        // StartMP4Recorder(filename);

        aacDecoder=new AACDecoder();
        if (!aacDecoder) {
            return ;
        }
        // int dsi[]={0x11,0x90};
        // printf("")
        // MP4E_set_dsi(mux, audio_track_id,  maacInfo!=NULL?maacInfo->confBuf:(void*)dsi, maacInfo!=NULL?maacInfo->confSize:2);
        // if (aacDecoder->initDecoder(maacInfo!=NULL?maacInfo->confBuf:(void*)dsi,maacInfo!=NULL?maacInfo->confSize:2)) {
        if (aacDecoder->initDecoder(mConfBuf,mConfBufLen)) {
//            pOut=  (char*)&OutputBuf[audioDataOutIndex][0];
            char* pOut=(char*)pData;
            if(aacDecoder->Decode((char *)(databuffer), static_cast<UINT>(len),pOut,outSize)){
//                pData=(void*)p
            }

//            if(++audioDataOutIndex>=MAX_AUDIO_QUEUE_SIZE) audioDataOutIndex=0;
        }else{
            return;
        }
//        if(mBPublish){
//            mAudioDeviceID=CallStartAudioStreamPublish((void*)(goInstance),(char*)mStreamath,0,0,mChannel);
//        }

    }else{
        char* pOut=(char*)pData;
//        pOut=(char *)&OutputBuf[audioDataOutIndex][0];
        aacDecoder->Decode((char *)(databuffer), static_cast<UINT>(len),pOut,outSize);
//        if(++audioDataOutIndex>=MAX_AUDIO_QUEUE_SIZE) audioDataOutIndex=0;
    }
//    int outSize = outsize;
#if USE_MIXER
    if(audiomixer== nullptr){
        audiomixer=new AudioMixer();
        if(audiomixer) {
            // 添加输入流
            // 参数根据实际pcm数据设置
//            printf("addAudioInput %d %d\n",mpeg4AudioConfig.SampleRate, mpeg4AudioConfig.ChannelLayout);
            audiomixer->addAudioInput(0, mAudioRate, mAudioChannels, 16, AV_SAMPLE_FMT_S16);
//                audiomixer->addAudioInput(1, rate, channels, 16, AV_SAMPLE_FMT_S16);
            // 添加输出流
            audiomixer->addAudioOutput(48000, 2, 16, AV_SAMPLE_FMT_S16);
            // tempchannels = 2;
            // 初始化
            if (audiomixer->init("longest") < 0) {
                printf("audioMix.init() failed!");
            }else{
//                    if ((audiomixer->addFrame(0, (uint8_t*)buffer, size) < 0) &&(audiomixer->addFrame(1, NULL, 0) < 0)) {
//                printf("addFrame %p %d\n",pOut,outsize);
                if(audiomixer->addFrame(0, (uint8_t*)pData, outSize) < 0){
                    printf("audioMix.addFrame() failed!");
                    return;

                }else {
                    while ((outSize = audiomixer->getFrame((uint8_t*)pData, 10240)) > 0) {
//                            pData = &outBuf[0];
//                        outsize=outSize;
                        printf("audiomixer->getFrame %d \n",outSize);
                        //            fwrite(outBuf, 1, outSize, fileOut);
                    }
                }
            }

        }
    }else{
//            if ((audiomixer->addFrame(0, (uint8_t*)buffer, size) < 0) && (audiomixer->addFrame(1, NULL, 0) < 0)) {
//        printf("addFrame %p %d\n",pOut,outsize);
        if(audiomixer->addFrame(0, (uint8_t*)pData, outSize) < 0){
            printf("audioMix.addFrame() failed!");
            return;


        }else {
            while ((outSize = audiomixer->getFrame((uint8_t*)pData, 10240)) > 0) {
//                    pData = &outBuf[0];
//                outsize=outSize;
                printf("audiomixer->getFrame %d \n",outSize);
                //            fwrite(outBuf, 1, outSize, fileOut);
            }
        }
    }
#endif
    if(!opusdecoder){
        // GetMediaAudioExternData(reinterpret_cast<void*>(goInstance),channel);
        opusdecoder = new OpusCodec;
//        auto audioconfig=GetMPEG4AudioConfig();
        // printf("audioconfig %d %d\n",audioconfig->SampleRate, audioconfig->ChannelLayout);
        // opusdecoder->initEncoder(audioconfig->SampleRate, audioconfig->ChannelLayout, 0);
        opusdecoder->initEncoder(48000, 2, 0);
        opus_frame_callback_=std::bind(&gomediainterface::SendToWebrtc, this, std::placeholders::_1,
                                       std::placeholders::_2, std::placeholders::_3,
                                       std::placeholders::_4);
        opusdecoder->encode((const short *) pData, outSize,channel, opus_frame_callback_);

    }else{
        opusdecoder->encode((const short *) pData, outSize,channel, opus_frame_callback_);
    }
}
相关推荐
海水冷却3 天前
2026年实时音视频服务计费模式指南
实时音视频
番茄灭世神5 天前
PN学堂GD32教程第8篇——RTC
实时音视频
runner365.git5 天前
RTC实现VoiceAgent(二)
大模型·webrtc·实时音视频·voiceagent
xuxie996 天前
N18 RTC
单片机·嵌入式硬件·实时音视频
runner365.git7 天前
RTC会议实时翻译系统
实时音视频
runner365.git7 天前
如何使用RTCPilot配置一个集群RTC服务
webrtc·实时音视频·音视频开发
深念Y8 天前
从WebSocket到WebRTC,豆包级实时语音交互背后的技术演进
websocket·网络协议·实时互动·webrtc·语音识别·实时音视频
海水冷却10 天前
2026 主流 RTC SDK 选型参考,7 大维度横向对比
实时音视频·rtc
TEL1892462247712 天前
IT6636/IT66362(3进1出 / 2进1出 HDMI 2.1 48Gbps Retiming Switch,内置 MCU)
音视频·实时音视频·视频编解码
天上路人16 天前
A-59F 多功能语音处理模组在本地会议系统扩音啸叫处理中的技术应用与性能分析
人工智能·神经网络·算法·硬件架构·音视频·语音识别·实时音视频