CLOVA Speechリアルタイムストリーミング API

Prev Next

Classic/VPC環境で利用できます。

CLOVA Spechリアルタイムストリーミング APIを通じて、リアルタイムで音声を認識し、テキストに変換する方法を説明します。

バージョン

Version Date Changes
v1.0.0 2023.12. 最初に作成
v1.1.0 2024.07. ガイドをアップデート

API URL

Host Port
clovaspeech-gw.ncloud.com 50051

CLOVA Speech gRPCの使い方

  • Clova Speechリアルタイムストリーミング APIは、gRPCを介してのみアクセスできます。
  • システムが提供するすべての機能設定リクエストとレスポンスは、JSON形式で行われます。
  • 現在、16kHz、1channel、16bits per sampleの PCM(headerのない raw wave)形式のみサポートします。
  • 次は、Rocky Linuxを基準に Protoc Compilerをインストールし、APIを使用するための全般的な初期設定方法を説明します。

1. Protoc Compilerのインストールと準備

  1. Protoc Compilerをインストールするサーバにリモートアクセスします。
  2. gRPC使用のためのパッケージとプラグインをインストールします。
  • Rocky Linux: Python
  # 最新状態の確認
  sudo dnf update
  
  # Pythonのインストール: Linuxサーバに Pythonをインストールします。
  sudo dnf install python3

  # pipのインストールとアップグレード: pipは Python用パッケージインストールプログラムです。
  sudo dnf install python3-pip
  pip3 install --upgrade pip
  
  # grpcio-toolsのインストール: pipを使用して「grpcio-tools」をインストールします。
  pip3 install grpcio-tools

  # nest.protoファイルの作成
  touch nest.proto

  # protoc compilerで nest.protoファイルをコンパイル
  python3 -m grpc_tools.protoc -I=. --python_out=. --grpc_python_out=. nest.proto
  • Rocky Linux: Java
# protoc-gen-grpc-javaプラグインのダウンロード(バージョン番号は確認が必要 https://github.com/grpc/grpc-java/releases)
curl -OL https://repo1.maven.org/maven2/io/grpc/protoc-gen-grpc-java/1.36.0/protoc-gen-grpc-java-1.36.0-linux-x86_64.exe

# PATHに追加
mv protoc-gen-grpc-java-1.36.0-linux-x86_64.exe /usr/local/bin/protoc-gen-grpc-java

# 実行権限に変更する
chmod +x /usr/local/bin/protoc-gen-grpc-java

# インストールの確認
protoc-gen-grpc-java --version

# nest.protoファイルの作成
touch nest.proto

# protoc compilerで nest.protoファイルをコンパイル
protoc --proto_path=. --java_out=output/directory --grpc-java_out=output/directory nest.proto
  1. nest.protoファイルを開き、以下のコードを入力して保存します。
syntax = "proto3";
option java_multiple_files = true;
package com.nbp.cdncp.nest.grpc.proto.v1;

enum RequestType {
  CONFIG = 0;
  DATA = 1;
}

message NestConfig {
  string config = 1;
}

message NestData {
  bytes chunk = 1;
  string extra_contents = 2;
}
message NestRequest {
  RequestType type = 1;
  oneof part {
    NestConfig config = 2;
    NestData data = 3;
  }
}

message NestResponse {
  string contents = 1;
}
service NestService {
  rpc recognize(stream NestRequest) returns (stream NestResponse){};
}

2. Authorization

ヘッダ名 説明
Authorization Bearer ${secretKey}
  • Protoc Compilerで grpcコードの作成が完了したら、次は認証を行う番です。

  • gRPC channeを設定し、nest_grpc_pb2にある client side proxyの stubを作成します。

  • Stubの作成後に recognizeメソッドで目的の関数を実行するには、認証キーがある metadataも一緒に含める必要があります。

    • リアルタイムストリーミング APIの secretKeyは、長文認識 APIの secretKeyを使用し、NAVERクラウドプラットフォームコンソールで長文認識ドメイン > ビルダー実行 > 設定で確認できます。
    • リアルタイムストリーミング APIは、Basic長文認識プランでのみサポートします。(Freeプランは不可)

  • Rocky Linux: Python

  1. Pythonファイルを1つ作成します。(demoを行うたまに nameを「main」に指定)
