> For the complete documentation index, see [llms.txt](https://docs.convai.com/api-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.convai.com/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/vision/scripting-api.md).

# Vision 脚本 API

Vision 脚本主要围绕 `ConvaiVisionPublisher` 用于发布控制，以及用于捕获状态的帧源状态接口。领域事件可让你无需轮询即可对生命周期变化作出响应 `IsPublishing` 每一帧。

### ConvaiVisionPublisher

`ConvaiVisionPublisher` 是一个 `MonoBehaviour` 用于管理 WebRTC 视频轨道。可通过以下方式获取引用： `GetComponent` 或一个序列化字段。

**属性**

| 属性               | 类型                    | 说明                                 |
| ---------------- | --------------------- | ---------------------------------- |
| `IsPublishing`   | `bool`                | `true` 当 WebRTC 视频轨道正在主动发送时。       |
| `FrameSource`    | `IVisionFrameSource`  | 当前正在使用的帧源。 `null` 直到运行时注册完成。       |
| `PublishPolicy`  | `VisionPublishPolicy` | 当前发布策略。                            |
| `VideoTrackName` | `string`              | WebRTC 轨道的名称（默认： `"unity-scene"`). |

**方法**

| 方法                                             | 说明                                                  |
| ---------------------------------------------- | --------------------------------------------------- |
| `SetPublishPolicy(VisionPublishPolicy policy)` | 更改客户端侧传输预算。会在下一个发布的帧上生效。                            |
| `EnablePublishing(bool enabled)`               | 在不更改所选策略的情况下开始或停止发布。只有在策略为 `Manual`时才有意义；自动发布策略会忽略。 |

**用法**

```csharp
using Convai.Modules.Vision;
using Convai.Runtime.Vision.Publishing;
using UnityEngine;

public class VisionController : MonoBehaviour
{
    [SerializeField] private ConvaiVisionPublisher _publisher;

    void Start()
    {
        // 为此场景切换到高响应性模式
        _publisher.SetPublishPolicy(VisionPublishPolicy.HighResponsiveness);
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.V))
        {
            bool isPublishing = _publisher.IsPublishing;
            Debug.Log($"轨道 '{_publisher.VideoTrackName}' 正在发布：{isPublishing}");
        }
    }
}
```

### IVisionFrameSource

由所有内置帧源以及任何自定义源实现。

| 成员                     | 种类                           | 说明                                        |
| ---------------------- | ---------------------------- | ----------------------------------------- |
| `IsCapturing`          | `bool` 属性                    | `true` 当源正在主动生成帧时。                        |
| `FrameCount`           | `long` 属性                    | 自捕获开始以来生成的总帧数。                            |
| `FrameDimensions`      | `(int Width, int Height)` 属性 | 输出分辨率。返回 `(0, 0)` 初始化前。                   |
| `TargetFrameRate`      | `float` 属性                   | 配置的每秒帧数。                                  |
| `SourceId`             | `string` 属性                  | 用于多源场景的标识字符串。                             |
| `CurrentRenderTexture` | `RenderTexture` 属性           | Y 轴翻转 `RenderTexture` 包含最新帧。 `null` 未捕获时。 |
| `IsFrameReady`         | `bool` 属性                    | `true` 在第一个可用帧可用后。                        |
| `FrameReady`           | `event Action`               | 每当有新帧可用时，都会在 Unity 主线程上触发。                |
| `StartCapture()`       | 方法                           | 开始帧捕获。                                    |
| `StopCapture()`        | 方法                           | 停止帧捕获并释放资源。                               |

### IVisionFrameSourceStatusProvider

内置源实现的可选配套接口。提供更丰富的状态和错误信息。

| 成员               | 种类                         | 说明                                                 |
| ---------------- | -------------------------- | -------------------------------------------------- |
| `State`          | `VisionSourceState` 属性     | 当前生命周期状态。                                          |
| `ErrorKind`      | `VisionSourceErrorKind` 属性 | 结构化错误类别，当 `State == Failed`.                       |
| `StatusMessage`  | `string` 属性                | 人类可读的状态详情。                                         |
| `HasUsableFrame` | `bool` 属性                  | `true` 当源至少已生成一个有效帧时。                              |
| `StatusChanged`  | `event Action`             | 每当 `State`, `ErrorKind`，或 `StatusMessage` 发生变化时触发。 |

### 监视状态变化

