本文为您介绍了Windows的输入外部视频流和音频流的接口示例。

背景信息

Windows支持1.16.2以上版本。

使用场景包括但不限于以下:
  • 需要将本地媒体文件(视频/音频)及第三方音视频数据,通过SDK传输到远端播放渲染,可使用音视频外部输入推流实现。
  • 需要在使用音频外部输入的同时,本地播放(耳返)输入内容,可使用外部音频输入播放实现。
  • 需要将通信过程中视频数据保存或输出处理(外部渲染,修改内容)时,可使用视频数据裸数据输出实现。

输入外部视频数据推流

外部视频输入推流涉及接口和方法如下所示:
/**
    * @brief 启用外部视频输入源
    * @param enable YES:开启,NO:关闭
    * @param useTexture 是否使用texture(纹理)模式 取值true|false
    * @param type 流类型
    * @note 启用后使用pushExternalVideoFrame接口输入视频数据
    */
    virtual int setExternalVideoSource(bool enable, bool useTexture, AliRtcVideoSource sourceType) = 0;

/**
    * @brief 输入外部输视频
    * @param frame 帧数据
    * @param type 流类型
    * @param 目前输入视频类型只支持I420
    */
    virtual int pushExternalVideoFrame(AliRtcVideoDataSample *frame, AliRtcVideoSource sourceType) = 0;
  1. 调用接口setExternalVideoSource启用外部视频输入推流,通过参数enable设置开启,通过参数sourceType指定要推流的track类型(摄像头流/屏幕流)。
    说明 目前Windows端不支持直接输入纹理,所以参数useTexture始终传入false
  2. 应用侧通过接口configLocalCameraPublishconfigLocalScreenPublish配置指定track推流,然后调用接口publish开始推视频流。
    说明 SDK允许先推流然后开启外部视频输入,即步骤1和步骤2时序对换,但这种情况下,默认开始推流时,先推送出的是原始采集源(摄像头或屏幕捕获)的视频数据,直到启用外部输入。
  3. 应用侧持续调用接口pushExternalVideoFrame,向SDK投递视频裸数据,进行推流,参数frame传入裸数据相关信息,参数sourceType指明推流track类型,与步骤1中设置开启的track类型保持一致。
    说明
    • 投递视频帧数据的频率由应用方控制,依据视频源帧率保持间隔投递,直至输入停止。建议应用侧独立开启线程,进行数据投递,保证数据输入及时性。
    • 目前Windows端支持输入YUV数据(格式I420),需要在参数frame的裸数据信息中,指定formatAliRtcVideoFormatI420bufferTypeAliRtcBufferTypeRawData
    • 赋值裸数据信息时,需要数据指针、视频宽高信息、stride信息等完整传入;旋转角度rotation目前暂未支持设置,请保持默认值0;时间戳取当期时间,单位为毫秒。
  4. 数据源输入结束或应用中止外部视频输入,同样调用接口setExternalVideoSource关闭外部视频输入。

代码示例:

AliRtcEngine *pEngine = AliRtcEngine::sharedInstance(this, "");
.....

//1.启用外部视频输入
pEngine->setExternalVideoSource(true, false, AliRtcVideoSourceCamera); //camera track启用外部输入
bPushExternalVideo = true;

//2.配置开始推流
pEngine->configLocalCameraPublish(true);
pEngine->publish();

.....

//3.独立线程推送外部视频数据
int frameRate = 30; // 30 fps
do
{
    size_t frameLength = videoWidth * videoHeight * 3 / 2;
    void* cacheBuf = (void*)malloc(frameLength);

    /*
    从外部数据源拷贝推送视频数据到cacheBuf
    */

    AliRtcVideoDataSample sample;
    sample.data = (unsigned char*)cacheBuf;
    sample.format = AliRtcVideoFormatI420;
    sample.width = videoWidth;
    sample.height = videoHeight;
    sample.strideY = videoWidth;
    sample.strideU = videoWidth / 2;
    sample.strideV = videoWidth / 2;
    sample.dataLen = frameLength;
    sample.rotation = 0;

    pEngine->pushExternalVideoFrame(&sample, AliRtcVideoSourceCamera);

    //控制帧数据投递频率
    Sleep(1000 / frameRate);            
} while (bPushExternalVideo);

