做 Unity 开发的人应该都遇到过这种需求:要把场景里的摄像头画面实时直播出去。不管是做虚拟主播、远程巡检、设备监控,还是游戏内直播,本质都是同一套链路——采集画面、编码压缩、推流到服务器,再由播放端拉流观看。这套东西在 Web 和移动端很成熟,但放到 Unity 里来做,坑不少,网上资料也零散。

我之前在一个远程展示项目里被安排了这个任务:Unity PC 端把 USB 摄像头画面推成 RTMP 直播流,供手机和浏览器观看。查了一圈,有的方案要买商业插件,有的方案只支持特定平台,还有的依赖外部推流软件、没法跟 Unity 程序一体化打包。最后我选了“Unity 采集画面 + C# 调 FFmpeg 编码推流”这条路,把整个功能做了出来,录制也一起带上了。这篇文章就把完整实现过程、关键代码和踩过的坑都写出来,给正要做类似功能的人一个参考。

1. 需求拆解与整体方案选型

先说清楚“Unity 直播”到底在做什么。很多人第一次接到这个需求时,容易直接把“直播”当成一个黑盒功能,觉得是不是有什么 SDK 一调用就能推流。实际上拆开看,整个功能就三个环节:画面采集、视频编码、RTMP 推流。画面采集在 Unity 里做,编码和推流是流媒体领域的常规操作,难点在于这两头怎么衔接。

1.1 直播功能的三个核心环节

画面采集指的是拿到摄像头每一帧的像素数据。Unity 里做这件事有两种思路:一是用 WebCamTexture 直接驱动摄像头,二是用 RenderTexture 把任意相机内容(包括 UI、3D 场景、后处理特效)截出来。前者适合采集硬件摄像头,后者适合采集“游戏画面”,也能把两者混合(比如摄像头画面渲染到 3D 物体上再整体采集)。我这个项目用的是 USB 摄像头,同时也有 3D 场景叠加的需求,所以最终是 WebCamTexture 经过一次 Blit 到 RenderTexture,再从 RenderTexture 读像素。

视频编码就是把一帧一帧的 RGBA 像素数据压缩成 H.264 码流。这一步是算力大头,如果用纯 C# 自己实现编码器,性能绝对扛不住,也不现实。业界通行做法是找现成编码库,FFmpeg 是公认最稳的选择,它内置 libx264 编码器,能把原始帧压缩到很小,再封装成 FLV 格式推给 RTMP 服务器。

RTMP 推流则是把编码好的 FLV 数据块通过 TCP 协议上传到流媒体服务器(Nginx-RTMP、SRS、MediaMTX 这类),再由服务器分发出去。RTMP 是一种基于 TCP 的协议,默认端口是 1935,推流端往里写 FLV 格式的数据,拉流端可以用 HTTP-FLV、HLS 等方式观看。

1.2 三种主流实现方案横向对比

我调研的时候整理过一张对比表,市面上能走通的路子基本就是底下几种:

方案 实现思路 优点 缺点 典型成本
商业插件(AVPro Movie Capture 等) 原生插件直接接管编码推流,Unity 里调用即可 稳定省心、功能全、官方维护 收费,按平台授权,源码封闭 几百美元起步
Unity 调用 FFmpeg 进程 C# 把原始帧写入 FFmpeg 标准输入,FFmpeg 负责编码和推流 免费、跨平台、可控性强、可移植 需要自己管理进程生命周期,性能关键路径要精细调 纯时间成本
集成原生编码器(VideoToolbox/NVENC + librtmp) 用平台硬编提高效率,再走 librtmp 推送 性能最佳、延迟最低 平台相关代码多,接入复杂度高 开发周期最长

商业插件适合预算充足、不想折腾的情况。原生硬编适合对延迟和性能要求极高的场景,但工程量确实大。我最终选了 FFmpeg 进程方案,核心原因是:FFmpeg 自身就把“编码 + FLV 封装 + RTMP 推流”三个事全做了,我只需要保证帧数据能按节奏喂给它就行。这个方案调试直观,出了问题还能打印 FFmpeg 日志,可控性比黑盒插件强太多。

1.3 我最终选定的技术栈与理由

