• 서비스
  • 가격
  • 리소스
  • 기술지원
이 페이지는 현재 영어로만 제공되며 한국어 버전은 곧 제공될 예정입니다. 기다려 주셔서 감사드립니다.
새로운 기능으로 더 부드럽고 멋진 AR 효과를 제공합니다!

뷰티처리 동영상 활용

Overview

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:
Does not depend on a camera or live stream; it operates on local or remote recorded mp4 files.
Awaits each frame sequentially with no dropped frames; processing time depends on device performance — the faster the device, the faster the processing.
Passes audio through unchanged (no decode / re-encode).
Uses WebCodecs hardware decode/encode (no software fallback).

Use cases

Local beauty processing on UGC videos before upload.
Batch-adding filters or sticker effects to recorded mp4 files.
Implementing a "record first, beautify later" workflow.

Notes

Browser requirements

WebCodecs (VideoEncoder / VideoDecoder) is required: Chrome 102+ / Edge 102+. Safari and most mobile browsers are not supported.
For large outputs (> 200 MB), File System Access API support (Chrome / Edge desktop) is recommended; otherwise the SDK falls back to in-memory mode, where files larger than ~1.5 GB may cause OOM.

Usage notes

Cannot coexist with ArSdk: instantiating ArSdk and ArRecorderSdk on the same page simultaneously corrupts shared state via the AROptionsManager singleton. Always destroy() one before creating the other.
Parameters locked during process: calls to 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 })
})
First-call lazy load: when 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.
WebCodecs guard: when the browser lacks VideoEncoder / VideoDecoder, the constructor emits an error event with ENVIRAONMENT_NOT_COMPATIBLE. Gate the SDK with environment detection up front.
Audio is not re-encoded: source audio is muxed into the output mp4 unchanged; no decode / re-encode happens during processing.

Demo download

Reference for the complete Demo recorder-demo-en.zip.

Importing

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 },
})
Note:
Do not instantiate ArSdk and ArRecorderSdk on the same page simultaneously — they will pollute each other's state. destroy() one before instantiating the other.

Full example

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 pipeline
await recorder.init()

// 2. Optionally set beauty / effects / filters at runtime
await recorder.setBeautify({ whiten: 0.5, lift: 0.2 })
await recorder.setEffect({ id: 'effectId', intensity: 0.7 })
await recorder.setFilter('filterId', 0.5)

// 3. Process
const result = await recorder.process(file, {
onProgress: (current, total) => {
console.log(`Processed ${current}/${total} frames`)
},
})

// 4. Consume the result
if (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 file
const file = await result.fileHandle.getFile()
}

// 5. Release resources
recorder.destroy()
}

Constructor parameters

new ArRecorder.ArRecorderSdk(options) accepts the following config:
Parameter
Description
Type
Required
auth
Authentication parameters; same shape as ArSdk's auth
{
licenseKey: string
appId: string
sdkAppId?: string
authFunc: () => 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 release
bitrate?: number | 'auto' // bps; 'auto' follows source
fps?: number | 'auto' // 'auto' follows source
}
No
initReport
Whether to initialize the reporting module
Boolean
No, default true

APIs

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.

process options

The 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: null
fileHandle: FileSystemFileHandle | null, // file mode: handle to the saved file; in-memory: null
mode: 'in-memory' | 'file', // resolved mode (auto resolves to a concrete value)
durationMs: number, // processing duration
frameCount: number, // total frames processed
}

Output mode selection

in-memory mode (default for small files)

Suitable for videos under 200 MB. The returned Blob is ready to use with URL.createObjectURL(blob) for preview or upload.

file mode (recommended for large files)

Suitable for videos larger than 200 MB to avoid memory exhaustion. You must call 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,
})
}
Note:
After 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.

auto mode (default)

The SDK estimates output size as 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.