Skip to main content
SDK 2.0.0. Hook Facebetter into a third-party SDK’s custom video preprocess callback: process frames on that SDK’s OpenGL ES thread (or pixel-buffer callback), then return the result. Full texture rules: Implement Beauty · External texture. Room join, publish, and permissions belong in the vendor’s docs — this page only covers the Facebetter hookup.

Shared contract

On Apple platforms, TRTC custom preprocess commonly uses OpenGL textures; Agora / LiveKit more often deliver CVPixelBuffer. Both work — do not mix both modes on one engine instance.

Shared: process texture

Create the engine (GL thread):

Shared: process CVPixelBuffer (NV12 sketch)

Agora / LiveKit callbacks often use NV12. Take plane pointers and strides from the CVPixelBuffer; how you write back into the vendor frame varies by SDK version.
For the pixel-buffer path, create the engine with externalContext = NO. Credentials: Subscription, Auth & License.

TRTC (Tencent)

Hook: setLocalVideoProcessDelegete:pixelFormat:bufferType: (official spelling Delegete). Use TRTCVideoPixelFormat_Texture_2D and TRTCVideoBufferType_Texture. Implement TRTCVideoFrameDelegate: create the engine in onGLContextCreated; set dstFrame.textureId in onProcessVideoFrame:dstFrame:; nil the engine in onGLContextDestory.
Nil the engine after clearing the delegate or leaving the room. See Tencent’s TRTC third-party beauty guide for the latest details. For Flutter, use Flutter · Third-party Integration (FacebetterPlugin.processTexture).

Agora

Hook: AgoraRtcEngineKit.setVideoFrameDelegate: (Video SDK 4.x). On iOS, captured frames are usually CVPixelBuffer (textureBuf / format 12), not an Android-style GL_TEXTURE_2D id. You need to:
  1. Return ReadWrite from getVideoFrameProcessMode
  2. Take the CVPixelBuffer in onCapture:sourceType: (or older onCaptureVideoFrame)
  3. Feed it via createWithNV12: / createWithBGRA:, then write the result back into AgoraOutputVideoFrame
How you write a processed CVPixelBuffer back into Agora’s frame differs across major versions. Facebetter keeps processImage: output in the same format as the input; injecting that into AgoraOutputVideoFrame must follow Agora raw video processing.
Call setVideoFrameDelegate:nil and nil beautyEngine before tearing down the engine.

LiveKit

Hook: implement Swift VideoProcessor, transform in process(frame:), return the new frame (or nil to drop). Pass processor: when creating the camera track, or set track.processor later.
Official sample processors (for example background blur) ship as BackgroundBlurVideoProcessor. Custom capturers can run Facebetter before frames enter the encoder.

Troubleshooting