.....

//4.停止外部视频输入
pEngine->setExternalVideoSource(false, false, AliRtcVideoSourceCamera);
bPushExternalVideo = false;

视频裸数据输出

视频裸数据输出涉及接口和方法如下所示:
/**
    * @brief 订阅视频数据输出
    * @note 输出数据将通过onCaptureVideoSample及onRemoteVideoSample回调返回
    */
    virtual void r    egisterVideoSampleObserver() = 0;

    /**
    * @brief 取消订阅视频数据输出
    */
    virtual void unRegisterVideoSampleObserver() = 0;

    /**
    * @brief 本地采集视频数据回调
    * @param videoSource 视频数据类型
    * @param videoSample 视频数据
    */
    virtual void onCaptureVideoSample(AliRtcVideoSource videoSource, AliRtcVideoDataSample *videoSample) {};

    /**
    * @brief 远端视频数据回调
    * @param uid user id
    * @param videoSource video source
    * @param videoSample video sample
    */
    virtual void onRemoteVideoSample(const AliRtc::String &uid, AliRtcVideoSource videoSource, AliRtcVideoDataSample *videoSample) {};
  1. 应用需先继承AliRtcEventListener接口,实现onCaptureVideoSampleonRemoteVideoSample回调,用于接收本地采集视频裸数据,以及订阅到的远端视频裸数据。
    说明 目前Windows端只支持输出YUV(I420)格式数据,可通过SDK提供的AliConvertVideoData静态接口,转换到RGBA格式。
  2. 调用接口registerVideoSampleObserver接口启动视频裸数据输出,启动后视频数据将通过onCaptureVideoSampleonRemoteVideoSample持续回调。
    说明 本地采集视频数据需要在开启摄像头(打开预览 或 正在推送视频流)前提下才会有回调输出,远端视频数据同样需要在订阅其他用户的视频流成功后才会有回调输出。
  3. 需要停止接收视频裸数据时,调用接口unRegisterVideoSampleObserver关闭视频裸数据输出即可。

代码示例:

AliRtcEngine *pEngine = AliRtcEngine::sharedInstance(this, "");
.....

//1. 注册视频裸数据输出
pEngine->registerVideoSampleObserver();

//开始预览/推流及订阅其他用户视频
.....

//2. 接收裸数据回调
void onCaptureVideoSample(AliRtcVideoSource videoSource, AliRtcVideoDataSample *videoSample)
{
    //处理本地采集数据回调
}

void onRemoteVideoSample(const AliRtc::String &uid, AliRtcVideoSource videoSource, AliRtcVideoDataSample *videoSample)
{
    //处理远端数据回调
}

//3.停止视频裸数据输出
pEngine->unRegisterVideoSampleObserver();

输入外部音频数据推流

