1. 软件和硬件要求
系统 | iOS 15.6 及以上系统 |
处理器架构 | arm64 CPU 架构 |
支持终端设备 | A12+ 处理器,M1+ 处理器 iPhone XS,iPhone XR,iPhone SE2,iPhone 11+ 及更新设备 iPad 8+,iPad Mini5+,iPad Air3+,iPad Pro3+,New iPad Pro1+ 及更新设备 (2018 年以后发布的 iPhone,2019 年以后发布的 iPad 设备) |
开发 IDE | XCode |
内存要求 | 大于500M |
2. 模型包说明
模型包分为两种:
基础模型包
基础模型包与形象无关, 是运行 2D 端渲染必须的,模型包目录名称为:
common_model/
如果有多个人物模型,可以共用一个基础模型包。人物形象包
人物形象包里包含了 2D 数智人驱动的图像数据和推理模型。每个人物形象一个模型包, 加载不同的人物模型包,即可更换形象。模型包目录名称为:
human_xxxxx_540p_v3/
,其中 xxxxx 为定制形象的名称。人物形象包支持 540p (960 x 540) 和 720p (1280 x 720) 分辨率。
目前 SDK 包里含一个公共形象,包名是:human_yunxi_540p_v3,您可直接使用。
注意:
SDK 接入方需要将模型包放到 App 的沙盒中,初始化 SDK 时需要将2个模型的绝对路径作为参数传递给 SDK。
模型包可以动态下载,动态下载和更新的逻辑,需要接入方 App 自行实现。
3. 数智人客户端渲染 SDK 接入
数智人 SDK 的调用流程如下:

3.1 引入 Framework
将 Framework 添加到工程中, 嵌入方式选择: Embed & Sign 。

