本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:阿里云视频点播服务提供了一站式的视频托管解决方案,结合STS(Security Token Service)临时授权机制,可在不暴露长期访问密钥的前提下实现安全、灵活的权限控制。本文围绕ThinkPHP5框架下的“tp5_阿里云临时授权sts”项目,系统讲解如何通过STS获取临时凭证,并将其应用于视频上传、播放等操作。内容涵盖视频点播核心功能、STS授权流程、临时凭证使用方法及安全最佳实践,帮助开发者构建高安全性的视频应用系统。

阿里云视频点播与STS临时授权的深度实践:从安全架构到ThinkPHP5落地

在智能设备无处不在、音视频内容爆炸式增长的今天,企业对“上传即可见、播放即安全”的用户体验要求越来越高。无论是在线教育平台上的课程录制,还是短视频App中用户随手一拍的Vlog,背后都离不开一个稳定、高效且 绝对安全 的视频点播(VOD)系统。

但你有没有想过——当你的前端页面可以直接上传视频到云端时,那些看似不起眼的AccessKey,其实就像一把万能钥匙,一旦泄露,攻击者就能肆意删除文件、盗取数据,甚至把你的OSS桶变成他们的免费CDN?😱

这可不是危言耸听。现实中,太多开发者还在用长期密钥硬编码的方式让客户端直连云服务,结果APK被反编译、JS代码被抓包,密钥瞬间暴露,损失惨重。

那怎么办?难道非得让用户先把视频传到服务器再中转?当然不!阿里云早就给出了更优雅的解法: STS(Security Token Service) + 临时凭证直传 。

它就像是给每个用户发一张“限时通行证”,只能进指定区域、只能干允许的事,时间一到自动作废。就算这张证被人捡到了,也顶多蹭几分钟,根本翻不了天!

今天我们就来彻底拆解这套机制,不仅讲清楚原理,还要手把手带你用 ThinkPHP5 搭建整套安全上传/播放体系 ,让你的应用真正实现“高可用、零信任、可追溯”。

准备好了吗?咱们开始👇


🔍 为什么说传统长期密钥是“定时炸弹”?

先别急着上代码,我们得搞明白:为什么非要折腾什么STS?直接用AccessKey不行吗?

行,当然行……短期内看确实简单粗暴。但问题就在于—— 它是“永久有效”的 。

想象一下这个场景:

小明开发了一个移动端App,为了让用户能快速上传视频,他在代码里埋了这样一段配置:

php $config = [ 'access_key_id' => 'LTAI5tKXxxxxxx', 'access_key_secret' => 'GmFjUoYyyyyyyyyy' ];

然后打包发布。
结果某天,有人反编译了APK,在 strings.xml 里搜到了这两个值……
下一秒,攻击者写了个脚本,疯狂调用OSS接口下载所有公开资源,顺带删掉几万个视频文件。
等运维发现时,账单已经飙了上千块,核心内容也被清空。

这就是典型的 长期密钥滥用风险 。

而如果我们换一种方式呢?

不再给前端任何“永久钥匙”,而是让它每次上传前先向你自己的后端申请一张“临时通行证”——包含 AccessKeyId 、 AccessKeySecret 和一个叫 SecurityToken 的东西,并且这张证 只管15分钟 ,权限也仅限于“往某个目录上传.mp4文件”。

即使被截获,攻击者也只能在这15分钟内做这一件事。等他想二次利用?抱歉,token已过期,无效。

这种模式的核心技术支撑,就是阿里云的 STS(Security Token Service) 。


🛠️ STS 到底是个啥?它是怎么工作的?

简单来说, STS 是阿里云提供的一种“临时身份签发服务” ,专门用来解决“如何安全地把云资源访问权委托出去”的问题。

你可以把它理解为一个“数字签证官”:你不直接给人护照(长期AK),而是根据对方的身份和目的,现场签发一张有明确有效期和活动范围的签证(临时Token)。

它长什么样?

当你成功调用 AssumeRole 接口后,STS会返回这样一个结构体:

{
  "Credentials": {
    "AccessKeyId": "STS.L4mXxxxxx",
    "AccessKeySecret": "DcYzKxxxxx",
    "SecurityToken": "CAISgwN1XXXXXXXX",
    "Expiration": "2025-04-05T10:00:00Z"
  },
  "AssumedRoleUser": {
    "Arn": "acs:ram::123456789012:role/VideoUploadRole/Session123"
  }
}

这四个字段合起来,就是所谓的“临时安全凭证四元组”。前端拿着它们去调用VOD或OSS API,就可以完成指定操作。

关键点来了:这个凭证是有生命周期的,默认最短900秒(15分钟),最长3600秒(1小时),到期自动失效,无需手动回收。


STS 的三大核心要素:角色、策略、令牌

要玩转STS,必须掌握它的三个“灵魂组件”:

组件 作用 类比
角色(Role) 虚拟身份,代表某种职责 “我是谁?”——比如“视频上传员”
策略(Policy) 权限描述,定义能做什么 “我能干啥?”——比如“只能上传mp4”
临时令牌(Token) 动态签发的短期凭证 “我的临时工牌”

它们之间的关系可以用一句话概括:

角色决定身份,策略划定边界,令牌承载执行权。

角色:不是账号,不能登录

角色是一个逻辑身份,通常以ARN形式表示:

acs:ram::123456789012:role/VideoUploadRole

注意!角色不能像子账号那样单独登录控制台,它必须被“承担”(Assume)才能生效。

创建角色时需要设置“信任策略”,也就是谁可以来扮演我。例如:

{
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": ["sts.aliyuncs.com"]
      },
      "Action": "sts:AssumeRole"
    }
  ],
  "Version": "1"
}

意思是:“我允许 sts.aliyuncs.com 这个服务来帮我代劳。”

策略:细粒度权限控制的语言

策略是JSON格式的权限声明,支持非常精细的操作限制。

举个例子,如果你想让用户只能上传不超过5GB的MP4/AVI文件,可以这么写:

{
  "Version": "1",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["vod:UploadVideo"],
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "vod:ContentType": ["video/mp4", "video/avi"]
        },
        "NumericLessThanEquals": {
          "vod:FileSize": 5368709120  // 5GB
        }
      }
    }
  ]
}

看到了吗?通过 Condition 字段,我们可以基于请求上下文做条件判断,真正做到“按需授权”。

临时令牌:动态签发,自动失效

最后生成的临时令牌,本质上是一次性会话凭证。它由STS签名生成,目标服务(如OSS/VOD)在收到请求后会主动回源验证其有效性。

整个流程如下图所示:

sequenceDiagram
    participant 用户 as 客户端用户
    participant 后端 as 应用服务器
    participant STS as 阿里云STS
    participant VOD as 视频点播服务

    用户->>后端: 请求上传权限
    后端->>STS: AssumeRole(role_arn, policy)
    STS-->>后端: 返回临时凭证
    后端-->>用户: 下发凭证
    用户->>VOD: 使用凭证上传视频
    VOD->>STS: 验证SecurityToken
    STS-->>VOD: 校验通过
    VOD-->>用户: 返回上传任务ID

整个过程中,主账号的长期密钥始终保留在服务端,绝不会暴露给客户端,完美规避了泄露风险。


🧩 STS 在 RAM 体系中的定位:不只是一个服务,更是权限中枢

很多人误以为 STS 是独立存在的,其实不然。它深深嵌入在阿里云的 RAM(Resource Access Management) 系统之中,是实现精细化权限治理的关键桥梁。

来看这张图:

graph TD
    A[主账号] --> B(RAM)
    B --> C[用户/User]
    B --> D[角色/Role]
    B --> E[策略/Policy]
    D --> F[STS服务]
    F --> G[临时凭证]
    G --> H[访问云资源]
    style F fill:#e6f7ff,stroke:#1890ff,color:#000

可以看到:

  • RAM 是统一的身份管理中心;
  • 角色和策略在这里预先配置好;
  • 当合法请求到达 STS 时,它会基于当前上下文动态生成符合规则的临时凭证;
  • 凭证下发后,客户端即可凭此访问具体资源。

所以, STS 不是替代 RAM,而是扩展了它的能力 ,让它不仅能管理“谁是谁”,还能灵活控制“谁在什么时候能干什么”。


