You need to enable JavaScript to run this app.
导航
客户端外部源注入
最近更新时间:2025.09.26 18:17:06首次发布时间:2023.12.18 10:43:49
复制全文
我的收藏
有用
有用
无用
无用

火山引擎云手机支持虚拟摄像头、虚拟麦克风功能,能够实现将外部音视频数据注入到云机实例的虚拟摄像头/麦克风中,供实例上运行的应用获取和使用。
本文分别介绍如何使用客户端 SDK 或 ADB 命令,将自定义采集的音视频数据、或离线音视频文件注入到云机实例。外部音视频数据注入功能适用于伪直播、虚拟人直播等应用场景。内部采集音视频注入功能请参考客户端内部源注入;音视频裸数据注入功能请参考 音视频裸数据注入(Pod 注入)

前提条件
  • 具备加强型及以上配置的实例资源,虚拟直播建议使用旗舰型。
  • 完成外部音视频数据准备:
    • 若使用 Android SDK API 注入外部采集音视频,请确保已自行实现音视频采集逻辑。
    • 若使用 ADB 命令注入离线音视频文件,请参考文件格式,准备符合要求的音视频文件。
  • 需要音视频注入的应用程序已在控制台完成应用加签(适用于自主开发的应用,第三方应用无需关注)。

注意事项
  • 外部视频数据注入目前只支持竖屏模式下使用,横屏模式下会出现显示异常等不兼容情况。
  • 三种音视频注入源存在优先级和切换限制:
    • 本文所介绍的外部源音视频注入与客户端内部源音视频注入、音视频裸数据注入(Pod 注入)功能互斥,即云机实例同一时间只能接收一种注入源;
    • 当三种注入源并存时,云机实例接收注入源的优先级为:客户端外部源音视频注入>音视频裸数据注入(Pod 注入)>客户端内部源音视频注入;
    • 若切换至其他注入源,请务必先关闭摄像头和麦克风,关闭当前正在注入的应用程序,否则会导致切换失败。
  • 伪直播、虚拟人直播场景推荐使用 AOSP 12 以提升画面清晰度。

文件格式

视频文件

  • 文件需以 .mp4 为文件扩展名称。

  • 当前不支持带 rotation 的视频,有可能导致播放时的方向显示错误。

  • 目前支持以下格式:

    编码类型

    文件头标识(HEX)

    文件描述

    m4a

    00 00 00 20 66 74 79 70 4D 34 41 20 00 00 00 00

    Apple Lossless Audio Codec file

    m4a; m4v

    00 00 00 20 66 74 79 70 4D 34 41 20 00 00 00 00

    QuickTime M4A/M4V file

    mp4

    00 00 00 18 66 74 79 70 33 67 70 35

    MPEG-4 video files

    3gg; 3gp; 3g2

    00 00 00 nn 66 74 79 70 33 67 70

    3rd Generation Partnership Project 3GPP (nn=0x14) and 3GPP2 (nn=0x20) multimedia files

音频文件

  • 编码格式:使用 AAC-LC 标准进行编码。
  • 采样率:支持 16kHz、32kHz、44.1kHz 和 48kHz 采样率。
  • 声道配置:支持单声道和双声道。
  • 采样深度:支持 8 位和 16 位。

使用客户端 API 注入

功能实现

集成火山引擎云手机客户端 SDK

参考 Android SDK 集成指南,集成 1.20.0+ 版本的 SDK。

获取音视频注入控制权(仅多端加房场景需要)

第一个进房的客户端默认拥有音视频注入权限。若后续存在多个客户端进房、且有音视频注入需求的情况,则后接入的客户端需通过特定的接口调用来获取注入控制权。
注意:同一时间内有且仅有一个客户端可持有向云机实例注入音视频的权限。

视频

public interface CameraManager {

    /**
     * 开启/关闭视频注入功能
     *
     *  @param  state 视频注入状态 true:开启 false:关闭
     *  @return  0:调用成功 -1:引擎未初始化
     */
    int setVideoInjectionState(boolean state);
    
    /**
     * 获取视频注入状态设置
     *
     *  @return  0:调用成功 -1:引擎未初始化
     */
    int getVideoInjectionState();
    
    /**
     * 设置视频注入功能相关回调
     *
     *  @param  listener 回调
     */
    void setVideoInjectionListener(VideoInjectionListener listener);
    
}

通过注册视频注入功能相关回调,监听视频注入权限变更:

interface VideoInjectionListener {
    /**
          * 视频注入状态改变的回调
          *
          *  @param  callUserId 改变视频注入状态的用户 ID
          *  @param  state 视频注入状态 true:开启 false:关闭
          *  @param  code 错误码 0:成功 <0:失败
          *  @param  msg 错误信息
          */
    void onVideoInjectionStateChanged(String callUserId, boolean state, int code, String msg);

    /**
          * 获取视频注入状态的结果回调
          *
          *  @param  state 视频注入状态 true:开启 false:关闭
          *  @param  code 错误码 0:成功 <0:失败
          *  @param  msg 错误信息
          */
    void onGetVideoInjectionState(boolean state, int code, String msg);
}

音频

public interface AudioService {
    
    
    /**
     * 设置音频注入状态(用于云手机场景)
     *
     *  @param  state 音频注入状态 true:开启 false:关闭
     *  @return  0:调用成功 -1:DataChannel为空
     */
    int setAudioInjectionState(boolean state);
    
    /**
     * 获取音频注入状态(用于云手机场景)
     *
     *  @return  0:调用成功 -1:DataChannel为空
     */
    int getAudioInjectionState();
    
    /**
     * 设置音频注入状态监听器(用于云手机场景)
     *
     *  @param  listener 音频注入状态监听器
     */
    void setAudioInjectionListener(AudioInjectionListener listener);

}

