Skip to main content

使用 AOQ 接入 qwen3.8-livetranslate-flash-realtime 實現實時語音翻譯

本文以 Android 平台為例,介紹如何接入 AOQ Client SDK,使用 qwen3.8-livetranslate-flash-realtime 實現語音的實時翻譯。其他平台的接入流程和模型事件處理方式類似,使用對應平台的 SDK 接口即可;下文客戶端代碼統一使用 Java。

模型能力詳情請參考實時語音/音視頻翻譯模型說明,媒體與模型事件的分工請參考 AOQ 接入。

SDK 獲取

AOQ Client SDK 及音頻 Opus 插件請參見 SDK 下載。本文的音頻傳輸示例使用 Opus,需要同時導入對應平台的插件。模型支持的地域、語種、計費和限流以實時語音/音視頻翻譯模型說明為準。

SDK 導入

  • Android
  • iOS
  • HarmonyOS
將 AoqClientSdk-release.aar 放入工程 app/libs/,將 libPluginOpus.so 按 ABI 放入 app/libs/armeabi-v7a/ 和 app/libs/arm64-v8a/。在 app/build.gradle 中聲明依賴:
android {
    defaultConfig {
        minSdk 21
        ndk { abiFilters 'armeabi-v7a', 'arm64-v8a' }
    }
    sourceSets { main { jniLibs.srcDirs = ['libs'] } }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.aar'])
}
如果宿主工程已包含同名原生庫,應先確認版本與依賴關係,避免重復打包不同版本的庫。在 AndroidManifest.xml 中聲明權限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
RECORD_AUDIO 還需要在運行時申請並取得用戶授權。純語音翻譯不需要攝像頭權限;啓用視覺增強時,再聲明和申請 CAMERA。本文 Java 示例使用以下導入:
import com.alibaba.aoq.clientsdk.AoqClientEngine;
import com.alibaba.aoq.clientsdk.AoqClientEngine.*;
import com.alibaba.aoq.clientsdk.AoqClientListener;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import org.json.JSONException;
import org.json.JSONObject;

AppServer 獲取 Token

客戶端不直接保存百煉 API Key。由業務 AppServer 使用對應地域、業務空間的 API Key,為每次翻譯連接申請臨時憑證,再通過 HTTPS 返回給已經完成業務鑒權的客戶端。 按照 Token 鑒權的 AOQ 章節搭建 AppServer。實時翻譯屬於 Realtime 協議,申請憑證時指定:
model=qwen3.8-livetranslate-flash-realtime
以下命令僅在 AppServer 側執行。BAILIAN_ENDPOINT 設置為實際業務空間對應的 {WorkspaceId}.cn-beijing.maas.aliyuncs.com 或 {WorkspaceId}.ap-southeast-1.maas.aliyuncs.com;DASHSCOPE_API_KEY 從服務端環境變量或密鑰管理服務讀取。
curl --fail-with-body --request POST \
  "https://${BAILIAN_ENDPOINT}/api/v1/webrtc/realtime?model=qwen3.8-livetranslate-flash-realtime" \
  --header "Content-Type: application/json" \
  --header "Authorization: Bearer ${DASHSCOPE_API_KEY}" \
  --header "x-dashscope-rtc-transport: moq" \
  --data '{}'
如果 AppServer 能從可信代理鏈中取得客戶端真實公網 IP,可在請求體中加入 clientIp,用於接入點調度。 客戶端將返回值映射到 AoqConnectConfig:
網關返回字段SDK 字段說明
aoqTokenForClienttoken本次連接的臨時令牌
sidsid本次會話標識
clientRelayCertFingerprintcertFingerprint原樣使用服務端返回的證書指紋
clientRelayEndpointsrelayEndpoints按結構轉換完整接入點列表
extraInfo.workspaceIdHashworkspaceIdHash使用返回值;未返回時按 SDK 要求處理

實現實時語音翻譯

整體交互流程

AOQ 实时语音翻译交互时序

創建引擎並設置回調

