本文介紹如何使用SDK來支持實時記錄場景下的音頻識別流程。
交互流程
前提條件
示例代碼
package com.alibaba.tingwu.client.demo.realtimemeeting;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.nls.client.protocol.NlsClient;
import com.alibaba.nls.client.protocol.asr.SpeechTranscriber;
import com.alibaba.nls.client.protocol.asr.SpeechTranscriberListener;
import com.alibaba.nls.client.protocol.asr.SpeechTranscriberResponse;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.FileInputStream;
import java.util.Arrays;
/**
* @author tingwu2023
* @desc 演示了實時會議場景,在創建會議后,根據返回的MeetingJoinUrl進行實時語音識別的 調用。
*/
public class RealtimeTransTest {
private static NlsClient NLS_CLIENT;
/**
* 初始化語音識別SDK,可復用,可全局使用
*/
@BeforeClass
public static void before() {
NLS_CLIENT = new NlsClient("default");
}
/**
* 單路實時轉寫,絕大多數情況下,您可只參考此處實現即可。
*/
@Test
public void testRealtimeTrans() throws Exception {
// 此處url來自于用戶通過OpenAPI創建會議時返回的推流url, url地址一般是:"wss://tingwu-realtime-cn-beijing.aliyuncs.com/api/ws/v1?mc={xxxxxxzzzzyyy}"
String meetingJoinUrl = "請輸入您創建實時會議時返回的MeetingJoinUrl";
SpeechTranscriber speechTranscriber = new SpeechTranscriber(NLS_CLIENT, "default" ,createListener(), meetingJoinUrl);
speechTranscriber.start();
// 使用本地文件模擬 真實場景下音頻流實時采集
String localAudioFile = "nls-sample-16k.wav";
byte[] buffer = new byte[3200];
FileInputStream fis = new FileInputStream(localAudioFile);
int len;
while ((len = fis.read(buffer)) > 0) {
// TODO 模擬實時發送的音頻數據幀
speechTranscriber.send(Arrays.copyOf(buffer, len));
// TODO 模擬音頻采集間隔
Thread.sleep(100L);
}
// 音頻流結束后,發送stop結束實時轉寫處理。
// TODO 注意: 此時該會議并沒有結束,會議的結束可以參考StopRealtimeMeetingTaskTest處理。
speechTranscriber.stop();
speechTranscriber.close();
}
public SpeechTranscriberListener createListener() {
return new SpeechTranscriberListener() {
@Override
public void onMessage(String message) {
System.out.println("onMessage " + message);
if (message == null || message.trim().length() == 0) {
return;
}
SpeechTranscriberResponse response = JSON.parseObject(message, SpeechTranscriberResponse.class);
if("ResultTranslated".equals(response.getName())) {
// 翻譯事件輸出,您可以在此處進行相關處理
System.out.println("--- ResultTranslated ---" + JSON.toJSONString(response, SerializerFeature.PrettyFormat));
} else {
// 原語音識別事件輸出,交由父類負責回調
super.onMessage(message);
}
}
@Override
public void onTranscriberStart(SpeechTranscriberResponse response) {
// task_idf非常重要,但需要說明的是,該task_id是在音頻流實時推送和識別過程中的標識,而非會議級別的TaskId
System.out.println("task_id: " + response.getTaskId() + ", name: " + response.getName() + ", status: " + response.getStatus());
}
@Override
public void onSentenceBegin(SpeechTranscriberResponse response) {
System.out.println("received onSentenceBegin: " + JSON.toJSONString(response));
}
@Override
public void onSentenceEnd(SpeechTranscriberResponse response) {
//識別出一句話。服務端會智能斷句,當識別到一句話結束時會返回此消息。
System.out.println("received onSentenceEnd: " + JSON.toJSONString(response));
System.out.println("task_id: " + response.getTaskId() +
", name: " + response.getName() +
// 狀態碼“20000000”表示正常識別。
", status: " + response.getStatus() +
// 句子編號,從1開始遞增。
", index: " + response.getTransSentenceIndex() +
// 當前的識別結果。
", result: " + response.getTransSentenceText() +
// 當前的詞模式識別結果。
", words: " + response.getWords() +
// 開始時間
", begin_time: " + response.getSentenceBeginTime() +
// 當前已處理的音頻時長,單位為毫秒。
", time: " + response.getTransSentenceTime());
// 當前的識別結果(固定的,不再變化的識別結果)
String text = response.getTransSentenceText();
// 當前的識別結果(不同于response.getTransSentenceText(), 此處的識別結果可能會出現變化)
SpeechTranscriberResponse.StashResult stashResult = response.getStashResult();
// 將上面兩段識別結果拼接起來
String stashText = stashResult == null ? "" : stashResult.getText();
System.out.println("[onSentenceEnd] text = " + text + " | stashText = " + stashText);
}
@Override
public void onTranscriptionResultChange(SpeechTranscriberResponse response) {
// 識別出中間結果。僅當OutputLevel=2時,才會返回該消息。
System.out.println("received onTranscriptionResultChange: " + JSON.toJSONString(response));
System.out.println("task_id: " + response.getTaskId() +
", name: " + response.getName() +
// 狀態碼“20000000”表示正常識別。
", status: " + response.getStatus() +
// 句子編號,從1開始遞增。
", index: " + response.getTransSentenceIndex() +
// 當前的識別結果。
", result: " + response.getTransSentenceText() +
// 當前的詞模式識別結果。
", words: " + response.getWords() +
// 當前已處理的音頻時長,單位為毫秒。
", time: " + response.getTransSentenceTime());
}
@Override
public void onTranscriptionComplete(SpeechTranscriberResponse response) {
// 識別結束,當調用speechTranscriber.stop()之后會收到該事件
System.out.println("received onTranscriptionComplete: " + JSON.toJSONString(response));
}
@Override
public void onFail(SpeechTranscriberResponse response) {
// 實時識別出錯,請關注錯誤碼,請記錄此task_id以便排查
System.out.println("received onFail: " + JSON.toJSONString(response));
}
};
}
}
package main
import (
"errors"
"log"
"os"
"time"
nls "github.com/aliyun/alibabacloud-nls-go-sdk"
)
const (
TOKEN = "default"
APPKEY = "default"
)
func onTaskFailed(text string, param interface{}) {
logger, ok := param.(*nls.NlsLogger)
if !ok {
return
}
logger.Println("TaskFailed:", text)
}
func onStarted(text string, param interface{}) {
logger, ok := param.(*nls.NlsLogger)
if !ok {
return
}
logger.Println("onStarted:", text)
}
func onSentenceBegin(text string, param interface{}) {
logger, ok := param.(*nls.NlsLogger)
if !ok {
return
}
logger.Println("onSentenceBegin:", text)
}
func onSentenceEnd(text string, param interface{}) {
logger, ok := param.(*nls.NlsLogger)
if !ok {
return
}
logger.Println("onSentenceEnd:", text)
}
func onResultChanged(text string, param interface{}) {
logger, ok := param.(*nls.NlsLogger)
if !ok {
return
}
logger.Println("onResultChanged:", text)
}
func onCompleted(text string, param interface{}) {
logger, ok := param.(*nls.NlsLogger)
if !ok {
return
}
logger.Println("onCompleted:", text)
}
func onClose(param interface{}) {
logger, ok := param.(*nls.NlsLogger)
if !ok {
return
}
logger.Println("onClosed:")
}
func waitReady(ch chan bool, logger *nls.NlsLogger) error {
select {
case done := <-ch:
{
if !done {
logger.Println("Wait failed")
return errors.New("wait failed")
}
logger.Println("Wait done")
}
case <-time.After(20 * time.Second):
{
logger.Println("Wait timeout")
return errors.New("wait timeout")
}
}
return nil
}
func run_push_audio_stream(url string) {
// 使用本地pcm或者opus格式文件模擬真實場景下音頻流實時采集
pcm, err := os.Open("tingwu-sample-16k.pcm")
if err != nil {
log.Default().Fatalln(err)
}
buffers := nls.LoadPcmInChunk(pcm, 320)
param := nls.DefaultSpeechTranscriptionParam()
config := nls.NewConnectionConfigWithToken(url, APPKEY, TOKEN)
logger := nls.NewNlsLogger(os.Stderr, "1", log.LstdFlags|log.Lmicroseconds)
logger.SetLogSil(false)
logger.SetDebug(true)
st, err := nls.NewSpeechTranscription(config, logger,
onTaskFailed, onStarted,
onSentenceBegin, onSentenceEnd, onResultChanged,
onCompleted, onClose, logger)
if err != nil {
logger.Fatalln(err)
return
}
logger.Println("Start pushing audio stream")
ready, err := st.Start(param, nil)
if err != nil {
logger.Fatalln(err)
return
}
err = waitReady(ready, logger)
if err != nil {
logger.Fatalln(err)
return
}
for _, data := range buffers.Data {
if data != nil {
st.SendAudioData(data.Data)
time.Sleep(10 * time.Millisecond)
}
}
ready, err = st.Stop()
if err != nil {
logger.Fatalln(err)
return
}
err = waitReady(ready, logger)
if err != nil {
logger.Fatalln(err)
return
}
st.Shutdown()
logger.Println("Push audio stream done")
}
func main() {
// 此處url來自于用戶通過OpenAPI創建記錄時返回的推流url
run_push_audio_stream("wss://tingwu-realtime-cn-hangzhou-pre.aliyuncs.com/api/ws/v1?mc=*********h-moSNWGZO5mq-uZzu1EQbVBABVn9y8VGzWmVcAEiLNE1idoml7JU_wr17G4dDdxwQ6jiMg8OCQCrptlCnSk4hJ9K_fVfP8ngWaYk2If*********")
}
#include <fstream>
#include <string>
#include <mutex>
#include <condition_variable>
#include <thread>
#include <chrono>
#include "nlsClient.h"
#include "nlsEvent.h"
#include "speechTranscriberRequest.h"
// 實現一個簡易信號量,用于同步事件處理, 您在實際項目中,可以替換您項目中的信號量實現即可
class Semaphore {
public:
Semaphore(int count = 0) : count_(count) {
}
void notify() {
std::unique_lock<std::mutex> lock(mtx_);
++count_;
cv_.notify_one();
}
void wait() {
std::unique_lock<std::mutex> lock(mtx_);
cv_.wait(lock, [this](){ return count_ > 0; });
--count_;
}
// Wait with timeout
bool wait_for(const std::chrono::milliseconds& timeout) {
std::unique_lock<std::mutex> lock(mtx_);
if (!cv_.wait_for(lock, timeout, [this](){ return count_ > 0; })) {
return false; // Timeout occurred
}
--count_;
return true;
}
private:
std::mutex mtx_;
std::condition_variable cv_;
int count_;
};
Semaphore kSemaphore(0);
// 服務端返回的所有信息會通過此回調反饋
// cbEvent 回調事件結構, 詳見nlsEvent.h
// cbParam 回調自定義參數,默認為NULL, 可以根據需求自定義參數
void onMessage(AlibabaNls::NlsEvent *cbEvent, void *cbParam) {
printf("onMessage: [%s]\n", cbEvent->getAllResponse());
int result = cbEvent->parseJsonMsg(true);
if (result) {
printf("onMessage: parseJsonMsg failed: [%d]", result);
return;
}
switch (cbEvent->getMsgType()) {
case AlibabaNls::NlsEvent::TaskFailed:
//識別過程(包含start(), sendAudio(), stop())發生異常時, sdk內部線程上報TaskFailed事件
//上報TaskFailed事件之后, SDK內部會關閉識別連接通道. 此時調用sendAudio會返回負值, 請停止發送
printf("onTaskFailed: status code=%d, task id=%s, error message=%s\n",
cbEvent->getStatusCode(),
cbEvent->getTaskId(),
cbEvent->getErrorMessage());
break;
case AlibabaNls::NlsEvent::TranscriptionStarted:
// 調用start()后, 成功與云端建立連接, sdk內部線程上報started事件
// 通知發送線程start()成功, 可以發送數據
kSemaphore.notify();
break;
case AlibabaNls::NlsEvent::SentenceBegin:
printf("##### %d, %d\n", cbEvent->getMsgType(), AlibabaNls::NlsEvent::SentenceBegin),
// 服務端檢測到了一句話的開始, sdk內部線程上報SentenceBegin事件
printf("onSentenceBegin: status code=%d, task id=%s, index=%d, time=%d\n",
cbEvent->getStatusCode(), cbEvent->getTaskId(),
cbEvent->getSentenceIndex(), //句子編號,從1開始遞增。
cbEvent->getSentenceTime()); //當前已處理的音頻時長,單位:毫秒。
break;
case AlibabaNls::NlsEvent::TranscriptionResultChanged:
// 識別結果發生了變化, sdk在接收到云端返回到最新結果時,
printf("onTranscriptionResultChanged: status code=%d, task id=%s, index=%d, time=%d, result=%s\n",
cbEvent->getStatusCode(),
cbEvent->getTaskId(),
cbEvent->getSentenceIndex(), //句子編號,從1開始遞增。
cbEvent->getSentenceTime(), //當前已處理的音頻時長,單位:毫秒。
cbEvent->getResult()); // 當前句子的完整識別結果
break;
case AlibabaNls::NlsEvent::SentenceEnd:
// 服務端檢測到了一句話結束, sdk內部線程上報SentenceEnd事件
printf("onSentenceEnd: status code=%d, task id=%s, index=%d, time=%d, begin_time=%d, result=%s | %s\n",
cbEvent->getStatusCode(),
cbEvent->getTaskId(),
cbEvent->getSentenceIndex(), //句子編號,從1開始遞增。
cbEvent->getSentenceTime(), //當前已處理的音頻時長,單位:毫秒。
cbEvent->getSentenceBeginTime(), // 對應的SentenceBegin事件的時間。
cbEvent->getResult(), cbEvent->getStashResultText()); // 當前句子的完整識別結果。注意這里需要把stashResultText也拼接上
break;
case AlibabaNls::NlsEvent::TranscriptionCompleted:
// 服務端停止實時音頻流識別時, sdk內部線程上報Completed事件
kSemaphore.notify();
break;
case AlibabaNls::NlsEvent::Close:
//識別結束或發生異常時,會關閉連接通道, sdk內部線程上報ChannelClosed事件
//通知發送線程, 最終識別結果已經返回, 可以調用stop()
break;
default:
// 其他可能出現的事件, 比如若您開啟了翻譯功能,翻譯結果報文會在此處返回,您可以在此處處理
printf("---otherEvent---\n");
break;
}
}
int main( int argc, char** argv ) {
// 此處url來自于用戶通過OpenAPI創建會議時返回的推流url, url地址一般是:"wss://tingwu-realtime-cn-beijing.aliyuncs.com/api/ws/v1?mc={xxxxxxzzzzyyy}"
const char* websocketUrl = "請輸入您創建實時會議時返回的MeetingJoinUrl";
const char* localAudioPath = "nls-sample-16k.wav"; // 本地測試用的音頻文件路徑, 該音頻較短,可能無法測試到相關摘要結果
// 根據需要設置SDK輸出日志。可選。
AlibabaNls::NlsClient::getInstance()->setLogConfig("log-transcriber", AlibabaNls::LogDebug, 400, 50);
// 啟動工作線程, 在創建請求和啟動前必須調用此函數, 可理解為對NlsClient的初始化,入參為負時, 啟動當前系統中可用的核數。
AlibabaNls::NlsClient::getInstance()->startWorkThread(1);
//創建實時音頻流識別SpeechTranscriberRequest對象
AlibabaNls::SpeechTranscriberRequest* request = AlibabaNls::NlsClient::getInstance()->createTranscriberRequest();
request->setUrl(websocketUrl);
// 設置所有服務端返回信息回調函數
request->setOnMessage(onMessage, NULL);
request->setEnableOnMessage(true);
int ret = request->start();
if(ret < 0) {
printf("start fail, error: [%d]", ret);
AlibabaNls::NlsClient::getInstance()->releaseTranscriberRequest(request);
return 0;
} else {
// 同步等待start返回,之后再繼續發送音頻數據
if (kSemaphore.wait_for(std::chrono::milliseconds(10000))) {
printf("start success\n");
} else {
printf("start timeout\n");
return -1;
}
}
std::ifstream fs;
fs.open(localAudioPath, std::ios::binary | std::ios::in);
while (!fs.eof()) {
const int FRAME_SIZE = 3200;
uint8_t data[FRAME_SIZE] = {0};
fs.read((char *) data, sizeof(uint8_t) * FRAME_SIZE);
size_t nlen = fs.gcount();
//發送音頻數據: sendAudio為異步操作, 返回負值表示發送失敗, 需要停止發送; 返回大于0 為成功.
ret = request->sendAudio(data, nlen);
if (ret < 0) {
// 發送失敗,退出循環數據發送。
printf("send data fail, ret: %d.\n", ret);
break;
}
// 對于16K采樣率音頻來說,3200字節約為100ms數據;如果是8K音頻,則1600字節約為100ms數據
// 實際使用中, 語音數據是實時的, 不用sleep控制速率, 直接發送即可。此處是用語音數據來自文件的方式進行模擬, 故發送時需要控制速率來模擬真實錄音場景.
std::this_thread::sleep_for(std::chrono::milliseconds(200));
} // while
fs.close();
request->stop();
// 等待服務端將最后數據返回, 結束識別
kSemaphore.wait_for(std::chrono::milliseconds(10000));
AlibabaNls::NlsClient::getInstance()->releaseTranscriberRequest(request);
// 所有工作完成,進程退出前,釋放nlsClient。
AlibabaNls::NlsClient::releaseInstance();
}
import time
import threading
import sys
import nls
class TestRealtimeMeeting:
def __init__(self, tid, test_file, url):
self.__th = threading.Thread(target=self.__test_run)
self.__id = tid
self.__test_file = test_file
self.__url = url
def loadfile(self, filename):
with open(filename, "rb") as f:
self.__data = f.read()
def start(self):
self.loadfile(self.__test_file)
self.__th.start()
def test_on_sentence_begin(self, message, *args):
print("test_on_sentence_begin:{}".format(message))
def test_on_sentence_end(self, message, *args):
print("test_on_sentence_end:{}".format(message))
def test_on_start(self, message, *args):
print("test_on_start:{}".format(message))
def test_on_error(self, message, *args):
print("on_error message=>{} args=>{}".format(message, args))
def test_on_close(self, *args):
print("on_close: args=>{}".format(args))
def test_on_result_chg(self, message, *args):
print("test_on_chg:{}".format(message))
def test_on_result_translated(self, message, *args):
print("test_on_translated:{}".format(message))
def test_on_completed(self, message, *args):
print("on_completed:args=>{} message=>{}".format(args, message))
def __test_run(self):
print("thread:{} start..".format(self.__id))
rm = nls.NlsRealtimeMeeting(
url=self.__url,
on_sentence_begin=self.test_on_sentence_begin,
on_sentence_end=self.test_on_sentence_end,
on_start=self.test_on_start,
on_result_changed=self.test_on_result_chg,
on_result_translated=self.test_on_result_translated,
on_completed=self.test_on_completed,
on_error=self.test_on_error,
on_close=self.test_on_close,
callback_args=[self.__id]
)
print("{}: session start".format(self.__id))
r = rm.start()
self.__slices = zip(*(iter(self.__data),) * 640)
for i in self.__slices:
rm.send_audio(bytes(i))
time.sleep(0.01)
time.sleep(1)
r = rm.stop()
print("{}: rm stopped:{}".format(self.__id, r))
time.sleep(5)
def multiruntest(num=1):
for i in range(0, num):
name = "thread" + str(i)
t = TestRealtimeMeeting(name, "nls-sample-16k.wav",
"請輸入您創建實時會議時返回的MeetingJoinUrl")
t.start()
nls.enableTrace(False)
multiruntest(1)
實時記錄語音推流
推流過程說明
1:建立推流通道,對應本文中交互流程1。
2:推送識別語音,對應本文中交互流程2推送音頻流。
3:接收識別結果 對應本文中交互流程2獲取返回事件結果。客戶端循環發送語音數據,持續接收識別結果:
句子開始事件(SentenceBegin)
句子開始事件表示服務端檢測到了一句話的開始,聽悟服務的智能斷句功能會判斷出一句話的開始與結束,示例如下。
{
"header":{
"namespace":"SpeechTranscriber",
"name":"SentenceBegin",
"status":20000000,
"message_id":"a426f3d4618447519c9d85d1a0d1****",
"task_id":"5ec521b5aa104e3abccf3d361822****",
"status_text":"Gateway:SUCCESS:Success."
},
"payload":{
"index":0,
"time":0
}
}
對象參數說明:
參數 | 類型 | 說明 |
header | object | 返回結果頭信息。 |
namespace | string | 消息所屬的命名空間,固定為:SpeechTranscriber。 |
name | string | 消息名稱,SentenceBegin表示一個句子的開始。 |
task_id | string | 推流任務全局唯一ID,請記錄該值,便于排查問題。 |
message_id | string | 本次消息的ID。 |
status | int | 狀態碼,表示請求是否成功,見公共錯誤碼。 |
status_text | string | 狀態消息。 |
payload | object | 返回結果。 |
index | int | 句子編號,從0開始遞增。 |
time | int | 當前已處理的音頻時長,單位是毫秒。 |
speaker_id | string | 識別結果對應的說話人,單路識別和混音識別時不返回。 |
句中識別結果變化事件(TranscriptionResultChanged)
句中識別結果變化事件表示識別結果發生了變化,示例如下。
{
"header":{
"namespace":"SpeechTranscriber",
"name":"TranscriptionResultChanged",
"status":20000000,
"message_id":"dc21193fada84380a3b6137875ab****",
"task_id":"5ec521b5aa104e3abccf3d361822****",
"status_text":"Gateway:SUCCESS:Success."
},
"payload":{
"index":0,
"time":1835,
"result":"北京的天",
"words":[
{
"text":"北京",
"startTime":630,
"endTime":930
},
{
"text":"的",
"startTime":930,
"endTime":1110
},
{
"text":"天",
"startTime":1110,
"endTime":1140
}
]
}
}
對象參數說明:
參數 | 類型 | 說明 |
header | object | 返回結果頭信息。 |
namespace | string | 消息所屬的命名空間,固定為:SpeechTranscriber。 |
name | string | 消息名稱,TranscriptionResultChanged表示句中識別結果變化。 |
task_id | string | 推流任務全局唯一ID,請記錄該值,便于排查問題。 |
message_id | string | 本次消息的ID。 |
status | int | 狀態碼,表示請求是否成功,見公共錯誤碼。 |
status_text | string | 狀態消息。 |
payload | object | 返回結果。 |
index | int | 句子編號,從0開始遞增。 |
time | int | 當前已處理的音頻時長,單位是毫秒。 |
result | string | 當前句子的識別結果。 |
words | list[] | 當前句子的詞信息。 |
text | string | 文本。 |
startTime | int | 詞開始時間,單位為毫秒。 |
endTime | int | 詞結束時間,單位為毫秒。 |
speaker_id | string | 識別結果對應的說話人,單路識別和混音識別時不返回。 |
句子結束事件(SentenceEnd)
句子結束事件表示服務端檢測到了一句話的結束,并附帶返回該句話的識別結果,示例如下。
{
"header":{
"namespace":"SpeechTranscriber",
"name":"SentenceEnd",
"status":20000000,
"message_id":"c3a9ae4b231649d5ae05d4af36fd****",
"task_id":"5ec521b5aa104e3abccf3d361822****",
"status_text":"Gateway:SUCCESS:Success."
},
"payload":{
"index":0,
"time":1835,
"result":"北京的天氣",
"words":[
{
"text":"北京",
"startTime":630,
"endTime":930
},
{
"text":"的",
"startTime":930,
"endTime":1110
},
{
"text":"天氣",
"startTime":1110,
"endTime":1140
}
],
"stash_result": {
"index": 1,
"currentTime": 1140,
"words": [
{
"startTime": 1150,
"text": "會",
"endTime": 1190
},
{
"startTime": 1190,
"text": "下雨",
"endTime": 1320
}
],
"beginTime": 1140,
"text": "會下雨"
}
}
}
對象參數說明:
參數 | 類型 | 說明 |
header | object | 返回結果頭信息。 |
namespace | string | 消息所屬的命名空間,固定為:SpeechTranscriber。 |
name | string | 消息名稱,SentenceEnd表示句子結束。 |
task_id | string | 推流任務全局唯一ID,請記錄該值,便于排查問題。 |
message_id | string | 本次消息的ID。 |
status | int | 狀態碼,表示請求是否成功,見公共錯誤碼。 |
status_text | string | 狀態消息。 |
payload | object | 返回結果。 |
index | int | 句子編號,從0開始遞增。 |
time | int | 當前已處理的音頻時長,單位是毫秒。 |
result | string | 當前句子的識別結果。 |
words | list[] | 當前句子的詞信息。 |
text | string | 文本。 |
startTime | int | 詞開始時間,單位為毫秒。 |
endTime | int | 詞結束時間,單位為毫秒。 |
speaker_id | string | 識別結果對應的說話人,單路識別和混音識別時不返回。 |
stash_result | object | 語音識別的暫存結果,是暫未完成斷句的下一句話信息。您需要將stash_result結果和上面的text結果拼接以便后續處理 |
stash_result.index | int | stash結果的句子編號 |
stash_result.text | string | stash結果的ASR文本 |
stash_result.words | list[] | stash結果的詞信息 |
識別結果翻譯事件(ResultTranslated)
識別結果翻譯事件表示在開啟翻譯時服務端檢測到識別結果并進行目標語言文本翻譯,示例如下。
{
"header":{
"namespace":"SpeechTranscriber",
"name":"ResultTranslated",
"status":20000000,
"message_id":"c3a9ae4b231649d5ae05d4af36fd****",
"task_id":"5ec521b5aa104e3abccf3d361822****",
"status_text":"Gateway:SUCCESS:Success.",
"source_message_id":"d4a9ae4b231649d5ae05d4af36fd****"
},
"payload":{
"speaker_id":"xxx",
"source_lang":"cn",
"target_lang":"en",
"translate_result":[
{
"text":"At that time.",
"index":110,
"beginTime":123000,
"endTime":125000
},
{
"text":"xxx",
"index":111,
"partial":true
}
]
}
}
對象參數說明:
參數 | 類型 | 說明 |
header | object | 返回結果頭信息。 |
namespace | string | 消息所屬的命名空間,固定為:SpeechTranscriber。 |
name | string | 消息名稱,ResultTranslated表示識別結果翻譯。 |
task_id | string | 推流任務全局唯一ID,請記錄該值,便于排查問題。 |
message_id | string | 本次消息的ID。 |
source_message_id | string | 本次翻譯的源識別結果消息ID。 |
status | int | 狀態碼,表示請求是否成功,見公共錯誤碼。 |
status_text | string | 狀態消息。 |
payload | object | 返回結果。 |
speaker_id | string | 識別結果對應的說話人標識,同推流pb傳入;無此字段時表示單通道識別或混音流識別的翻譯。 |
source_lang | string | 翻譯的源語言。 |
target_lang | string | 翻譯的目標語言。 |
translate_result | list[] | 識別結果翻譯信息。 |
text | string | 翻譯后的文本。 |
index | int | 翻譯句子編號,從0開始遞增。 |
partial | boolean | 為true時對應非SentenceEnd的識別內容的翻譯結果。 |
beginTime | int | 翻譯句子的開始時間,單位為毫秒,在翻譯SentenceEnd識別結果時會返回。 |
endTime | int | 翻譯句子的結束時間,單位為毫秒,在翻譯SentenceEnd識別結果時會返回。 |
4:暫停推流識別,對應本文中交互流程3。
結束識別完成事件
參數 | 類型 | 說明 |
header | object | 返回結果頭信息。 |
namespace | string | 消息所屬的命名空間,固定為:SpeechTranscriber。 |
name | string | 消息名稱,TranscriptionCompleted表示此次推流識別完成,TaskFailed表示此次推流識別異常中斷。 |
task_id | string | 推流任務全局唯一ID,請記錄該值,便于排查問題。 |
message_id | string | 本次消息的ID。 |
status | int | 狀態碼,表示請求是否成功,公共錯誤碼。 |
status_text | string | 狀態消息。 |
payload | object | 返回結果。 |
5:繼續推流識別(可選),如需繼續推流,可重復本文中交互流程即可實現。