视频字幕功能整体处理流程分为三个阶段:
设置鉴权内容,请参考鉴权方法。
请求地址:https://openspeech.bytedance.com/api/v1/vc/submit
请求方式:HTTP POST
字段 | 说明 | 是否必填 | 备注 |
---|---|---|---|
appid | 应用标识 | ✓ | 用于标识当前应用。 |
words_per_line | 每行最多展示字数 | 默认值 46 。 | |
max_lines | 每屏最多展示行数 | 默认 1 行。 | |
use_itn | 是否使用数字转换功能 | 默认关闭(False)。 如果设置为开启(True),会将识别结果中的中文数字自动转成阿拉伯数字。 | |
language | 字幕语言类型 | 见支持语种 | |
caption_type | 字幕识别类型 | 默认值为auto(同时识别说话和唱歌部分) 。 可以选择speech(只识别说话部分), 可以选择singing(只识别唱歌部分)。 | |
use_punc | 增加标点 | 默认False, 如果设置为True,则会将识别结果中增加标点符号。 当且仅当(caption_type=speech的时候生效) | |
use_ddc | 使用顺滑标注水词 | 默认 False,如果设置为 True,则会在返回的 utterances 里增加 text 为空的静音句子,其 attribute 的 event 是 silent。且 words 中可能需要被顺滑的词会被标注出来,如"extra": { "smoothed": "repeat" },smoothed 的值可能为 repeat(重复词)或 filler(口水词)。 | |
boosting_table_id | 自学习平台热词 ID | id 与 name 二选一,只需要提供其中一个即可。同时需要传 asr_appid(与 appid 值一样)。 | |
boosting_table_name | 自学习平台热词的文件名称 | ||
asr_appid | 传给 ASR 的 APPID | 使用自学习平台热词时必填,与 appid 值一致即可。 | |
with_speaker_info | 返回说话人信息 | 默认 False,如果设置为 True,则会在 utterance 和 workd 的 attribute 中增加 speaker 信息如"attribute": {"speaker": "1"} |
语音字幕
序号 | 语言 | Language Code | 分句长度推荐值 |
---|---|---|---|
1 | 中文普通话(简体) | zh-CN | 15 |
粤语 | yue | 15 | |
吴语-上海话 | wuu | 15 | |
闽南语 | nan | 15 | |
西南官话 | xghu | 15 | |
中原官话 | zgyu | 15 | |
2 | 维语 | ug | 55 |
3 | 英语(美国) | en-US | 55 |
4 | 日语 | ja-JP | 32 |
5 | 韩语 | ko-KR | 32 |
6 | 西班牙语 | es-MX | 55 |
7 | 俄语 | ru-RU | 55 |
8 | 法语 | fr-FR | 55 |
歌词字幕
序号 | 语言 | Language Code | 分句长度推荐值 |
---|---|---|---|
1 | 中文普通话(简体) | zh-CN | 15 |
2 | 英语(美国) | en-US | 55 |
Header 需要加入内容类型标识(匹配 audio/* 都可以):
Content-Type: audio/wav
Body 直接传输音频二进制数据
请求示例:
POST /api/v1/vc/submit?appid=test&words_per_line=20&max_lines=2 HTTP/1.1 Host: openspeech.bytedance.com Content-Type: audio/wav Connection: keep-alive Content-Length: xxxxx [[wav binary data]]
Header 需要加入内容类型标识:
Content-Type: application/json
字段 | 说明 | 是否必填 | 备注 |
---|---|---|---|
url | 音频地址 | ✓ | 用于标识当前应用。 |
请求示例:
POST /api/v1/vc/submit?appid=test&token=tmp_token&words_per_line=20&max_lines=2 HTTP/1.1 Host: openspeech.bytedance.com Content-Type: application/json Connection: keep-alive Content-Length: xxxxx {"url":"http://xxx.xxx.com/obj/video-caption/phone.mp3"}
应答格式: JSON
应答字段:
字段 | 说明 | 层级 | 格式 | 是否必填 | 备注 |
---|---|---|---|---|---|
code | 状态码 | 1 | int | ✓ | 0 为成功,非 0 为失败。 |
message | 状态信息 | 1 | string | ✓ | 失败时标记失败原因。 |
id | 任务 ID | 1 | string | 仅当提交成功时填写。 |
应答示例:
{ "code": "0", "message": "Success", "id": "fc5aa03e-6ae4-46a3-b8cf-1910a44e0d8a" }
请求地址:https://openspeech.bytedance.com/api/v1/vc/query
请求方式:HTTP GET
字段 | 说明 | 是否必填 | 备注 |
---|---|---|---|
appid | 应用标识 | ✓ | 用于标识当前应用。 |
id | 任务 ID | ✓ | 这里填写的是submit接口返回的id。 |
blocking | 查询结果时是否阻塞 | 0表示非阻塞,1表示阻塞(默认是阻塞模式)。 |
请求示例:
GET /api/v1/vc/query?appid=lv&id=fc5aa03e-6ae4-46a3-b8cf-1910a44e0d8a HTTP/1.1 Host: openspeech.bytedance.com
应答格式:JSON
应答字段:
字段 | 说明 | 层级 | 格式 | 是否必填 | 备注 |
---|---|---|---|---|---|
id | 任务 ID | 1 | string | ✓ | |
code | 状态码 | 1 | int | ✓ | 0 为成功,非 0 为失败。详情请参考错误码 。 |
message | 状态信息 | 1 | string | ✓ | |
utterances | 分句结果 | 1 | list | 仅当成功时填写。 | |
start_time | 起始时间 | 2 | int | 距离音频开始的毫秒偏移值。 | |
end_time | 结束时间 | 2 | int | 距离音频开始的毫秒偏移值。 | |
text | 文本 | 2 | string | ||
words | 词粒度信息 | 2 | list |
应答示例:
{ "id": "d22cca84-8c8a-4d15-aa2c-ac550518d5ae", "code": 0, "message": "Success", "duration": 5.3174375, "utterances": [ { "text": "如果您没有其他需要举报的话这边就先挂断了", "start_time": 0, "end_time": 3197, "words": [ { "text": "如", "start_time": 0, "end_time": 208 }, { "text": "果", "start_time": 208, "end_time": 317 }, { "text": "您", "start_time": 322, "end_time": 460 }, { "text": "没", "start_time": 460, "end_time": 580 }, { "text": "有", "start_time": 580, "end_time": 717 }, { "text": "其", "start_time": 722, "end_time": 877 }, { "text": "他", "start_time": 882, "end_time": 1037 }, { "text": "需", "start_time": 1042, "end_time": 1180 }, { "text": "要", "start_time": 1180, "end_time": 1317 }, { "text": "举", "start_time": 1322, "end_time": 1477 }, { "text": "报", "start_time": 1482, "end_time": 1637 }, { "text": "的", "start_time": 1642, "end_time": 1780 }, { "text": "话", "start_time": 1780, "end_time": 1917 }, { "text": "这", "start_time": 2042, "end_time": 2180 }, { "text": "边", "start_time": 2180, "end_time": 2317 }, { "text": "就", "start_time": 2322, "end_time": 2460 }, { "text": "先", "start_time": 2460, "end_time": 2597 }, { "text": "挂", "start_time": 2602, "end_time": 2757 }, { "text": "断", "start_time": 2802, "end_time": 2957 }, { "text": "了", "start_time": 3042, "end_time": 3197 } ] }, { "text": "祝您生活愉快再见", "start_time": 3442, "end_time": 4877, "words": [ { "text": "祝", "start_time": 3442, "end_time": 3580 }, { "text": "您", "start_time": 3580, "end_time": 3717 }, { "text": "生", "start_time": 3722, "end_time": 3877 }, { "text": "活", "start_time": 3882, "end_time": 4020 }, { "text": "愉", "start_time": 4020, "end_time": 4157 }, { "text": "快", "start_time": 4162, "end_time": 4317 }, { "text": "再", "start_time": 4562, "end_time": 4717 }, { "text": "见", "start_time": 4722, "end_time": 4877 } ] } ] }
请求地址:https://openspeech.bytedance.com/api/v1/vc/feedback
请求方式:HTTP POST
Header 需要加入内容类型标识:
Content-Type: application/json
Url 参数:
字段 | 说明 | 是否必填 | 备注 |
---|---|---|---|
appid | 应用标识 | ✓ | 用于标识当前应用。 |
id | 任务 ID | ✓ |
Body 参数:
字段 | 说明 | 层级 | 格式 | 是否必填 | 备注 |
---|---|---|---|---|---|
video_url | 视频URL | 1 | string | 视频链接。 | |
utterances | 分句结果 | 1 | list | ✓ | |
start_time | 起始时间 | 2 | int | ✓ | |
end_time | 结束时间 | 2 | int | ✓ | |
text | 文本 | 2 | string | ✓ |
请求示例:
POST /api/v1/vc/feedback?appid=lv&id=fc5aa03e-6ae4-46a3-b8cf-1910a44e0d8a HTTP/1.1 Host: openspeech.bytedance.com Content-Type: application/json Connection: keep-alive Content-Length: xxxxx {"utterances":[{"text":"这里是字节跳动","start_time":1500,"end_time":3000},{"text":"今日头条母公司","start_time":4000,"end_time":6350}]}
应答格式: JSON
应答字段:
字段 | 说明 | 层级 | 格式 | 是否必填 | 备注 |
---|---|---|---|---|---|
code | 状态码 | 1 | int | ✓ | 0 为成功,非 0 为失败,详情请参考错误码 。 |
message | 状态信息 | 1 | string | ✓ | 失败时标记失败原因。 |
id | 任务 ID | 1 | string | 仅当提交成功时填写。 |
应答示例:
{ "code": "0", "message": "Success", "id": "fc5aa03e-6ae4-46a3-b8cf-1910a44e0d8a" }
类别/状态号 | 含义 | 说明 |
---|---|---|
0 | 成功 | |
2000 | 正在处理 | 任务处理中。 |
请求类 | ||
1001 | 请求参数无效 | 请求参数缺失必需字段 / 字段值无效 / 重复请求。 |
1002 | 无访问权限 | token 无效 / 过期 / 无权访问指定服务。 |
1003 | 访问超频 | 当前 appid 访问 QPS 超出设定阈值。 |
1004 | 访问超额 | 当前 appid 访问次数超出限制。 |
1005 | 服务器繁忙 | 服务过载,无法处理当前请求。 |
1008 - 1009 | 保留号段 | 待定。 |
音频类 | ||
1010 | 音频过长 | 音频数据时长超出阈值。 |
1011 | 音频过大 | 音频数据大小超出阈值。 |
1012 | 音频格式无效 | 音频 header 有误 / 无法进行音频解码。 |
1013 | 音频静音 | 音频未识别出任何文本结果。 |
1014 - 1019 | 保留号段 | 待定。 |
识别类 | ||
1020 | 识别等待超时 | 识别等待过程超时。 |
1021 | 识别处理超时 | 识别处理过程超时。 |
1022 | 识别错误 | 识别过程中发生错误。 |
1023 - 1029 | 保留号段 | 待定。 |
1099 | 未知错误 | 未归类错误。 |
以下 demo 均使用 token 鉴权方式。
submit
curl -X POST -H 'Accept: */*' -H 'Authorization: Bearer; ${your_token}' -H 'Connection: keep-alive' -H 'User-Agent: python-requests/2.22.0' -H 'content-type: application/json' -d '{"url": "${your_url}"}' 'https://openspeech.bytedance.com/api/v1/vc/submit?appid=${your_appid}&language=zh-CN&use_itn=True&use_capitalize=True&max_lines=1&words_per_line=15'
query
curl -X GET -H 'Accept: */*' -H 'Authorization: Bearer; ${your_token}' -H 'Connection: keep-alive' -H 'User-Agent: python-requests/2.22.0' 'https://openspeech.bytedance.com/api/v1/vc/query?appid=en0yefmkvssp&id=fbf5edcf-378e-4f54-8a7f-5d8fd5e5e2f1'
#include <bits/stdc++.h> #include <curl/curl.h> #include <nlohmann/json.hpp> using namespace std; const char QUERY_URL[] = "https://openspeech.bytedance.com/api/v1/vc/query?appid=%s&id=%s"; const char SUBMIT_URL[] = "https://openspeech.bytedance.com/api/v1/vc/submit?appid=%s&language=%s&use_itn=True&use_capitalize=True&max_lines=1&words_per_line=15"; const char APPID[] = ""; const char TOKEN[] = ""; const char LANGUAGE[] = "zh-CN"; const char AUTH_HEADER[] = "Authorization: Bearer; %s"; const char FILE_URL[] = "https://www.iflyrec.com/AudioStreamService/v1/outLinks/2a7e5980004145ea8a01fb73e88b4ddd?action=play"; size_t write_data(void *buffer, size_t size, size_t nmemb, void *userp){ char *d = (char*)buffer; string *b = (string*)(userp); int result = 0; if (b != NULL){ b->append(d, size*nmemb); result = size*nmemb; } return result; } std::string submit() { CURL *curl; CURLcode res; curl = curl_easy_init(); char target_url[2000] = ""; char auth_header[2000] = ""; sprintf(target_url, SUBMIT_URL, APPID, LANGUAGE); sprintf(auth_header, AUTH_HEADER, TOKEN); std::string result; if(curl) { curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); curl_easy_setopt(curl, CURLOPT_URL, target_url); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https"); struct curl_slist *headers = NULL; headers = curl_slist_append(headers, "Accept: */*"); headers = curl_slist_append(headers, auth_header); headers = curl_slist_append(headers, "Connection: keep-alive"); headers = curl_slist_append(headers, "content-type: application/json"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); char data[2000] = ""; sprintf(data, "{\"url\": \"%s\"}", FILE_URL); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &write_data); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &result); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10); res = curl_easy_perform(curl); } curl_easy_cleanup(curl); std::cout << result << std::endl; auto job_id = nlohmann::json::parse(result)["id"]; return job_id; } void query(const char* job_id) { CURL *curl; CURLcode res; curl = curl_easy_init(); char target_url[2000] = ""; char auth_header[2000] = ""; sprintf(target_url, QUERY_URL, APPID, job_id); sprintf(auth_header, AUTH_HEADER, TOKEN); if(curl) { curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); curl_easy_setopt(curl, CURLOPT_URL, target_url); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https"); struct curl_slist *headers = NULL; headers = curl_slist_append(headers, "Accept: */*"); headers = curl_slist_append(headers, auth_header); headers = curl_slist_append(headers, "Connection: keep-alive"); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); res = curl_easy_perform(curl); } curl_easy_cleanup(curl); } void work() { std::string job_id = submit(); query(job_id.c_str()); } int main(int argc, char *argv[]) { work(); return 0; }
package com.bytedance.myapp; import com.alibaba.fastjson.JSON; import okhttp3.*; import java.io.IOException; import java.util.HashMap; import java.util.Map; public class myapp { static final String SUBMIT_URL = "https://openspeech.bytedance.com/api/v1/vc/submit?appid=%s&language=%s&use_itn=True&use_capitalize=True&max_lines=1&words_per_line=15"; static final String QUERY_URL = "https://openspeech.bytedance.com/api/v1/vc/query?appid=%s&id=%s"; static final String LANGUAGE = "zh-CN"; static final String APPID = "${your_appid}"; static final String TOKEN = "${your_token}"; public static void main(String[] args) throws IOException { String job_id = submit(); query(job_id); } public static void query(String job_id) throws IOException { OkHttpClient client = new OkHttpClient().newBuilder().build(); String url = String.format(QUERY_URL, APPID, job_id); System.out.println(url); Request request = new Request.Builder() .url(url) .method("GET", null) .addHeader("Accept", "*/*") .addHeader("Authorization", String.format("Bearer; %s", TOKEN)) .addHeader("Connection", "keep-alive") .build(); Response response = client.newCall(request).execute(); if (response.code() != 200) { throw new IOException(response.toString()); } System.out.println(response.body().string()); } public static String submit() throws IOException { OkHttpClient client = new OkHttpClient().newBuilder().build(); MediaType mediaType = MediaType.parse("application/json"); RequestBody body = RequestBody.create(mediaType, "{\"url\": \"https://www.iflyrec.com/AudioStreamService/v1/outLinks/2a7e5980004145ea8a01fb73e88b4ddd?action=play\"}"); Request request = new Request.Builder() .url(String.format(SUBMIT_URL, APPID, LANGUAGE)) .method("POST", body) .addHeader("Accept", "*/*") .addHeader("Authorization", String.format("Bearer; %s", TOKEN)) .addHeader("Connection", "keep-alive") .addHeader("content-type", "application/json") .build(); Response response = client.newCall(request).execute(); Map resp = JSON.parseObject(response.body().byteStream(), HashMap.class); System.out.println(resp.toString()); return resp.get("id").toString(); } }
import time import requests base_url = 'https://openspeech.bytedance.com/api/v1/vc' appid = "" access_token = "" language = 'zh-CN' file_url = '' def log_time(func): def wrapper(*args, **kw): begin_time = time.time() func(*args, **kw) print('total cost time = {time}'.format(time=time.time() - begin_time)) return wrapper @log_time def main(): response = requests.post( '{base_url}/submit'.format(base_url=base_url), params=dict( appid=appid, language=language, use_itn='True', use_capitalize='True', max_lines=1, words_per_line=15, ), json={ 'url': file_url, }, headers={ 'content-type': 'application/json', 'Authorization': 'Bearer; {}'.format(access_token) } ) print('submit response = {}'.format(response.text)) assert(response.status_code == 200) assert(response.json()['message'] == 'Success') job_id = response.json()['id'] response = requests.get( '{base_url}/query'.format(base_url=base_url), params=dict( appid=appid, id=job_id, ), headers={ 'Authorization': 'Bearer; {}'.format(access_token) } ) print('query response = {}'.format(response.json())) assert(response.status_code == 200) if __name__ == '__main__': main()