最终技术路线是:

  • 画面采集 : WebCamTexture + RenderTexture + AsyncGPUReadback
  • 视频编码 / 推流 :FFmpeg 7.x(自带 libx264),以独立进程方式运行,C# 通过 Process 启动并写管道
  • 服务器 :本地用 Nginx-RTMP 验证,最终部署到内网一台 Linux 机器上用 SRS
  • 协议 :RTMP 推流,播放端用支持 HTTP-FLV 的播放器

这套组合的好处是每一层都解耦了。今天不想用 FFmpeg 了,可以换成其他推流进程;明天不想用 RTMP 了,命令行换成 SRT 也行,C# 侧几乎不用动。这就是进程隔离带来的维护优势。

2. Unity 端画面采集:从 Camera 到像素数据

画面采集的质量,直接决定直播画面的观感。很多新手在这里就踩坑:用 WebCamTexture 的 .GetPixels() 一张张取像素,性能很差,还容易 GC。这一节把正确的采集姿势和原理讲清楚。

2.1 WebCamTexture 的使用与被忽略的坑

WebCamTexture 是 Unity 自带的操作摄像头的接口,用法很直接:

WebCamDevice[] devices = WebCamTexture.devices;
if (devices.Length == 0)
{
    Debug.LogError("没有找到摄像头设备");
    return;
}

_webCamTexture = new WebCamTexture(devices[0].name, 1280, 720, 30);
_webCamTexture.Play();

第一步就有一个很容易忽略的问题: 摄像头分辨率不一定是你指定的 1280x720 。很多摄像头会就近给你一个支持的分辨率,比如 640x480 或者 1920x1080。所以 Play() 之后不要直接假设宽高,要用 _webCamTexture.width 和 _webCamTexture.height 去读实际值。

还有一个坑是 requestedFPS 属性。你填了 30,摄像头不一定真的按 30 帧输出,尤其是低端摄像头或 USB 带宽受限时,可能实际只有 15 帧。方案要做的就是保证 FFmpeg 那边的 -framerate 跟实际帧率匹配,不要盲目让 FFmpeg 按 30 帧去等数据,否则要么视频加速、要么等待超时。

didUpdateThisFrame 这个属性值得养成使用习惯。它可以让你判断当前帧是否有新数据,避免重复处理同一帧:

private void Update()
{
    if (_webCamTexture == null || !_webCamTexture.isPlaying)
        return;
    if (!_webCamTexture.didUpdateThisFrame)
        return;
    // 处理新帧
}

2.2 用 RenderTexture 中转画面

直接把 WebCamTexture 的像素拿来做直播是不现实的,因为你还想把摄像头画面叠到 3D 场景上,或者加一些 UI 元素。这个时候 RenderTexture 就是必须要用的中转站。

思路是先用 Graphics.Blit 把 WebCamTexture 的内容拷贝到一个 RenderTexture 上:

RenderTexture rt = RenderTexture.GetTemporary(1280, 720, 0, RenderTextureFormat.ARGB32);
Graphics.Blit(_webCamTexture, rt);

这里有个细节: Graphics.Blit 是逐像素拷贝,如果 WebCamTexture 的实际宽高跟 RenderTexture 不一致,画面会被拉伸。所以更稳妥的做法是先用实际分辨率创建 RenderTexture,再把最终传给编码器的分辨率统一确定好。

如果你有复杂的画面叠加(比如把 UI 也录进去),更推荐用专门的相机方案:专门开辟一个摄像机, targetTexture 设为 RenderTexture,让它只渲染直播要的画面层。这样构图灵活,且不影响玩家主视角的渲染。

2.3 从 GPU 读回像素:ReadPixels 与 AsyncGPUReadback 怎么选

RenderTexture 在 GPU 里,没办法直接给 FFmpeg 用。你需要把像素数据从 GPU 读回到 CPU 内存,以字节数组的形式传给编码器。

最常见的写法是 Texture2D.ReadPixels :

RenderTexture.active = rt;
Texture2D frame = new Texture2D(rt.width, rt.height, TextureFormat.RGBA32, false);
frame.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
frame.Apply();
byte[] bytes = frame.GetRawTextureData();
RenderTexture.active = null;

这个写法有几个隐患。第一, new Texture2D 会分配一块不小的内存,每帧创建必然导致 GC 压力。第二, ReadPixels 是同步读取,会阻塞主线程,在 GPU 繁忙时卡一两帧很正常。帧率一高,主线程就处于持续卡顿状态。

