You need to enable JavaScript to run this app.
导航
音视频字幕生成
最近更新时间:2025.02.06 11:00:44首次发布时间:2021.12.20 14:44:12

1. 流程简介

视频字幕功能整体处理流程分为三个阶段:

  1. 客户端抽取视频中音轨,转成音频文件;
  2. 把音频文件发送至后端集群,获取任务 ID;
  3. 通过任务 ID 访问后端接口获取结果。

非阻塞查询流程

Image

阻塞查询流程

Image

2. 鉴权

设置鉴权内容,请参考鉴权方法

3. 提交音频

3.1 请求

请求地址:https://openspeech.bytedance.com/api/v1/vc/submit
请求方式:HTTP POST

3.1.1 Url 参数

字段

说明

是否必填

备注

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"}

3.1.2 支持语种

语音字幕

序号

语言

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

3.1.3 音频二进制请求方式

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]]

3.1.4 音频地址请求方式

Header 需要加入内容类型标识:

Content-Type: application/json

Body 为 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"}

3.2 应答

应答格式: JSON
应答字段:

字段

说明

层级

格式

是否必填

备注

code

状态码

1

int

0 为成功,非 0 为失败。

message

状态信息

1

string

失败时标记失败原因。

id

任务 ID

1

string

仅当提交成功时填写。

应答示例:

{
    "code": "0",
    "message": "Success",
    "id": "fc5aa03e-6ae4-46a3-b8cf-1910a44e0d8a"
}

4. 查询结果

4.1 请求

请求地址: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

4.2 应答

应答格式: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
                }
            ]
        }
    ]
}

5. 错误码

类别/状态号

含义

说明

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

未知错误

未归类错误。

6. Demo

以下 demo 均使用 token 鉴权方式。

curl

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'

CPP

#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;
}

JAVA

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();
    }
}

Python

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()