```csharp
using Convai.Runtime.Vision.Sources;
using UnityEngine;

public class FrameSourceMonitor : MonoBehaviour
{
    [SerializeField] private MonoBehaviour _frameSourceComponent;

    private IVisionFrameSourceStatusProvider _statusProvider;

    void Start()
    {
        _statusProvider = _frameSourceComponent as IVisionFrameSourceStatusProvider;
        if (_statusProvider != null)
            _statusProvider.StatusChanged += OnStatusChanged;
    }

    void OnDestroy()
    {
        if (_statusProvider != null)
            _statusProvider.StatusChanged -= OnStatusChanged;
    }

    private void OnStatusChanged()
    {
        Debug.Log($"[Vision] 状态：{_statusProvider.State} 错误：{_statusProvider.ErrorKind} {_statusProvider.StatusMessage}");

        if (_statusProvider.State == VisionSourceState.Failed)
            HandleCaptureFailure(_statusProvider.ErrorKind);
    }

    private void HandleCaptureFailure(VisionSourceErrorKind errorKind)
    {
        switch (errorKind)
        {
            case VisionSourceErrorKind.PermissionDenied:
                // 显示 UI，提示用户授予相机权限
                break;
            case VisionSourceErrorKind.DeviceUnavailable:
                // 提议切换到其他帧源
                break;
        }
    }
}
```

### VisionSourceState 参考

| State  | 含义                                            |
| ------ | --------------------------------------------- |
| `空闲`   | 尚未开始捕获。                                       |
| `等待授权` | 等待用户授予相机权限（Android / iOS）。                    |
| `启动中`  | 正在初始化捕获——设备正在打开， `RenderTexture`s 正在创建。       |
| `就绪`   | 捕获正在运行，且正在生成帧。                                |
| `降级`   | 捕获正在运行，但帧健康检查检测到问题（例如连续空白帧）。                  |
| `已停止`  | 捕获已正常停止。                                      |
| `失败`   | 捕获失败且无法继续。检查 `ErrorKind` 是位于 `StatusMessage`. |

### 领域事件

通过以下方式订阅领域事件： `ConvaiManager.ActiveManager.EventHub` 以响应 Vision 生命周期变化，而无需轮询。所有 Vision 事件都是值类型（`只读结构体`）——只需分配一次处理程序并持有其引用。

```csharp
using Convai.Domain.DomainEvents.Vision;
using Convai.Runtime.Core;
using Convai.Domain.EventSystem;
using UnityEngine;

public class VisionAnalytics : MonoBehaviour
{
    void Start()
    {
        var hub = ConvaiManager.ActiveManager?.EventHub;
        if (hub == null) return;

        hub.Subscribe<VisionCaptureStarted>(OnCaptureStarted, EventDeliveryPolicy.MainThread);
        hub.Subscribe<VisionCaptureStopped>(OnCaptureStopped, EventDeliveryPolicy.MainThread);
        hub.Subscribe<VideoTrackPublished>(OnTrackPublished, EventDeliveryPolicy.MainThread);
        hub.Subscribe<VideoTrackUnpublished>(OnTrackUnpublished, EventDeliveryPolicy.MainThread);
    }

    void OnDestroy()
    {
        var hub = ConvaiManager.ActiveManager?.EventHub;
        if (hub == null) return;

        hub.Unsubscribe<VisionCaptureStarted>(OnCaptureStarted);
        hub.Unsubscribe<VisionCaptureStopped>(OnCaptureStopped);
        hub.Unsubscribe<VideoTrackPublished>(OnTrackPublished);
        hub.Unsubscribe<VideoTrackUnpublished>(OnTrackUnpublished);
    }

    private void OnCaptureStarted(VisionCaptureStarted e)
        => Debug.Log($"[Vision] 捕获已开始：{e.Width}x{e.Height} @ {e.FramesPerSecond} fps（来源：{e.SourceId}）");

    private void OnCaptureStopped(VisionCaptureStopped e)
    {
        Debug.Log($"[Vision] 在 {e.TotalFramesCaptured} 帧后停止捕获。原因：{e.Reason}");
        if (e.IsError)
            Debug.LogError($"[Vision] 错误：{e.ErrorMessage}（代码：{e.ErrorCode}）");
    }

    private void OnTrackPublished(VideoTrackPublished e)
        => Debug.Log($"[Vision] 轨道 '{e.TrackName}' 已发布。SID：{e.TrackSid}");

    private void OnTrackUnpublished(VideoTrackUnpublished e)
        => Debug.Log($"[Vision] 轨道 '{e.TrackName}' 已取消发布。原因：{e.Reason}");
}
```

**VisionCaptureStarted**

当帧源开始生成帧时触发。

| 属性                | 类型         | 说明                                       |
| ----------------- | ---------- | ---------------------------------------- |
| `宽度`              | `int`      | 以像素为单位的捕获宽度。                             |
| `高度`              | `int`      | 以像素为单位的捕获高度。                             |
| `FramesPerSecond` | `float`    | 配置的帧率。                                   |
| `时间戳`             | `DateTime` | 捕获开始时的 UTC 时间。                           |
| `SourceId`        | `string`   | 来源标识符（来自 `IVisionFrameSource.SourceId`). |
| `宽高比`             | `float`    | `宽度 / 高度`.                               |
| `总像素数`            | `int`      | `宽度 * 高度`.                               |

