Parse transcription data

Updated

Encrypt the captions transcribed with RTT

Agora uses Protocol Buffers (protobuf) to serialize transcription data. Protobuf, developed by Google, is a language-neutral, platform-independent way to serialize structured data. It enables efficient, consistent data handling across platforms by generating source code in multiple programming languages. Learn more at protobuf.dev.

Understand the tech

Agora Real-Time STT supports transmitting transcription and translation results through the data stream in either Protobuf or JSON format. Protobuf is used by default. If you set enableJsonProtocol to true when calling join, the service uses JSON format instead. This guide explains how to parse both Protobuf and JSON data on the receiving end, and how to extract specific text fields from the parsed data structure.

Prerequisites

To follow this procedure, you must:

  • Have a valid Agora Account.

  • Have a valid Agora project with an app ID and a temporary token or a token server. For details, see Agora account management.

  • Have a computer with access to the internet. If your network has a firewall, follow the steps in Firewall requirements.

  • Join an RTC channel as a host and start streaming. Refer to the Voice SDK quickstart guide.

  • Enable Real-Time STT for your app.

  • Install the Protobuf compiler to generate code classes that process transcription text.

Since the format of Protobuf may vary across versions, best practice is to ensure that the Protobuf SDK versions used for generated code and client deserialization are consistent.

Parse transcription data using Protobuf

This section explains how to use protoc to generate sample code in different languages to deserialize received Protobuf data. If you use JSON format instead, skip directly to JSON protocol data structure to parse the messages.

Follow these steps to write a script that calls the protoc compiler to generate code in different languages.

Create a Protobuf definition file

Protobuf allows you to generate source code in your preferred language based on the structure defined in the .proto file. Agora provides the following Protobuf definition for parsing RTT data. To use the file for generating code:

  1. Copy the following Protobuf definition to a local SttMessage.proto file:

    syntax = "proto3";
    
    package Agora.SpeechToText;
    option objc_class_prefix = "Stt";
    option csharp_namespace = "AgoraSTTSample.Protobuf";
    option java_package = "io.agora.rtc.speech2text";
    option java_outer_classname = "AgoraSpeech2TextProtobuffer";
    
    message Text {
      reserved 1 to 3, 5, 7 to 9, 11, 17;
      int64 uid = 4;
      int64 time = 6;
      repeated Word words = 10;
      int32 duration_ms = 12;
      string data_type = 13;
      repeated Translation trans = 14;
      string culture = 15;
      int64 text_ts = 16;
      OriginalTranscript original_transcript = 18;
      int64 sentence_id = 19;
    }
    
    message Word {
      reserved 2, 3, 5;
      string text = 1;
      bool is_final = 4;
    }
    
    message Translation {
      bool is_final = 1;
      string lang = 2;
      repeated string texts = 3;
    }
    
    message OriginalTranscript {
      string culture = 1;
      repeated Word words = 2;
    }

    For a description of each field in the SttMessage.proto file, browse the Reference section.

  2. Edit the following properties in your .proto file to match your project:

    • package: The source code package namespace.
    • option: The desired language options.

Generate source code script

Create a shell script named generate_code.sh with the following content:

#!/bin/sh

# Specify the path to the protoc compiler. In the example code
# The Protobuf version used is 21.12. You can replace it according to your actual needs.
PROTOC_PATH=./protoc-21.12-osx-aarch_64/bin/protoc

# Specify the path to the .proto file.
# The detailed description of the data structure can be found in the reference section.
PROTO_FILE=./SttMessage.proto

# Specify the output directory.
JAVA_OUT_DIR=$(pwd)/code/java

# Create the output directory (if it doesn't exist).
mkdir -p $JAVA_OUT_DIR

# Generate Java code.
$PROTOC_PATH --java_out=$JAVA_OUT_DIR $PROTO_FILE

# Output a message once code generation is finished.
echo "Code generation completed."

Ensure Protobuf dependencies are installed. If the dependencies are already installed, skip this step.

Install Protobuf Dependencies
  1. Edit your project’s Podfile to add the following line:

    # 3.21.12 indicates the Protobuf version. You can choose the appropriate version according to your actual needs.
             pod "Protobuf", "3.21.12"
  2. Run the following command in the Podfile directory:

    pod install

Open the .xcworkspace file generated in the project folder to proceed in Xcode.

Create a shell script, named generate_code.sh and add the following code to it:

#!/bin/sh

# Specify the path to the protoc compiler. In the example code, the Protobuf version used is 21.12. You can replace it according to your actual needs.
PROTOC_PATH=./protoc-21.12-osx-aarch_64/bin/protoc

# Specify the path to the .proto file. The detailed description of the data structure can be found in the reference information.
PROTO_FILE=./SttMessage.proto