調用 createEngine 創建引擎,注冊連接、Data 消息及錯誤回調。業務對象需在會話期間持有引擎和監聽對象。
// 配置引擎工作目录。
AoqCreateConfig createConfig = new AoqCreateConfig();
createConfig.workDir = appCtx.getFilesDir().getAbsolutePath();
// 创建引擎并注册事件监听对象。
engine = AoqClientEngine.createEngine(appCtx, createConfig, listener);
AoqClientListener可以獲取連接、模型消息和媒體設備等事件,請實現並在創建引擎的時候傳入。
AoqClientListener listener = new AoqClientListener() {
    // 接收音频采集与播放设备状态。
    @Override
    public void onAudioDeviceStateChanged(AoqAudioDeviceState state) {

    }

    // 接收耳机、蓝牙及扬声器等音频路由变化。
    @Override
    public void onAudioDeviceRouteChanged(int routeType) {
        android.util.Log.i("LiveTranslate", "音频路由: " + AoqAudioDeviceRouteType.fromValue(routeType));
    }

    // 接收音频设备中断及中断结束通知。
    @Override
    public void onAudioDeviceInterrupted(boolean interrupt) {
        android.util.Log.i("LiveTranslate", "音频设备中断: " + interrupt);
    }

    // 接收 Android 音频焦点变化。
    @Override
    public void onAudioDeviceFocusChanged(int audioFocus) {
        android.util.Log.i("LiveTranslate", "音频焦点: " + audioFocus);
    }

    // 接收 SDK 异步错误。
    @Override
    public void onError(int code, String message) {
        android.util.Log.e("LiveTranslate", "SDK 错误: " + code + ", " + message);
    }
};

啓動音頻採集與播放

在已經取得麥克風權限的前提下,準備採集和播放。先關閉音頻發送,再啓動採集,避免引擎建連後默認推流。 採集已啓動不代表模型已能接收音頻。頁面應在收到 session.updated 並成功開啓發送後,再提示用戶開始說話;本文不實現連接前音頻緩存。
// 关闭音频上行,等待模型配置确认。
engine.enableSendMediaStream(AoqTrackType.AoqTrackTypeAudio, false);

AoqAudioCaptureConfig audioCaptureConfig = new AoqAudioCaptureConfig();
audioCaptureConfig.channel = 1;
audioCaptureConfig.isExternal = false;
// 启动 SDK 内置麦克风采集。
engine.startAudioCapture(audioCaptureConfig);

AoqAudioPlaybackConfig audioPlaybackConfig = new AoqAudioPlaybackConfig();
audioPlaybackConfig.channel = 1;
audioPlaybackConfig.isExternal = false;
// 启动 SDK 内置播放器,播放下行译音。
engine.startAudioPlayer(audioPlaybackConfig);

設置編解碼及建立連接

主場景需要:
方向軌道用途
發佈Audio向模型輸入語音
發佈Data發送會話配置與結束事件
訂閱Audio接收翻譯語音
訂閱Data接收原文、譯文與狀態事件
音頻上行和下行分別配置。以下代碼以 Opus、16 kHz、單聲道傳輸配置為例。
AoqAudioCodecConfig audioEncoderConfig = new AoqAudioCodecConfig();
audioEncoderConfig.codecType = AoqEncoderType.AoqEncoderTypeAudioOpus;
audioEncoderConfig.sampleRate = 16000;
audioEncoderConfig.channel = 1;
// 设置上行音频编码参数。
engine.setAudioEncoderConfig(audioEncoderConfig);

AoqAudioCodecConfig audioDecoderConfig = new AoqAudioCodecConfig();
audioDecoderConfig.codecType = AoqEncoderType.AoqEncoderTypeAudioOpus;
audioDecoderConfig.sampleRate = 16000;
audioDecoderConfig.channel = 1;
// 设置下行音频解码参数。
engine.setAudioDecoderConfig(audioDecoderConfig);

AoqTrackParam audioTrackParam = new AoqTrackParam();
audioTrackParam.trackType = AoqTrackType.AoqTrackTypeAudio;
AoqTrackParam dataTrackParam = new AoqTrackParam();
dataTrackParam.trackType = AoqTrackType.AoqTrackTypeData;
// 发布音频和 Data 轨,发送语音及模型事件。
connectConfig.publishTracks.add(audioTrackParam);
connectConfig.publishTracks.add(dataTrackParam);
// 订阅音频和 Data 轨,接收译音及字幕事件。
connectConfig.subscribeTracks.add(audioTrackParam);
connectConfig.subscribeTracks.add(dataTrackParam);

// 发起 AOQ 连接,连接结果通过 onConnectionStatusChange 返回。
engine.connect(connectConfig);
connect 返回成功僅表示調用被接受。等待 onConnectionStatusChange 通知連接成功後,再發送模型配置。