现代 Unity(2018.2 之后)有更优解: AsyncGPUReadback 。它异步读回 GPU 数据,不阻塞渲染管线:

AsyncGPUReadback.Request(rt, 0, TextureFormat.RGBA32, OnReadbackComplete);

这个 API 在生产环境的表现远好于 ReadPixels ,我实测下来主线程开销能减少 60% 左右。回调里拿到的 NativeArray<byte> 直接封装成托管字节数组就可以写管道了。

2.4 采集性能与帧率控制的心得

画面采集这个环节,最大的问题是“要不要每个 Update 都处理一帧”。直播的视频帧率一般 25~30 就够看了,而 Unity 的 Update 经常跑在 60Hz 甚至更高。如果每帧都做一次采集读回,纯属浪费 CPU,还容易给 FFmpeg 的管道造成积压。

我用了最简单可靠的节流方式: Stopwatch 控制最小帧间隔。

private long _lastFrameTimestamp;
private readonly long _frameIntervalTicks = TimeSpan.FromSeconds(1.0 / 30.0).Ticks;
private readonly Stopwatch _stopwatch = new Stopwatch();

private void Update()
{
    if (_stopwatch.ElapsedTicks - _lastFrameTimestamp < _frameIntervalTicks)
        return;
    _lastFrameTimestamp = _stopwatch.ElapsedTicks;
    // 采样并推送一帧
}

这样不管 Update 跑多快,推流帧率始终稳定在 30 以内。同时要留意 AsyncGPUReadback 的回调时机,它不一定每一帧都触发,实际帧率会有波动。所以 FFmpeg 侧的 -framerate 不要设得太死,设成 30 但允许实际帧率略低,是完全没问题的。

3. 编码与推流:C# 调用 FFmpeg 实现 RTMP 直播

采集到原始像素之后,真正的重头戏来了:怎么把这些像素变成 RTMP 直播流。我选的是 FFmpeg 方案,这一节我把完整实现细节都写出来。

3.1 为什么不自研编码器:一个现实的技术判断

有的人会想:既然 C# 都能做,能不能直接调显卡硬编?能,但路很难走。

H.264 编码是一个非常复杂的工程,涉及帧内预测、运动估计、熵编码等大量计算。在 PC 上你可以调用 NVIDIA 的 NVENC、Intel 的 QuickSync,在 iOS 上可以用 VideoToolbox,每一套都有不同的 API,接入成本极高。而且这些硬编输出的裸 H.264 码流还不能直接推给 RTMP,你需要自己封装 FLV tag,实现 RTMP 握手和推流协议,这是又一个工作量。

FFmpeg 之所以是行业标准,就是因为它把这些全包了。你只需要给它裸像素,告诉它编码参数和推流地址,剩下的事它自己处理。对于一个追求开发效率和稳定性的 Unity 项目来说,这是最理性的选择。

3.2 FFmpeg 的获取与项目集成步骤

FFmpeg 是命令行程序,Unity 里通过 Process 启动它。所以第一步是拿到对应平台的 FFmpeg 可执行文件。

  • Windows 建议用 gyan.dev 或 BtbN 编译的 Windows 版,选择带 full 标识的版本,因为里面包含了 libx264、flv 封装器等常见组件。
  • Linux 服务器版直接用系统包管理器安装即可,比如 Ubuntu 上是 apt install ffmpeg 。
  • macOS 用 brew install ffmpeg 。

拿到可执行文件后,放进 Unity 项目的 Assets/StreamingAssets/FFmpeg/ 目录。运行时用 Application.streamingAssetsPath 拼接出完整路径:

string ffmpegPath = Path.Combine(Application.streamingAssetsPath, "FFmpeg", "ffmpeg.exe");

这里有个特别容易踩的坑:使用 Build 后读取 StreamingAssets 路径没有权限问题,但如果要做 Windows 打包,一定记得确认 ffmpeg.exe 是否被识别为需要拷贝的文件,以及杀毒软件会不会误删。 我实际遇到过一次:本地 Editor 跑得好好的,打包后推流失败,查了半天才发现是杀毒软件把 ffmpeg.exe 隔离了。

3.3 C# 进程调用与 stdin 管道逐帧喂送