⚔️ 长期密钥 vs 临时凭证:一场关于安全性的降维打击

为了更直观看出差异,我们来做个对比表:

对比维度 长期密钥(AK/SK) 临时凭证(STS Token)
生命周期 永久有效(除非禁用) 900~3600秒自动过期
泄露后果 可被无限次滥用 影响窗口极小
权限粒度 固定绑定 可动态调整
是否可审计 难以区分行为来源 支持 RoleSessionName 追踪
适用场景 服务端内部调用 客户端直传、跨账号访问

重点说说最后一点: 可审计性 。

假设你有一个AK被多个模块共用,突然发现有个IP在批量删除视频。你能知道是哪个用户干的吗?不能!

但如果你用了STS,每次签发都带上唯一的 RoleSessionName ,比如:

upload_session_user_123_device_A1B2C3

那么在日志里就能清晰看到:“哦,原来是用户123用安卓手机在凌晨两点删的”,方便溯源和风控。


🔐 最小权限原则(POLP)如何靠STS落地?

信息安全领域的黄金法则之一就是 最小权限原则(Principle of Least Privilege, POLP) :每个主体只能拥有完成任务所必需的最低权限。

而 STS 正是践行这一原则的理想工具。它通过以下几种机制实现:

✅ 1. 角色隔离:不同业务走不同通道

不要搞“全能角色”!你应该为不同功能创建专用角色:

  • VideoUploadRole :只允许上传
  • VideoPlayRole :只允许获取播放权
  • AdminDeleteRole :用于后台管理删除

彼此互不交叉,避免权限蔓延。

✅ 2. 策略限制:精确到路径和类型

不要写 "Resource": "*" !一定要限定具体资源路径:

"Resource": "acs:oss:*:*:my-bucket/uploads/user_123/*"

或者结合 Condition 控制文件类型、大小、来源IP等。

✅ 3. 动态策略注入:一次一授权

这是最关键的一步!在调用 AssumeRole 时,可以传入一个额外的策略参数,进一步收紧权限。

比如你想让某个用户的上传权限仅限于他的专属目录:

$policy = json_encode([
    'Version' => '1',
    'Statement' => [
        [
            'Effect' => 'Allow',
            'Action' => ['vod:UploadVideo'],
            'Resource' => '*',
            'Condition' => [
                'StringLike' => [
                    'oss:Prefix' => 'uploads/user_' . $userId . '/*'
                ]
            ]
        ]
    ]
]);

$request->setPolicy($policy);

这样一来,哪怕角色本身权限很宽,最终下发的凭证也只能在这个沙箱里活动。

✅ 4. 时间限制:绝不留“僵尸会话”

设置合理的 DurationSeconds ,建议一般设为1800秒(30分钟),大文件上传可适当延长至3600秒。

千万别图省事设成最大值!越长的风险窗口意味着更高的攻击可能性。


💻 ThinkPHP5 实战:一步步搭建 STS 授权服务体系

理论讲完,现在进入实战环节。我们将使用 ThinkPHP5 搭建一套完整的 STS 凭证签发服务,涵盖环境准备、SDK集成、接口设计、动态策略生成等全流程。

📦 1. Composer 引入阿里云 PHP SDK

首先确保项目已启用 Composer 自动加载。然后安装必要的依赖包:

composer require aliyuncs/oss-sdk-php

📌 提示:虽然名字是 OSS SDK,但它包含了通用的 DefaultAcsClient 和签名逻辑,是调用 STS 所必需的基础库。

如果你追求简洁,也可以选择社区封装好的轻量级包:

composer require jianyan/aliyun-sts

两者各有优劣:

方式 优点 缺点 适用场景
原生SDK 功能完整、文档齐全 代码冗长 大型企业系统
第三方封装 快速集成、语法友好 更新慢、功能受限 MVP验证、中小型项目

推荐初期用封装版快速验证,后期迁移到原生SDK提升可控性。


🔄 2. 配置 Autoload 与命名空间引用

ThinkPHP5 默认支持 Composer 自动加载,只要入口文件中有这句就行:

require __DIR__ . '/../vendor/autoload.php';

接着我们可以创建一个专门的服务类来封装 STS 调用逻辑:

// application/service/StsService.php

namespace app\service;

use Aliyun\Core\Profile\DefaultProfile;
use Aliyun\Core\DefaultAcsClient;
use Aliyun\Sts\Request\V20150401\AssumeRoleRequest;

class StsService
{
    private $client;

    public function __construct()
    {
        $regionId = config('aliyun.region_id');
        $accessKeyId = config('aliyun.access_key_id');
        $accessKeySecret = config('aliyun.access_key_secret');

        // 显式注册STS endpoint
        DefaultProfile::addEndpoint($regionId, $regionId, 'Sts', 'sts.' . $regionId . '.aliyuncs.com');

        $profile = DefaultProfile::getProfile($regionId, $accessKeyId, $accessKeySecret);
        $this->client = new DefaultAcsClient($profile);
    }

    public function assumeRole($roleArn, $sessionName, $policy = '', $duration = 3600)
    {
        $request = new AssumeRoleRequest();
        $request->setRoleArn($roleArn);
        $request->setRoleSessionName($sessionName);
        $request->setPolicy($policy);
        $request->setDurationSeconds($duration);

        try {
            $response = $this->client->getAcsResponse($request);
            return [
                'credentials' => [
                    'AccessKeyId'     => $response->Credentials->AccessKeyId,
                    'AccessKeySecret' => $response->Credentials->AccessKeySecret,
                    'SecurityToken'   => $response->Credentials->SecurityToken,
                    'Expiration'      => $response->Credentials->Expiration,
                ],
                'request_id' => $response->RequestId
            ];
        } catch (\Exception $e) {
            throw new \RuntimeException("STS调用失败:" . $e->getMessage());
        }
    }
}

几点说明:

  • 构造函数中初始化客户端,绑定Region和主账号AK/SK;
  • 使用 addEndpoint 显式指定STS地址,防止DNS解析异常;
  • assumeRole 方法封装了核心调用,返回结构化数组便于JSON输出;
  • 捕获异常并抛出运行时错误,便于上层统一处理。

🔐 3. 敏感信息外置:用配置文件 + .env 管理密钥

绝对禁止硬编码!我们应该把敏感信息放在外部配置中。

新建 config/aliyun.php :

// config/aliyun.php
return [
    'access_key_id'     => env('ALIYUN_ACCESS_KEY_ID'),
    'access_key_secret' => env('ALIYUN_ACCESS_KEY_SECRET'),
    'role_arn'          => 'acs:ram::123456789012:role/VideoUploadRole',
    'region_id'         => 'cn-hangzhou',
    'token_expire'      => 1800,
];

同时添加 .env 文件进行环境隔离:

ALIYUN_ACCESS_KEY_ID=LTAI5tKXxxxxxx
ALIYUN_ACCESS_KEY_SECRET=GmFjUoYyyyyyyyyy

这样开发、测试、生产环境可以各自配置,不怕误提交到Git。


🌐 4. 设计 RESTful 接口:/api/sts/issueToken

接下来我们要暴露一个受保护的API接口,供前端申请临时凭证。

路由配置:

// route/api.php
use think\Route;
Route::post('sts/issueToken', 'api/Sts/issueToken');

控制器实现:

// application/controller/api/Sts.php

namespace app\controller\api;

use app\service\StsService;
use think\Controller;
use think\facade\Config;

class Sts extends Controller
{
    public function issueToken()
    {
        $userId = input('post.user_id/d', 0);
        if (!$userId) {
            return json(['code' => 400, 'msg' => '缺少用户ID']);
        }

        $service = new StsService();
        $roleArn = Config::get('aliyun.role_arn');
        $sessionName = "user_{$userId}_" . uniqid();

        $policy = $this->buildUploadPolicy($userId);

        try {
            $result = $service->assumeRole($roleArn, $sessionName, $policy, 1800);
            return json(['code' => 0, 'data' => $result]);
        } catch (\Exception $e) {
            return json(['code' => 500, 'msg' => '签发失败:' . $e->getMessage()]);
        }
    }

