# CameraFeedClient

<Badge tone="type">class · transport assembly</Badge> <Badge>IDisposable</Badge>

```csharp
public sealed class CameraFeedClient : IDisposable
```

**Namespace:** `AR51.Unity.SDK` (transport assembly, separate from `.Client`).

The caller-facing client for a remote CVS camera-feed endpoint. Obtain one from the live connection — it lets you enumerate remote cameras, fetch their [`CvsCameraInfo`](#cvscamerainfo), register a frame listener to receive JPG frames, and drive remote recording.

You don't construct it directly; get it from [`ServiceManager.Server`](/docs/sdk-api/unity-api/classes/servicemanager):

```csharp
var client = ServiceManager.Server.GetCameraFeedClient(address, port);
```

`GetCameraFeedClient(address, port)` is on the transport `ServiceContainer` exposed by `ServiceManager.Server`; use [`CameraService`](/docs/sdk-api/unity-api/classes/cameraservice) for **local** device capture, and [`RenderService`](/docs/sdk-api/unity-api/classes/renderservice) to composite a remote feed with holograms.

:::caution[Units]
AR 51 mocap and camera positions are Unity world-space **meters**; FOV/angle results are in **degrees**. The [`CvsCameraInfo`](#cvscamerainfo) extrinsic matrix is the camera's world pose in meters (handedness-corrected — see below).
:::

:::tip[Lifetime]
`CameraFeedClient` owns a transport connection and implements `IDisposable`. `Dispose()` it (or wrap it in `using`) when you're done so the underlying connection is released.
:::

## Properties

<div className="apiPropTable">

| Property | Type | Description |
|---|---|---|
| **Endpoint** | | |
| `Address` | `string` | the remote camera-feed host address |
| `Port` | `int` | the remote camera-feed port |
| `EndPoint` | `string` | the resolved `"address:port"` endpoint |
| `IsConnected` | `bool` | `true` while the client's transport connection is live |

</div>

## Method summary

**Cameras**

| Method | Returns | |
|---|---|---|
| [`GetCameraInfo`](#getcamerainfo) | [`CvsCameraInfo`](#cvscamerainfo) | |
| [`GetAvailableCameras`](#getavailablecameras) | [`CvsCameraInfo[]`](#cvscamerainfo) | |
| [`GetAvailableCamerasAsync`](#getavailablecameras) | `Task<CvsCameraInfo[]>` | <Badge tone="accent">async</Badge> |
| [`TryGetRealCameraInfos`](#trygetrealcamerainfos) | [`CvsCameraInfo[]`](#cvscamerainfo) | |
| [`TryGetRealCameraInfosAsync`](#trygetrealcamerainfos) | `Task<CvsCameraInfo[]>` | <Badge tone="accent">async</Badge> |

**Frame listeners**

| Method | Returns | |
|---|---|---|
| [`Register`](#register) | `void` | |
| [`Unregister`](#unregister) | `void` | |

**Recording**

| Method | Returns | |
|---|---|---|
| [`GetRecordingReady`](#getrecordingready) | `void` | |
| [`StartRecordingAsync`](#startrecordingasync) | `void` | <Badge tone="accent">async</Badge> |
| [`StopRecordingAsync`](#stoprecording) | `void` | <Badge tone="accent">async</Badge> |
| [`StopRecording`](#stoprecording) | `void` | |

**Lifetime**

| Method | Returns | |
|---|---|---|
| [`Stop`](#stop) | `void` | |
| [`Disconnect`](#disconnect) | `void` | |
| [`Dispose`](#dispose) | `void` | <Badge>IDisposable</Badge> |

→ Full descriptions in [Method details](#method-details) below.

## Method details

### GetCameraInfo

<ApiBody kind="method">

```csharp
public CvsCameraInfo GetCameraInfo(string cameraId);
```

Fetches the current [`CvsCameraInfo`](#cvscamerainfo) for one camera by id (resolution, frame rate, connection state, and the packed intrinsic/extrinsic matrices).

<Params>
<Param name="cameraId" type="string">the remote camera's `Id` (see `CvsCameraInfo.Id`)</Param>
</Params>

<Returns type="CvsCameraInfo" typeHref="#cvscamerainfo">the camera's info model</Returns>

</ApiBody>

### GetAvailableCameras

<ApiBody kind="method" tags="async variant">

```csharp
public CvsCameraInfo[] GetAvailableCameras();
public Task<CvsCameraInfo[]> GetAvailableCamerasAsync();
```

Enumerates every camera the remote endpoint advertises, including synthetic/relayed cameras. Use the `…Async` form off the Unity main thread to avoid blocking.

<Returns type="CvsCameraInfo[]" typeHref="#cvscamerainfo">all advertised cameras</Returns>

<Example inferred>

```csharp
var client = ServiceManager.Server.GetCameraFeedClient(address, port);
foreach (CvsCameraInfo cam in client.GetAvailableCameras())
    Debug.Log($"{cam.Id}: {cam.Width}x{cam.Height} @ {cam.FrameRate}fps  connected={cam.IsConnected}");
```

</Example>

</ApiBody>

### TryGetRealCameraInfos

<ApiBody kind="method" tags="async variant">

```csharp
public CvsCameraInfo[] TryGetRealCameraInfos();              // excludes synthetic/relayed cameras
public Task<CvsCameraInfo[]> TryGetRealCameraInfosAsync();
```

Like [`GetAvailableCameras`](#getavailablecameras) but filtered to **real** (physical) cameras — synthetic / relayed cameras are excluded. Prefer this when you only want to place or render actual hardware views.

<Returns type="CvsCameraInfo[]" typeHref="#cvscamerainfo">the physical cameras only</Returns>

</ApiBody>

### Register

<ApiBody kind="method">

```csharp
public void Register(string cameraId, ICameraFeedListener listener);
```

Subscribes a listener to a camera's frame stream. The listener receives JPG frames via its `OnCameraFeedReceived` callback. (`ICameraFeedListener` is a transport interface — see the [Connection area](/docs/sdk-api/unity-api/connection#camerafeedclient).)

<Params>
<Param name="cameraId" type="string">the camera to subscribe to</Param>
<Param name="listener" type="ICameraFeedListener">receives JPG frames via `OnCameraFeedReceived`</Param>
</Params>

<Example inferred>

```csharp
class FeedSink : ICameraFeedListener
{
    public void OnCameraFeedReceived(byte[] jpg) { /* decode / display */ }
}

var sink = new FeedSink();
client.Register("cam-0", sink);
// …later
client.Unregister("cam-0", sink);
```

</Example>

</ApiBody>

### Unregister

<ApiBody kind="method">

```csharp
public void Unregister(string cameraId, ICameraFeedListener listener);
```

Removes a previously [registered](#register) listener from a camera so it stops receiving frames. Pass the same `cameraId`/`listener` pair you registered.

</ApiBody>

### GetRecordingReady

<ApiBody kind="method">

```csharp
public void GetRecordingReady(string cameraId, string fileName);
```

Prepares the remote side to record the named camera into `fileName` (call before [`StartRecordingAsync`](#startrecordingasync)).

<Params>
<Param name="cameraId" type="string">the camera to record</Param>
<Param name="fileName" type="string">remote output file name for the recording</Param>
</Params>

</ApiBody>

### StartRecordingAsync

<ApiBody kind="method" tags="async">

```csharp
public void StartRecordingAsync(string cameraId);
```

Starts remote recording of the camera's feed. Pair with [`GetRecordingReady`](#getrecordingready) (to set the output file) and stop with [`StopRecording`](#stoprecording) / `StopRecordingAsync`.

<Params>
<Param name="cameraId" type="string">the camera to start recording</Param>
</Params>

<Example inferred>

```csharp
client.GetRecordingReady("cam-0", "session-01.mp4");
client.StartRecordingAsync("cam-0");
// …later
client.StopRecording("cam-0");
```

</Example>

</ApiBody>

### StopRecording

<ApiBody kind="method" tags="async variant">

```csharp
public void StopRecording(string cameraId);
public void StopRecordingAsync(string cameraId);
```

Stops a recording started with [`StartRecordingAsync`](#startrecordingasync). Use the `…Async` variant when you don't want to block the caller.

<Params>
<Param name="cameraId" type="string">the camera to stop recording</Param>
</Params>

</ApiBody>

### Stop

<ApiBody kind="method">

```csharp
public void Stop();
```

Stops the client's active feed/recording work without tearing down the connection.

</ApiBody>

### Disconnect

<ApiBody kind="method">

```csharp
public void Disconnect();
```

Closes the transport connection to the remote camera-feed endpoint. After this, [`IsConnected`](#properties) is `false`.

</ApiBody>

### Dispose

<ApiBody kind="method" tags="IDisposable">

```csharp
public void Dispose();
```

Releases the client and its underlying connection. Call when you're done, or use a `using` block. After disposal the instance must not be reused.

<Example inferred>

```csharp
using var client = ServiceManager.Server.GetCameraFeedClient(address, port);
var cams = client.GetAvailableCameras();
// client is disposed at end of scope
```

</Example>

</ApiBody>

---

## CvsCameraInfo

<Badge tone="type">proto message · transport</Badge>

```protobuf
message CvsCameraInfo
```

**Namespace:** `AR51.GRPC.CVS` (proto-generated, in the transport assembly).

The camera data model the client receives via `CameraFeedClient` and [`RenderService`](/docs/sdk-api/unity-api/classes/renderservice). It carries the camera's identity, resolution, frame rate, connection state, and **packed** intrinsic/extrinsic/distortion matrices.

:::note[Transport type — use the Extensions helpers]
Because `CvsCameraInfo` is a generated proto type, its matrices arrive as packed `bytes`. Don't unpack them by hand — the documented, usable API is the [`Extensions`](#extensions-helpers-on-cvscamerainfo) helpers below, which return Unity `Matrix4x4`/FOV values.
:::

### Fields

<div className="apiPropTable">

| Field | Type | Description |
|---|---|---|
| **Identity** | | |
| `Id` | `string` | camera identifier |
| `CameraType` | `string` | camera type label |
| **Image** | | |
| `Width` | `int32` | image width in **pixels** |
| `Height` | `int32` | image height in **pixels** |
| `FrameRate` | `int32` | frames per second |
| **State** | | |
| `IsConnected` | `bool` | whether the camera is currently connected |
| `IsRemote` | `bool` | whether the camera is relayed from a remote source |
| `RemoteEndpoint` | `string` | endpoint of the remote source (when `IsRemote`) |
| **Calibration (packed)** | | |
| `Intrisic` | `bytes` | 4×4 intrinsic matrix, row-major `float[16]` (packed) — **[sic] spelling** |
| `Extrensic` | `bytes` | 4×4 extrinsic (camera pose) matrix, `float[16]` (packed) — **[sic] spelling** |
| `Distortion` | `bytes` | distortion coefficients (packed) |

</div>

⚠️ The `Intrisic` and `Extrensic` field names are misspelled in the proto **[sic]** — preserved here because that's the wire name you'll see on the type. Read them through the helpers below rather than touching the raw `bytes`.

### Extensions helpers (on CvsCameraInfo)

`public static class Extensions` — namespace `AR51.Unity.SDK`. These unpack the packed matrices into Unity types and are the **documented, usable API** for `CvsCameraInfo`. See the full helper list on the [`Extensions`](/docs/sdk-api/unity-api/classes/extensions) page.

```csharp
public static Matrix4x4 GetIntrisic(this CvsCameraInfo info);    // unpacks Intrisic → Unity Matrix4x4
public static Matrix4x4 GetExtrinsic(this CvsCameraInfo info);   // unpacks Extrensic, handedness-corrected
public static float GetVerticalFOV(this CvsCameraInfo info);     // degrees, from Height & fy
public static float GetHorizontalFOV(this CvsCameraInfo info);   // degrees, from Width & fx
```

- **`GetIntrisic`** — unpacks `Intrisic` into a Unity `Matrix4x4`. It's a pinhole 3×3 embedded in a 4×4: `fx = [0,0]`, `fy = [1,1]`. (Method name preserves the proto **[sic]** spelling.)
- **`GetExtrinsic`** — unpacks `Extrensic` (the camera's world pose) into a `Matrix4x4`, **rotated 180° about Z** to map the CVS y-up / right-handed convention into Unity. Translation is in **meters**.
- **`GetVerticalFOV`** — vertical field of view in **degrees**, derived from `Height` and `fy`.
- **`GetHorizontalFOV`** — horizontal field of view in **degrees**, derived from `Width` and `fx`.

<Example inferred>

```csharp
CvsCameraInfo cam = client.GetCameraInfo("cam-0");

Matrix4x4 intrinsic = cam.GetIntrisic();     // [sic] proto spelling
Matrix4x4 worldPose = cam.GetExtrinsic();    // handedness-corrected; translation in meters
float vFov = cam.GetVerticalFOV();           // degrees
float hFov = cam.GetHorizontalFOV();         // degrees

// Place a Unity camera at the CVS camera's pose:
renderCamera.transform.position = worldPose.GetColumn(3);
renderCamera.fieldOfView = vFov;             // Unity uses vertical FOV
```

</Example>

## See also

- [`ServiceManager`](/docs/sdk-api/unity-api/classes/servicemanager) — provides `Server.GetCameraFeedClient(address, port)`
- [`RenderService`](/docs/sdk-api/unity-api/classes/renderservice) — composites a remote `CvsCameraInfo` feed with holograms, using these matrix/FOV helpers
- [`CameraService`](/docs/sdk-api/unity-api/classes/cameraservice) — local device capture (the counterpart to this remote client)
- [`Extensions`](/docs/sdk-api/unity-api/classes/extensions) — the full set of `CvsCameraInfo` and `Core`↔Unity helpers
- [Connection & Services](/docs/sdk-api/unity-api/connection) — `ServiceNames.CameraFeedService`, discovery, and the transport overview
- [Class index](/docs/sdk-api/unity-api/classes) — all Unity SDK types