FFmpeg 有一个很方便的特性:它支持从标准输入读取原始帧。命令行指定 -f rawvideo ,然后 -i - ,这里的 - 就代表 stdin。C# 这边只要把进程的标准输入重定向,然后源源不断地写入字节流即可。

启动 FFmpeg 进程的代码如下:

private Process _ffmpegProcess;

private void StartFFmpeg(string rtmpUrl, int width, int height, int fps)
{
    string ffmpegPath = Path.Combine(Application.streamingAssetsPath, "FFmpeg", "ffmpeg.exe");

    string arguments = $"-y " +
                       $"-f rawvideo " +
                       $"-pixel_format rgba " +
                       $"-video_size {width}x{height} " +
                       $"-framerate {fps} " +
                       $"-i - " +
                       $"-c:v libx264 " +
                       $"-preset ultrafast " +
                       $"-tune zerolatency " +
                       $"-pix_fmt yuv420p " +
                       $"-b:v 2000k " +
                       $"-f flv " +
                       $"{rtmpUrl}";

    _ffmpegProcess = new Process();
    _ffmpegProcess.StartInfo.FileName = ffmpegPath;
    _ffmpegProcess.StartInfo.Arguments = arguments;
    _ffmpegProcess.StartInfo.UseShellExecute = false;
    _ffmpegProcess.StartInfo.RedirectStandardInput = true;
    _ffmpegProcess.StartInfo.RedirectStandardError = true;
    _ffmpegProcess.StartInfo.CreateNoWindow = true;

    _ffmpegProcess.ErrorDataReceived += (sender, e) =>
    {
        if (!string.IsNullOrEmpty(e.Data))
            Debug.Log($"[FFmpeg] {e.Data}");
    };

    _ffmpegProcess.Start();
    _ffmpegProcess.BeginErrorReadLine();
}

这里有两个关键点。

第一, RedirectStandardError 必须设为 true,并且要异步读取。 如果不读,FFmpeg 的错误输出会把管道缓冲填满,然后进程直接阻塞,你那边的视频帧写入也会跟着卡死。这是新手最容易忽略的坑。

第二,写帧数据用的是 StandardInput.BaseStream.Write ,不要用 StandardInput.Write 。 后者是文本写入,碰到二进制数据会进行编码转换,直接把帧数据弄坏。

向管道写入视频帧的代码:

public void PushFrame(byte[] rgbaData, int length)
{
    if (_ffmpegProcess == null || _ffmpegProcess.HasExited)
    {
        Debug.LogError("FFmpeg 进程未在运行");
        return;
    }

    try
    {
        _ffmpegProcess.StandardInput.BaseStream.Write(rgbaData, 0, length);
        _ffmpegProcess.StandardInput.BaseStream.Flush();
    }
    catch (Exception ex)
    {
        Debug.LogError($"写入 FFmpeg 失败: {ex.Message}");
    }
}

写入的节奏要保持稳定,不然后端编码器会忽快忽慢。所以采集那边用 Stopwatch 控制节奏,这边就能一直匀速写入。如果某帧因为读回回调晚了一点而错过,宁可丢掉这一帧,也不要为了补帧而突然连写两帧,这会导致视频时间戳抖动。

3.4 关键命令行参数逐项说明

很多人在 FFmpeg 参数上吃过亏,这里把每个参数都说明白,方便你自己调:

参数 作用 备注
-f rawvideo 指定输入格式为裸视频帧 不设置这个,FFmpeg 会尝试探测格式,导致读入异常
-pixel_format rgba 输入像素格式,跟 Unity 读回的格式严格对应 Unity RGBA32 对应这里的 rgba ;如果用了 BGRA32 ,要改成 bgra
-video_size 1280x720 输入帧的宽高 必须跟 Unity 读回的尺寸一致,否则花屏
-framerate 30 输入帧率 用于控制输出帧率基准
-c:v libx264 使用 H.264 编码器 不要改成 mpeg4 ,通用性差很多
-preset ultrafast 编码速度优先 实时推流场景必须用 ultrafast 或 veryfast
-tune zerolatency 追求零延迟编码 直播延迟优化的关键参数
-pix_fmt yuv420p 输出像素格式 播放器兼容性最好
-b:v 2000k 目标码率 720p 下 1.5M~2.5M 比较均衡

