说明
适用版本:ve_vod Flutter SDK ≥ 1.50.16
平台:iOS / Android
能力简介:当签名播放 URL 过期、或 HLS 分片请求报错时,SDK 自动回调业务层换取新的播放地址,实现无感续播,无需重建播放器或重新起播。
播放源自动刷新用于解决签名播放地址有时效的问题。火山引擎 VOD 的播放 URL 通常带有签名鉴权参数(如 auth_key、过期时间戳),长视频播放、后台挂起后恢复、HLS 长拉流等场景下,URL 可能在播放过程中过期,导致拉流失败、卡住或报错。
开启本能力后,SDK 在检测到地址过期或分片错误时,会主动回调到业务层索取新的播放地址并热替换,播放过程无感知、不中断。
典型适用场景
三步即可接入:实现回调 → 注册回调 → 起播前开启策略。
import 'package:ve_vod/ve_vod.dart'; // 1. 实现业务刷新回调 class MyUrlRefreshCallback implements TTVideoEngineUrlRefreshCallback { @override Future<dynamic> refreshUrl(TTVideoEngineUrlRequest request) async { // request.url / request.cacheKey / request.vid // 向业务服务端换取新的播放信息(可返回 GetPlayInfo 的 PlayInfoList) final playInfoList = await fetchNewPlayInfoList(request.vid); return playInfoList; // 也可直接返回 TTVideoEngineUrlResult(url: newUrl, expireTimeInMS: ts); } @override void cancel() { // 取消正在进行的网络请求 } } // 2. 注册回调(建议在 App 启动或进入播放页时) TTVideoEngineSourceRefreshStrategy.setUrlRefreshCallback( MyUrlRefreshCallback(), ); // 3. 起播前开启策略,然后再设置播放源 await player.enableSourceRefreshStrategy(enableHlsSegError: true); await player.setMediaSource(mediaSource);
说明
调用顺序很重要:必须先 setUrlRefreshCallback() 注册回调,并在 setMediaSource() 之前调用 enableSourceRefreshStrategy(),策略才会对本次播放源生效。
abstract class TTVideoEngineUrlRefreshCallback { /// 刷新过期的 URL。可返回 TTVideoEngineUrlResult, /// 或返回包含全部播放地址的 MediaSource / Map / List,由 SDK 自动匹配。 Future<dynamic> refreshUrl(TTVideoEngineUrlRequest request); /// 取消正在进行的刷新操作 void cancel(); }
| 类型 | 字段 | 说明 |
|---|---|---|
TTVideoEngineUrlRequest | url(String) | 已过期的旧播放地址 |
cacheKey(String) | 该地址对应的缓存 key | |
vid(String?) | 视频 ID(设置播放源时绑定,可能为空) | |
TTVideoEngineUrlResult | url(String) | 刷新后的新播放地址 |
expireTimeInMS(int) | 新地址过期时间(毫秒时间戳,详见 expireTimeInMS 时间单位) |
| 方法 | 说明 |
|---|---|
init() | 初始化 channel handler(首次调用其他方法时会自动初始化,一般无需手动调用) |
setUrlRefreshCallback(cb) | 注册 URL 刷新回调(必需) |
clearUrlRefreshCallback() | 清除 URL 刷新回调 |
setGenerateFileKeyCallback(cb) | 注册 HLS cacheKey 生成回调(可选,详见 HLS CacheKey 生成) |
clearGenerateFileKeyCallback() | 清除 HLS cacheKey 生成回调 |
/// 启用源过期自动刷新 /// [enableHlsSegError] 是否启用 HLS 分片错误处理,默认 true /// 建议在 setMediaSource() 之前调用 Future<void> enableSourceRefreshStrategy({bool enableHlsSegError = true}); /// 禁用源过期自动刷新 Future<void> disableSourceRefreshStrategy();
refreshUrl() 的返回值非常灵活,SDK 会自动归一化为一条 {url, expireTimeInMS}。支持以下几种返回形式:
| 返回类型 | 行为 |
|---|---|
TTVideoEngineUrlResult | 直接使用该 url 与过期时间 |
Map(含 url + expireTimeInMS/expireInMS) | 直接取该地址 |
TTVideoEngineMediaSource / Map(完整播放信息) | SDK 按旧 URL 的 path 匹配出对应档位的新地址 |
List(如 PlayInfoList) | 遍历每一项做 path 匹配,取第一个命中的地址 |
推荐做法
业务侧直接把服务端 GetPlayInfo 返回的 PlayInfoList(或整个响应)原样返回即可,无需自己挑选档位——SDK 会用旧地址的 path 自动匹配回同一清晰度的新地址。
urls / MainPlayUrl / BackupPlayUrl、expires / urlExpiredTimes / MainUrlExpire / BackupUrlExpire、playInfoList / PlayInfoList,以及 Result 包裹层;a=0 查询参数判断优先匹配主地址(MainPlayUrl)还是备地址(BackupPlayUrl)。过期时间同时兼容秒级与毫秒级时间戳:小于 100000000000 的值会被视为秒并自动 ×1000 转为毫秒。因此服务端无论返回秒级(如 1875780093)还是毫秒级时间戳都能正确解析。
注意
当返回 TTVideoEngineUrlResult 或 Map 形式时,expireTimeInMS 为必填,缺失会抛出 TTError。返回完整 PlayInfoList 时由 SDK 从 MainUrlExpire 等字段读取。
HLS 场景下,TS 分片的缓存 key 默认由完整 URL 推导。当签名参数变化时,同一分片可能因 URL 不同而被认为是不同文件,影响缓存命中。可选地注册 generateFileKey 回调,剥离签名参数后生成稳定 cacheKey,保证签名变化时缓存仍唯一且可复用。
typedef GenerateFileKeyCallback = String Function( String url, String fileKey, Map<String, String> extraInfo); String myGenerateFileKey( String url, String fileKey, Map<String, String> extraInfo) { final uri = Uri.parse(url); final params = Map<String, String>.from(uri.queryParameters) ..removeWhere((k, v) => k.toLowerCase().contains('token') || k.toLowerCase().contains('sign') || k.toLowerCase().contains('auth')); final cleanUrl = uri.replace(queryParameters: params).toString(); return cleanUrl.hashCode.toString(); // 实际应使用 MD5 等稳定哈希 } // 注册(可选) TTVideoEngineSourceRefreshStrategy.setGenerateFileKeyCallback(myGenerateFileKey);
说明
该回调为可选项:不注册时使用 SDK 默认 cacheKey 生成方式。若业务的签名参数名固定且明确,建议实现以提升缓存命中率。
| 问题 | 说明 / 排查 |
|---|---|
| 回调一直没被触发? | 确认:① 已 setUrlRefreshCallback();② 在 setMediaSource() 前调用了 enableSourceRefreshStrategy();③ 地址确实触发了过期/分片错误。可过滤日志 TAG Flutter_SourceRefresh 查看流程。 |
| 报 “Invalid refreshUrl result”? | 返回值类型不被支持,或返回的播放信息中找不到与旧 URL path 匹配的地址。请确认返回的新地址 path 与旧地址一致(同一档位)。 |
| 报 “expireTimeInMS is required”? | 返回 TTVideoEngineUrlResult/Map 时必须带过期时间;或改为返回完整 PlayInfoList 让 SDK 自行读取。 |
| 纯 MP4 源需要开 HLS 分片处理吗? | 不需要,可将 enableHlsSegError 设为 false。 |
| 如何关闭? | 调用 player.disableSourceRefreshStrategy();如不再使用回调,可 clearUrlRefreshCallback()。 |