# Specify the output directory.
OBJC_OUT_DIR=$(pwd)/code/objective-c

# Create the output directory (if it doesn't exist).
mkdir -p $OBJC_OUT_DIR

# Generate Objective-C code.
$PROTOC_PATH --objc_out=$OBJC_OUT_DIR $PROTO_FILE

# Output a message once code generation is finished.
echo "Code generation completed."

Create a shell script named generate_code.sh with the following content:

#!/bin/sh

# Path to the protoc compiler
PROTOC_PATH=./protoc-21.12-osx-aarch_64/bin/protoc

# Path to the .proto file
PROTO_FILE=./SttMessage.proto

# Output directory
CSHARP_OUT_DIR=$(pwd)/code/csharp

# Create output directory if it doesn't exist
mkdir -p $CSHARP_OUT_DIR

# Generate C# code
$PROTOC_PATH --csharp_out=$CSHARP_OUT_DIR $PROTO_FILE

echo "C# code generation completed."

Replace ./SttMessage.proto with the correct path as explained in the Create a Protobuf definition section.

To generate JavaScript code, ensure that the necessary Protobuf dependencies are installed. Follow the steps below to install them.

Install Protobuf Dependencies
  1. Open your project’s root directory and edit the package.json file to include the following dependencies:

    {
                "dependencies": {
                  "protobufjs": "^7.2.5"
                },
                "devDependencies": {
                  "pbjs": "^0.0.14",
                  "protobufjs-cli": "^1.1.2"
                }
              }
  2. Run the following command to install the dependencies:

    npm install

Next, create a shell script:

  1. Create a file named generate_code.sh.

  2. Add the following content:

    # Add the executable file path of protobufjs-cli to the PATH environment variable
    # Replace {absolute path of protobufjs-cli in your node_modules}/bin with the absolute path of protobufjs-cli in node_modules
    export "PATH=$PATH:{absolute path of protobufjs-cli in your node_modules}/bin"
    
    # Generate JavaScript example code
    pbjs -t json-module -w es6 ./SttMessage.proto > ./SttMessage_es6.js
    
    echo "JavaScript code generation completed."

    Replace ./SttMessage.proto with the path to the file you created in the Create a Protobuf definition file section.

Run the script

To generate a Protobuf class, run these commands in your terminal:

# Make the script executable
chmod +x generate_code.sh

# Run the script
./generate_code.sh

Deserialize transcription data

When transcription text is available, your RTC SDK event handler receives the stream message callback. Use the generated Protobuf class to deserialize the received data and convert it back into a data structure or object.

// Join a channel and add callback events
 rtcManager.joinChannel(roomName, localUid, agora_token, roleType.equals(ROLE_TYPE_BROADCAST), new RtcManager.OnChannelListener() {
     ...
     // Callback for receiving a stream message
     @Override
     public void onStreamMessage(int uid, int streamId, byte[] data) {
         // Check if the remote user ID matches the specified streaming bot ID.
         // If so, decode the stream data into a text object.
         if (String.valueOf(uid).equalsIgnoreCase(RTC_UID_STT_STREAM)) {
             AgoraSpeech2TextProtobuffer.Text text = STTManager.getInstance().parseTextByte(roomName, data);
             // Convert the parsed text object to JSON format and print it to the log
             LogUtil.d(originLogName, mGson.toJson(text));
         }
     }
     ...
 });

 public AgoraSpeech2TextProtobuffer.Text parseTextByte(String channel, byte[] data) {
     // Declare a variable of type AgoraSpeech2TextProtobuffer.Text to store the deserialized object
     AgoraSpeech2TextProtobuffer.Text textStream;
     try {
         // Deserialize the byte array data into an AgoraSpeech2TextProtobuffer.Text object
         textStream = AgoraSpeech2TextProtobuffer.Text.parseFrom(data);
     } catch (Exception ex) {
         notifyErrorHandler(new ErrorInfo("parseTextByte", "-1", "parseTextByte parseFrom error >> " + ex.toString()));
         return null;
     }
     ...
 }

Reference

This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.

Sample project

Agora provides an open-source Agora-RTT-Demo sample project. You can download it or view its source code.

SttMessage.proto fields

The following tables describe the fields in the SttMessage.proto file.

Text message fields

Field nameTypeDescription
uidint64The user ID associated with the text.
timeint64The start time of this sentence segment transcription . Has a value only when isFinal is true; otherwise, the value is 0.
wordsrepeatedAn array of transcription results. See WordMessage type for details.
duration_msint32The duration of the transcribed text in milliseconds.
data_typestring

The type of data:

  • transcribe: Transcription
  • translate: Text translation
