# IObjectDetectionClient

<Badge tone="type">interface</Badge> <Badge>thread-safe</Badge>

```csharp
namespace AR51.Net;

public interface IObjectDetectionClient
```

Live tracked objects, for example props. Use it through [`IAR51Client.ObjectDetection`](/docs/sdk-api/csharp-api/classes/iar51client/), or on its own as an [`ObjectDetectionClient`](/docs/sdk-api/csharp-api/classes/objectdetectionclient/) with the service's address.

Works exactly like [`ISkeletonClient`](/docs/sdk-api/csharp-api/classes/iskeletonclient/). Two ways to receive objects: call [`StartAsync`](#startasync) and handle `ObjectReceived`, or read [`StreamAsync`](#streamasync) with `await foreach`. All members are safe to use from any thread.

:::note[Units]
Positions are in **meters**, in the AR 51 world: **left-handed, X right, Y up, Z forward** (the same axes as Unity). See [Data conventions](/docs/csharp-api/conventions/).
:::

:::tip[How-to]
This page is **reference** (facts only). For a walkthrough with a complete sample, see the [Object detection guide](/docs/csharp-api/objects/).
:::

## Properties

<div className="apiPropTable apiTable">

| Property | Type | Access | Description |
|---|---|---|---|
| `IsAvailable` | `bool` | `get` | `true` when the object detection service's address is known (always, for a standalone [`ObjectDetectionClient`](/docs/sdk-api/csharp-api/classes/objectdetectionclient/)). |

</div>

## Method summary

<div className="apiTable">

| Method | Returns | |
|---|---|---|
| [`StartAsync`](#startasync) | [`Task`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task) |  |
| [`StopAsync`](#stopasync) | [`Task`](https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task) |  |
| [`StreamAsync`](#streamasync) | [`IAsyncEnumerable<TrackedObject>`](/docs/sdk-api/csharp-api/classes/trackedobject/) |  |

</div>

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

## Events

Standard .NET events (`EventHandler<T>`): subscribe with `+=`, unsubscribe with `-=`. They are raised on a background thread.

<div className="apiTable">

| Event | Args | Raised when |
|---|---|---|
| `ObjectReceived` | [`TrackedObjectEventArgs`](#trackedobjecteventargs) | Raised for every object received while started, one event per object per frame, on a background thread. Keep handlers short: they run on the receiving thread. An exception thrown by a handler is logged and does not stop the others or the stream. |

</div>

<Example>

```csharp
ar51.ObjectDetection.ObjectReceived += (_, e) =>
    Console.WriteLine($"{e.Object.Name} at {e.Object.Position} m");
```

</Example>

## Method details

### StartAsync

<ApiBody kind="method">

```csharp
Task StartAsync(CancellationToken cancellationToken = default);
```

Starts receiving objects into `ObjectReceived`. Returns at once; calling it again does nothing.

<Params>
<Param name="cancellationToken" type="CancellationToken" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken">Ignored. Optional.</Param>
</Params>

<Returns type="Task" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task">A completed task.</Returns>

<Throws>
<Throw type="ObjectDisposedException" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.objectdisposedexception">The client was disposed.</Throw>
</Throws>

:::note
Receiving begins as soon as the service is reachable, and resumes by itself after the service restarts or the network drops, until [`StopAsync`](#stopasync) or dispose.
:::

</ApiBody>

### StopAsync

<ApiBody kind="method">

```csharp
Task StopAsync(CancellationToken cancellationToken = default);
```

Stops receiving objects: stops `ObjectReceived`, ends every [`StreamAsync`](#streamasync) loop running now, and waits until the event stream is closed. Calling it again does nothing.

<Params>
<Param name="cancellationToken" type="CancellationToken" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken">Stops waiting for the stream to close; the stream still closes. Optional.</Param>
</Params>

<Returns type="Task" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task">A task that completes when the event stream is closed.</Returns>

:::note
[`StreamAsync`](#streamasync) loops end normally, without an exception, after the object they are handling. Loops started after this call are not affected.
:::

</ApiBody>

### StreamAsync

<ApiBody kind="method">

```csharp
IAsyncEnumerable<TrackedObject> StreamAsync(CancellationToken cancellationToken = default);
```

Streams objects until [`StopAsync`](#stopasync) is called (the loop ends normally), `cancellationToken` is cancelled, or the connection drops. Works without [`StartAsync`](#startasync).

<Params>
<Param name="cancellationToken" type="CancellationToken" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken">Stops the stream. Optional.</Param>
</Params>

<Returns type="IAsyncEnumerable<TrackedObject>" typeHref="/docs/sdk-api/csharp-api/classes/trackedobject/">Objects in the order they arrive, one per object per frame.</Returns>

<Throws>
<Throw type="AR51Exception" typeHref="/docs/sdk-api/csharp-api/classes/ar51exception/">The object detection service is not available, or the stream failed.</Throw>
<Throw type="OperationCanceledException" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.operationcanceledexception">`cancellationToken` was cancelled.</Throw>
<Throw type="ObjectDisposedException" typeHref="https://learn.microsoft.com/en-us/dotnet/api/system.objectdisposedexception">The client was disposed.</Throw>
</Throws>

:::note
Each call opens its own stream, which closes when the loop ends. Unlike `ObjectReceived`, it does not reconnect: start a new loop after an [`AR51Exception`](/docs/sdk-api/csharp-api/classes/ar51exception/).
:::

</ApiBody>

## Related types

Small types used by this interface, documented here.

### TrackedObjectEventArgs

<Badge tone="type">class · sealed</Badge>

```csharp
public sealed class TrackedObjectEventArgs : EventArgs
```

Data for `IObjectDetectionClient.ObjectReceived`.

<div className="apiPropTable apiTable">

| Property | Type | Access | Description |
|---|---|---|---|
| `Object` | [`TrackedObject`](/docs/sdk-api/csharp-api/classes/trackedobject/) | `get` | The received object. |

</div>

<details>
<summary>Constructor</summary>

```csharp
public TrackedObjectEventArgs(TrackedObject trackedObject);
```

Creates the event data.

</details>

## See also

- [Object detection guide](/docs/csharp-api/objects/) — walkthrough with a complete, compiled sample
- [Class index](/docs/sdk-api/csharp-api/classes/) — every AR51.Net type
- [Data conventions, threading and errors](/docs/csharp-api/conventions/)
