制作一个分身形象的完整流程如下:
step1:上传制作形象所需的视频素材
step2:视频素材上传完成后,调用触发形象制作接口,开始形象制作
step3:调用分身形象制作信息查询接口,查询形象制作状态
说明:
1、完整上传一个视频调用接口流程如下:创建分片上传任务--> 顺序上传文件分片 --> 完成文件分片上传
2、conf_name字段是形象的唯一标识,由客户自己定义,后续使用sta等服务时,需要传递该值来表明调用哪个形象 格式要求:允许大小写字母数字以及'-'
3、input_resource字段为必传字段,用于表明数据来源,建议填写公司名称
分身形象制作需要提供如下视频素材:
针对在控制台下单的数字人资产,在调用训练接口时:
path:/init_part_upload
参数:json
{ "conf_name": "LiuXuan", //string 必传 用户上传形象调用conf_name,格式要求:允许大小写字母数字以及'-' "role_show_name": "刘璇", //string 必传 用户上传形象展示中文名 "type": 1, //int 必传 视频类型1:训练视频 2:模版视频 3:审核视频 "input_resource": "", //string 必传 视频上传来源,建议填写公司名称 "appid": "xxxx", //string 必传 appid "file_suffix": "mp4" //string 必传 视频格式尾缀 mp4或mov }
Headers:
headers = {'Content-Type': 'application/json', 'Authorization': token}
返回:
{ "errno": 0, "errMsg": "success", "data": { "upload_id": "48501f0206918d1b5db63714" // 返回upload_id,用于标识此次分片上传任务 } }
说明:建议分片大小为10M,最后一片不足5M的与前一分片合并上传
path:/video_part_upload
参数:json
{ "conf_name": conf_name, // string 必传 用户上传形象调用conf_name,格式要求:允许大小写字母数字以及'-' "type": video_type, //int 必传 视频类型1:训练视频 2:模版视频 3:审核视频 "upload_id": upload_id, //string 必传 init_part_upload接口返回的upload_id "file_suffix": "mp4", //string 必传 视频格式尾缀 mp4或mov "part_num": 1, //int 必传 分片顺序:1,2,3,4... 要求第一个分片必须从1开始 "content": "base64", //string 必传 分片内容,base64字符串 "appid": "xxxx", //string 必传 appid }
Headers:
headers = {'Content-Type': 'application/json', 'Authorization': token}
返回:
{ "errno": 0, "errMsg": "success", "data": "video part upload success" }
path:/video_complete_upload
参数:json
{ "conf_name": conf_name, // string 必传 用户上传形象调用conf_name,格式要求:允许大小写字母数字以及'-' "type": video_type, //int 必传 视频类型1:训练视频 2:模版视频 3:审核视频 "upload_id": upload_id, //string 必传 init_part_upload接口返回的upload_id "file_suffix": "mp4", //string 必传 视频格式尾缀 mp4或mov "appid": "xxxx", //string 必传 appid }
Headers:
headers = {'Content-Type': 'application/json', 'Authorization': token}
返回:
{ "errno": 0, "errMsg": "success", "data": "finish video part upload successs" }
说明:触发形象训练
path:/init_train_digime
参数:json
{ "appid": "xxx", //string 必传 appid "conf_name": "xxx", //string 必传 用户上传形象调用conf_name,格式要求:允许大小写字母数字以及'-' "callback_url": "http://xxx.net/xx" // 回调地址,POST,用于接收制作状态回调 }
Headers:
headers = {'Content-Type': 'application/json', 'Authorization': token}
返回:
{ "errno": 0, "errMsg": "success", "data": "init train digime succ" }
制作状态回调格式如下:
传递的URL地址需支持POST method
{ "conf_name": "xxx", //string 形象调用conf_name "role_pic": "https://xxx.png", //形象图 "pre_pic": "https://xxx.png", //前景图 "state": 2 // 2:制作成功,3:制作失败 }
说明:通过conf_name查询分身形象制作状态信息
path:/digime_training_info
参数:json
{ "appid": "xxx", //string appid "conf_name": "xxx" //string 用户上传形象调用conf_name,格式要求:允许大小写字母数字以及'-' }
Headers:
headers = {'Content-Type': 'application/json', 'Authorization': token}
返回:
{ "errno": 0, "errMsg": "success", "data": { "conf_name": "xxx", //string 形象调用conf_name "role_pic": "https://xxx.png", //形象图 "pre_pic": "https://xxx.png", //前景图 "state": 2 //制作状态 1:制作中,2:制作成功,3:制作失败 } }
正式环境:https://openspeech.bytedance.com/virtual_human/avatar_platform/digime
# script/init.py # coding=utf-8 ##################### import json import requests import os import base64 UPLOAD_URL = "https://openspeech.bytedance.com/virtual_human/avatar_platform/digime" def check_res(result): if result.status_code != 200: raise Exception(f"request code is not {result.status_code}") # print(result.text) res = result.json() if res.get("errMsg") != "success" or res.get("errno") != 0: msg = res.get("errMsg") raise Exception(f"request err, msg: {msg}") return res def post(url, data, token): """封装post方法""" headers = {'Content-Type': 'application/json', 'Authorization': token} result = requests.post(url=url, headers=headers, data=json.dumps(data)) return check_res(result) # def post_form(url, filepath, filekey, **kwargs): # """封装post方法 body是format data形式""" # data = kwargs.get("data") # fl = open(filepath, 'rb') # files = { # filekey: (filepath, fl) # } # result = requests.post(url, files=files, data=data) # print(check_res(result)) """ 按块读取 """ def read_file_by_chunk(file, chunk_size=1024 * 1024 * 10): with open(file, mode='rb') as f: while True: chunk = f.read(chunk_size) if not chunk: return yield chunk def part_upload_file(file_path, conf_name, role_show_name, video_type, input_resource, app_id, token): init_data = { "conf_name": conf_name, "type": video_type, "role_show_name": role_show_name, "input_resource": input_resource, "appid": app_id, "file_suffix": file_path[-3:], } ret = post(UPLOAD_URL + "/init_part_upload", init_data, token) upload_id = ret["data"] print("upload_id is:", upload_id) upload_data = { "conf_name": conf_name, "type": video_type, "upload_id": upload_id, "appid": app_id, "file_suffix": file_path[-3:], } part_num = 1 # 暂存前一个分片 tmp_content_str = "" tmp_bytes = bytes() final_deal = True chunks = read_file_by_chunk(file_path) for chunk in chunks: # print("chunk size is:", len(bytes(chunk))) if part_num == 1: content = base64.b64encode(bytes(chunk)) tmp_content_str = content.decode() tmp_bytes = bytes(chunk) part_num += 1 continue if len(bytes(chunk)) >= 5242880: # 当前分片长度大于等于5m,上传前一个分片 upload_data["part_num"] = part_num - 1 upload_data["content"] = tmp_content_str print("upload part:", part_num - 1) ret = post(UPLOAD_URL + "/video_part_upload", upload_data, token) content = base64.b64encode(bytes(chunk)) tmp_content_str = content.decode() tmp_bytes = bytes(chunk) else: # print("deal with last partiton merged") # 当前分片长度小于5m,与前一个分片合并上传 # print("tmp_bytes size is:%d, chunk size is:%d", len(tmp_bytes), len(bytes(chunk))) content = base64.b64encode(tmp_bytes + bytes(chunk)) content_str = content.decode() upload_data["part_num"] = part_num - 1 upload_data["content"] = content_str print("upload part:", part_num - 1) ret = post(UPLOAD_URL + "/video_part_upload", upload_data, token) final_deal = False part_num += 1 if final_deal: # print("deal with last partiton") upload_data["part_num"] = part_num - 1 upload_data["content"] = tmp_content_str print("upload part:", part_num - 1) ret = post(UPLOAD_URL + "/video_part_upload", upload_data, token) complete_data = { "conf_name": conf_name, "type": video_type, "upload_id": upload_id, "appid": app_id, "file_suffix": file_path[-3:] } ret = post(UPLOAD_URL + "/video_complete_upload", complete_data, token) if __name__ == "__main__": print("########begin part upload############") part_upload_file("2.mp4", "conf_name", "role_show_name", 1, "input_resource", "appid", "token")
制作一个音色的完整流程如下:
step1:上传制作音色所需音频素材压缩包(多条音频文件放到一个文件夹下压缩),格式要求zip或者tar
说明:
1、完整上传一个音频素材压缩包调用接口流程如下:创建分片上传任务--> 顺序上传文件分片 --> 完成文件分片上传
2、input_resource字段为必传字段,用于表明数据来源,建议填写公司名称
path:/init_part_upload
参数:json
{ "conf_name": "", //string 可选 音色对应分身形象conf_name "type": 11, //int 必传 数据类型 11:音频压缩包数据 "input_resource": "", //string 必传 视频上传来源,建议填写公司名称 "appid": "xxxx", //string 必传 appid "file_suffix": "zip", //string 必传 压缩格式尾缀 zip或tar "tts_type": "xxx" //tts_model_v1 30min复刻 tts_model_v2 10min复刻 }
Headers:
headers = {'Content-Type': 'application/json', 'Authorization': token}
返回:
{ "errno": 0, "errMsg": "success", "data": { "upload_id": "48501f0206918d1b5db63714" // 返回upload_id,用于标识此次分片上传任务 } }
说明:建议分片大小为10M,最后一片不足5M的与前一分片合并上传
path:/video_part_upload
参数:json
{ "type": 11, //int 必传 数据类型 11:音频压缩包数据 "upload_id": upload_id, //string 必传 init_part_upload接口返回的upload_id "file_suffix": "zip" //string 必传 压缩格式尾缀 zip或tar "part_num": 1, //int 必传 分片顺序:1,2,3,4... 要求第一个分片必须从1开始 "content": "base64", //string 必传 分片内容,base64字符串 "appid": "xxxx", //string 必传 appid }
Headers:
headers = {'Content-Type': 'application/json', 'Authorization': token}
返回:
{ "errno": 0, "errMsg": "success", "data": "video part upload success" }
path:/video_complete_upload
参数:json
{ "type": 11, //int 必传 数据类型 11:音频压缩包数据 "upload_id": upload_id, //string 必传 init_part_upload接口返回的upload_id "file_suffix": "zip" //string 必传 压缩格式尾缀 zip或tar "appid": "xxxx", //string 必传 appid }
Headers:
headers = {'Content-Type': 'application/json', 'Authorization': token}
返回:
{ "errno": 0, "errMsg": "success", "data": "finish video part upload successs" }