# 1. 总体说明

# 1.1 简介

虚拟人交互 SDK(以下简称 AIKit)为接入方提供支持虚拟人服务与视频流服务方案的SDK。接入方需按此集成文档集成 SDK,以使用相关能力。

# 1.2 适用范围

本文档定义了 AIKit 在iOS操作系统中的使用说明和体系结构及API接口。

# 1.3 开发准备

开发者在使用iOS SDK进行开发前,需要经过我们的授权,获取对应的appId、apiKey、apiSecret、avatarid、vcn。

# 1.4 支持平台

支持iOS9.0及以上的平台,支持arm64 架构。

# 2. 使用说明

# 2.1 申请使用

请与平台业务人员联系获取集成 SDK 包,需要提供接入方客户端包名、应用签名等信息,并获取接入方 appId、apiKey、apiSecret、虚拟形象、发音人,以及其他相关资源。

# 3. SDK集成

# 3.1 集成步骤

  1. AIKit.frameworkXRTCSDK.framework添加至目标项目中。

  2. 在主工程的 TARGET > Bulid Phases > Link Bianry With Libraries 添加以下依系统赖库。

    libc++.tbd
    libc++abi.tbd
    Security.framework
    MobileCoreServices.framework
    CFNetwork.framework
    CoreMedia.framework
    VideoToolbox.framework
    AudioToolbox.framework
    CoreVideo.framework
    CoreAudio.framework
    CoreMotion.framework
    
  3. 在工程配置Build Settings里的Other Linker Flags中添加-ObjC标记,BitCode 设置为NO。

    如图所示:

    ![image-20220906101726249](../../../Library/Application Support/typora-user-images/image-20220906101726249.png)

  4. 主工程 Info.plist添加权限设置。

    Privacy - Microphone Usage Description   #并添加文字说明
    
  5. 主工程添加以下三方开源库

    # 建议以cocoPods,carthage方式集成最新版本,需要添加 use_frameworks,可参见demo中的podfile
    #CocoaLumberjack要求最低版本3.0.0
    CocoaLumberjack.framework  
    #SocketRocket要求最低版本0.5.0
    SocketRocket.framework     
    

# 4. SDK使用

# 4.1 SDK初始化

初始化SDK,传入相关鉴权参数。

/**
 * SDK初始化函数用以初始化整个SDK
 * @param param  SDK配置参数
 * @return 结果错误码,0=成功
 */
int ret = [ILibrary initSDK:^(AiHelperMaker *maker) {                            maker.appId(@"appId").apiKey(@"apiKey").apiSecret(@"apiSecret").authInterval(300).logOpen(true).iLogOpen(true).workDir(@"xx/xx/workDir");
}]; 
参数 类型 说明 必填
appId string 应用ID
apiKey string 唯一的应用标识
apiSecret string 唯一的应用密钥
authInterval int 授权更新间隔,单位为秒 否,默认为300秒
iLogOpen bool 是否开启控制台日志输出
logOpen bool 是否开始日志输出文件
workDir string sdk工作目录

# 4.2 配置虚拟人能力回调、渲染视图

//设置虚拟人能力回调代理
[AiHelper shareInstance].delegate = self;
//设置虚拟人拉流渲染视图 
AiHelper.shareInstance.virtualView = self.remoteView;

能力输出回调:AIKitCoreDelegate协议

//result 回调
- (void)aikitOnResult:(NSString*)ability outputData:(NSArray<AIKITDataModel*>*)data usrCtx:(AIKITUserContext*)context;
//event 回调  event:19表示ws已经关闭连接
- (void)aikitOnEvent:(NSString*)ability event:(NSInteger)event eventData:(NSArray<AIKITDataModel*>*)data usrCtx:(AIKITUserContext*)context;
//error 回调
- (void)aikitOnError:(NSString*)ability error:(AIKITError*)error usrCtx:(AIKITUserContext*)context;

