使用美颜处理视频
概述
ArRecorderSdk 是 SDK 2.0.3 起新增的离线美颜能力,用于对已有的 mp4 文件或在线视频 URL 二次处理,加美颜、特效、滤镜等效果,并导出一份新的 mp4 文件。与实时模式的 ArSdk 不同,ArRecorderSdk 特点如下:不依赖摄像头/直播流,处理本地或远端的录播 mp4。
串行 await 每一帧,不丢帧,处理时长视设备性能,性能越好处理越快。
音频原样透传,不解码不重编码。
视频走 WebCodecs 硬解硬编(不做软解兜底)。
使用场景
UGC 视频上传前的本地美颜.
对已经录制好的 mp4 批量增加滤镜或贴纸特效。
实现"先录制再美颜"的工作流。
注意事项
浏览器要求
必须支持 WebCodecs(
VideoEncoder / VideoDecoder):Chrome 102+ 或 Edge 102+。
Safari、移动端浏览器不满足要求。
大文件输出(> 200 MB)建议浏览器支持 File System Access API(Chrome / Edge 桌面版),否则会回退到内存模式,超过 ~1.5 GB 可能 OOM。
使用注意
不可与 ArSdk 共存:同页面同时实例化
ArSdk 和 ArRecorderSdk 会因共享 AROptionsManager 单例发生状态污染。如需在两者间切换,务必先 destroy() 已有实例。导出期间锁定参数:
process() 进行中调用 setBeautify / setEffect / setFilter 会报错。如需在导出第一帧前完成参数设置,请监听 beforeRun 事件并在 handler 里 await 设置:recorder.on('beforeRun', async ({ mode }) => {await recorder.setEffect({ id: 'xxx', intensity: 0.5 })})
首次调用懒加载:构造时如果未传
beautify,运行期首次 setBeautify / setEffect 会触发管线懒加载(mediapipe + wasm,约 2-3 秒)。如希望第一帧立即带特效,请在构造时传 beautify,让 init() 阶段一并预热。WebCodecs 错误兜底:浏览器不支持
VideoEncoder / VideoDecoder 时,构造阶段会通过 error 事件抛出 ENVIRAONMENT_NOT_COMPATIBLE,请提前用环境检测屏蔽。音频不重编码:源视频音频会原样透传到输出 mp4,处理过程中不重新编码。
Demo 下载
引入方式
ArRecorderSdk 与 ArSdk 同源同包,从同一个 npm 包按需引入即可:import { ArRecorder } from 'tencentcloud-webar'const recorder = new ArRecorder.ArRecorderSdk({auth: { licenseKey, appId, authFunc },beautify: { whiten: 0.3, dermabrasion: 0.5 },})
注意:
同一个页面不要同时实例化
ArSdk 和 ArRecorderSdk,否则会互相污染状态。可以先 destroy() 一个再实例化另一个。完整示例
import { ArRecorder } from 'tencentcloud-webar'async function processVideo(file) {const recorder = new ArRecorder.ArRecorderSdk({auth: {licenseKey: 'YOUR_LICENSE_KEY',appId: 'YOUR_APP_ID',authFunc: yourAuthFunc, // 与 ArSdk 同样的鉴权回调},beautify: {whiten: 0.3,dermabrasion: 0.5,},})// 1. 鉴权 + 预热处理管线await recorder.init()// 2. 运行期可继续设置美颜/特效/滤镜await recorder.setBeautify({ whiten: 0.5, lift: 0.2 })await recorder.setEffect({ id: 'effectId', intensity: 0.7 })await recorder.setFilter('filterId', 0.5)// 3. 处理const result = await recorder.process(file, {onProgress: (current, total) => {console.log(`已处理 ${current}/${total} 帧`)},})// 4. 拿到结果if (result.mode === 'in-memory') {const url = URL.createObjectURL(result.blob)document.querySelector('video').src = url} else {// file 模式:已直接写入用户选择的磁盘文件const file = await result.fileHandle.getFile()}// 5. 清理资源recorder.destroy()}
构造参数
new ArRecorder.ArRecorderSdk(options) 接受以下配置:参数 | 说明 | 类型 | 是否必传 |
auth | 鉴权参数,结构同 ArSdk | {licenseKey: stringappId: stringsdkAppId?: stringauthFunc: () => Promise<{ signature, timestamp }>language?: string} | 是 |
beautify | 美颜参数,结构同 ArSdk 的 BeautifyOptions。 不传时 init 阶段不会预热 detect+render 链路(节省加载时间),但运行期首次调用 setBeautify / setEffect 会触发懒加载(约 2-3 秒)。 | BeautifyOptions | 否 |
module | 模块配置,结构同 ArSdk 的 ModuleConfig。 Recorder 当前实际消费 faceDetectModel 字段;其余字段(segmentation / handLandmark 等)保留同样字段名以便迁移配置,但不会生效。 | {faceDetectModel?: 'short' | 'full'// 其它 ArSdk 同名字段保留兼容,但不消费} | 否 |
output | 输出 mp4 编码配置 | {codec?: 'avc' // 当前仅支持 H.264bitrate?: number | 'auto' // bps,'auto' 跟随源视频fps?: number | 'auto' // 'auto' 跟随源视频} | 否 |
initReport | 是否初始化日志上报模块 | Boolean | 否,默认 true |
对象方法
接口 | 参数 | 返回 | 说明 |
async init() | - | Promise | 鉴权 + 预热处理管线。构造时传了 beautify 时一并预热 detect+render 链路,第一帧不会再卡 2-3 秒。 |
async process(source, options?) | source: Blob | File | string,本地文件或在线 URL(CORS 必须允许)options:见"process 参数" | Promise | 对源视频做离线美颜处理,导出新的 mp4。处理过程中禁止再调 set* 接口。 |
async setBeautify(options) | BeautifyOptions(同 ArSdk) | Promise | 热更新美颜参数,下一帧立即生效。导出期间(process 进行中)禁止调用。 |
async setEffect(input) | input:特效 ID | Effect 对象 | (特效 ID | Effect 对象) 数组 | 空数组(清空) 参数风格同 ArSdk 的 setEffect | Promise | 设置/切换/清空特效(滤镜 / 贴纸 / 美妆 / 3D 试戴)。导出期间禁止调用。 |
async setFilter(id?, intensity?) | id:滤镜 ID。缺省 / '' / 'none' 等于清除当前滤镜intensity:滤镜强度 0-1,默认 1 | Promise | 独立设置滤镜,与 setEffect 走不同通道(互斥滤镜)。导出期间禁止调用。 |
startPreview(source) | source: HTMLVideoElement | MediaStream | MediaStream | 启动实时预览,返回处理后的 MediaStream,业务方设置到 `.srcObject` 即可。源视频 element 的播放/暂停/拖动会自动同步到预览。 |
stopPreview() | - | - | 停止预览循环并释放资源。process() 执行前会自动调用 |
destroy() | - | - | 销毁实例,释放 detector / renderer / GPU 资源。同页面切换到 ArSdk 之前必须先 destroy。 |
process 参数
process(source, options) 的 options 字段:参数 | 说明 | 类型 |
signal | AbortSignal,用于业务方主动取消。 | AbortSignal |
onProgress | 编码进度回调。current 为已编码完成的帧数,total 为总帧数。回调在编码完成时触发,避免出现"100% 后还要再等几分钟" 的卡死错觉。 | (current: number, total: number) => void |
onDownloadProgress | URL 输入时的下载进度。total = -1 表示服务端未返回 Content-Length。 | (loaded: number, total: number) => void |
stallTimeoutMs | URL 模式下,超过该毫秒数未收到新字节判定为网络卡死,主动 abort。默认 30000,设为 0 关闭检测。 | Number |
mode | 输出模式: 'in-memory':内存中累积整个 mp4,finalize 时返回 Blob。受 V8 单 buffer 上限(~1.5-2 GB)。'file':流式写入用户磁盘文件,需要 File System Access API(仅 Chrome / Edge 桌面)。'auto'(默认):按估算输出大小自动切换。 | 'in-memory' \| 'file' \| 'auto' |
autoFileModeThreshold | 'auto' 模式下切到 'file' 的字节数阈值,默认 200 MB。 | Number |
suggestedName | 'file' 模式下浏览器保存对话框的默认文件名。 | String |
fileHandle | 'file' / 'auto' 模式下推荐由业务方在 click handler 里同步调用 window.showSaveFilePicker() 拿到的 FileSystemFileHandle,避免 user activation 失效(详见下文)。 | FileSystemFileHandle |
process() 返回 ProcessResult:{blob: Blob | null, // in-memory 模式下为完整 mp4 Blob;file 模式下为 nullfileHandle: FileSystemFileHandle | null, // file 模式下保存的文件句柄;in-memory 下为 nullmode: 'in-memory' | 'file', // 实际使用的模式(auto 解析后的具体值)durationMs: number, // 处理耗时frameCount: number, // 处理的视频帧数}
输出模式选择
in-memory 模式(默认小文件)
适合小于 200 MB 的视频。
process() 返回的 Blob 可直接 URL.createObjectURL(blob) 用于预览或上传。file 模式(推荐大文件)
适合大于 200 MB 的视频,避免内存爆掉。必须在浏览器的 user gesture(如按钮点击)同步路径上调用
window.showSaveFilePicker() 拿到 fileHandle,再传给 process():button.onclick = async () => {const fileHandle = await window.showSaveFilePicker({suggestedName: 'output.mp4',types: [{ description: 'MP4 Video', accept: { 'video/mp4': ['.mp4'] } }],})const result = await recorder.process(file, {mode: 'file',fileHandle,})}
注意:
经过
await input.open() 等异步步骤后,user activation 已过期,SDK 内部弹出保存对话框会失败。请务必在 click 事件回调的同步开头先调用 showSaveFilePicker()。auto 模式(默认)
按
source.bitrate × duration / 8 估算输出大小,超过 autoFileModeThreshold(默认 200 MB)切换到 file 模式;浏览器不支持 File System Access API 时回退到 in-memory 并打印 warning。