配置翻譯會話

連接成功後,通過 Data 軌發送下面的 session.update。該配置使用默認音色,不開啓聲音復刻:
{
  "type": "session.update",
  "session": {
    "output_modalities": ["text", "audio"],
    "audio": {
      "input": {
        "turn_detection": {
          "type": "speaker_detection",
          "threshold": 0.5
        }
      }
    },
    "translation": {
      "language": "en"
    }
  }
}
交互流程請參考實時翻譯模型說明,session.update 完整參數請參考實時翻譯客戶端事件。 將上述 JSON 編碼為 UTF-8 字節,通過 sendDataMsg 發送。
private static final String SESSION_UPDATE_JSON =
        "{\"type\":\"session.update\",\"session\":{"
        + "\"output_modalities\":[\"text\",\"audio\"],"
        + "\"audio\":{\"input\":{\"turn_detection\":{"
        + "\"type\":\"speaker_detection\",\"threshold\":0.5}}},"
        + "\"translation\":{\"language\":\"en\"}}}";

// 接收 AOQ 连接状态变化。
@Override
public void onConnectionStatusChange(AoqConnectionStatus status) {
    if (status == AoqConnectionStatus.AoqConnectionStatusConnected) {
        AoqDataMsg message = new AoqDataMsg();
        message.data = SESSION_UPDATE_JSON.getBytes(StandardCharsets.UTF_8);
        // 通过 Data 轨发送翻译会话配置。
        engine.sendDataMsg(message);
    }
}

收到 session.updated 後開啓媒體發送

在 onDataMsg 中解析事件類型。 下面先展示控制事件分流;原文與譯文事件的處理見服務端事件。收到 session.finished 後設置業務完成狀態,再由“斷開連接與銷毀引擎”中的釋放方法收尾。
// 接收 Data 轨中的模型事件。
@Override
public void onDataMsg(AoqDataMsg message) {
    if (message == null || message.data == null) { return; }
    try {
        JSONObject event = new JSONObject(new String(message.data, StandardCharsets.UTF_8));
        String type = event.optString("type");
        if ("session.updated".equals(type)) {
            // 模型配置生效后,开启音频上行。
            engine.enableSendMediaStream(AoqTrackType.AoqTrackTypeAudio, true);
        } else if ("session.finished".equals(type)) {
            android.util.Log.i("LiveTranslate", "服务端已完成本次翻译处理");
        } else if ("error".equals(type)) {
            android.util.Log.e("LiveTranslate", "翻译模型错误: " + event.optJSONObject("error"));
        }
    } catch (JSONException error) {
        android.util.Log.e("LiveTranslate", "无法解析模型事件", error);
    }
}
AOQ 與 WebSocket 的區別:本文通過 Audio 軌直接發送和接收音頻。不要再發送 input_audio_buffer.append 重復上傳同一份音頻,也不要照搬 WebSocket 示例,在收到 response.audio.delta 後另起一個播放器重復播音。譯音由 SDK 音頻訂閱和播放器處理;Data 軌用於字幕與模型事件。媒體與 Data 軌的使用方式請參考 AOQ 接入。

結束輸入,等待尾句翻譯

點擊結束時,正常結束順序為:
  1. 停止採集,不再提交新的輸入。
  2. 完成輸入收尾後,關閉音頻上行,發送一次 session.finish;保持 AOQ 連接、Data 訂閱和音頻播放。
  3. 繼續接收尾句原文、譯文和譯音,等待 session.finished。
  4. 按本地播放收尾策略斷開連接、釋放資源。
session.finish 的發送要求請參考實時翻譯客戶端事件,session.finished 的字段與含義請參考實時翻譯服務端事件。
AoqDataMsg message = new AoqDataMsg();
message.data = "{\"type\":\"session.finish\"}".getBytes(StandardCharsets.UTF_8);
// 通知模型输入结束,等待 session.finished。
engine.sendDataMsg(message);

斷開連接與銷毀引擎

收到 session.finished 後,服務端已經完成本會話處理,但這不等於本地揚聲器已經播放完最後一段音頻。如果產品要求完整播完譯音,應先完成本地播放收尾,再執行以下方法。
// 停止译音播放。
engine.stopAudioPlayer();
// 断开 AOQ 连接。
engine.disconnect();
// 销毁本会话独占的引擎。
AoqClientEngine.destroy();

相關文檔