touch main.py
  1. 作成した Pythonファイルに以下の内容を追加します。
import grpc
import json

import nest_pb2
import nest_pb2_grpc

channel = grpc.secure_channel(
		'clovaspeech-gw.ncloud.com:50051', 
		grpc.ssl_channel_credentials()
)
client = NestServiceStub(channel)
metadata = (("authorization", f"Bearer {secretKey}"),) #小文字 authorizationは必須 / secretkeyは長文認識ドメインで確認
call = client.YourMethod(YourRequest(), metadata=metadata)
  • Rocky Linux: Java
  1. Javaファイルを1つ作成します。(demoを行うたまに nameを「main」に指定)
touch main.java
  1. 作成した Javaファイルに以下の内容を追加します。
ManagedChannel channel = NettyChannelBuilder
			.forTarget("clovaspeech-gw.ncloud.com:50051")
			.useTransportSecurity()
			.build();
NestServiceGrpc.NestServiceStub client = NestServiceGrpc.newStub(channel);
Metadata metadata = new Metadata();
metadata.put(Metadata.Key.of("Authorization", Metadata.ASCII_STRING_MARSHALLER),
             "Bearer ${secretKey}");
client = MetadataUtils.attachHeaders(client, metadata);

3. Config JSONのリクエスト

  • ストリーミング APIを初めて呼び出す際は、以下のような config jsonを先に送る必要があります。
  • protocで作成された nest_pb2の NestRequestオブジェクトを使って config JSONをストリーミングエンドポイントに送ります。
  • Config JSONには以下の4つのフィールドがあります。4つが全部必須データではありませんが、明確な音声認識のために、transcriptionは認識をリクエストする言語を設定することをお勧めします。
    • transcription: 音声認識言語の設定
    • keywordBoosting: 入力された単語の認識率を高める設定
    • forbidden: 禁止語を設定
    • semanticEpd: 音声認識結果生成基準を設定
  • サンプルコードは下段にあります
{
  # Transcriptionの設定情報
  "transcription": {      # optional, top level key
    "language": string
  }
  # Keyword Boostingの設定情報
  "keywordBoosting": {    # optional, top level key
    "boostings": [
      {
        "words": string,
        "weight": float64
      }
    ]
  },
  # Forbiddenの設定情報
  "forbidden": {    # optional, top level key
    "forbiddens":  string
  }
}
  # semanticEpdの設定情報
  "semanticEpd": {
    "skipEmptyText": bool,
    "useWordEpd": bool,
    "usePeriodEpd": bool,
    "gapThreshold": int,
    "durationThreshold": int,
    "syllableThreshold": int
  }
}

1) Transcription

  • language 音声認識対象の言語コードです。明確な音声認識のために、認識をリクエストする言語を設定することをお勧めします。
    • 韓国語(ko)
    • 英語(en)
    • 日本語(ja)

Transcription JSON Format

# transcription設定リクエスト json format
{
  "transcription": {    
    "language": string        # required key
  }
}
  • 設定例
# transcription設定リクエスト例
{
  "transcription": {
    "language": "ko"
  }
}

2) Keyword Boosting

  • あらかじめ登録したキーワードの認識率を高めることができます。
    • キーワードの重み付け(weight)は0~5.0の間の実数範囲です。
      • 重み付けが0の場合、ブースティングしません。
      • すべてのキーワードの重み付けは同じでなければなりません。
        • 同じ重み付けに設定する単語の集まりは、words key string内でコンマ(,)で繋げて転送することができます。
  • Keyword Boosting実行時、単語の前後のスペース(space)も考慮されます。
  • 機能の設定方法の詳細は、Keyword Boosting設定 JSON Formatをご参照ください。

Keyword Boosting JSON Format

# keyword boosting設定リクエスト json format
{
  "keywordBoosting": {
    "boostings": [
      {
        "words": string,
        "weight": float64
      }
    ]
  }
}
  • 設定例
# keyword boostingの設定リクエスト例
{
  "keywordBoosting": {
    "boostings": [
      {
        "words": "test,test1,test2",
        "weight": 1 
      },
      {
        "words": "テスト,テスト1,テスト2",
        "weight": 0.5
      }
    ]
  }
}