# 4.3 上传个性化数据

/// 上传个性化数据
/// @param ability 能力标识
/// @param data 数据
+ (AIKITOutput*)loadDataSync:(NSString*)ability data:(AIKITCustomData*)data;    

代码示例:

/// 例如:获取上传背景的res_id
- (nullable NSString *)getResidWithUploadData:(NSData *)fileData{

    AIKITCustomData *customData = [[AIKITCustomData alloc] init];
    NSString *base64Str = [fileData base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];   
    [customData addText:base64Str key:@"background_data" index:0];
    
    AIKITOutput *output = [AiHelper loadDataSync:VMS_ABILITY data:customData];
    
    if (output.code != 0) {
        NSLog(@"loadData failed code:%ld",(long)output.code);
        return nil;
    }
    
    if (output.data.count > 0) {
        for (AIKITDataModel *model in output.data) {
            Byte *bytes = (Byte *)model.value;
            if (bytes == NULL) {
                continue;
            }
            NSData *data = [[NSData alloc] initWithBytes:bytes length:model.len];
            NSString *value = [NSString stringWithUTF8String:[data bytes]];
            
            if ([model.key isEqualToString:@"result"]) {
                NSError *error;
                NSDictionary *dic = [VMTools jsonToArrayOrDictionary:value];
                if ([dic.allKeys containsObject:@"data"]) {
                    NSDictionary *resultData = dic[@"data"];
                    return resultData[@"res_id"];
                }
            }
        }
    }
}

#

/// 上传字幕模板数据,注意字幕模板上传后需要进行文本驱动,详细见demo
 AIKITCustomData *customData = [[AIKITCustomData alloc] init];
 NSString *base64Str = [data base64EncodedStringWithOptions:0];
    
 if (!kStringIsEmpty(base64Str)) {
    [customData addText:base64Str key:@"template_data" index:0];
}
 _template_id = [self getResidWithUploadCustomData:customData];  

# 4.4 启动虚拟人服务

/// 启动会话
/// @param ability 能力标识
/// @param param AIKITParameters能力参数
/// @param content 用户自定义标识
+ (AiHandle*)start:(NSString*)ability param:(nullable AIKITParameters*)param ctxContent:(nullable AIKITCtxContent*)content;    

创建AIKITParameters

参数 类型 说明 必填
uid string 唯一标识
avatarid string 形象ID
width int 视频分辨率:宽 是,默认为1280
height int 视频分辨率:高 否,默认为720
scale double 主播在背景图像中的大小 (0,1.0] 否,默认为1
maskRegion bool 形象截取区域,用于控制形象展示区域,比如可以截取半身等 示例 : [0,0,1080,1920]
moveH int 主播平移像素距离 [-4096, +4096],控制主播中心位置距离合成图像中心位置水平距离,为负数表示向左平移,为正数表示向右平移 否,默认为0
moveV int 主播垂直移动像素距离 [-4096, +4096],控制主播在展示画面中的上下移动距离,0为默认情况,主播在画面中下贴边位置。该值为负数表示向下移动,为正数表示向上移动 否,默认为0
res_id string 上传背景生成唯一标识
interactive_scene string 部分形象需要传值为type=live
template_id string 后处理模板生成的唯一标识

# 4.5 文本驱动

/// 会话模式输入数据
/// @param data AIKITInputData能力数据
/// @param handle 启动服务的handle对象
/// @param param AIKITParameters能力参数
+ (int)write:(nullable AIKITInputData*)data handle:(nonnull AiHandle *)handle param:(nullable AIKITParameters *)param; 

代码示例:

AIKITParameters *param = [[AIKITParameters alloc] init];
//发音人
[param addSection:@"tts"];
[param addString:@"vcn" value:vcn];
//文本驱动传rhy有字幕效果,其他可不传入
[param addInt:@"rhy" value:3];