**VisionFrameCaptured**

每次捕获到帧时触发。

{% hint style="warning" %}
`VisionFrameCaptured` 会在每个捕获到的帧上触发。在 60 秒的会话中以 15 fps 运行，这将产生 900 个事件。请使用 `EventDeliveryPolicy.Immediate` 并保持处理程序轻量。对于分析，按每第 N 帧进行采样，而不是订阅每个事件。
{% endhint %}

| 属性           | 类型         | 说明             |
| ------------ | ---------- | -------------- |
| `宽度`         | `int`      | 以像素为单位的帧宽度。    |
| `高度`         | `int`      | 以像素为单位的帧高度。    |
| `FrameIndex` | `long`     | 从零开始的捕获帧索引。    |
| `SizeBytes`  | `long`     | 以字节为单位的帧数据大小。  |
| `时间戳`        | `DateTime` | 捕获该帧时的 UTC 时间。 |
| `SourceId`   | `string`   | 来源标识符。         |

**VisionCaptureStopped**

当帧源停止生成帧时触发。

| 属性                    | 类型                        | 说明                                                         |
| --------------------- | ------------------------- | ---------------------------------------------------------- |
| `TotalFramesCaptured` | `long`                    | 会话期间捕获的总帧数。                                                |
| `时间戳`                 | `DateTime`                | UTC 停止时间。                                                  |
| `原因`                  | `VisionCaptureStopReason` | 捕获停止原因（`用户请求`, `会话结束`, `相机丢失`, `错误`, `组件已禁用`).             |
| `SourceId`            | `string`                  | 来源标识符。                                                     |
| `错误消息`                | `string`                  | 人类可读的错误详情。仅在 `Reason == Error`.                            |
| `错误代码`                | `string`                  | 来自 `SessionErrorCodes` （Vision\* 常量）。仅在 `Reason == Error`. |
| `IsError`             | `bool`                    | `true` 当 `Reason == Error`.                                |
| `IsNormalStop`        | `bool`                    | `true` 当 `原因` 为 `用户请求` 或 `会话结束`.                           |
| `HasErrorCode`        | `bool`                    | `true` 当 `错误代码` 非空时。                                       |

**VideoTrackPublished**

当 WebRTC 视频轨道成功打开时触发。

{% hint style="warning" %}
`IsVisionTrack` 检查轨道名称 `"vision"`，而不是 `"unity-scene"`。使用默认轨道名称时， `IsVisionTrack` 返回 `false`请使用 `TrackName` 直接识别轨道，或者将轨道重命名为 `"vision"` 如果你的集成依赖于 `IsVisionTrack`.
{% endhint %}

| 属性              | 类型         | 说明                                                  |
| --------------- | ---------- | --------------------------------------------------- |
| `TrackSid`      | `string`   | LiveKit 轨道会话 ID。                                    |
| `TrackName`     | `string`   | 由以下项设置的轨道名称 `VideoTrackName` （默认： `"unity-scene"`). |
| `时间戳`           | `DateTime` | UTC 发布时间。                                           |
| `RoomSessionId` | `string`   | 房间会话 ID。                                            |
| `IsVisionTrack` | `bool`     | `true` 当 `TrackName == "vision"` （不区分大小写）。          |

**VideoTrackUnpublished**

当 WebRTC 视频轨道被移除时触发。

| 属性                  | 类型                          | 说明                                                                             |
| ------------------- | --------------------------- | ------------------------------------------------------------------------------ |
| `TrackSid`          | `string`                    | LiveKit 轨道会话 ID。                                                               |
| `TrackName`         | `string`                    | 轨道名称。                                                                          |
| `时间戳`               | `DateTime`                  | UTC 取消发布时间。                                                                    |
| `原因`                | `VideoTrackUnpublishReason` | 轨道为何被取消发布（`用户请求`, `会话结束`, `源丢失`, `错误`, `组件已禁用`).                               |
| `RoomSessionId`     | `string`                    | 房间会话 ID。                                                                       |
| `IsVisionTrack`     | `bool`                      | `true` 当 `TrackName == "vision"` （同样注意，如 `VideoTrackPublished.IsVisionTrack`). |
| `IsNormalUnpublish` | `bool`                      | `true` 当 `原因` 为 `用户请求` 或 `会话结束`.                                               |

### 下一步

{% content-ref url="/pages/cb7758289bc48a93210bce51933fd819fd05e245" %}
[自定义帧源](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/vision/custom-frame-sources.md)
{% endcontent-ref %}

{% content-ref url="/pages/86612bc613c5a299a468b71b3fb8a40e625ee1fe" %}
[排查 Vision 问题](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/vision/troubleshooting-and-diagnostics.md)
{% endcontent-ref %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.convai.com/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/vision/scripting-api.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