3) Forbidden

  • あらかじめ登録したキーワードの認識結果に禁止語タグを提供する機能です。
    • 禁止語タグ: <forbidden>禁止語</forbidden>
  • 禁止語タグは認識結果のtext keyの valueにのみ追加されます。
  • 追加された禁止語タグは、認識結果のpositionperiodPositionalignInfoに影響を与えません。
  • 2つ以上の禁止語を登録する場合、forbiddens key string内でコンマ(,)で繋げて転送することができます。
  • 禁止語処理時、禁止語の前後のスペース(space)も考慮されます。
  • 機能の設定方法の詳細は、禁止語設定 JSON Formatをご参照ください。

Forbidden JSON Format

# forbidden設定リクエスト json format
{
  "forbidden": {
    "forbiddens":  string
  }
}
  • 設定例
{
  "forbidden": {
    "forbiddens":  "禁止語1,禁止語2" 
  }
}

4) SemanticEpd

  • 音声認識結果の生成基準を設定するオプションです。
  • 各オプションの基準に基づいて認識結果を生成し、発話形態に合わせてオプションを設定できます。
  • 機能の設定方法の詳細は、SemanticEpd JSON Formatをご参照ください。
  • 各属性の基準は次の通りです。
    • skipEmptyText
      • 認識結果がない結果値を転送するかどうかを選択するオプションです。
      • 基本設定はfalseです。trueに設定する場合、認識された結果がないと転送しません。
    • useWordEpd
      • 単語に基づいて認識結果を生成するオプションです。
      • 基本設定はfalseです。
    • usePeriodEpd
      • 句読点に基づいて認識結果を生成するオプションです。
      • 基本設定はfalseです。
    • gapThreshold
      • gapThreshold以上の黙字が発生した場合に認識結果を生成するオプションです。
      • 基本設定はfalseで、単位はミリ秒です。
    • durationThreshold
      • durationThresholdに基づいて、認識結果の durationに応じて認識結果を生成するオプションです。
      • 基本設定はfalseで、単位はミリ秒です。
    • syllableThreshold
      • 音節数に基づいて認識結果を生成するオプションです。
      • スペース「 」とピリオド「.」も1つの音節として処理されます。

SemanticEpd JSON Format

# semanticEpd設定リクエスト json format
{
  "semanticEpd": {
    "skipEmptyText": bool,
    "useWordEpd": bool,
    "usePeriodEpd": bool,
    "gapThreshold": int,
    "durationThreshold": int,
    "syllableThreshold": int
  }
}
  • 設定例
{
  "semanticEpd": {
    "skipEmptyText": false,
    "useWordEpd": true,
    "usePeriodEpd": true,
    "gapThreshold": 500,
    "durationThreshold": 5000,
    "syllableThreshold": 20
  }
}

4.Config JSONレスポンス形式

{
  "uid": string,                  # required
  "responseType": [ "config" ],   # required
  "config": {                     # required
    "status":string,              # required
    # configの設定によっては、以下の fieldsが存在しない場合があります。
    # 例) 禁止語を設定していない場合、「forbidden」keyは存在しません。
    "keywordBoosting": {          # optional, top level key
      "status":string,
    },
    "forbidden": {                # optional, top level key
      "status":string,
    },
    "semanticEpd": {                # optional, top level key
      "status":string,
    }
  }
}