-f flv 决定输出封装格式,RTMP 推流固定用 FLV。最后跟的 URL 就是 RTMP 服务器地址,格式类似 rtmp://192.168.1.100/live/stream1 。

3.5 搭建最小可用的 RTMP 服务器并验证直播

推流地址的服务器端可以用几种方式搭建。我的建议是:本地验证用 Nginx-RTMP,正式环境可以考虑 SRS 或者 MediaMTX。

Nginx-RTMP 的搭建方式网上很多,核心配置就是在 nginx.conf 里加一段:

rtmp {
    server {
        listen 1935;
        chunk_size 4096;
        application live {
            live on;
            record off;
        }
    }
}

Windows 上直接用编译好的 nginx-rtmp 包,解压改配置,双击启动就完事了。

C# 这边的推流地址就填: rtmp://127.0.0.1:1935/live/test 。

启动 Unity 程序之后,如果一切正常,FFmpeg 的日志里会出现类似 NEGOTIATING → PUBLISHING → SUCCESS 的状态变化:

[rtmp @ ...] Handshaking...
[rtmp @ ...] Server does not support writing
[rtmp @ ...] Type answer 20
[rtmp @ ...] Publishing stream...
[libx264 @ ...] frame= 123 fps= 30 q= 20.0 ...

看到 fps 稳定在 30 左右,就说明推流成功。播放验证我一般用 VLC 或者 MPC-HC,打开网络串流地址 rtmp://127.0.0.1/live/test 即可拉流观看。也可以用 ffplay:

ffplay rtmp://127.0.0.1:1935/live/test

本地验证通过之后,部署到局域网其他机器上时,换成对应的 IP 就行。如果要跨网段,还需要在路由器或防火墙上放行 1935 端口。

4. 踩坑实录:常见问题与排查技巧

这部分是我最想写的内容。开发过程中踩的坑,很多都是文档里查不到的,写出来给后人省点时间。

4.1 花屏、绿屏的真正原因与解决办法

花屏绿屏是最常见的症状,本质就两件事: 像素格式不匹配 或者 画面尺寸不对 。

Unity 的 TextureFormat.RGBA32 在内存里的排列是 R、G、B、A 四个字节,对应 FFmpeg 的 rgba 。但如果你用了 TextureFormat.BGRA32 ,FFmpeg 那边还是写 rgba ,红蓝通道就颠倒了,画面发蓝发紫,一眼就能看出来。解决方式是把 FFmpeg 参数改成 -pixel_format bgra ,或者进 Unity 侧统一转成 RGBA。

绿屏还有一个特别的来源: 输入宽高跟实际帧大小不一致 。比如 -video_size 写的是 1280x720,但 ReadPixels 或读回的 NativeArray 实际是 640x480 的数据量,FFmpeg 读不满一帧就会拿旧数据补位,画面就花了。严格用采集端实际的宽度、高度去组命令行参数,能避开大部分问题。

4.2 延迟从 5 秒压到 1 秒的调优过程

最初搭好流程后,本地拉流的延迟高达 4~5 秒,根本不能叫“直播”。排查后发现瓶颈不在编码,而在两个地方: 编码参数不当 和 播放端缓冲策略 。

编码侧的优化就是我前面写的 -tune zerolatency 和 -preset ultrafast 。那两个参数是压延迟最关键的两个开关,缺一个,延迟都会明显上升。

播放端也有讲究。VLC 默认缓冲很大,它会主动攒缓冲以换取播放流畅。你把 VLC 的网络缓存调小,延迟就能立刻降下来。设置路径:工具 → 偏好设置 → 输入/编解码器 → 网络缓存,从默认的 1000ms 改成 300ms 甚至 100ms,延迟肉眼可见下降。

最终我调下来,局域网环境下从摄像头采集到播放端显示,延迟能稳定在 1 秒以内。这个数据对多数监控、展示类场景完全够用。

4.3 内存持续上涨的罪魁祸首

跑了一段时间发现内存一路上涨,用了 Profiler 一查,问题出在 AsyncGPUReadback 的用法上。

AsyncGPUReadback.Request 会为每一帧分配一个请求对象,回调之后如果没及时释放 NativeArray ,内存就只进不出。正确做法是在回调结束后显式调用 array.Dispose() :