    private function buildUploadPolicy($userId)
    {
        return json_encode([
            'Version' => '1',
            'Statement' => [
                [
                    'Effect' => 'Allow',
                    'Action' => ['vod:UploadVideo'],
                    'Resource' => '*',
                    'Condition' => [
                        'StringEquals' => [
                            'acs:UserId' => (string)$userId
                        ],
                        'IpAddress' => [
                            'acs:SourceIp' => request()->ip() . '/32'
                        ]
                    ]
                ]
            ], JSON_UNESCAPED_SLASHES);
    }
}

亮点功能:

  • 强制校验 user_id ,防止未授权访问;
  • 会话名包含用户ID + 随机串,保证唯一性和可追溯性;
  • 动态生成策略,限制只能由该用户IP发起请求;
  • 返回标准JSON结构,前端可直接使用。

🚀 客户端直传实战:浏览器端如何使用STS凭证?

有了后端签发的临时凭证,前端就可以绕过服务器,直接上传到OSS或调用VOD接口了。

🧰 浏览器端集成 Web Uploader

推荐使用百度开源的 Web Uploader 实现分片上传。

基本流程如下:

$.post('/api/sts/issueToken', { user_id: 123 }).then(res => {
    const cred = res.data.credentials;

    const uploader = WebUploader.create({
        server: 'https://your-bucket.oss-cn-shanghai.aliyuncs.com',
        fileVal: 'file',
        method: 'POST',
        formData: {
            OSSAccessKeyId: cred.AccessKeyId,
            policy: base64Policy,
            Signature: sign(base64Policy, cred.AccessKeySecret),
            security_token: cred.SecurityToken,
            key: `uploads/user_123/${Date.now()}.mp4`
        }
    });

    uploader.upload();
});

🔐 注意: Signature 必须使用 cred.AccessKeySecret 对 policy 做 HMAC-SHA1 签名,不能暴露主账号Secret!


⏳ 大文件上传如何应对 token 过期?

默认 token 有效期只有15分钟,如果上传一个2GB的视频要半小时怎么办?

解决方案有三招:

✅ 1. 分片预估时间,提前刷新

估算上传耗时,若超过阈值,则在初始化前就申请更长时间的token(最长1小时)。

✅ 2. 异步刷新机制

监听上传进度,当剩余时间 < 300秒 时,提前请求新凭证并更新formData:

uploader.on('uploadProgress', function(file, percentage) {
    const remainingTime = estimateRemaining(percentage);
    if (remainingTime < 300 && !refreshing) {
        refreshing = true;
        $.get('/api/sts/issueToken').then(newCred => {
            updateUploaderCredentials(uploader, newCred);
            refreshing = false;
        });
    }
});
✅ 3. 利用 OSS Multipart Upload ID 续传

开启断点续传功能,记录 UploadId 。即使token过期,只要重新获取凭证,仍可用同一个 UploadId 继续上传未完成的部分。


📥 上传完成后如何同步数据库?

建议启用 OSS 回调通知(Callback) ,在文件上传成功后触发服务端回调,完成元数据落库。

ThinkPHP5接收回调示例:

public function onUploadCallback()
{
    $input = file_get_contents('php://input');
    $data = json_decode($input, true);

    // 校验签名 & 来源IP
    if (!$this->validateOssCallback($input)) {
        return json(['code' => 403], 403);
    }

    Db::name('videos')->insert([
        'video_id' => $data['videoId'],
        'user_id'  => $data['userId'],
        'status'   => 'uploaded',
        'created_at' => time()
    ]);

    return json(['code' => 200, 'msg' => 'ok']);
}

这样既减轻了前端负担,又保证了数据一致性。


▶️ 播放环节的安全控制:如何防止视频被盗链?

上传解决了,播放也不能马虎。否则别人拿个URL就能无限播放,流量费都得炸穿。

🔑 使用 PlayAuth 实现鉴权播放

阿里云VOD支持生成 PlayAuth ,这是一种加密的播放令牌,内置权限策略,过期自动失效。

后端生成:

$request = new Vod\GetVideoPlayAuthRequest();
$request->setVideoId("e8bxxxxx-xxxx-xxxx-xxxx-xxxxxxxx");
$response = $vodClient->getAcsResponse($request);
$playAuth = $response->PlayAuth;

前端Aliplayer集成:

<script src="https://g.alicdn.com/de/prismplayer/2.9.24/prism-min.js"></script>
<div id="player-container"></div>

<script>
let player = new Aliplayer({
    id: 'player-container',
    vid: 'e8bxxxxx-xxxx-xxxx-xxxx-xxxxxxxx',
    playauth: 'eyJvIjoiRGlkIiwiQiI6IjEifQ==',
    region: 'cn-shanghai',
    encryptType: 1
}, function(player) {
    console.log('播放器就绪');
});
</script>

没有有效的 playauth ,连视频地址都拿不到,从根本上杜绝盗链。


🛡️ 多层防护:Referer + IP + 时间三重过滤

还可以在策略中叠加多重限制:

"Condition": {
    "StringEquals": {
        "acs:Referer": ["https://www.yourdomain.com/*"]
    },
    "IpAddress": {
        "acs:SourceIp": "203.0.113.1/24"
    },
    "DateLessThan": {
        "acs:CurrentTime": "2025-04-05T12:00:00Z"
    }
}
  • Referer 白名单:防止其他网站嵌入;
  • SourceIp 限制:只允许特定IP段访问;
  • 时间窗控制:链接只能在某段时间内播放。

组合拳出击,安全性拉满!


🛠️ 高阶技巧:打造全链路可审计的视频安全体系

真正的企业级系统,不仅要防得住,还得看得清、管得了。

📊 全链路日志追踪

建议在关键节点打日志:

[2025-04-05 10:00:00] STS_TOKEN_ISSUED uid=U10086 ip=203.0.113.1 exp=1800s
[2025-04-05 10:02:30] VIDEO_UPLOAD_START vid=e8bxxxxx by=U10086
[2025-04-05 10:08:12] PLAY_AUTH_REQUESTED vid=e8bxxxxx result=success
[2025-04-05 10:10:05] HLS_SEGMENT_ACCESSED path=index.m3u8 ip=198.51.100.1

设定规则触发告警:

  • 单IP高频申请token → 触发限流
  • 非法Referer访问m3u8 → 加入黑名单
  • 异地登录播放 → 发送短信验证码

🧹 凭证生命周期监控

部署定时任务扫描活跃会话:

SessionId ExpireAt Status
sess_20250405_xxx 2025-04-05 10:30:00 active
sess_expired_yyy 2025-04-05 09:15:00 expired

发现异常长期存活的会话,立即告警并排查原因。

📈 定期权限评审与优化

每月生成《STS策略使用分析报告》:

统计项 数值
日均STS请求数 12,430
平均凭证有效时长 12.3分钟
最高并发会话数 864
异常失败率 0.7%
权限过度授予案例 3例

根据数据持续优化策略粒度,逐步迈向“零信任架构”。


🎯 总结:STS 不只是一个功能,而是一种安全思维

看到这里,你应该已经明白:

STS 的价值远不止于“避免密钥泄露”这么简单。

它代表着一种现代云原生应用的安全范式转变——

从“静态授权”走向“动态授权”,
从“全权开放”走向“最小权限”,
从“事后补救”走向“事前预防”。

而在 ThinkPHP5 这样的主流框架中落地这套机制,不仅能极大提升系统的安全水位,还能为未来的微服务拆分、多租户支持、跨账号协作打下坚实基础。

所以别再犹豫了,赶紧把你的老项目里的硬编码AK都换成 STS 临时凭证吧!🔐💪

未来属于那些把安全刻进DNA的开发者。你,准备好迎接这场进化了吗?🚀

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

简介:阿里云视频点播服务提供了一站式的视频托管解决方案,结合STS(Security Token Service)临时授权机制,可在不暴露长期访问密钥的前提下实现安全、灵活的权限控制。本文围绕ThinkPHP5框架下的“tp5_阿里云临时授权sts”项目,系统讲解如何通过STS获取临时凭证,并将其应用于视频上传、播放等操作。内容涵盖视频点播核心功能、STS授权流程、临时凭证使用方法及安全最佳实践,帮助开发者构建高安全性的视频应用系统。


本文还有配套的精品资源,点击获取
menu-r.4af5f7ec.gif

Logo

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

更多推荐