外部音频输入推流涉及接口和方法如下所示:
    /**
    * @brief 设置是否启用外部音频输入源
    * @param enable YES 开启,NO 关闭
    * @param sampleRate 采样率 16k 48k...
    * @param channelsPerFrame 采样率 16k 48k...
    * @return >=0表示成功, <0表示失败
    */
    virtual int setExternalAudioSource(bool enable, unsigned int sampleRate,  unsigned int channelsPerFrame) = 0;

    /**
    * @brief 输入音频数据
    * @param audioSamples 音频数据
    * @param sampleLength 音频数据长度
    * @param timestamp 时间戳
    * @return <0表示失败,返回值为ERR_AUDIO_BUFFER_FULL时,需要在间隔投递数据时间长度后再次重试投递
    */
    virtual int pushExternalAudioFrameRawData(const void* audioSamples, unsigned int sampleLength, long long timestamp) = 0;

    /**
    * @brief 设置外部输入是否与麦克风采集音频混合
    * @param mixed YES 混音,NO 完全替换麦克风采集数据
    */
    virtual int setMixedWithMic(bool mixed) = 0;

    /**
    * @brief 设置外部输入音频混音音量
    * @param vol 音量 0-100
    */
    virtual int setExternalAudioVolume(int volume) = 0;

    /**
    * @brief 获取外部输入音频混音音量
    * @return vol 音量
    */
    virtual int getExternalAudioVolume() = 0;
  1. 调用接口setExternalAudioSource启用外部音频输入推流,通过参数enable设置开启,通过参数sampleRate和参数channelsPerFrame指定要输入音频数据的采样率和声道数。
    说明 目前仅支持输入音频PCM数据,数据编码为Signed 16-bit。
  2. 应用侧通过接口configLocalAudioPublish配置音频推流,然后调用publish接口开始推音频流。
    说明 SDK允许先推流在开启外部音频输入,即步骤1和步骤2时序对换,但这种情况下,默认开始推流时,先推送出的是麦克风采集音频,直到启用外部输入。
  3. 应用侧持续调用pushExternalAudioFrameRawData接口,向SDK投递音频PCM数据,参数audioSamples带入音频数据地址,参数sampleLength指明音频长度,参数timestamp为当前时间。
    说明
    • 投递音频裸数据的频率由应用方控制,每次投递数据量不要超过240ms的音频数据量,建议每次投递20ms的音频数据,保持循环投递直到结束。当输入数据频率过快,SDK缓存已满暂时无法消费数据时,接口会返回错误码ERR_AUDIO_BUFFER_FULL,此时应用侧需要等待一个数据时间长度后,再次重试投递此数据,直到成功,否则将丢失输入音频数据。
    • 与视频输入一致,同样建议应用侧单独开启线程,投递音频裸数据,直到输入停止。
    • 由于外部输入音频数据的同时,可能同时还有麦克风在采集推流,应用可设置是否需要将外部输入音频与麦克风采集音频混音后一起推出,或单独只推送外部输入音频,通过调用接口setMixedWithMic可开启或关闭与麦克风采集音频的混音,同时可通过接口setExternalAudioVolumegetExternalAudioVolume设置和获取输入音频的混音音量,音量可调整范围为[0 - 100],默认50。
    • 当应用同时推送麦克风采集音频 与 外部输入音频,接口muteLocalMic的默认行为是停止所有音频的推送(包括麦克风与外部输入),如果应用只需要停止麦克风音频(保持外部输入音频推送),可通过设置muteLocalMic接口中的第二个参数 mode,选择AliRtcMuteOnlyMicAudioMode模式即可。
  4. 数据源输入结束或应用中止外部音频输入,同样调用接口setExternalAudioSource关闭外部视音频输入即可。

代码示例:

AliRtcEngine *pEngine = AliRtcEngine::sharedInstance(this, "");
.....

//1. 启用外部音频输入播放
unsigned int sampleRate = 44100; //44.1k
unsigned int channelsPerFrame = 1; //单声道
pEngine->setExteranlAudioRender(true, sampleRate, channelsPerFrame); //启用音频输入播放
bRenderExternalAudio = true;

//2.独立线程推送外部音频数据
unsigned int audioDataInterval = 20; //20 ms
size_t bytePerSample = 16 / 8; // Signed 16-bit
size_t dataSize = sampleRate * channelsPerFrame * bytePerSample / (1000 / audioDataInterval);
unsigned int* data = (unsigned int*)malloc(dataSize);

do
{
    /*
    从外部数据源拷贝推送音频数据到data
    */

    std::chrono::milliseconds now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
    int ret = mpEngine->pushExternalAudioRenderRawData(data, dataSize, sampleRate, channelsPerFrame, now.count());
    if (ret < 0)
    {
        //发生错误,中断播放
        break;
    }

    //返回结果不为0,需要进行错误判断,检查是否缓冲区满
    //返回结果如果为0,投递成功,继续读取并投递数据,无需间隔
    while (ret == ERR_AUDIO_BUFFER_FULL && bRenderExternalAudio)
    {
        Sleep(audioDataInterval); //缓冲区满,间隔一个数据长度后再次重试,直到成功
        if (!bRenderExternalAudio) break;

        ret = mpEngine->pushExternalAudioRenderRawData(data, read_size, sampleRate, channelsPerFrame, now.count());

        if (ret < 0)
        {
            //发生错误,中断推送
            break;
        }
    }
} while (bRenderExternalAudio);

.....

//4.停止外部音频输入播放
mpEngine->setExteranlAudioRender(false, 0, 0);
bRenderExternalAudio = false;

外部音频输入播放