通过注册音频注入功能相关回调,监听音频注入权限变更:

interface AudioInjectionListener {
    /**
          * 音频注入状态改变的回调
          *
          *  @param  callUserId 改变音频注入状态的用户ID
          *  @param  state 音频注入状态 true:开启 false:关闭
          *  @param  code 错误码 0:成功 <0:失败
          *  @param  msg 错误信息
          */
    void onAudioInjectionStateChanged(String callUserId, boolean state, int code, String msg);

    /**
          * 获取音频注入状态的结果回调
          *
          *  @param  state 音频注入状态 true:开启 false:关闭
          *  @param  code 错误码 0:成功 <0:失败
          *  @param  msg 错误信息
          */
    void onGetAudioInjectionState(boolean state, int code, String msg);
}

推送外部音视频数据到云机实例

Android SDK 外部音视频数据注入适用于向云机实例注入自定义采集音视频数据的场景。

视频

客户端首先通过 setVideoSourceType 设置视频采集模式为外部采集(即自定义采集),然后调用 pushExternalVideoFrame 将自定义采集到的数据推送至 SDK,最后调用 publishLocalVideo 向云机发布视频流。发布完成后,可调用 unpublishLocalVideo 取消发布。

public interface CameraManager {
    /**
     * 设置本地视频源类型
     *  @param  index 视频流索引
     *  @param  type 视频源类型
     *  @return  <code>-1</code>表示调用失败,<code>0</code>表示调用成功
     */
    int setVideoSourceType(@StreamIndex int index, @VideoSourceType int type);
    
    /**
     * 视频自采集推流接口
     *  @param  index 视频流索引
     *  @param  frame 视频帧
     *  @return  <code>-1</code>表示调用失败,<code>0</code>表示调用成功
     */
    int pushExternalVideoFrame(@StreamIndex int index, VeVideoFrame frame);
    
    /**
     * 发布本地视频,视频自采集需要调用此接口
     *  @return  <code>-1</code>表示调用失败,<code>0</code>表示调用成功
     */
    int publishLocalVideo();
    
    /**
     * 取消发布本地视频,视频自采集需要调用此接口
     *  @return  <code>-1</code>表示调用失败,<code>0</code>表示调用成功
     */
    int unpublishLocalVideo();
    
   

}

音频

客户端首先调用 setAudioSourceType 设置音频采集模式为外部采集(即自定义采集),然后调用 pushExternalAudioFrame 将自定义采集到的数据推送至 SDK,最后调用 publishLocalAudio 向云机发布音频流。发布完成后,可调用 unpublishLocalAudio 取消发布。

public interface AudioService {

    /**
     * 用于设置自采集模式,默认为 SDK 内部采集。在调用自采集 API 前,需使用该 API 设置音频为自采集模式
     *  @param  streamIndex 流属性
     *  @param  sourceType 音频源
     */
    int setAudioSourceType(@StreamIndex int streamIndex, @AudioSourceType int sourceType);
    
    /**
     * 音频自采集发送音频帧
     *  @param  streamIndex 音频类型
     *  @param  frame 音频帧数据
     *  @return  <code>0</code>: 成功;<code><0</code>: 失败
     * @notes  你必须每隔 10 毫秒推送一次外部采集的音频数据。单次推送的 samples (音频采样点个数)应该为 audioFrame.sampleRate/100。例如,当采样率设为 48000 时,单次应推送 480 个采样点。
     */
    int pushExternalAudioFrame(@StreamIndex int streamIndex, VeAudioFrame frame);
    
    /**
     * 发布本地音频,调用{@link  AudioService#pushExternalAudioFrame(int, VeAudioFrame)}前需要调用此接口
     *  @return  <code>0</code>: 成功;<code><0</code>: 失败
     */
    int publishLocalAudio();
    
    /**
     * 取消发布本地音频,停止调用{@link  AudioService#pushExternalAudioFrame(int, VeAudioFrame)}后需要调用此接口
     *  @return  <code>0</code>: 成功;<code><0</code>: 失败
     */
    int unpublishLocalAudio();    
}

示例代码

  • 视频:

https://github.com/volcengine/vePhone/blob/main/Quick%20Start/Android/app/src/main/java/com/example/sdkdemo/feature/CameraManagerActivity.java

  • 音频:

https://github.com/volcengine/vePhone/blob/main/Quick%20Start/Android/app/src/main/java/com/example/sdkdemo/feature/AudioServiceActivity.java

使用 ADB 命令注入

1. 推送文件

文件需以 .mp4 为文件扩展名称。将需要注入的视频文件推送到目标云机实例支持的路径下,当前支持的目标路径包括 /sdcard//data/local/

adb push test1.mp4 /sdcard/playmp4/
adb push test2.mp4 /sdcard/playmp4/
adb push test3.mp4 /sdcard/playmp4/

注意:文件路径不得包含中文字符。

2. 使用 adb 设置 settings 数据库

# 使用前先关闭注入。注意:执行该命令前,请确保所有与摄像头相关的应用程序已完全关闭
   adb shell settings put global camera_file_preview off
   # 指定单个文件时,默认循环播放该文件
   adb shell settings put global camera_file_preview_path /sdcard/playmp4/test1.mp4
   # 指定文件夹,会循环播放该文件夹下列所有文件,顺序随机,无法指定播放某个文件
   adb shell settings put global camera_file_preview_path /sdcard/playmp4
   # 设置是否循环播放,true:是;false:否
   adb shell settings put global camera_file_preview_loop true
   # 完成前置设置后开启注入
   adb shell settings put global camera_file_preview on
   # 当需要停止注入时,请设置为 off
   adb shell settings put global camera_file_preview off

3. 观看文件播放

设置完成后,打开视频注入目标应用程序,即可观看上传的视频文件画面。