# Process external frames on iOS

> Send BGRA pixel buffers through the Nosmai off-screen pipeline and receive processed output for your own encoder or renderer.

> For AI agents: the complete documentation index is at https://nosmai.com/llms/effects.txt

Product: Nosmai Effects
Group: guide
Source: https://nosmai.com/docs/effects/guides/streaming/render-frames-off-screen/ios/

## Prerequisites

- A working [iOS quickstart](/docs/effects/quickstart/ios/)
- An app-owned BGRA `CVPixelBuffer` source with stable dimensions

## When to use this guide

Use off-screen mode when the host supplies external frames. If Nosmai owns the
camera and another component only needs processed output, use
`NosmaiCore.liveFrameStreamCallback` instead.

## Prepare the output callback

```objective-c
NosmaiSDK *sdk = [NosmaiSDK sharedInstance];

[sdk setCVPixelBufferCallback:^(CVPixelBufferRef output, double timestamp) {
    CVPixelBufferRetain(output);
    dispatch_async(encoderQueue, ^{
        [consumer consumePixelBuffer:output timestamp:timestamp];
        CVPixelBufferRelease(output);
    });
}];
```

Retain the output only when the consumer continues after the callback returns.

## Initialize off-screen mode

```objective-c
BOOL ready = [sdk initializeOffscreenWithWidth:width height:height];
if (ready) {
    [sdk setProcessingMode:NosmaiProcessingModeOffscreen];
}
```

Input buffers must use `kCVPixelFormatType_32BGRA` and keep stable dimensions.

## Submit a frame

```objective-c
BOOL accepted = [sdk processFrame:inputPixelBuffer mirror:NO];
```

You can pass a `CMSampleBufferRef` with
`processSampleBuffer:mirror:`. For asynchronous submission, retain the input
until `processFrameAsync:mirror:completion:` finishes.

## Return to live camera mode

Stop the external source first, then clear its callback:

```objective-c
[sdk setCVPixelBufferCallback:nil];
[sdk setProcessingMode:NosmaiProcessingModeLive];
```

## Verify it worked

Apply a visible effect and confirm the output buffer orientation, colors,
timestamps, and ownership on a physical device. Use `getProcessingMetrics`
during development to inspect processed and dropped frames.

## Common errors

| Symptom | Cause | Fix |
| --- | --- | --- |
| `processFrame` returns `NO` | Off-screen mode is not ready or the pixel format is unsupported | Initialize the exact dimensions and submit `kCVPixelFormatType_32BGRA` |
| Output crashes after the callback | The consumer kept a borrowed buffer without retaining it | Retain before asynchronous use and release after consumption |

## Next steps

- [Understand off-screen rendering](/docs/effects/concepts/off-screen-rendering/)
- [Integrate Agora on iOS](/docs/effects/integrations/agora/ios/)

## References

- [Off-screen rendering](/docs/effects/concepts/off-screen-rendering/)
- [iOS platform guide](/docs/effects/platforms/ios/)
- [Limits and performance](/docs/effects/limits-and-performance/)