1) 補足説明

  • Config JSONレスポンスは次のように構成されます。
    • Config JSONレスポンスのconfig.status keyの valueは以下のような値を持つことができます。
      • Success
        • Config JSONリクエストが成功し、正常に gRPC Serviceに目的の設定が保存された場合
      • Failure
        • Config JSONリクエストが含む機能はサーバで認識されたが、詳細設定に失敗した場合
      • top_level_key:
        • transcription
        • keywordBoosting
        • forbidden
    • Config JSONレスポンスのconfig.${top_level_key}.statusは以下のような値を持つことができます。
      • 共通
        • Unknown key: ${top_level_key}-${unknown_key}
          • Config JSONリクエストが、サーバがサポートしない sub level keyを持っている場合
        • Invalid type: ${top_level_key}-${invalid_type_key}
          • Config JSONリクエストが、サーバがサポートしない sub level value typeを持っている場合
      • transcription
        • Invalid language code: ${invalid_language_code}
          • Config JSONリクエストで、languageが事前に定義された言語コードではない場合
      • keywordBoosting
        • Internal system error
          • サーバ内部で問題が発生した場合
    • "${message}"
      • Config JSONリクエストが正常に認識されなかったか、Config JSONリクエストが正常に処理できない場合
      • ${message}内容の候補になれるのは以下の通りです。
        • Invalid request json format
          • Config JSONリクエストが正常な JSON形式ではない場合
        • Unknown key: ${unknown_key}
          • Config JSONリクエストが、サーバがサポートしない top level keyを持っている場合
        • Invalid type: ${invalid_type_key}
          • Config JSONリクエストが、サーバがサポートしない top level value typeを持っている場合
        • Required key is not provided
          • Config JSONリクエストが、サーバで定義した Required keyを含まない場合
        • No more slot
          • 現在、サーバで収容可能なリソースがない場合
        • ConfigRequest did not complete
          • Config JSONリクエストの処理が完了していない状態でサーバが認識リクエストを受けた場合
        • Lifespan expired
          • gRPC Serviceの使用時間が期限切れになった場合
          • gRPC Serviceの使用時間は100時間に設定されます。
        • Failed to received request msg
          • サーバがリクエストメッセージを正常に受信できなかった場合
        • Model server is not working
          • サーバ内部で問題が発生した場合
        • Internal server error
          • サーバ内部で問題が発生した場合

2) Config JSON例

# Config JSONリクエスト例
# Keyword Boosting && 禁止語機能設定をリクエストする場合
{
  "keywordBoosting": {                  
    "boostings": [
      {
        "words": "test,test1,test2",
        "weight": 1
      },
      {
        "words": "テスト,テスト1,テスト2",
        "weight": 0.5
      }
    ]
  },
  "forbidden": {
    "forbiddens":  "禁止語1,禁止語2"
  }
}

