本文介绍数字人视频合成服务的接入流程和相关开发方案。
数字人视频合成服务(包括3D数字人视频合成和2D数字人视频合成)提供根据指定文本让数字人进行文本播报,平台会基于数字人播报的文本智能同步驱动数字人做出相应的嘴型、表情和动作,同时将渲染的数字人画面合成指定格式的视频文件。目前平台支持合成透明格式的视频和特定绿幕背景的非透明视频,更多信息可以参考下方的详细接入使用指南。
下面详细介绍下数字人视频合成服务的使用链路。
1. 完整的技术链路图
2. 核心链路介绍
2.1 提交数字人视频合成任务
目的:
提交数字人视频合成任务,获取到对应的任务ID。
核心流程:
通过调用虚拟数字人开放平台服务端SDK的提交视频合成任务API(目前平台服务端SDK支持java/python/php三种开发语言,具体接入方案可参考服务端API接入),获取到API返回的任务ID。
2.2 轮询数字人视频合成任务状态
目的:
根据上一步获取到的视频合成任务ID获取对应的任务状态。
核心流程:
通过调用虚拟数字人开放平台服务端SDK的查询视频合成任务状态API,获取对应视频合成任务的状态,由于数字人视频合成需要一定的时间,所以该接口需要定时轮询调用,建议轮询间隔3s,轮询过于频繁可能会导致查询失败。查询任务状态直到状态显示为已完成或者失败,状态为已完成的时候可以获取到对应的视频下载URL,然后直接通过URL可以下载到对应的视频,针对失败的任务可以根据对应的失败原因进行修改重新提交。
2.3 视频合成事件回调通知
目的:
通过接收视频合成事件回调,及时的了解视频合成任务是否完成。目前视频合成事件有:视频开始合成和视频合成结束。收到视频合成结束事件之后可以调用2.2的接口获取视频合成的结果信息。
核心流程:
参考文档:数字人回调事件通知。
3. 数字人视频合成完整调用示例代码
3.1 引入二方包
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>avatar20220130</artifactId>
<version>${使用最新版本}</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>${选择一个合适版本}</version>
</dependency>
3.2 示例代码
package com.alibaba.avatar.sample;
import java.text.SimpleDateFormat;
import java.util.Date;
import com.aliyun.avatar20220130.Client;
import com.aliyun.avatar20220130.models.GetVideoTaskInfoRequest;
import com.aliyun.avatar20220130.models.GetVideoTaskInfoRequest.GetVideoTaskInfoRequestApp;
import com.aliyun.avatar20220130.models.GetVideoTaskInfoResponse;
import com.aliyun.avatar20220130.models.GetVideoTaskInfoResponseBody;
import com.aliyun.avatar20220130.models.SubmitAudioTo3DAvatarVideoTaskRequest;
import com.aliyun.avatar20220130.models.SubmitAudioTo3DAvatarVideoTaskRequest.SubmitAudioTo3DAvatarVideoTaskRequestApp;
import com.aliyun.avatar20220130.models.SubmitAudioTo3DAvatarVideoTaskRequest.SubmitAudioTo3DAvatarVideoTaskRequestAvatarInfo;
import com.aliyun.avatar20220130.models.SubmitAudioTo3DAvatarVideoTaskRequest.SubmitAudioTo3DAvatarVideoTaskRequestVideoInfo;
import com.aliyun.avatar20220130.models.SubmitAudioTo3DAvatarVideoTaskResponse;
import com.aliyun.avatar20220130.models.SubmitAudioTo3DAvatarVideoTaskResponseBody;
import com.aliyun.avatar20220130.models.SubmitTextTo3DAvatarVideoTaskRequest;
import com.aliyun.avatar20220130.models.SubmitTextTo3DAvatarVideoTaskRequest.SubmitTextTo3DAvatarVideoTaskRequestApp;
import com.aliyun.avatar20220130.models.SubmitTextTo3DAvatarVideoTaskRequest.SubmitTextTo3DAvatarVideoTaskRequestAudioInfo;
import com.aliyun.avatar20220130.models.SubmitTextTo3DAvatarVideoTaskRequest.SubmitTextTo3DAvatarVideoTaskRequestAvatarInfo;
import com.aliyun.avatar20220130.models.SubmitTextTo3DAvatarVideoTaskRequest.SubmitTextTo3DAvatarVideoTaskRequestVideoInfo;
import com.aliyun.avatar20220130.models.SubmitTextTo3DAvatarVideoTaskResponse;
import com.aliyun.avatar20220130.models.SubmitTextTo3DAvatarVideoTaskResponseBody;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.models.RuntimeOptions;
/**
* <pre>
* 虚拟数字人开放平台视频合成服务接入示例代码
* </pre>
*
* @author avatar sample
* @date 2023/07/11
*/
public class AvatarOfflineSample {
private Client client;
/**
* 初始化
*
* @throws Exception
*/
public void init() throws Exception {
// 请确保代码运行环境设置了环境变量 ALIBABA_CLOUD_ACCESS_KEY_ID 和 ALIBABA_CLOUD_ACCESS_KEY_SECRET。
// 工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。以下代码示例使用环境变量获取 AccessKey 的方式进行调用,仅供参考,建议使用更安全的 STS 方式,更多鉴权访问方式请参见:https://help.aliyun.com/document_detail/378657.html
String accessKeyId = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_ID");
String accessKeySecret = System.getenv("ALIBABA_CLOUD_ACCESS_KEY_SECRET");
client = createClient(accessKeyId, accessKeySecret);
}
/**
* 使用AK&SK初始化账号Client
*
* @param accessKeyId
* @param accessKeySecret
* @return Client
* @throws Exception
*/
public Client createClient(String accessKeyId, String accessKeySecret) throws Exception {
Config config = new Config()
// 必填,您的 AccessKey ID
.setAccessKeyId(accessKeyId)
// 必填,您的 AccessKey Secret
.setAccessKeySecret(accessKeySecret);
// 访问的域名
config.endpoint = "avatar.cn-zhangjiakou.aliyuncs.com";
return new Client(config);
}
/**
* 提交一条文本合成任务,通过文本驱动数字人,合成对应的视频。详细参数介绍参考接入文档:https://help.aliyun.com/document_detail/447834.html
*
* @param tenantId
* @param appId
* @param avatarCode 数字人形象code
* @param title 视频标题
* @param text 视频文本内容
*/
public String submitTextTask(Long tenantId, String appId, String avatarCode, String title, String text) throws Exception {
// 设置appId信息
SubmitTextTo3DAvatarVideoTaskRequestApp app = new SubmitTextTo3DAvatarVideoTaskRequestApp();
app.setAppId(appId);
// 设置合成的视频参数
SubmitTextTo3DAvatarVideoTaskRequestVideoInfo videoInfo = new SubmitTextTo3DAvatarVideoTaskRequestVideoInfo();
// 视频分辨率
videoInfo.setResolution(4);
// 是否包含字幕文件,当前字幕文件是单独提供一份字幕的ass文件
videoInfo.setIsSubtitles(true);
// 视频背景图
videoInfo.setBackgroundImageUrl("https://nuwa-live-image.oss-cn-beijing.aliyuncs.com/yun-vh/image/video-system-bg/vcg_VCG211132322795_RF.png");
// 是否透明视频,透明视频可用于二次编辑,
videoInfo.setIsAlpha(false);
// 设置合成的音频参数,包括TTS发音人、音速、音调、音量信息
SubmitTextTo3DAvatarVideoTaskRequestAudioInfo audioInfo = new SubmitTextTo3DAvatarVideoTaskRequestAudioInfo();
// TTS发音人,可以从平台获取,参考文档: https://help.aliyun.com/document_detail/479093.html
audioInfo.setVoice("zhitian_emo");
// 音频音量
audioInfo.setVolume(50);
// 音频语速
audioInfo.setSpeechRate(0);
// 音频音调
audioInfo.setPitchRate(0);
// 设置数字人形象参数
SubmitTextTo3DAvatarVideoTaskRequestAvatarInfo avatarInfo = new SubmitTextTo3DAvatarVideoTaskRequestAvatarInfo();
// 数字人形象code,3D形象需要调用3D接口,2D形象需要调用2D接口,本示例代码调用的是3D接口,所以这里需要使用3D形象code
avatarInfo.setCode(avatarCode);
// 数字人机位
avatarInfo.setLocate(2);
// 数字人角度
avatarInfo.setAngle(0);
// 数字人动作行业
avatarInfo.setIndustryCode("live");
SubmitTextTo3DAvatarVideoTaskRequest request = new SubmitTextTo3DAvatarVideoTaskRequest();
request.setTenantId(tenantId);
request.setApp(app);
request.setTitle(title);
request.setText(text);
request.setVideoInfo(videoInfo);
request.setAudioInfo(audioInfo);
request.setAvatarInfo(avatarInfo);
// 是否接收平台视频合成事件回调,具体可参考文档: https://help.aliyun.com/document_detail/2261152.html
request.setCallback(false);
RuntimeOptions runtime = new RuntimeOptions();
SubmitTextTo3DAvatarVideoTaskResponse response = client.submitTextTo3DAvatarVideoTaskWithOptions(request, runtime);
SubmitTextTo3DAvatarVideoTaskResponseBody responseBody = response.getBody();
if (null != responseBody.getSuccess() && responseBody.getSuccess()) {
// 调用成功
System.out.println(formatCurrentTime() + ":提交成功,taskUuid:" + responseBody.getData().getTaskUuid());
return responseBody.getData().getTaskUuid();
} else {
// 调用失败
System.out.println("提交失败,原因:" + responseBody.getCode() + ":" + responseBody.getMessage());
throw new Exception("提交失败");
}
}
/**
* 提交一条音频合成任务,通过音频驱动数字人,合成对应的视频。详细参数介绍参考接入文档:https://help.aliyun.com/document_detail/447834.html
*
* @param tenantId
* @param appId
* @param avatarCode 数字人形象code
* @param title 视频标题
* @param audioUrl 音频url
*/
public String submitAudioTask(Long tenantId, String appId, String avatarCode, String title, String audioUrl) throws Exception {
// 设置appId信息
SubmitAudioTo3DAvatarVideoTaskRequestApp app = new SubmitAudioTo3DAvatarVideoTaskRequestApp();
app.setAppId(appId);
// 设置合成的视频参数
SubmitAudioTo3DAvatarVideoTaskRequestVideoInfo videoInfo = new SubmitAudioTo3DAvatarVideoTaskRequestVideoInfo();
// 视频分辨率
videoInfo.setResolution(4);
// 视频背景图
videoInfo.setBackgroundImageUrl("https://nuwa-live-image.oss-cn-beijing.aliyuncs.com/yun-vh/image/video-system-bg/vcg_VCG211132322795_RF.png");
// 是否透明视频,透明视频可用于二次编辑,
videoInfo.setIsAlpha(false);
// 设置数字人形象参数
SubmitAudioTo3DAvatarVideoTaskRequestAvatarInfo avatarInfo = new SubmitAudioTo3DAvatarVideoTaskRequestAvatarInfo();
// 数字人形象code,D形象需要调用3D接口,2D形象需要调用2D接口,本示例代码调用的是3D接口,所以这里需要使用3D形象code
avatarInfo.setCode(avatarCode);
// 数字人机位
avatarInfo.setLocate(2);
// 数字人角度
avatarInfo.setAngle(0);
// 数字人动作行业
avatarInfo.setIndustryCode("live");
SubmitAudioTo3DAvatarVideoTaskRequest request = new SubmitAudioTo3DAvatarVideoTaskRequest();
request.setTenantId(tenantId);
request.setApp(app);
request.setTitle(title);
request.setUrl(audioUrl);
request.setVideoInfo(videoInfo);
request.setAvatarInfo(avatarInfo);
// 是否接收平台视频合成事件回调,具体可参考文档: https://help.aliyun.com/document_detail/2261152.html
request.setCallback(false);
RuntimeOptions runtime = new RuntimeOptions();
SubmitAudioTo3DAvatarVideoTaskResponse response = client.submitAudioTo3DAvatarVideoTaskWithOptions(request, runtime);
SubmitAudioTo3DAvatarVideoTaskResponseBody responseBody = response.getBody();
if (null != responseBody.getSuccess() && responseBody.getSuccess()) {
// 调用成功
System.out.println(formatCurrentTime() + ":提交成功,taskUuid:" + responseBody.getData().getTaskUuid());
return responseBody.getData().getTaskUuid();
} else {
// 调用失败
System.out.println("提交失败,原因:" + responseBody.getCode() + ":" + responseBody.getMessage());
throw new Exception("提交失败");
}
}
/**
* 查询任务状态
*
* @param tenantId
* @param appId
* @param taskUuid
*/
public boolean queryTaskInfo(Long tenantId, String appId, String taskUuid) throws Exception {
// 设置appId信息
GetVideoTaskInfoRequestApp app = new GetVideoTaskInfoRequestApp();
app.setAppId(appId);
GetVideoTaskInfoRequest request = new GetVideoTaskInfoRequest();
request.setTenantId(tenantId);
request.setApp(app);
request.setTaskUuid(taskUuid);
RuntimeOptions runtime = new RuntimeOptions();
GetVideoTaskInfoResponse response = client.getVideoTaskInfoWithOptions(request, runtime);
GetVideoTaskInfoResponseBody responseBody = response.getBody();
if (null != responseBody.getSuccess() && responseBody.getSuccess()) {
// 调用成功
switch (responseBody.getData().getStatus()) {
case "1":
System.out.println(formatCurrentTime() + ":查询成功,当前任务状态:等待执行...");
return false;
case "2":
System.out.println(formatCurrentTime() + ":查询成功,当前任务状态:执行中...");
return false;
case "3":
System.out.println(formatCurrentTime() + ":查询成功,当前任务状态:执行成功,视频URL:" + responseBody.getData().getTaskResult().getVideoUrl());
return true;
case "4":
System.out.println(formatCurrentTime() + ":查询成功,当前任务状态:执行失败,失败原因:" + responseBody.getData().getTaskResult().getFailReason());
return true;
case "5":
System.out.println(formatCurrentTime() + ":查询成功,当前任务状态:已取消");
return true;
case "6":
System.out.println(formatCurrentTime() + ":查询成功,当前任务状态:已过期");
return true;
default:
System.out.println(formatCurrentTime() + ":查询成功,当前任务状态:未知");
return true;
}
} else {
// 调用失败
System.out.println("查询失败,原因:" + responseBody.getCode() + ":" + responseBody.getMessage());
throw new Exception("查询失败");
}
}
public static void main(String[] args) throws Exception {
// 开发者信息定义,获取方式参考文档:https://help.aliyun.com/document_detail/479093.html
Long tenantId = null;
String appId = null;
// 数字人形象code,需要从开放平台获取,参考文档: https://help.aliyun.com/document_detail/479093.html
// 本示例代码中使用到的一些动作code是根据daisy官方形象配置的,这里建议使用daisy官方形象code
String avatarCode = null;
int maxWait = 100;
int waitInterval = 3000;
AvatarOfflineSample sample = new AvatarOfflineSample();
// 1. 初始化
sample.init();
// 2. 提交一个普通文本合成视频任务
// 2.1 提交任务
String title = "这是一个测试文本合成3D视频任务标题";
String text = "当前时间是: " + formatCurrentTime() + ",这是一个测试文本合成3D视频任务内容,我是阿里云虚拟数字人官方数字人形象。";
String taskUuid = sample.submitTextTask(tenantId, appId, avatarCode, title, text);
// 2.2 轮询任务状态,建议通过接入平台视频合成事件回调监听合成完成状态,参考文档: https://help.aliyun.com/document_detail/2261152.html
for (int i = 0; i < maxWait; i++) {
boolean isOver = sample.queryTaskInfo(tenantId, appId, taskUuid);
if (isOver) {
break;
}
Thread.sleep(waitInterval);
}
// 3. 提交一个自定义数字人动作的文本合成视频任务(2D暂不支持自定义数字人动作)
// 3.1 提交任务
String title2 = "这是一个测试自定义数字人动作的文本合成3D视频任务标题";
String text2 = "<speak>当前时间是<vh-action code=\"animation_4953\" interrupt=\"true\" />: " + formatCurrentTime() +
",这是一个测试自定义数字人动作的文本合成3D视频任务内容,大家好<vh-action code=\"animation_4960\" interrupt=\"true\" />,我是阿里云虚拟数字人官方数字人形象。</speak>";
String taskUuid2 = sample.submitTextTask(tenantId, appId, avatarCode, title2, text2);
// 3.2 轮询任务状态,建议通过接入平台视频合成事件回调监听合成完成状态,参考文档: https://help.aliyun.com/document_detail/2261152.html
for (int i = 0; i < maxWait; i++) {
boolean isOver = sample.queryTaskInfo(tenantId, appId, taskUuid);
if (isOver) {
break;
}
Thread.sleep(waitInterval);
}
// 4. 提交一个音频合成视频任务
// 4.1 提交任务
String title3 = "这是一个测试音频合成3D视频任务标题";
String url = "https://virtualhuman.oss-cn-beijing.aliyuncs.com/platform/public/test_audio.mp3";
String taskUuid3 = sample.submitAudioTask(tenantId, appId, avatarCode, title3, url);
// 4.2 轮询任务状态,建议通过接入平台视频合成事件回调监听合成完成状态,参考文档: https://help.aliyun.com/document_detail/2261152.html
for (int i = 0; i < maxWait; i++) {
boolean isOver = sample.queryTaskInfo(tenantId, appId, taskUuid);
if (isOver) {
break;
}
Thread.sleep(waitInterval);
}
}
private static String formatCurrentTime() {
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss,SSS");
return format.format(new Date());
}
}
以上就是一个完整的数字人视频合成服务的使用链路,关于数字人视频合成服务更多信息可参考下方详细的使用指南:
文档内容是否对您有帮助?