> ## Documentation Index
> Fetch the complete documentation index at: https://docs.facebetter.net/llms.txt
> Use this file to discover all available pages before exploring further.

# Third-party Integration

> Wire Facebetter beauty into TRTC, Agora, LiveKit, and other browser realtime pipelines

SDK **2.0.0**. The Web SDK has **no** native-style unified “GL texture preprocess callback”. Use this pipeline instead:

**Capture → `processImage` → draw to canvas → `captureStream` / take a `MediaStreamTrack` → hand it to the third-party SDK (`replaceTrack` or a custom video track).**

Auth must use one of the Web options (`licenseToken` / `authProxyUrl` / `fetchAuthResponse`). **Never** embed `appKey` in frontend JS. See [Auth & License](/intro/license) and [Implement Beauty](/web/implement-beauty).

```
getUserMedia / vendor preview track
       ↓
HTMLVideoElement
       ↓
BeautyEffectEngine.processImage(...)
       ↓
canvas + putImageData
       ↓
canvas.captureStream() → MediaStreamTrack
       ↓
TRTC / Agora / LiveKit publish or replaceTrack
```

## Recommended pipeline (vendor-agnostic)

```javascript theme={null}
import {
  BeautyEffectEngine,
  EngineConfig,
  FrameType,
  MirrorMode,
} from 'facebetter';

const engine = new BeautyEffectEngine(new EngineConfig({
  authProxyUrl: '/api/facebetter/auth', // production: your auth proxy
  resourcePath: '/resource.fbd',
}));
await engine.setLogConfig({ consoleEnabled: true, level: 2 });
await engine.init();

const video = document.createElement('video');
video.playsInline = true;
video.muted = true;
video.srcObject = await navigator.mediaDevices.getUserMedia({
  video: { width: 1280, height: 720 },
  audio: false,
});
await video.play();

const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');

function loop() {
  if (engine.initialized && video.readyState >= 2 && video.videoWidth > 0) {
    const result = engine.processImage(
      video,
      video.videoWidth,
      video.videoHeight,
      FrameType.Video,
      MirrorMode.Horizontal, // front camera as needed
    );
    if (result) {
      if (canvas.width !== result.width) canvas.width = result.width;
      if (canvas.height !== result.height) canvas.height = result.height;
      ctx.putImageData(result, 0, 0);
    }
  }
  requestAnimationFrame(loop);
}
loop();

// Beauty video track — pass to any RTC / meeting SDK
const beautyStream = canvas.captureStream(30);
const beautyTrack = beautyStream.getVideoTracks()[0];
```

<Tip>
  Prefer `processImage`. `processTexture` is for existing GPU texture handles and **does not apply mirror**. For live preview / publish, use the canvas pipeline above.
</Tip>

On page unload: `beautyTrack.stop()`, cancel `requestAnimationFrame`, and `engine.destroy()`.

***

## Hook into third-party SDKs

Vendor Web APIs change across versions. Below is only how to **feed `beautyTrack` in**; join, tokens, and device picking stay in their docs.

### TRTC (Tencent Web)

Two common patterns (check your `trtc-js-sdk` / TUI version):

1. **Create the local stream from the beauty track** (custom video source / pass a `MediaStreamTrack`)
2. **Create a camera stream first, then `replaceTrack(beautyTrack)`**

```javascript theme={null}
// Sketch: replace the video track on a local stream
// const localStream = TRTC.createStream({ ... });
// await localStream.initialize();
// await localStream.replaceTrack(beautyTrack);
// await client.publish(localStream);
```

Do not assume a native-style `onProcessVideoFrame(textureId)` exists on Web.

### Agora Web

Use a **custom video track** or `replaceTrack` on an existing track:

```javascript theme={null}
import AgoraRTC from 'agora-rtc-sdk-ng';

// A: custom video track from MediaStreamTrack (API name per agora-rtc-sdk-ng)
const customVideoTrack = AgoraRTC.createCustomVideoTrack({
  mediaStreamTrack: beautyTrack,
});
await client.publish([customVideoTrack /*, microphoneTrack */]);

// B: already have a camera track
// await cameraTrack.replaceTrack(beautyTrack);
```

### LiveKit Web

Pass the processed track into LiveKit’s local video track factory, or replace on a published track:

```javascript theme={null}
import { Room, LocalVideoTrack, createLocalVideoTrack } from 'livekit-client';

// Sketch: create from MediaStreamTrack and publish (factory name per livekit-client)
const track = LocalVideoTrack.createFromMediaStreamTrack(beautyTrack);
await room.localParticipant.publishTrack(track);

// Or on an existing LocalVideoTrack:
// await localVideoTrack.replaceTrack(beautyTrack);
```

***

## Notes

| Item         | Detail                                                                                              |
| ------------ | --------------------------------------------------------------------------------------------------- |
| Auth         | Production: `authProxyUrl` or your own `fetchAuthResponse`; `createDirectAuthFetcher` is debug-only |
| Perf         | Reuse one canvas; cap `captureStream` FPS; do not allocate a new canvas every frame                 |
| Audio        | Example is video-only; take the mic from a separate `getUserMedia({ audio: true })` and publish it  |
| Mirror       | Preview can use `MirrorMode` in `processImage`; decide whether encode should match                  |
| Vendor churn | `replaceTrack` / `createCustomVideoTrack` signatures change — follow the current Web SDK            |

## Troubleshooting

| Symptom              | Check                                                                           |
| -------------------- | ------------------------------------------------------------------------------- |
| Black frame          | `video.videoWidth === 0`, forgot `await engine.init()`, canvas size not updated |
| Remote has no beauty | You published **beautyTrack**, not the raw camera track                         |
| Auth failure         | Proxy returns the auth body verbatim; domain bound in the Dashboard             |
| Stutter              | Lower resolution / FPS; ensure a single `processImage` loop                     |

## Related

* [Implement Beauty](/web/implement-beauty)
* [Best Practices](/web/best-practices)
* [Error Handling](/web/error-handling)
* [Auth & License](/intro/license)
* [API Reference](/web/api-reference)