# Config JSONレスポンス例
# Keyword Boosting && 禁止語機能設定をリクエストし、成功した場合
{
  "uid": "2023-03-02_13-13-16_b49f35ec-7cf0-434b-9489-30b0b66f6d58",
  "responseType": [ "config" ],
  "config": {
    "status": "Success",
    "keywordBoosting": {
      "status": "Success"
    },
    "forbidden" : {
      "status": "Success"
    }
}

# Keyword Boosting && 禁止語機能設定をリクエストし、失敗した場合 1
# 禁止語リクエスト jsonに top level keyをサポートしない「forbidden」に設定してリクエストした場合 
{
  "uid": "2023-03-02_13-13-16_b49f35ec-7cf0-434b-9489-30b0b66f6d58",
  "responseType": [ "config" ]
  "config": {
    "status": "Unknown key: forbidden"
  }
}

5. Recognizeリクエスト

  • Config JSONで目的の設定値を指定したら、次は音声認識を行う番です。
  • protocで作成してくれたコードにある NestRequestと上で設定した認証メタデータを stubのメソッドである recognizeを通じて音声認識 APIを呼び出します。
  • サンプルコードは下段にあります。NestRequestを呼び出す際は、次のような Optional設定も一緒に呼び出すことができます。

Recognize Request JSON Format (ExtraContents)

{
  "epFlag": bool    # optional
  "seqId": int      # optional
}

recognizeリクエストで使用される JSON Formatです。

  • epFlag

    • recognizeリクエストのうち、一時停止または最後の認識リクエストで使用される flagです。
    • 一時停止または最後のリクエストの場合、epFlagtrueに設定するとエンジンに溜めておいた認識リクエスト Bufferを直ちに返して認識を終了し、delayなく認識結果を受け取ることができます。(コネクション接続は維持)
    • 設定せずに転送する場合はfalseに設定されます。unvoiceTime(10秒)を超えるまで認識リクエストがない場合、溜めておいた認識リクエスト Bufferを直ちに返して認識を終了し、delayなく認識結果を受け取ることができます。(コネクション接続は維持)
    • optionalフィールドです。
  • seqId

    • Recognize接続後に送信する認識リクエストごとに持つ固有の IDです。
    • epFlagtrueに設定してリクエストした場合、その後に受け取った認識結果で、本リクエストに対する結果かどうかを確認する用途に使用できます。
    • seqIdを設定して転送しない場合、認識結果のseqIdは0に設定されます。
    • seqId使用時は0ではない値に設定して転送することをお勧めします。
    • optionalフィールドです。
  • 参考事項

    • epFlagseqIdのどちらも使用しない場合、リクエスト JSONを""(empty string)に設定できます。

6. Recognizeレスポンス

  • ストリーミング APIを呼び出すと、次のようなレスポンスをサーバから受け取ることができます。

Transcription JSON Format

{
  "uid": string
  "responseType": [ "transcription" ]
  "transcription": {
    "text": string,
    "position": int,
    "periodPositions": [ int ],
    "periodAlignIndices": [ int ],
    "epFlag": bool,
    "seqId": int,
    "epdType": string,         // 結果が生成された epd基準
    "startTimestamp": int,
    "endTimestamp": int,
    "confidence": float64,
    "alignInfos": [
      {
        "word": string,        // 音節
        "start": int,          // StartTimestamp in ms 
        "end": int,            // EndTimestamp in ms
        "confidence": float64  // 認識信頼度
      }
    ]
  }
}
  • 設定例
{
  "uid": "2023-03-02_13-13-16_b49f35ec-7cf0-434b-9489-30b0b66f6d58"
  "responseType": [ "transcription" ]
  "transcription": {
    "text": "これはテキストです。",
    "position": 0,
    "periodPositions": [3],
    "periodAlignIndices": [3],
    "epFlag": false,
    "seqId": 0,
    "epdType": "durationThreshold",
    "startTimestamp": 190,
    "endTimestamp": 840,
    "confidence": 0.997389124199423,
    "alignInfos": [
      {"word":"これは","start":190,"end":340,"confidence":0.9988637124943075}, 
      {"word":"テキスト","start":341,"end":447,"confidence":0.9990018488549978},
      {"word":"です","start":448,"end":580,"confidence":0.9912501264550316},
      {"word":".","start":581,"end":700,"confidence":0.9994397226648595},
      {"word":" ","start":701,"end":840,"confidence":0.9984142043105126}
    ]
  }
}

Failed Recognize Response JSON Format

{
  "uid": string,                     # required
  "responseType": [ "recognize" ],    # required
  "recognize": {                     # required
    "status": string,                # required
    "epFlag": {                      # optional
      "status": string
    },
    "seqId": {                       # optional
      "status": string
    },
    "audio": {                       # optional
      "status": string
    }
  }
}

認識リクエストが失敗したか、認識リクエストが正常に処理できない場合、レスポンスの JSON形式です。

  • recognize.statusは recognizeリクエスト失敗の理由または処理できない理由を表す keyで、以下のような値を持ちます。
  • Invalid Type
    • epFlagまたはseqId value typeが事前に定義された typeと合致しない場合
  • recognize.statusは recognizeリクエスト失敗の理由または処理できない理由を表す keyで、以下のような値を持ちます。
    • Required key is not provided

      • extraContentsの required keyであるepFlagを渡さなかった場合
    • Invalid request json format

      • extraContentsが json formatではない場合
    • Unknown key

      • extraContentsに protocol specにない keyを作成した場合

      • 無効な keyを入力した場合には、ユーザーの便宜のために、statusの末尾に無効な key情報を付けて渡す

        # invalid extraContents
        {
          "test1": "test-val1",
          "test2": "test-val2"
        }
        # response msg
        {
          "uid": "2023-03-02_13-13-16_b49f35ec-7cf0-434b-9489-30b0b66f6d58"
          "responseType": ["recognize"],
          "recognize": {
            "status": "Unknown key: test1, test2"
          }
        }
        
    • ConfigRequest is already called

      • サーバで configリクエストの処理が完了した状態で再度 configリクエストを受けた場合
    • Lifespan expired

      • gRPC Serviceの使用時間が期限切れになった場合
      • gRPC Serviceの使用時間は100時間に設定されます。
    • Failed to received request msg

      • サーバでリクエストメッセージを正常に受信できなかった場合
    • Model server is not working

      • サーバ内部で問題が発生した場合
    • Internal server error

      • サーバ内部で問題が発生した場合
    • Invalid format

      • 転送したオーディオ形式が無効な形式の場合
    • Failure

      • recognizeリクエスト jsonにextraContentsが無効な形式の場合
      • epFlag.statusまたはseqId.statusに、失敗の詳細な理由が表示されます。
  • epFlag.statusは epFlagの入力に失敗した理由を表し、以下のような値を持ちます。
    • Not found
      • required keyの epFlagに対して作成しなかった場合
    • Invalid type
      • value typeが事前に定義された typeと合致しない場合
    • seqId.statusは seqIdの入力に失敗した理由を表し、以下のような値を持ちます。
      • Invalid type
        • value typeが事前に定義された typeと合致しない場合
  • audio.statusはオーディオデータの処理に失敗した理由を表し、以下のような値を持ちます。
    • Invalid format
      • Audio formatが事前に定義された formatと合致しない場合
  • 参考事項
    • recongnize.statusの値によって、epFlagseqIdaudio keyは省略されることがあります。

分析方法

  • text
    • 認識結果を担当する keyです。
  • position
    • 渡された full textに対し、textで渡された textの offsetを担当する keyです。

    • textpositionを使用して、full textを構成する方法

      渡された順序 認識結果 full text
      1 {text: "ABC", position: 0, ...}
      2 {text: "DEFG", position: 3, ...}
  • periodPositions
    • 渡された full textに対し、textで渡された.(句読点)の offsetを担当する keyです。
    • textに句読点がない場合、空の listで渡されます。
  • periodAlignIndices
    • textで渡された.(句読点)のalignInfosでの index情報を担当する keyです。
    • textに句読点がない場合、空の listで渡されます。
  • epFlag
    • リクエストで epFlagを Trueに設定し、送信した音源の認識結果を含めるかどうかを表す keyです。
  • seqId
    • 本認識結果が含む最後のリクエストのseqIdを表す keyです。
    • epFlag keyの valueが falseの時は0が返されます。
    • epFlag keyの値が trueのときは、処理された最後の認識リクエストの seqIdが返されます。
  • epdType
    • 認識結果を生成するのに使用された epd基準を担当する keyです。
    • epd基準に基づいたepdType
      • 黙字に基づいて認識結果が生成された場合、gap
      • 最後の audio chunkが含まれて認識結果が生成された場合、endPoint
      • 時間に基づいて認識結果が生成された場合、durationThreshold
      • 句読点に基づいて認識結果が生成された場合、period
      • 音節数に基づいて認識結果が生成された場合、syllableThreshold
      • unvoiceTime実行(サーバ設定)により認識結果が生成された場合、unvoice
  • startTimestampendTimestamp
    • 認識結果のタイムスタンプ情報です。
    • 単位はms(ミリ秒)を使用します。
  • confidence
    • 認識結果(text)に対する信頼度を表す keyです。
    • 認識結果に含まれるすべての音節信頼度(alignInfos.confidence)値の幾何平均で計算されます。
  • alignInfos
    • textを構成する各音節のalign情報を担当する keyです。
    • textに認識された音節がない場合、""(empty text)align情報を追加して渡します。
    • align情報の説明
      • word
        • 音節情報を担当する keyです。
      • start
        • 音節の開始 timestampを担当する keyです。
        • 単位はmsを使用します。
      • end
        • 音節の終了 timestampを担当する keyです。
        • 単位はmsを使用します。
      • confidence
        • 音節が認識された結果に対する信頼度を表す keyです。
        • 0から1の間の値を持ちます。

Demo [Python]

import grpc
import json

import nest_pb2
import nest_pb2_grpc

AUDIO_PATH = "path/to/audio/file"          #認識対象の Audioファイルがあるパスを入力してください。(16kHz、1channel、16 bits per sampleの PCM(headerがない raw wave)形式)
CLIENT_SECRET = "長文認識 secretKey"

def generate_requests(audio_path):
    # 初期設定リクエスト: 音声認識設定
    yield nest_pb2.NestRequest(
        type=nest_pb2.RequestType.CONFIG,
        config=nest_pb2.NestConfig(
            config=json.dumps({"transcription": {"language": "ko"}})
        )
    )

    # オーディオファイルを開いて32,000バイトずつ読み取る
    with open(audio_path, "rb") as audio_file:
        while True:
            chunk = audio_file.read(32000)  # オーディオファイルのチャンクを読み取る
            if not chunk:
                break  # データがなくなったらループ終了
            yield nest_pb2.NestRequest(
                type=nest_pb2.RequestType.DATA,
                data=nest_pb2.NestData(
                    chunk=chunk,
                    extra_contents=json.dumps({"seqId": 0, "epFlag": False})
                )
            )

def main():
    # Clova Speechサーバに対するセキュリティ gRPCチャンネルを設定
    channel = grpc.secure_channel(
        "clovaspeech-gw.ncloud.com:50051", 
        grpc.ssl_channel_credentials()
    )
    stub = nest_pb2_grpc.NestServiceStub(channel)  # NestServiceのスタブを作成
    metadata = (("authorization", f"Bearer {CLIENT_SECRET}"),)  # 認証トークンと一緒にメタデータを設定
    responses = stub.recognize(generate_requests(AUDIO_PATH), metadata=metadata)  # 作成されたリクエストで recognizeメソッドを呼び出す

    try:
        # サーバからのレスポンスを繰り返し処理
        for response in responses:
            print("Received response: " + response.contents)
    except grpc.RpcError as e:
        # gRPCエラー処理
        print(f"Error: {e.details()}")
    finally:
        channel.close()  # 作業が終わったらチャンネルを閉じる

if __name__ == "__main__":
    main()

Demo [Java]

Project Structure

├───pom.xml
│   │
└───src
│   ├───main
│   │   ├───java
│   │   │   └───com
│   │   │       └───example
│   │   │           └───grpc
│   │   │                   GRpcClient.java
│   │   │
│   │   ├───proto
│   │   │       nest.proto

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>clova-speech-grpc</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <java.version>1.8</java.version>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <netty.version>4.1.52.Final</netty.version>
        <grpc.version>1.35.0</grpc.version>
        <protoc.version>3.14.0</protoc.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-netty</artifactId>
            <version>${grpc.version}</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-netty-shaded</artifactId>
            <version>${grpc.version}</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-protobuf</artifactId>
            <version>${grpc.version}</version>
        </dependency>
        <dependency>
            <groupId>io.grpc</groupId>
            <artifactId>grpc-stub</artifactId>
            <version>${grpc.version}</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
            <version>1.18.12</version>
        </dependency>
    </dependencies>

    <build>
        <extensions>
            <extension>
                <groupId>kr.motd.maven</groupId>
                <artifactId>os-maven-plugin</artifactId>
                <version>1.6.1</version>
            </extension>
        </extensions>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <executions>
                    <execution>
                        <id>compile</id>
                        <phase>compile</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>testCompile</id>
                        <phase>test-compile</phase>
                        <goals>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <showDeprecation>true</showDeprecation>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.xolstice.maven.plugins</groupId>
                <artifactId>protobuf-maven-plugin</artifactId>
                <version>0.6.1</version>
                <configuration>
                    <protocArtifact>
                        com.google.protobuf:protoc:${protoc.version}:exe:${os.detected.classifier}
                    </protocArtifact>
                    <pluginId>grpc-java</pluginId>
                    <pluginArtifact>
                        io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:${os.detected.classifier}
                    </pluginArtifact>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

grpcClient.java

package com.example.grpc;

import java.io.FileInputStream;
import java.util.concurrent.CountDownLatch;

import com.google.protobuf.ByteString;
import com.nbp.cdncp.nest.grpc.proto.v1.NestConfig;
import com.nbp.cdncp.nest.grpc.proto.v1.NestData;
import com.nbp.cdncp.nest.grpc.proto.v1.NestRequest;
import com.nbp.cdncp.nest.grpc.proto.v1.NestResponse;
import com.nbp.cdncp.nest.grpc.proto.v1.NestServiceGrpc;
import com.nbp.cdncp.nest.grpc.proto.v1.RequestType;
import io.grpc.ManagedChannel;
import io.grpc.Metadata;
import io.grpc.StatusRuntimeException;
import io.grpc.netty.NettyChannelBuilder;
import io.grpc.stub.MetadataUtils;
import io.grpc.stub.StreamObserver;

public class GRpcClient {
	public static void main(String[] args) throws Exception {

		CountDownLatch latch = new CountDownLatch(1);
		ManagedChannel channel = NettyChannelBuilder
			.forTarget("clovaspeech-gw.ncloud.com:50051")
			.useTransportSecurity()
			.build();
		NestServiceGrpc.NestServiceStub client = NestServiceGrpc.newStub(channel);
		Metadata metadata = new Metadata();
		metadata.put(Metadata.Key.of("Authorization", Metadata.ASCII_STRING_MARSHALLER),
			"Bearer ${secretKey}");
		client = MetadataUtils.attachHeaders(client, metadata);

		StreamObserver<NestResponse> responseObserver = new StreamObserver<NestResponse>() {
			@Override
			public void onNext(NestResponse response) {
				System.out.println("Received response: " + response.getContents());
			}

			@Override
			public void onError(Throwable t) {
				if(t instanceof StatusRuntimeException) {
					StatusRuntimeException error = (StatusRuntimeException)t;
					System.out.println(error.getStatus().getDescription());
				}
				latch.countDown();
			}

			@Override
			public void onCompleted() {
				System.out.println("completed");
				latch.countDown();
			}
		};

		StreamObserver<NestRequest> requestObserver = client.recognize(responseObserver);

		requestObserver.onNext(NestRequest.newBuilder()
			.setType(RequestType.CONFIG)
			.setConfig(NestConfig.newBuilder()
				.setConfig("{\"transcription\":{\"language\":\"ko\"}}")
				.build())
			.build());

		java.io.File file = new java.io.File("~/media/42s.wav");
		byte[] buffer = new byte[32000];
		int bytesRead;
		FileInputStream inputStream = new FileInputStream(file);
		while ((bytesRead = inputStream.read(buffer)) != -1) {
			requestObserver.onNext(NestRequest.newBuilder()
				.setType(RequestType.DATA)
				.setData(NestData.newBuilder()
					.setChunk(ByteString.copyFrom(buffer, 0, bytesRead))
					.setExtraContents("{ \"seqId\": 0, \"epFlag\": false}")
					.build())
				.build());
		}
		requestObserver.onCompleted();
		latch.await();
		channel.shutdown();
	}

}

FAQs

  • Recognize APIのextraContentsフィールドで epFlag、seqId項目はどのように活用できますか?
    • 一時停止の目的で活用したり、送信したリクエストに対するレスポンスをすべて受け取ったかどうかを確認する用途に使用できます。
  • gRPC Serviceで一時停止機能をサポートしますか?
    • 一時停止機能は提供していません。ただし、Recognize APIでextraContentsフィールドのepFlag項目をtrueに設定してリクエストを転送した後、一定時間 Recognizeリクエストを行わない方法で実装が可能です。epFlag項目の説明をご参照ください。
    • epFlagを trueに設定せずに Recognizeをリクエストして一定時間再リクエストしない場合、サーバ内部で設定されたunvoiceTime(10秒)に基づいてバッファリングされている認識リクエストを処理し、レスポンス結果を表示します。
  • Recognize APIはどんな音源データ形式をサポートしますか?
    • 現在、16kHz、1channel、16bits per sampleの PCM(headerのない raw wave)形式のみサポートします。
  • Close APIを呼び出す前に、Recognize APIの extraContentsにepFlag項目をtrueに設定するのは必須ですか?
    • epFlag項目をtrueに設定する必要はありません。ただし、最後の Recognizeリクエストに対して迅速なレスポンス結果を受け取りたい場合は、epFlag項目をtrueに設定することをお勧めします。epFlag項目の説明をご参照ください。
  • 送ったリクエストに対するレスポンスをすべて受け取ったかどうかはどのように確認できますか?
    • Recognize APIを呼び出す際に、extraContentsフィールドのepFlagseqId項目を活用できます。epFlag項目をtrueに設定し、seqId項目を0ではない任意の値に設定した Recognizeリクエストの処理結果は、RecognizeレスポンスのepFlagseqIdを照合すると確認できます。Recognize Responseのレスポンス JSON形式をご参照ください。
  • gRPC Serviceの Connection Lifetime制限がありますか?
    • gRPC Serviceは、Connection Lifetimeの制限を100時間に設定していますが、ネットワーク問題などで切断が発生する場合があります。安定的なサービス利用のために retryロジックを反映することをお勧めします。