火山引擎云手机支持虚拟摄像头、虚拟麦克风功能,能够实现将外部音视频数据注入到云机实例的虚拟摄像头/麦克风中,供实例上运行的应用获取和使用。
本文分别介绍如何使用客户端 SDK 或 ADB 命令,将自定义采集的音视频数据、或离线音视频文件注入到云机实例。外部音视频数据注入功能适用于伪直播、虚拟人直播等应用场景。内部采集音视频注入功能请参考客户端内部源注入;音视频裸数据注入功能请参考 音视频裸数据注入(Pod 注入)。
文件需以 .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 |
参考 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(); }
文件需以 .mp4 为文件扩展名称。将需要注入的视频文件推送到目标云机实例支持的路径下,当前支持的目标路径包括 /sdcard/ 和 /data/local/:
adb push test1.mp4 /sdcard/playmp4/ adb push test2.mp4 /sdcard/playmp4/ adb push test3.mp4 /sdcard/playmp4/
注意:文件路径不得包含中文字符。
# 使用前先关闭注入。注意:执行该命令前,请确保所有与摄像头相关的应用程序已完全关闭 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
设置完成后,打开视频注入目标应用程序,即可观看上传的视频文件画面。