并引入头文件:
在 Objective-C 中使用时,直接引入头文件, 在 Swift 中使用时, 需要在 xxxxx-Bridging-Header.h 文件中引入头文件。
#import <TencentVirtualHumanSDK/TencentVirtualHumanSDK.h>
3.2 设置日志级别(可选)
SDK 日志默认输出到标准输出 stdio 中, 可设置日志输出等级。 默认等级为
TVHLogLevelOff
注意:
日志等级仅可以设置一次, 多次调用仅第一次调用生效。
[[TVHLogManager shareInstance] setLogLevel:TVHLogLevelInfo];
TVHLogManager.shareInstance().setLogLevel(TVHLogLevelInfo)
日志输出等级:
日志等级 | 等级标识 |
关闭日志 | TVHLogLevelOff |
错误日志 | TVHLogLevelError |
警告日志 | TVHLogLevelWarn |
信息日志 | TVHLogLevelInfo |
调试日志 | TVHLogLevelDebug |
追踪日志 | TVHLogLevelTrace |
3.3 License 校验
在使用数智人 SDK 前,先调用授权方法,否则其他所有类的初始化方法都会失败。
鉴权可以在 App 启动时,也可以在第一次打开数智人界面时。鉴权模块为单例,可以调用多次,以最后一次鉴权结果为准。
int result = [[TVHLicenseManager shareInstance] authWithAppID:@"0000000000" andSecretKey:@"xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"];NSLog(@"license result: %d", result);
let result = TVHLicenseManager.shareInstance().auth(withAppID: "0000000000", andSecretKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");print("license result: \\(result)")
返回 0 则表示鉴权成功,返回其他值为鉴权失败。
3.4 创建数智人控制器(TVHController)
通过控制器,可以初始化数智人,控制数智人开始和停止渲染, 控制数智人张嘴说话。
// 请事先将通用模型和形象模型放入沙盒 Documents 目录下NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);NSString *documentsPath = [paths firstObject];NSString* common_model = [NSString stringWithFormat:@"%@/common_model", documentsPath];NSString* human_model = [NSString stringWithFormat:@"%@/human_yunxi_540p_v3", documentsPath];// 初始化控制器self.controller = [[TVHController alloc] initWithCommonModelPath:common_model humanModelPath:human_model];self.controller.delegate = self;// 开始渲染[self.controller start];
// 请事先将通用模型和形象模型放入沙盒 Documents 目录下let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)let documentsPath = paths.first!let commonModel = "\\(documentsPath)/common_model"let humanModel = "\\(documentsPath)/human_youyou3_720p"// 初始化控制器controller = TVHController(commonModelPath: commonModel, humanModelPath: humanModel)if(controller != nil) {controller.delegate = self;// 开始渲染controller.start()}
3.5 创建渲染视图(TVHRenderView)
数智人 SDK 使用 Metal 实现了高性能、背景透明的渲染界面,数智人的图像画面会被绘制到此
RenderView
上,您可以和使用普通 View 一样设置此 RenderView
的大小位置等,可以将此 RenderView
作为子 View 添加到其他 View。说明:
如果接入方有特殊需求,可以自行实现渲染 View, 需要自行实现
TVHRenderViewDelegate
来获取 RGBA 原始数据进行绘制。我们强烈建议您使用 SDK 内封装的
TVHRenderView
以获得最好的渲染效果self.renderView = [[TVHRenderView alloc] initWithFrame:self.view.bounds];self.renderView.fillMode =TVHMetalViewContentModeFit;[self.view addSubview:self.renderView];// 将 renderView 设置给控制器self.controller.renderView = self.renderView;
renderView = TVHRenderView(frame: self.view.bounds)renderView.fillMode = .fitview.addSubview(renderView)// 将 renderView 设置给控制器controller.renderView = renderView
可以设置渲染 View 的填充模式:
TVHMetalViewContentModeStretch | 拉伸图像 |
TVHMetalViewContentModeFit | 裁切图像 |
TVHMetalViewContentModeFill | 保留黑边(透明边) |
3.6 流式输入音频驱动数智人
流式输入音频,让数智人开口说话。
输入的音频格式为 s16le (signed 16 bits little endian, 有符号16位小端) PCM, 采样率16000, 单声道。
可以多次调用
appendAudioData
流式输入音频数据,输入的音频会被保存到音频缓冲队列中,每次调用时输入的音频数据时长可以是任意值。注意:
输入数据的实时率必须大于 1.0, 要保证输入数据的数量要比实时播放音频消耗的数据多, 如果音频数据不足可能导致等待音频数据,数智人播报过程中卡住不动。
输入音频最后的一片数据需要设置 isFinal 为 YES,否则 SDK 会认为音频没有结束,等待音频导致数智人播报时卡住不动。
// 此代码片段演示了从文件读取 PCM 数据,模拟流式分段发送给控制器NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);NSString *documentsPath = [paths firstObject];NSString* pcmPath = [NSString stringWithFormat:@"%@/test.pcm", documentsPath];// 读取音频数据NSData* data = [NSData dataWithContentsOfFile:pcmPath];// 模拟流式,分片发送数据(仅供演示,丢掉了最后一个分片)int packageSize = 1280;int packageCount =(int)[data length] / packageSize;for(int i=0; i<packageCount; i++) {BOOL isFinal = (i == packageCount -1);[self.controller appendAudioData:[data subdataWithRange:NSMakeRange(i*packageSize, packageSize)] metaData:@"" isFinal:isFinal];}
// 此代码片段演示了从文件读取 PCM 数据,模拟流式分段发送给控制器let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)guard let documentsPath = paths.first else { return }let pcmPath = "\\(documentsPath)/test.pcm"// 读取音频数据guard let data = try? Data(contentsOf: URL(fileURLWithPath: pcmPath)) else { return }// 模拟流式,分片发送数据(仅供演示,丢掉了最后一个分片)let packageSize = 1280let packageCount = data.count / packageSizefor i in 0..<packageCount {let range = i * packageSize..<(i * packageSize + packageSize)let subData = data.subdata(in: range)let isFinal = (i == packageCount - 1)controller.appendAudioData(subData, metaData: "", isFinal: isFinal)}
3.7 打断播报
在数智人播报过程中,可以随时打断播报进入静默状态, 调用控制器的
interrupt
方法,数智人就会立刻闭嘴并停止音频播放。[self.controllerinterrupt
]
controller.interrupt
()
注意:
打断后,也会收到
speakFinish
消息。打断调用后数智人会立刻闭嘴并停止音频播放,但是需要等1 - 2秒的时间,才会收到
speakFinish
消息, 在收到 speakFinish
消息之前,调用 appendAudioData
时,也会等到 speakFinish
后才会继续开始播报。3.8 处理 App 切换前后台时暂停和恢复数智人
对于大部分 App 来说,当应用被切换到后台时,数智人的渲染和播报应暂停,待应用重新被激活回到前台后,渲染和播报应自动恢复继续运行。
控制器提供了
pause
和 resume
两个方法,用来控制数智人暂停和恢复。
在 iOS 平台,可以使用
NSNotificationCenter
注册监听器,获得应用进入后台和进入前台的消息。@implementation DemoViewController- (void)viewDidLoad {[super viewDidLoad];// ... 其他代码// 注册通知[[NSNotificationCenter defaultCenter] addObserver:selfselector:@selector(appDidEnterBackground:)name:UIApplicationDidEnterBackgroundNotificationobject:nil];[[NSNotificationCenter defaultCenter] addObserver:selfselector:@selector(appWillEnterForeground:)name:UIApplicationWillEnterForegroundNotificationobject:nil];}- (void)dealloc{// 在销毁时取消委托[[NSNotificationCenter defaultCenter] removeObserver:self];}- (void)appDidEnterBackground:(NSNotification *)notification {// 处理暂停[self.controller pause];}- (void)appWillEnterForeground:(NSNotification *)notification {// 处理恢复[self.controller resume];}@end
class DemoViewController2: UIViewController {override func viewDidLoad() {super.viewDidLoad()// ... 其他代码// 注册通知NotificationCenter.default.addObserver(self,selector: #selector(appDidEnterBackground(_:)),name: UIApplication.didEnterBackgroundNotification,object: nil)NotificationCenter.default.addObserver(self,selector: #selector(appWillEnterForeground(_:)),name: UIApplication.willEnterForegroundNotification,object: nil)}deinit {// 在销毁时取消委托NotificationCenter.default.removeObserver(self)}@objc private func appDidEnterBackground(_ notification: Notification) {// 处理暂停controller.pause()}@objc private func appWillEnterForeground(_ notification: Notification) {// 处理恢复controller.resume()}}
3.9 处理数智人的通知消息
数智人渲染通知消息,通过委托(Delegate)模式实现。 实现
TVHControllerDelegate
委托,在控制器中注册委托对象,就可以收到消息通知。// 实现 TVHControllerDelegate@interface DemoViewController () <TVHControllerDelegate>// ... 其他代码@end@implementation DemoViewController// 渲染开始- (void)renderStart {NSLog(@"render start");}// 播报开始- (void)speakStart {NSLog(@"speak start");}// 播报完成- (void)speakFinish {NSLog(@"speak finish");}// 播报错误- (void)speakError:(NSInteger)errorCode message:(NSString*)message {NSLog(@"speak error code:%ld, message:%@", errorCode, message);}@end
// 实现 TVHControllerDelegateextension DemoViewController2 : TVHControllerDelegate {// 渲染开始nonisolated func renderStart() {print("render start");}// 播报开始nonisolated func speakStart() {print("speak start");}// 播报完成nonisolated func speakFinish() {print("speak finish");}// 播报错误nonisolated func speakError(_ errorCode: Int, message: String!) {print("speak error code:\\(errorCode), message:\\(message)");}}
3.10 处理 Meta 信息
给数智人输入音频后,有时需要获取播放到某个特定时刻的时机来处理一些逻辑。 比较常见的应用场景是在数智人播报的时候,同时显示字幕。
数智人 SDK 引入了一个 MetaData(附加信息)的概念,在输入音频片段的时候,可以附加一个字符串到音频片段的开头,当数智人驱动到该音频片段时,就会通过 delegate 的方式通知调用者。
如果需要有格式的信息,可以将 JSON 序列化为字符串传入。
输入音频时携带 MetaData
[self.controller appendAudioData:data metaData:@"这里是附加信息" isFinal:isFinal];
controller.appendAudioData(data, metaData: "这里是附加信息", isFinal: isFinal)
获取通知
// 实现 TVHControllerDelegate@interface DemoViewController () <TVHControllerDelegate>// ... 其他代码@end@implementation DemoViewController// 播报 Meta信息 开始- (void)speakMetaStart:(NSString *)metaData {NSLog(@"speak meta start: %@", metaData);}// 播报 Meta信息 完成- (void)speakMetaFinish:(NSString *)metaData {NSLog(@"speak meta finish: %@", metaData);}@end
// 实现 TVHControllerDelegateextension DemoViewController2 : TVHControllerDelegate {// 播报 Meta信息 开始nonisolated func speakMetaStart(_ metaData: String!) {print("speak meta start: \\(metaData)");}// 播报 Meta信息 完成nonisolated func speakMetaFinish(_ metaData: String!) {print("speak meta finish: \\(metaData)");}}
3.11 针对早期设备的降级方案
端渲染数智人 SDK 是使用客户端的计算能力,完成 AI 算法推理和图像合成,对移动设备的性能有一定的要求。
我们支持 2018 年以后发布的 iPhone 和 2019 年以后发布的 iPad 设备,对于较早期的设备需要进行降级,提示用户不支持或者用其他产品方案代替。
可以使用
canSmoothlyRun
方法判断当前设备是否支持端渲染 SDK。BOOL canRun = [[TVHDeviceManager shareInstance] canSmoothlyRun]
let canRun = TVHDeviceManager.shareInstance().canSmoothlyRun()
注意:
1. 此API 是基于 SDK 发布前的性能测试给出的建议结果,不是实际运行的测试。 实际运行结果会受客户程序抢占 CPU 的影响导致不准确。
2. 结果偏保守,返回 NO 的部分机型,可能也可以流畅运行。
4. Demo App 运行说明
随 SDK 附带了一个能完整运行的 Demo App。 项目工程结构如下:

4.1 修改 Bundle ID
请修改 Bundle ID 保证和申请 License 提供的 Bundle ID 一致, 并更新设置证书和签名。

4.2 修改 Const.h 文件
Const.h 文件包含了所有用到的 AppKey, Secret 等密钥, 请修改为客户申请的。

4.3 拷贝模型到沙盒
先运行一次 App,然后在 Mac 电脑的 Finder 应用中,拷贝模型到手机。

4.4 运行 Demo
单击运行按钮, 即可运行 2D 端渲染 Demo 应用。