//打断模式:追加、打断
[param addSection:@"vms_dispatch"];
[param addInt:@"interactive_mode" value:interactiveMode];

AIKITInputData *input = [[AIKITInputData alloc] init];
//合成文本
[input addText:text key:@"text" status:DataStatusOnce];

//动作
if (actions.count == 0){
      [input addText:@"" key:@"ctrl_w" status:DataStatusOnce];
}else{
    NSMutableArray *avatarArray = [NSMutableArray array];
    for (NSString *action in actions) {
      NSDictionary *dic = @{@"type":@"action",
                               @"value":action,
                               @"wb":@2,
                               @"we":@10,
       };
      [avatarArray addObject:dic];
    }
    NSDictionary *avatarDic = @{@"avatar":avatarArray.copy};
    NSData *data = [NSJSONSerialization dataWithJSONObject:avatarDic options:NSJSONWritingPrettyPrinted error:nil];
    [input addTextData:data key:@"ctrl_w" status:DataStatusOnce];
}
//后处理字幕相关
if (!kStringIsEmpty(_template_id)){
     NSString *path = [ConfigManager.sharedInstance getSandBoxDefaultFilePath:ConfigJsonFileTypeWordLevel3];
     NSData *templateData = [[NSData alloc] initWithContentsOfFile:path];
     NSDictionary *json = [NSJSONSerialization JSONObjectWithData:templateData
                                                             options:kNilOptions
                                                               error:nil];
     TemplateModel *model = [TemplateModel yy_modelWithDictionary:json];
     if (!kStringIsEmpty(_res_id)){
        model.controls.firstObject.elements.firstObject.resource.res = _res_id;
     }
     NSString *jsonStr = [model yy_modelToJSONString];

     if(!kStringIsEmpty(jsonStr)){
       [input addText:jsonStr key:@"ctrl_postproc" status:DataStatusOnce];
    }
}

//文本、动作附属参数
AIKITParameters *inputParam = [[AIKITParameters alloc] init];
[inputParam addString:@"encoding" value:@"utf8"];
[inputParam addString:@"compress" value:@"raw"];
[inputParam addString:@"format" value:@"plain"];

[input addControl:inputParam key:@"text"];
[input addControl:inputParam key:@"ctrl_w"];
//写入驱动
int ret = [AiHelper write:input handle:self.handle param:param];
if (ret != 0) {
    NSLog(@"vms write failed! ret is %ld", (long)ret);
}

创建param

tts

参数 类型 说明 必填
vcn string 合成发言人
rhy int 文本驱动传rhy有字幕效果

vms_dispatch

参数 类型 说明 必填
interactive_mode int 打断模式 [0,1] (0 追加模式 1 打断模式) 否,默认为1

创建input

参数 类型 说明 必填
text string 合成的文本数据
ctrl_w data 字偏移控制指令
ctrl_postproc string 后处理控制指令

control

参数 类型 说明 必填
text object 含有 encoding、compress、format参数
encoding string 文本编码: utf8
compress string 文本压缩格式: raw
format string 文本格式: raw
ctrl_w object 含有 encoding、compress、format参数,动作相关
ctrl_postproc object 含有 encoding、compress、format参数,后处理相关

# 4.5 ws文本驱动 - 支持回调播报状态

/// 会话模式输入数据
/// @param data AIKITInputData能力数据
/// @param handle 启动服务的handle对象
/// @param param AIKITParameters能力参数
+ (int)write:(nullable AIKITInputData*)data handle:(nonnull AiHandle *)handle param:(nullable AIKITParameters *)param; 

代码示例:

    DataStatus status = DataStatusEnd;
    if (_wsTextStatus == 0) {
        status = DataStatusBegin;
    }else {
        status = DataStatusContinue;
    }
    if (_wsTextStatus == 0) {
        _wsTextStatus ++;
    }
    
    AIKITParameters *param = [[AIKITParameters alloc] init];
    [param addSection:@"tts"];
    [param addString:@"vcn" value:vcn];
    //文本驱动传rhy有字幕效果,其他可不传入
    [param addInt:@"rhy" value:3];
    
