ArRecorderSdk, added in SDK 2.0.3, performs secondary processing on an existing mp4 file or online video URL — applying beauty effects, stickers, filters, and the like — and exports a new mp4. Compared with the realtime ArSdk, ArRecorderSdk:VideoEncoder / VideoDecoder) is required: Chrome 102+ / Edge 102+. Safari and most mobile browsers are not supported.ArSdk and ArRecorderSdk on the same page simultaneously corrupts shared state via the AROptionsManager singleton. Always destroy() one before creating the other.setBeautify / setEffect / setFilter while process() is running will throw. To set parameters before the first encoded frame, listen on the beforeRun event and await your set* calls inside the handler:recorder.on('beforeRun', async ({ mode }) => {await recorder.setEffect({ id: 'xxx', intensity: 0.5 })})
beautify is omitted in the constructor, the first runtime call to setBeautify / setEffect triggers a pipeline lazy-load (mediapipe + wasm, ~2-3 s). To make the first frame include effects, pass beautify in the constructor so init() can pre-warm.VideoEncoder / VideoDecoder, the constructor emits an error event with ENVIRAONMENT_NOT_COMPATIBLE. Gate the SDK with environment detection up front.ArRecorderSdk ships in the same npm package as ArSdk:import { ArRecorder } from 'tencentcloud-webar'const recorder = new ArRecorder.ArRecorderSdk({auth: { licenseKey, appId, authFunc },beautify: { whiten: 0.3, dermabrasion: 0.5 },})
ArSdk and ArRecorderSdk on the same page simultaneously — they will pollute each other's state. destroy() one before instantiating the other.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, // Same auth callback as ArSdk},beautify: {whiten: 0.3,dermabrasion: 0.5,},})// 1. Authenticate and pre-warm the processing pipelineawait recorder.init()// 2. Optionally set beauty / effects / filters at runtimeawait recorder.setBeautify({ whiten: 0.5, lift: 0.2 })await recorder.setEffect({ id: 'effectId', intensity: 0.7 })await recorder.setFilter('filterId', 0.5)// 3. Processconst result = await recorder.process(file, {onProgress: (current, total) => {console.log(`Processed ${current}/${total} frames`)},})// 4. Consume the resultif (result.mode === 'in-memory') {const url = URL.createObjectURL(result.blob)document.querySelector('video').src = url} else {// file mode: already written to the user-selected disk fileconst file = await result.fileHandle.getFile()}// 5. Release resourcesrecorder.destroy()}
new ArRecorder.ArRecorderSdk(options) accepts the following config:Parameter | Description | Type | Required |
auth | Authentication parameters; same shape as ArSdk's auth | {licenseKey: stringappId: stringsdkAppId?: stringauthFunc: () => Promise<{ signature, timestamp }>language?: string} | Yes |
beautify | Beauty parameters; same shape as ArSdk's BeautifyOptions. When omitted, init() does not pre-warm the detect+render pipeline (saving load time), but the first runtime call to setBeautify / setEffect triggers a lazy load (~2-3 s). | BeautifyOptions | No |
module | Module config; same shape as ArSdk's ModuleConfig. Recorder currently consumes only the faceDetectModel field; the remaining fields (segmentation / handLandmark, etc.) keep the same names for migration compatibility but are not yet honored. | {faceDetectModel?: 'short' | 'full'// Other ArSdk fields accepted for compatibility but no-op} | No |
output | Output mp4 encoding config | {codec?: 'avc' // Only H.264 supported in current releasebitrate?: number | 'auto' // bps; 'auto' follows sourcefps?: number | 'auto' // 'auto' follows source} | No |
initReport | Whether to initialize the reporting module | Boolean | No, default true |
API | Parameters | Returns | Description |
async init() | - | Promise | Authenticate and (optionally) pre-warm the detect+render pipeline. When beautify is provided in the constructor, init() also pre-loads it so the first frame is not stalled by 2-3 s. |
async process(source, options?) | source: Blob | File | string — local file or online URL (CORS must allow access)options: see "process options" | Promise | Process the source video offline and export a new mp4. set* methods are forbidden during processing. |
async setBeautify(options) | BeautifyOptions (same as ArSdk) | Promise | Hot-update beauty parameters; effective on the next frame. Forbidden while process() is running. |
async setEffect(input) | input: effect ID | Effect object | array of either | empty array (clear) Same parameter style as ArSdk's setEffect. | Promise | Set / switch / clear effects (filter / sticker / makeup / 3D try-on). Forbidden while process() is running. |
async setFilter(id?, intensity?) | id: filter ID. Omit / '' / 'none' clears the current filter.intensity: 0-1, default 1 | Promise | Standalone LUT filter; uses a separate (mutually exclusive) channel from setEffect. Forbidden while process() is running. |
startPreview(source) | source: HTMLVideoElement | MediaStream | MediaStream | Start realtime preview. Returns a processed MediaStream the caller can attach to `.srcObject`. Source video play / pause / seek automatically syncs to the preview. |
stopPreview() | - | - | Stop the preview loop and release reader/writer. Called automatically before process(). |
destroy() | - | - | Destroy the instance and release detector / renderer / GPU resources. Required before instantiating ArSdk on the same page. |
options argument to process(source, options):Parameter | Description | Type |
signal | AbortSignal for caller-side cancellation | AbortSignal |
onProgress | Encoding progress callback. current is the count of fully encoded frames; total is the total frame count. Reported on encode completion to avoid the "100% then waits a few minutes" feel caused by encoder queue draining. | (current: number, total: number) => void |
onDownloadProgress | Download progress for URL inputs. total = -1 means the server did not return Content-Length. | (loaded: number, total: number) => void |
stallTimeoutMs | URL mode: if no new bytes are received within this many ms, treat as a stalled network and abort. Default 30000; set 0 to disable. | Number |
mode | Output mode: 'in-memory': accumulate the whole mp4 in memory, return a Blob from finalize. Subject to V8 single-buffer limit (~1.5-2 GB).'file': stream-write to a user-selected disk file. Requires File System Access API (Chrome / Edge desktop only).'auto' (default): switch automatically based on estimated output size. | 'in-memory' \\| 'file' \\| 'auto' |
autoFileModeThreshold | Threshold (bytes) above which 'auto' switches to 'file'. Default 200 MB. | Number |
suggestedName | Default file name shown in the browser's save dialog ( 'file' mode). | String |
fileHandle | In 'file' / 'auto' modes, the recommended pattern is to call window.showSaveFilePicker() synchronously in the click handler and pass the resulting FileSystemFileHandle here, to avoid losing user activation (see below). | FileSystemFileHandle |
process() returns ProcessResult:{blob: Blob | null, // in-memory mode: the full mp4 Blob; file mode: nullfileHandle: FileSystemFileHandle | null, // file mode: handle to the saved file; in-memory: nullmode: 'in-memory' | 'file', // resolved mode (auto resolves to a concrete value)durationMs: number, // processing durationframeCount: number, // total frames processed}
URL.createObjectURL(blob) for preview or upload.window.showSaveFilePicker() synchronously on the user gesture path (e.g., a button click handler) to obtain a fileHandle, then pass it to 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() and similar async steps, user activation has expired and any subsequent showSaveFilePicker() call inside the SDK will fail. Always grab the fileHandle at the very top of the click handler before any await.source.bitrate × duration / 8. If it exceeds autoFileModeThreshold (default 200 MB), the SDK switches to 'file' mode. If File System Access API is unavailable, it falls back to 'in-memory' and prints a warning.피드백