WebSocket API


API WebSocket hỗ trợ nhận dạng online. Do không có thông tin về audio nên bạn cần cung cấp thông tin này tới API thông qua uri query string:

content-type=audio/x-raw,+layout=(string)interleaved,+rate=(int)16000,+format=(string)S16LE,+channels=(int)1

Trong đó:

Field Mô tả
content-type trường lưu trữ thông tin của audio, các tham số dưới đây được lưu trong trường này
rate sample rate của audio
format định dạng PCM của audio. Tham khảo: PCM format
channel số kênh của audio

Để sử dụng API với account của riêng mình, bạn cần thêm thông tin token của mình thông qua trường 'token' trong uri query string: token=$YOUR_TOKEN

Để nhận dạng sử dụng API WebSocket, bạn cần:


  1. Tạo kết nối WebSocket tới API với các thông tin cần thiết đã nêu ở trên.

  2. Gửi data là mảng byte của luồng audio cần nhận dạng với tần suất 4 lần/giây, mỗi lần gửi với số byte = (byte rate của audio)/4. Ví dụ Audio có rate =16000, format S16LE (16bit <=> 2 byte/sample) có byte rate = 16000*2 = 32000 => Mỗi cần lần gửi lên 8000 byte.

  3. Khi kết thúc audio, client nên gửi lên byte của string 'EOS' encode UTF-8 để server biết & kết thúc nhận dạng. Hoặc server nhận dạng sẽ tự kết thúc nhận dạng sau một khoảng thời gian ngắn sau khi giọng nói cuối cùng được gửi lên.

  4. Đóng kết nối tới API. Kết thúc.


!!! notice "Lưu ý" Do một số client chưa được cập nhật chứng chỉ của https://viettelgroup.ai, để có thể sử dụng được kết nối bảo mật.

PYTHON

Có thể sử dụng file chứng chỉ trực tiếp trong code, xem toàn bộ code tại đây: Speech to text WebSocket

JAVA

Bạn cần import chứng chỉ vào truststore thông qua công cụ keytool đi kèm với Java

$JAVA_HOME/bin/keytool -import -file $VTCC_CERT -alias wwwvtccai -keystore $JAVA_HOME/jre/lib/security/cacerts

Trong đó:

    - JAVA_HOME: thư mục JDK trên máy client.
    - VTCC_CERT: Đường dẫn đến file chứng chỉ của https://viettelgroup.ai
    - $JAVA_HOME/jre/lib/security/cacerts: Vị trí file lưu trữ truststore mặc định của Java

Nếu yêu cầu mật khẩu, vui lòng nhập vào: 'changeit'

Do chứng chỉ sẽ được import vào TrustStore mặc định của JAVA nên bạn chỉ cần import 1 lần duy nhất.

Xem ví dụ đầy đủ tại đây: VTCC Asr WebSocketSample

Sample code:


    #!/usr/bin/env python3
# -*- coding: utf-8 -*-
import logging
import os.path
import sys
import time
from pathlib import Path
from threading import Thread

import websocket

if sys.version_info[0] < 3:
    raise Exception("Must be using Python 3.xx")
root = logging.getLogger()
root.setLevel(logging.DEBUG)

handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
root.addHandler(handler)

dirname = os.path.dirname(__file__)
audio_path = os.path.join(Path(dirname).parent, 'test_audio.wav')
cert_path = os.path.join(Path(dirname).parent.parent, 'vtcc-cert/wwwvtccai.crt')

def rate_limited(max_per_second):
    min_interval = 1.0 / float(max_per_second)

    def decorate(func):
        last_time_called = [0.0]

        def rate_limited_function(*args, **kargs):
            elapsed = time.clock() - last_time_called[0]
            left_to_wait = min_interval - elapsed
            if left_to_wait > 0:
                time.sleep(left_to_wait)
            ret = func(*args, **kargs)
            last_time_called[0] = time.clock()
            return ret

        return rate_limited_function

    return decorate

@rate_limited(4)
def send_data(ws, data):
    ws.send(data)

def on_message(ws, message):
    print(message)

def on_error(ws, error):
    logging.warn(error)

def on_close(ws):
    logging.info("### closed ###")
    ws.close()

class AsrWebSocket:
    def __init__(self, sample_rate=16000, audio_format='S16LE', channels=1, token='anonymous',
                 url='wss://viettelgroup.ai/voice/api/asr/v1/ws/decode_online'):
        self.sample_rate = sample_rate
        self.format = audio_format
        self.channels = channels
        self.token = token
        self.url = url
        self.audio_stream = None
        self.ws = None
        self.sslopt = {
            'ca_certs': cert_path
        }

    def generate_url_query(self):
        query_url = self.url + '?content-type=audio/x-raw,+layout=(string)interleaved,+rate=(int)' + str(
            self.sample_rate)
        query_url += ',+format=(string)' + self.format + ',+channels=(int)' + str(self.channels) + '&token=' + self.token
        return query_url

    def on_open(self):
        def run():
            for block in iter(lambda: self.audio_stream.read(self.sample_rate // 4), b''):
                send_data(self.ws, block)
            self.ws.send('EOS')

        logging.info('Upload thread terminated')
        my_thread = Thread(target=run)
        my_thread.start()

    def recognize(self, audio_steam):
        self.audio_stream = audio_steam
        self.ws = websocket.WebSocketApp(self.generate_url_query(),
                                         on_message=on_message,
                                         on_error=on_error,
                                         on_close=on_close,
                                         on_open=self.on_open)
        self.ws.run_forever(sslopt=self.sslopt)

ws_asr = AsrWebSocket()
ws_asr.recognize(open(audio_path, 'rb'))
package speech.asr.ws;

import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketFrame;
import speech.asr.utils.PCMFormat;

import java.io.BufferedInputStream;
import java.nio.file.Files;
import java.nio.file.Paths;

public class AsrWebSocketSample {

    public static void main(String[] args) throws Exception {
        IResponseHandler<WebSocketFrame> handler = new IResponseHandler<WebSocketFrame>() {
            @Override
            public void onMessage(WebSocketFrame frame) {
                if (frame instanceof TextWebSocketFrame) {
                    TextWebSocketFrame textFrame = (TextWebSocketFrame) frame;
                    System.out.println(textFrame.text());
                } else
                    System.out.println(frame);
            }

            @Override
            public void onFailure(Throwable cause) {
                cause.printStackTrace();
            }

            @Override
            public void onComplete() {
                System.err.println("completed");
            }
        };
        try (
                BufferedInputStream bi = new BufferedInputStream(Files.newInputStream(Paths.get("src/main/resources/test_audio.wav")))
        ) {
            AsrWebSocketClient client = AsrWebSocketClient.newBuilder()
                    .setSampleRate(16000)
                    .setAudioFormat(PCMFormat.S16LE)
                    .setChannels(1)
                    .setHandler(handler)
                    .build();
            client.recognize(bi);
        }
    }
}
Nội dung