//    [param addSection:@"realtime_status"];
//    [param addInt:@"tts_status" value:1];
//    [param addInt:@"vmr_status" value:1];
//    [param addInt:@"vmr_action_status" value:1];

    [param addSection:@"vms_dispatch"];
    [param addInt:@"interactive_mode" value:interactiveMode];

    [param addSection:@"header"];
    [param addInt:@"status" value:status];
    
    AIKITInputData *input = [[AIKITInputData alloc] init];
    [input addText:text key:@"text" status:status];
    //动作
    if (actions.count == 0){
        [input addText:@"" key:@"ctrl_w" status:DataStatusOnce];
    }else{
        NSMutableArray *avatarArray = [NSMutableArray array];
        for (NSString *action in actions) {
            NSDictionary *dic = @{@"type":@"action",
                                  @"value":action,
                                  @"wb":@2,
                                  @"we":@10,
            };
            [avatarArray addObject:dic];
        }
        NSDictionary *avatarDic = @{@"avatar":avatarArray.copy};
        NSData *data = [NSJSONSerialization dataWithJSONObject:avatarDic options:NSJSONWritingPrettyPrinted             error:nil];
        [input addTextData:data key:@"ctrl_w" status:DataStatusOnce];
    }
    
    
    AIKITParameters *inputParam = [[AIKITParameters alloc] init];
    [inputParam addString:@"encoding" value:@"utf8"];
    [inputParam addString:@"compress" value:@"raw"];
    [inputParam addString:@"format" value:@"plain"];
    //后处理字幕相关
    if (!kStringIsEmpty(_template_id)){
        NSString *path = [ConfigManager.sharedInstance getSandBoxDefaultFilePath:ConfigJsonFileTypeWordLevel3];
        NSData *templateData = [[NSData alloc] initWithContentsOfFile:path];
        NSDictionary *json = [NSJSONSerialization JSONObjectWithData:templateData
                                                             options:kNilOptions
                                                               error:nil];
        TemplateModel *model = [TemplateModel yy_modelWithDictionary:json];
        if (!kStringIsEmpty(_res_id)){
            model.controls.firstObject.elements.firstObject.resource.res = _res_id;
        }
        NSString *jsonStr = [model yy_modelToJSONString];

        if(!kStringIsEmpty(jsonStr)){
            [input addText:jsonStr key:@"ctrl_postproc" status:DataStatusOnce];
        }
    }
    
    [input addControl:inputParam key:@"text"];
    [input addControl:inputParam key:@"ctrl_w"];
    [input addControl:inputParam key:@"ctrl_postproc"];
    
    int ret = [AiHelper write:input handle:self.handle param:param];
    if (ret != 0) {
        NSLog(@"vms write failed! ret is %ld text: %@", (long)ret,text);
    }

创建param

header

参数 类型 说明 必填
status int 数据状态 0:开始, 1:开始, 2:结束

tts

参数 类型 说明 必填
vcn string 合成发言人
rhy int 文本驱动传rhy有字幕效果

vms_dispatch

参数 类型 说明 必填
interactive_mode int 打断模式 [0,1] (0 追加模式 1 打断模式) 否,默认为1

创建input

参数 类型 说明 必填
text string 合成的文本数据
ctrl_w data 字偏移控制指令
ctrl_postproc string 后处理控制指令

control

参数 类型 说明 必填
text object 含有 encoding、compress、format等参数
encoding string 文本编码: utf8
compress string 文本压缩格式: raw
format string 文本格式: raw
ctrl_w object 含有 encoding、compress、format等参数,动作相关
ctrl_postproc object 含有 encoding、compress、format等参数,后处理相关

# 4.6 音频驱动