外部音频输入涉及接口和方法如下所示:
/**
    * @brief 设置是否启用外部输入音频播放
    * @param enable YES 开启,NO 关闭
    * @param sampleRate 采样率 16k 48k...
    * @param channelsPerFrame 采样率 16k 48k...
    * @return >=0表示成功, <0表示失败
    */
    virtual int setExteranlAudioRender(bool enable, unsigned int sampleRate, unsigned int channelsPerFrame) = 0;

    /**
    * @brief 输入音频播放数据
    * @param audioSamples 音频数据
    * @param sampleLength 音频数据长度
    * @param sampleRate 音频采样率
    * @param channelsPerFrame 音频声道数
    * @param timestamp 时间戳
    * @return <0表示失败
    */
    virtual int pushExternalAudioRenderRawData(const void* audioSamples, unsigned int sampleLength, unsigned int sampleRate, unsigned int channelsPerFrame, long long timestamp) = 0;

    /**
    * @brief 设置外部音频播放音量
    * @param vol 音量 0-100
    */
    virtual int setExternalAudioRenderVolume(int volume) = 0;

    /**
    * @brief 获取音频播放音量
    * @return vol 音量
    */
    virtual int getExternalAudioRenderVolume() = 0;
  1. 调用接口setExteranlAudioRender启用外部音频输入播放,通过参数enable设置开启,通过参数sampleRate和参数channelsPerFrame指定要输入音频数据的采样率和声道数。
    说明 目前仅支持输入音频PCM数据,数据编码为Signed 16-bit,输入播放音频的声道数与采样率,可以在推流过程中动态变更,下一步骤2中,投递接口pushExternalAudioRenderRawData中可指定当次音频数据的采样率和声道数。
  2. 应用侧持续调用pushExternalAudioRenderRawData接口,向SDK投递音频PCM数据播放,参数audioSamples带入音频数据地址,参数sampleLength指明音频长度,参数sampleRate和参数channelsPerFrame指定要输入音频数据的采样率和声道数,参数timestamp为当前时间戳。
    说明
    • 与外部输入音频数据推送相同,播放音频也需要由应用侧保持频率投递,建议应用侧独立开启线程,进行数据投递,保证数据输入及时性。
    • 外部输入音频播放过程中,可通过setExternalAudioRenderVolume接口和getExternalAudioRenderVolume接口调整设置播放音频音量,音量可调整范围为 [0 - 100],默认50。
  3. 数据源输入结束或应用中止音频播放时,调用接口setExteranlAudioRender关闭外部音频播放即可。

代码示例:

AliRtcEngine *pEngine = AliRtcEngine::sharedInstance(this, "");
.....

//1. 启用外部音频输入播放
unsigned int sampleRate = 44100; //44.1k
unsigned int channelsPerFrame = 1; //单声道
pEngine->setExteranlAudioRender(true, sampleRate, channelsPerFrame); //启用音频输入播放
bRenderExternalAudio = true;

//2.独立线程推送外部音频数据
unsigned int audioDataInterval = 20; //20 ms
size_t bytePerSample = 16 / 8; // Signed 16-bit
size_t dataSize = sampleRate * channelsPerFrame * bytePerSample / (1000 / audioDataInterval);
unsigned int* data = (unsigned int*)malloc(dataSize);

do
{
    /*
    从外部数据源拷贝推送音频数据到data
    */

    std::chrono::milliseconds now = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch());
    int ret = mpEngine->pushExternalAudioRenderRawData(data, dataSize, sampleRate, channelsPerFrame, now.count());
    if (ret < 0)
    {
        //发生错误,中断播放
        break;
    }

    //返回结果不为0,需要进行错误判断,检查是否缓冲区满
    //返回结果如果为0,投递成功,继续读取并投递数据,无需间隔
    while (ret == ERR_AUDIO_BUFFER_FULL && bRenderExternalAudio)
    {
        Sleep(audioDataInterval); //缓冲区满,间隔一个数据长度后再次重试,直到成功
        if (!bRenderExternalAudio) break;

        ret = mpEngine->pushExternalAudioRenderRawData(data, read_size, sampleRate, channelsPerFrame, now.count());

        if (ret < 0)
        {
            //发生错误,中断推送
            break;
        }
    }
} while (bRenderExternalAudio);

.....

//4.停止外部音频输入播放
mpEngine->setExteranlAudioRender(false, 0, 0);
bRenderExternalAudio = false;