Use an external video source instead of the device camera
Describes the two custom video input modes supported by the AOQ Client SDK -- raw frame mode and encoded frame mode -- including configuration and code examples for each.
The AOQ Client SDK's built-in video module covers basic video needs, but in some scenarios the built-in capture module may not be sufficient. Custom video capture is useful when you need to:
Coming soon.
Choose one of the two modes based on your use case. The two modes cannot be used simultaneously: only one push interface can be active at a time.
Capture raw video frames in formats such as BGRA, I420, NV12, or NV21 and push them to the SDK for encoding and transmission. The SDK handles the complete encoding and sending pipeline internally.
The SDK's internal encoder encodes the raw frames you push. Adjust encoding parameters to suit your use case.
Parameters:
Call
Parameters:
Call
BGRA is a packed format with 4 bytes per pixel (Blue, Green, Red, Alpha). Frame size = width x height x 4 bytes.
I420 is a planar format with three separate planes (Y, U, V). Y plane size = width x height; U and V planes are each (width/2) x (height/2).
NV12 and NV21 are semi-planar formats consisting of a Y plane and an interleaved UV plane. NV12 interleaves UV in that order; NV21 interleaves VU. Frame size = width x height x 3 / 2 bytes, packed into the
On iOS and macOS, you can pass a
When you no longer need to push frames, stop the push timer first, then call
Encode video frames yourself (currently JPEG only) and push the encoded data directly to the SDK, bypassing the internal encoder. This mode does not require calling
Call
Once configured, you can push encoded frames immediately -- there is no need to call
Call
AoqVideoEncodedFrame parameters:
Encoded frame mode does not involve any capture device, so stopping is simply a matter of halting your push timer.
Overview
The AOQ Client SDK's built-in video module covers basic video needs, but in some scenarios the built-in capture module may not be sufficient. Custom video capture is useful when you need to:
- Work around camera device conflicts or compatibility issues.
- Feed video data from a custom capture system or video file into the SDK for transmission.
- Publish AI-generated frames, screen recordings, or virtual camera content through the SDK.
- Raw frame mode: Capture raw video frames in formats such as BGRA, I420, NV12, or NV21, then push them to the SDK via
pushExternalVideoCapturedFrame. The SDK handles encoding and transmission internally. - Encoded frame mode: Encode video frames yourself (currently JPEG only), then push the encoded data directly to the SDK via
pushExternalVideoEncodedFrame, bypassing the SDK's internal encoder.
Sample code
Coming soon.
Prerequisites
- An engine instance has been created by calling
createEngine. - A connection to the server has been established (the
onConnectionStatusChangecallback has reportedAoqConnectionStatusConnected).
Implementation
Choose one of the two modes based on your use case. The two modes cannot be used simultaneously: only one push interface can be active at a time.
Mode 1: Raw frame mode
Capture raw video frames in formats such as BGRA, I420, NV12, or NV21 and push them to the SDK for encoding and transmission. The SDK handles the complete encoding and sending pipeline internally.
1. Configure video encoding parameters
The SDK's internal encoder encodes the raw frames you push. Adjust encoding parameters to suit your use case.
| Parameter | Type | Default | Description |
|---|---|---|---|
| trackType | AoqTrackType | AoqTrackTypeVideo | Video track type |
| codecType | AoqEncoderType | AoqEncoderTypeVideoH264 | Encoder type |
| width | int | 720 | Encoding width (pixels) |
| height | int | 1280 | Encoding height (pixels) |
| fps | int | 5 | Frame rate |
| bitrate | int | 500000 | Starting bitrate (bps) |
| minBitrate | int | 128000 | Minimum bitrate (bps) |
| keyframeInterval | int | 2 | Keyframe interval (seconds) |
| isExternal | boolean | false | Keep false for raw frame mode |
| mirrorMode | AoqMirrorMode | AoqMirrorModeDisabled | Mirror mode |
| orientationMode | AoqOrientationMode | AoqOrientationModeAuto | Orientation mode |
2. Start video capture in external capture mode
Call startVideoCapture with isExternal=true to tell the SDK not to open the camera and to expect frames from an external source. This is required for raw frame mode -- if you skip this call, the SDK will not consume any frames you push.
| Parameter | Type | Default | Description |
|---|---|---|---|
| width | int | 1280 | Capture width (ignored when isExternal=true) |
| height | int | 720 | Capture height (ignored when isExternal=true) |
| fps | int | 15 | Capture frame rate (ignored when isExternal=true) |
| isExternal | boolean | false | true: do not open the camera; external source provides frames |
| cameraDirection | AoqCameraDirection | AoqCameraDirectionFront | Camera direction (ignored when isExternal=true) |
3. Push raw video frames
Call pushExternalVideoCapturedFrame to feed captured raw frames into the SDK. The SDK handles encoding and transmission.
Supported formats: BGRA, I420, NV12, NV21, RGBA. On Apple platforms, CVPixelBuffer zero-copy is also supported.
3.1 BGRA format
BGRA is a packed format with 4 bytes per pixel (Blue, Green, Red, Alpha). Frame size = width x height x 4 bytes.
3.2 I420 format
I420 is a planar format with three separate planes (Y, U, V). Y plane size = width x height; U and V planes are each (width/2) x (height/2).
3.3 NV12 / NV21 format
NV12 and NV21 are semi-planar formats consisting of a Y plane and an interleaved UV plane. NV12 interleaves UV in that order; NV21 interleaves VU. Frame size = width x height x 3 / 2 bytes, packed into the data field.
3.4 CVPixelBuffer format (Apple platforms)
On iOS and macOS, you can pass a CVPixelBufferRef directly for zero-copy transfer, avoiding the performance overhead of memory copies.
4. Stop raw frame capture
When you no longer need to push frames, stop the push timer first, then call stopVideoCapture to shut down video capture.
Mode 2: Encoded frame mode
Encode video frames yourself (currently JPEG only) and push the encoded data directly to the SDK, bypassing the internal encoder. This mode does not require calling startVideoCapture or any other capture-related APIs.
1. Configure video encoding parameters and enable external encoding
Call setVideoEncoderConfig with isExternal=true to tell the SDK to skip the internal encoder and expect pre-encoded data from outside.
startVideoCapture.
2. Push encoded video frames
Call pushExternalVideoEncodedFrame to pass pre-encoded video data directly to the SDK. Only JPEG encoding is currently supported.
| Parameter | Type | Default | Description |
|---|---|---|---|
| codec | AoqVideoCodecType | AoqVideoCodecTypeJPEG | Encoding format; currently only JPEG is supported |
| data | byte[] | null | Encoded frame data |
| width | int | 0 | Frame width (pixels) |
| height | int | 0 | Frame height (pixels) |
| timeStamp | long | 0 | Timestamp (milliseconds); when 0, the SDK uses the local clock |
3. Stop pushing encoded frames
Encoded frame mode does not involve any capture device, so stopping is simply a matter of halting your push timer.
Reference
AoqVideoFrame (raw frame mode)
| Field | Type | Description |
|---|---|---|
| format | AoqVideoPixelFormat | Pixel format |
| width | int | Frame width (pixels) |
| height | int | Frame height (pixels) |
| data | byte[] | Packed format data (NV12/NV21/BGRA/RGBA) |
| dataY | byte[] | I420 Y plane data |
| dataU | byte[] | I420 U plane data |
| dataV | byte[] | I420 V plane data |
| strideY | int | I420 Y plane row stride (bytes) |
| strideU | int | I420 U plane row stride (bytes) |
| strideV | int | I420 V plane row stride (bytes) |
| textureId | int | Android texture ID (TextureOES/Texture2D) |
| transformMatrix | float[] | Texture transform matrix (4x4, row-major) |
| eglContext | EGLContext | Android shared EGL context (for texture mode) |
| pixelBuffer | CVPixelBufferRef | Apple zero-copy CVPixelBuffer (iOS/macOS only) |
| timeStamp | long | Timestamp (milliseconds); when 0, the SDK uses the local clock |
AoqVideoPixelFormat enum values
| Enum value | Numeric value | Description |
|---|---|---|
| AoqVideoPixelFormatUnknown | 0 | Unknown format |
| AoqVideoPixelFormatI420 | 1 | I420 planar format |
| AoqVideoPixelFormatNV12 | 2 | NV12 semi-planar format (UV interleaved) |
| AoqVideoPixelFormatNV21 | 3 | NV21 semi-planar format (VU interleaved) |
| AoqVideoPixelFormatBGRA | 4 | BGRA packed format |
| AoqVideoPixelFormatRGBA | 5 | RGBA packed format |
| AoqVideoPixelFormatCVPixelBuffer | 6 | Apple CVPixelBuffer (iOS/macOS only) |
| AoqVideoPixelFormatTextureOES | 7 | Android OES external texture |
| AoqVideoPixelFormatTexture2D | 8 | Android 2D texture |
AoqVideoEncodedFrame (encoded frame mode)
| Field | Type | Description |
|---|---|---|
| codec | AoqVideoCodecType | Encoding format |
| data | byte[] | Encoded frame data |
| width | int | Frame width (pixels) |
| height | int | Frame height (pixels) |
| timeStamp | long | Timestamp (milliseconds); when 0, the SDK uses the local clock |
AoqVideoCodecType enum values
| Enum value | Numeric value | Description |
|---|---|---|
| AoqVideoCodecTypeJPEG | 0 | JPEG encoding format |
Important notes
- Raw frame mode: you must call
startVideoCapture(isExternal=true)before pushing frames. If you skip this call, the SDK returns a parameter error. - Encoded frame mode: call
setVideoEncoderConfig(isExternal=true)and push immediately -- callingstartVideoCaptureis not required. - Raw frame mode and encoded frame mode cannot be used simultaneously: only one push interface can be active at a time.
- Encoded frame mode currently supports JPEG only.
- After you push a frame, the SDK manages its lifecycle internally. You do not need to retain the data after the push call returns.