/// 会话模式输入数据
/// @param data AIKITInputData能力数据
/// @param handle 启动服务的handle对象
+ (int)write:(nullable AIKITInputData*)data handle:(nonnull AiHandle *)handle; 

代码示例:

int ret = 0;
//一次性音频文件
int leftAudioBytes = (int)audioData.length;
int byteWrites = 1280;
int writeLen   = 0;
int x_sb = 0;
while (leftAudioBytes > 0) {
    bool isLast = false;
    if (leftAudioBytes > byteWrites) {
        writeLen = byteWrites;
    } else {
        isLast   = true;
        writeLen = leftAudioBytes;
    }
    leftAudioBytes -= writeLen;
    
    int loc_dsb = x_sb*byteWrites;
    x_sb ++;
    
    @autoreleasepool {
        AIKITInputData *inputs = [[AIKITInputData alloc] init];
        NSData *part = [audioData subdataWithRange:NSMakeRange(loc_dsb, writeLen)];
        
        //传入音频数据
        if (loc_dsb == 0) {
            [inputs addAudio:part key:@"audio" status:DataStatusBegin];
        } else if (isLast) {
            [inputs addAudio:part key:@"audio" status:DataStatusEnd];
        } else {
            [inputs addAudio:part key:@"audio" status:DataStatusContinue];
        }
        //音频附属参数
        AIKITParameters *audioParam = [[AIKITParameters alloc] init];
        [audioParam addString:@"encoding" value:@"raw"];
        [audioParam addInt:@"sample_rate" value:16000];
        [audioParam addInt:@"seq" value:x_sb];
        
        [inputs addControl:audioParam key:@"audio"];
        ret = [AiHelper write:inputs handle:self.handle];
    }
    if (ret != 0) {
        NSLog(@"音频写入失败:%d",ret);
        return;
    }
    [NSThread sleepForTimeInterval:0.04]; //间隔时长
}

创建input

参数 类型 说明 必填
audio data 驱动音频
ctrl_t data 字偏移控制指令

control

参数 类型 说明 必填
audio object 含有 encoding、sample_rate、seq等参数
encoding string 编码格式: 默认值raw
sample_rate int 采样率: 16000
seq int 数据序号:标明数据为第几块
ctrl_t object 含有 encoding、sample_rate、seq等参数

# 4.7 重置接口

reset 接口用于中止当前正在进行中的所有驱动任务,回到虚拟形象静默状态

/** 
 * 中止进行中的驱动
 * @return 结果错误码,0=成功
 */
 int ret = [AiHelper reset:self.handle];

# 4.8 结束虚拟人服务

/** 
 * 结束会话实例
 * @return 结果错误码,0=成功
 */
 int ret = [AiHelper end:self.handle];

# 4.9 SDK逆初始化

/** 
 * SDK逆初始化函数用以释放SDK所占资源
 * @return 结果错误码,0=成功
 */
int ret = [ILibrary unInit];  

# 字偏移控制指令

ctrl_w

参数 类型 说明 必填
avatar [] 形象控制,单次输入含1个或多个控制项
type string 控制数据类型,支持动作控制 取值: action
value 控制数据取值 动作id 如:A_LH_introduced_O
wb int 字偏移起始位置,char begin 文本驱动需要;音频驱动不需要该字段
we char 字偏移终止位置,char end 动作终止位置(暂不支持)音频驱动时,不要传该字段

# 字幕

字幕相关的模板详细见demo,其中subtitle_portrait.json 竖屏字幕模板,subtitle_landscape.json 横屏字幕模板,demo中的字幕模板默认用的subtitle_portrait.json ,想看demo中横屏效果,修改config_new.json中的 宽高为1920 × 1080,字幕模板使用subtitle_landscape.json ,字幕模板上传后需重新启动虚拟人服务然后进行文本驱动

# 5. 附录

虚拟形象实时交互服务接口文档 (opens new window)

在线
咨询
建议
反馈
体验
中心