> ## 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.

# Integração com terceiros

> Conecte a beleza Facebetter a TRTC, Agora, LiveKit e outros pipelines de vídeo no Android

SDK **2.0.0**. Encaixe o Facebetter no callback de **pré-processamento de vídeo personalizado** de um SDK de terceiros: no **thread OpenGL ES** desse SDK, execute `processImage` em uma textura externa e, em seguida, devolva a textura de saída.

Regras completas de textura: [Implementar beleza · Textura externa](/pt-BR/android/implement-beauty#external-texture-opengl-es). Entrar na sala, publicar e permissões ficam na documentação do fornecedor — esta página cobre apenas o encaixe do Facebetter.

```
Third-party SDK (GL thread)
  input textureId
       ↓
BeautyEffectEngine (externalContext = true)
  ImageFrame.createWithTexture → processImage
       ↓
  output textureId → write back to vendor frame / callback
```

## Contrato compartilhado

| Item           | Requisito                                                                                             |
| -------------- | ----------------------------------------------------------------------------------------------------- |
| Mecanismo      | `externalContext = true`, criação lazy no **thread do callback GL do fornecedor**                     |
| Entrada        | `GL_TEXTURE_2D` (RGBA); `stride` costuma ser `width * 4`                                              |
| Tipo de quadro | `ImageFrame.FrameType.VIDEO`                                                                          |
| Saída          | O SDK é dono da textura de saída — não chame `glDeleteTextures`. Sempre `ImageFrame.release()`        |
| Ciclo de vida  | Quando o fornecedor destruir o contexto GL, `engine.release()` e, em seguida, recrie no novo contexto |

O Facebetter **não** aceita `GL_TEXTURE_EXTERNAL_OES` em `createWithTexture`. Se o fornecedor usar OES por padrão, mude para **Texture2D / RGB** conforme a documentação dele, ou faça blit para `GL_TEXTURE_2D` primeiro.

### Helper compartilhado

Reutilize isto em cada callback GL do fornecedor (precisa rodar no thread GL):

```java theme={null}
import net.pixpark.facebetter.BeautyEffectEngine;
import net.pixpark.facebetter.ImageFrame;

/** @return beauty output GL_TEXTURE_2D; on failure returns srcTextureId (or 0, your choice) */
static int processTexture(BeautyEffectEngine engine, int srcTextureId, int width, int height) {
    if (engine == null || srcTextureId == 0 || width <= 0 || height <= 0) {
        return srcTextureId;
    }
    int stride = width * 4;
    ImageFrame input = ImageFrame.createWithTexture(srcTextureId, width, height, stride);
    if (input == null) {
        return srcTextureId;
    }
    input.type = ImageFrame.FrameType.VIDEO;
    ImageFrame output = engine.processImage(input);
    if (output == null) {
        input.release();
        return srcTextureId;
    }
    int dst = output.getTexture();
    output.release();
    input.release();
    return dst != 0 ? dst : srcTextureId;
}
```

Crie o mecanismo (também no thread GL):

```java theme={null}
BeautyEffectEngine.EngineConfig config = new BeautyEffectEngine.EngineConfig();
config.appId = "your appId";
config.appKey = "your appKey";
// or config.licenseToken = "...";
config.externalContext = true;
BeautyEffectEngine engine = new BeautyEffectEngine(context, config);
engine.setSmoothing(0.5f);
```

Credenciais: [Assinatura](/pt-BR/intro/enable-service), [Autenticação e licença](/pt-BR/intro/license).

***

## TRTC (Tencent)

Hook: `TRTCCloud.setLocalVideoProcessListener`. Use o formato de pixel **Texture\_2D** e o tipo de buffer **TEXTURE**.

Crie o Facebetter em `onGLContextCreated`, processe em `onProcessVideoFrame` e defina `dstFrame.texture.textureId`, chame `release()` em `onGLContextDestory`.

```java theme={null}
import com.tencent.trtc.TRTCCloud;
import com.tencent.trtc.TRTCCloudDef;
import com.tencent.trtc.TRTCCloudListener;

trtcCloud.setLocalVideoProcessListener(
    TRTCCloudDef.TRTC_VIDEO_PIXEL_FORMAT_Texture_2D,
    TRTCCloudDef.TRTC_VIDEO_BUFFER_TYPE_TEXTURE,
    new TRTCCloudListener.TRTCVideoFrameListener() {
        @Override
        public void onGLContextCreated() {
            // TRTC has bound the GL context — lazy-create BeautyEffectEngine here
            ensureEngine();
        }

        @Override
        public int onProcessVideoFrame(
                TRTCCloudDef.TRTCVideoFrame srcFrame,
                TRTCCloudDef.TRTCVideoFrame dstFrame) {
            int out = processTexture(
                    beautyEngine,
                    srcFrame.texture.textureId,
                    srcFrame.width,
                    srcFrame.height);
            dstFrame.texture.textureId = out;
            return 0;
        }

        @Override
        public void onGLContextDestory() {
            if (beautyEngine != null) {
                beautyEngine.release();
                beautyEngine = null;
            }
        }
    });
```

Libere o mecanismo depois de remover o listener ou sair da sala. Consulte o guia de beleza de terceiros da Tencent para os detalhes mais recentes do TRTC.

No Flutter, use [Flutter · Integração com terceiros](/pt-BR/flutter/third-party-integration) (`FacebetterPlugin.processTexture`, não esta API Java).

***

## Agora

Hook: `RtcEngine.registerVideoFrameObserver` (Video SDK **4.x**). Você precisa:

1. Retornar `PROCESS_MODE_READ_WRITE` de `getVideoFrameProcessMode()`
2. Preferir um **TextureBuffer** com tipo **RGB (`GL_TEXTURE_2D`)**
3. Executar o Facebetter em `onCaptureVideoFrame` (ou na posição de observação escolhida) e, em seguida, **escrever** a textura processada de volta no `VideoFrame`

```java theme={null}
import io.agora.rtc2.RtcEngine;
import io.agora.rtc2.video.IVideoFrameObserver;
import io.agora.base.VideoFrame;

engine.registerVideoFrameObserver(new IVideoFrameObserver() {
    @Override
    public boolean onCaptureVideoFrame(int sourceType, VideoFrame videoFrame) {
        VideoFrame.Buffer buffer = videoFrame.getBuffer();
        if (!(buffer instanceof VideoFrame.TextureBuffer)) {
            return true; // non-texture: pass through or convert, per your app
        }
        VideoFrame.TextureBuffer tex = (VideoFrame.TextureBuffer) buffer;
        if (tex.getType() != VideoFrame.TextureBuffer.Type.RGB) {
            return true; // OES: convert to GL_TEXTURE_2D, or adjust format preference
        }

        ensureEngine(); // must use the current GL / EGL context
        int outId = processTexture(
                beautyEngine,
                tex.getTextureId(),
                tex.getWidth(),
                tex.getHeight());

        // Wrap outId as a TextureBuffer and write it back into videoFrame.
        // Exact APIs (TextureBufferHelper / replaceBuffer, etc.) vary by Agora version —
        // follow Agora’s raw video-frame docs, then return true.
        return true;
    }

    @Override
    public int getVideoFrameProcessMode() {
        return IVideoFrameObserver.PROCESS_MODE_READ_WRITE;
    }

    @Override
    public int getVideoFormatPreference() {
        // Prefer a processable texture; use the constant from your Agora version
        return IVideoFrameObserver.VIDEO_PIXEL_DEFAULT;
    }

    // Implement other callbacks as needed; POSITION_POST_CAPTURER is common
});
```

<Warning>
  Como injetar um novo `textureId` de volta no `VideoFrame` da Agora depende dos helpers de TextureBuffer e muda entre versões principais. O Facebetter só produz um novo `GL_TEXTURE_2D`; o passo de write-back precisa seguir o [observador de quadro de vídeo da Agora](https://docs.agora.io/en/video-calling/develop/product-workflow) / Referência da API.
</Warning>

Cancele o registro do observer e chame `beautyEngine.release()` antes de destruir o `RtcEngine`.

***

## LiveKit

Hook: passe um `org.webrtc.VideoProcessor` (ou processador de track equivalente) ao criar a track de vídeo local. Processe texturas em `onFrameCaptured` e, em seguida, entregue via `sink.onFrame(...)`.

```kotlin theme={null}
import io.livekit.android.room.track.LocalVideoTrackOptions
import org.webrtc.VideoFrame
import org.webrtc.VideoProcessor
import org.webrtc.VideoSink

class FacebetterVideoProcessor(
    private val appContext: android.content.Context,
) : VideoProcessor {
    private var sink: VideoSink? = null
    private var beautyEngine: BeautyEffectEngine? = null

    override fun setSink(videoSink: VideoSink?) {
        sink = videoSink
    }

    override fun onCapturerStarted(success: Boolean) {}

    override fun onCapturerStopped() {
        beautyEngine?.release()
        beautyEngine = null
    }

    override fun onFrameCaptured(frame: VideoFrame) {
        val buffer = frame.buffer
        if (buffer !is VideoFrame.TextureBuffer ||
            buffer.type != VideoFrame.TextureBuffer.Type.RGB
        ) {
            sink?.onFrame(frame)
            return
        }

        ensureEngine() // LiveKit / WebRTC current EGL context
        val outId = processTexture(
            beautyEngine,
            buffer.textureId,
            buffer.width,
            buffer.height,
        )

        // Build a new TextureBuffer / VideoFrame from outId, then:
        // sink?.onFrame(processedFrame)
        // processedFrame.release()
        //
        // TextureBuffer construction needs WebRTC Handler / YuvConverter, etc. —
        // follow LiveKit Android SDK and WebRTC samples for the wrap.
        sink?.onFrame(frame)
    }
}

// Attach the processor when creating the track (API per your livekit-android version)
val videoTrack = room.localParticipant.createVideoTrack(
    options = LocalVideoTrackOptions(),
    videoProcessor = FacebetterVideoProcessor(context),
)
videoTrack.startCapture()
room.localParticipant.publishVideoTrack(videoTrack)
```

Você também pode implementar um `VideoCapturer` personalizado, executar `processTexture` após a captura e, em seguida, chamar `capturerObserver.onFrameCaptured`. Processadores oficiais (por exemplo fundo virtual) ficam no módulo `livekit-android-track-processors` do LiveKit.

***

## Solução de problemas

| Sintoma            | Verifique                                                                                                      |
| ------------------ | -------------------------------------------------------------------------------------------------------------- |
| Preto / sem vídeo  | Mecanismo criado e `processImage` no **thread GL** do fornecedor; quadros com falha não descartados por engano |
| Sem beleza         | `externalContext == true`; a entrada é `GL_TEXTURE_2D`                                                         |
| Crash intermitente | `release()` na destruição GL / parada do capturer; nunca `glDeleteTextures` na saída                           |
| Distorção          | Largura / altura / rotação / espelhamento correspondem ao quadro do fornecedor; OES não convertido para 2D     |

## Relacionado

* [Implementar beleza](/pt-BR/android/implement-beauty)
* [Práticas recomendadas](/pt-BR/android/best-practices)
* [Tratamento de erros](/pt-BR/android/error-handling)
* [Referência da API](/pt-BR/android/api-reference)