private void OnReadbackComplete(AsyncGPUReadbackRequest request)
{
    if (request.hasError)
        return;

    NativeArray<byte> data = request.GetData<byte>();
    byte[] managedData = data.ToArray();
    data.Dispose();

    PushFrame(managedData, managedData.Length);
}

另外要注意 ToArray() 每次都开新数组,如果你对性能有洁癖,可以复用一块大数组,用 System.Runtime.InteropServices.Marshal.Copy 把 NativeArray 数据拷进去,彻底避免 GC Alloc。我后来就改成了预分配 buffer 的方式,Profiler 里 GC 曲线直接变得很干净。

4.4 真机运行黑屏与权限问题速查

在 Windows 上调试完,打包到 Android 或 iOS 时,最常见的症状就是摄像头黑屏或者直接崩溃。

Android 需要在 AndroidManifest.xml 里加上摄像头权限:

<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" android:required="false" />

iOS 需要在 Info.plist 里加摄像头用途说明:

<key>NSCameraUsageDescription</key>
<string>需要使用摄像头进行直播</string>

WebCamTexture.requestedFPS 在移动端经常得不到满足,很多国产 Android 机的采集中间帧率会掉到 20 以下。这就更考验 FFmpeg 侧的帧率容错,命令行里 -framerate 不要写死成 30,可以配合 -vsync 或者直接给 25,让编码器按实际节奏来,反而更稳。

注意:移动端真机上使用 AsyncGPUReadback 时,部分旧手机 GPU 不支持,会出现长时间无回调的现象。遇到这类问题,可以在真机上做一次回调超时检测,超过 500ms 没有回调就降级到 ReadPixels 同步方案。

4.5 问题排查速查表

现象 可能原因 解决方式
推流后播放端画面全绿 输入尺寸/格式不匹配 核对宽高和 -pixel_format
延迟越来越大 编码参数没用 zerolatency 加 -tune zerolatency
内存只增不减 NativeArray 未 Dispose 回调里显式释放
进程启动即退出 FFmpeg 路径不对或被杀软拦截 检查 StreamingAssets 路径及日志
画面出现横纹/花屏 编码输入分辨率与读回不一致 强制统一读写尺寸
推流中断后无法恢复 管道写入异常未处理 监听进程序退出,自动重启
编辑器正常,打包后无法推流 ffmpeg.exe 未随包发布 检查 StreamingAssets 拷贝设置

排查的时候,第一件事永远是看 FFmpeg 的错误输出。我在代码里把 RedirectStandardError 的内容打到了 Unity Console,几乎 80% 的问题都能从那里直接看出来。 我不止一次见到有人反复改 C# 代码,结果问题出在 FFmpeg 版本太老或者缺少某个 dll 上。

最后说点实际的体会

整套方案做下来,我最深的感受是: Unity 直播功能的技术难点并不在 Unity 本身,而在流媒体知识的积累。 只要理解了“采集 → 编码 → 推送”这条链路,并且用对 FFmpeg 这个工具,Unity 端需要写的代码其实很少。

如果你只是想快速跑通 Demo,完全不用先搭服务器。可以先安装一个带 RTMP 模块的 Nginx,本地拉流验证,花不了半小时。真正值得花时间的,是把采集端的性能、进程的稳定性、异常恢复这些细节打磨好。我做项目的时候,突发断电、网络断开、摄像头热插拔,这些情况都需要让直播功能能自动恢复,否则上线之后运维会疯掉。

这个方案后续还能扩展的方向也很多:比如接入 Android 端的硬编(MediaCodec)降低 CPU 占用,或者在推流的同时把 FFmpeg 输出重定向到本地文件,实现边直播边录制。甚至显卡硬件编码,Unity 侧代码都不需要变,只改 FFmpeg 参数就行。这也是我喜欢这个方案的最大原因——上层接口稳定,底层能力随时可以升级替换。

Logo

火山引擎视频云技术社区,是面向 AI 音视频开发者的技术交流平台。这里汇聚源自抖音、豆包等亿级 DAU 产品的 RTC、直播、点播、AI 媒体处理、音视频互动技术,提供接入指南、最佳实践、性能调优、场景案例、Demo 代码、开源项目、白皮书和 API 文档。社区汇聚官方工程师与一线开发者,为 AI 视频通话、数字人、AI 视频处理等应用的开发与落地提供技术支持。

更多推荐