transrepeatedAn array of translation results. See TranslationMessage type for details.
culturestringThe source language of the transcription.
text_tsint64The continuously incremented timestamp of the transcription result, used to align source and target text during real-time translation.
original_transcriptOriginalTranscriptThe transcribed text used for translation.
sentence_idint64A unique identifier for the subtitle in the data stream used for precise alignment between the original and translated subtitles.

Word message fields

Field NameTypeDescription
textstringThe transcription result.
is_finalbool

Indicates whether this sentence is the final transcription result.

  • true: The transcription engine has determined the result for this sentence, and no further modifications are expected. This does not mean the sentence is semantically complete.
  • false: The result is not yet final and may change.

Translation message fields

Field NameTypeMeaning
is_finalbool

Indicates whether this sentence is the final translation result.

  • true: The translation engine has determined that the translation result is final and no further modification is required. This does not mean the sentence is semantically complete.
  • false: The translation result is not yet final and may be updated.
langstringThe target language of the translation.
textsrepeatedThe translated text results.

Original transcript message fields

Field NameTypeMeaning
culturestringThe source language of the transcription.
wordsrepeatedAn array of transcription results.

A single Protobuf Text message can carry multiple segments. For example, the words[] array in a transcription message or the trans[] array in a translation message can have a length greater than 1. Do not assume that a single data stream message contains only one transcription or translation segment.

JSON protocol data structure

If you set enableJsonProtocol to true when calling join, the service pushes caption data in JSON format instead. The top-level JSON object contains a transcript or translation field:

  • transcript: The transcription result.
  • translation: The translation result.

The JSON protocol uses results[] to explicitly carry one or more segments. For a single segment, results[] has a length of 1. For a mixed result, a single message can contain both a stabilized prefix segment and a segment that may still change, in which case results[] has a length greater than 1.

This is a breaking change to the JSON client protocol. If your client uses the JSON protocol, you must upgrade it together with the server. After upgrading, do not assume that a single data stream message corresponds to only one transcription or translation segment. Iterate over results[] to process each segment, and handle messages where results[] is an empty array.

transcript fields

FieldTypeDescription
uidNumberThe user ID of the audio stream.
textTsNumberThe primary timestamp for this batch of transcription results, taken from the text_id of the first segment.
offsetNumberThe start time of this batch of transcription results, in milliseconds.
durationNumberThe duration of this batch of transcription results, in milliseconds.
languageStringThe source language of the first transcription segment.
textStringThe transcription text.
isFinalBooleanWhether the transcription text is the final result.
sentenceIdNumberThe sentence-level aggregation unit ID. Indicates that the transcription segments in results[] belong to the same sentence or aggregation round.
resultsArrayAn array of transcription segments. See transcript.results fields.

transcript.results fields

FieldTypeDescription
textStringThe text of the current transcription segment.
isFinalBooleanWhether the current transcription segment is the final result.
offsetNumberThe start time of the current transcription segment, in milliseconds.
durationNumberThe duration of the current transcription segment, in milliseconds.

translation fields

FieldTypeDescription
uidNumberThe user ID of the audio stream.
textTsNumberThe primary timestamp for this batch of translation results, taken from the text_id of the first translation segment.
offsetNumberThe start time of this batch of translation results, in milliseconds.
durationNumberThe duration of this batch of translation results, in milliseconds.
isFinalBooleanWhether the translation result is the final result.
sentenceIdNumberThe sentence-level aggregation unit ID. Indicates that the translation segments in results[] belong to the same sentence or aggregation round.
results0ObjectThe translation result field retained for backward compatibility. Its structure is unchanged.
resultsArrayAn array of translation segments. See translation.results fields.
original_transcriptObjectThe original transcription result. Present only when returning the original text is enabled.

translation.results fields

FieldTypeDescription
languageStringThe target language of the translation.
textsArrayAn array of translated texts for the current segment.
isFinalBooleanWhether the current translation segment is the final result.

original_transcript fields

FieldTypeDescription
languageStringThe source language of the original text.
textStringThe merged original text.
resultsArrayAn array of original text segments. Each item includes text and isFinal.

Translate and transcribe examples

This section shows sample output from the STT service for transcribed and translated sentences, in both Protobuf and JSON format.

Transcribe

time: 1753359518654
 words {
   text: "Hello, how are you?"
   is_final: true
 }
 duration_ms: 770
 data_type: "transcribe"
 culture: "en-US"
 text_ts: 1753359520754
 sentence_id: 1753359518654

Translate

time: 1753359518654
duration_ms: 770
data_type: "translate"
trans {
  is_final: true
  lang: "es-ES"
  texts: "Hola, ¿cómo estás? "
}
text_ts: 1753359520754
sentence_id: 1753359518654
original_transcript {
  culture: "en-US"
  words {
    text: "Hello, how are you?"
    is_final: true
  }
}