廣播功能指南
目錄
概述
VAS 廣播功能提供 1 對多的即時字幕廣播,主講者透過 WebSocket 傳送音訊進行語音辨識與翻譯,觀眾透過 SSE 即時接收多語言字幕。
核心特色
- 即時字幕:主講者語音即時轉為多語言字幕
- 1 對多架構:一個主講者,多個觀眾同時接收
- 多語言翻譯:支援多個翻譯語言,觀眾可選擇偏好語言
- TTS 語音:觀眾端可接收翻譯語音播放
- 預備階段:主講者可先熱機測試,確認設備正常後再正式開播
- 密碼保護:支援公開或密碼保護的廣播
- 容量管理:觀眾上限與排隊機制
- 說話者辨識:支援多人語者分離(Speaker Diarization)
涉及的 API 類型
| API 類型 | 角色 | 用途 |
|---|---|---|
| REST API | 主講者 | 建立、查詢、更新、撤銷廣播 |
| REST API | 觀眾 | 查詢廣播資訊、密碼驗證 |
| WebSocket | 主講者 | 音訊傳送、控制廣播、觀眾管理 |
| SSE | 觀眾 | 接收即時字幕、翻譯、TTS |
| 廣播 REST API | 觀眾 | 查詢廣播即時狀態 |
認證方式
- 主講者端:REST API 使用 API Key,WebSocket 使用 Ticket 認證。詳見 認證說明。
- 觀眾端:透過廣播 Token 認證,無需 API Key。密碼保護廣播需額外取得
viewer_access_token。
完整流程圖
主講者端 觀眾端
======== ========
[1] POST /api/v1/broadcasts
建立廣播,取得 token
|
[2] 以 token 組成觀眾頁網址分享 ────────────> 收到分享連結
| |
[3] POST /api/v1/auth/ticket [A] GET /api/v1/viewer/broadcasts/{token}
取得 WebSocket Ticket 查詢廣播公開資訊
| |
[4] WebSocket 連線 [B] (若密碼保護)
wss://vas-poc.vurbo.ai/ws POST /viewer/broadcasts/{token}/verify
| 取得 viewer_access_token
[5] 發送 start action |
type: "broadcast" [C] EventSource 連線
broadcast_token: "xxx" GET /broadcast/{token}/text
broadcast_phase: "standby" |
| |
===== 預備階段(Standby)===== |
| 收到 connected 事件
[6] STT/翻譯結果僅主講者可見 收到 standby 事件
主講者測試設備 顯示「準備中,請稍候...」
| |
===== 切換到正式階段 ===== |
| |
[7] broadcast_go_live ─────────────────────> 收到 phase_changed 事件
| phase: "live"
===== 正式直播(Live)===== |
| |
[8] 傳送音訊 (audio action) [D] 接收即時字幕
接收辨識結果 origin 事件(原文)
接收翻譯結果 translation 事件(翻譯)
| tts_ready 事件(TTS 音訊)
| |
[9] 暫停 (pause) ──────────────────────────> 收到 paused 事件
| |
[10] 恢復 (resume) ────────────────────────> 收到 resumed 事件
| |
[11] 發送公告 ─────────────────────────────> 收到 announcement 事件
| |
[12] 停止 (stop) ──────────────────────────> 收到 ended 事件
關閉 SSE 連線
主講者端流程
步驟 1:建立廣播
透過 REST API 建立廣播頻道,取得分享 Token 和連結。
curl -X POST "https://vas-poc.vurbo.ai/api/v1/broadcasts" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"transcription_languages": ["zh-TW", "en-US"],
"translation_languages": ["en-US", "ja-JP"],
"name": "技術研討會即時字幕",
"access_type": "public",
"max_viewers": 100,
"speaker_diarization": false,
"tts_config": {
"en-US": {"voice": "en-US-JennyNeural", "speaking_rate": 1.0},
"ja-JP": {"voice": "ja-JP-NanamiNeural", "speaking_rate": 1.0}
},
"summary_template": "meeting",
"summary_language": "zh-TW"
}'
關鍵請求參數:
| 參數 | 必填 | 說明 |
|---|---|---|
transcription_languages | 是(或改用已棄用的 transcription_language) | 轉錄語言陣列(string[],最多 10 個、不可重複;主講者說的語言)。啟用說話者辨識(speaker_diarization: true)時僅支援單一轉錄語言,同時提供多語言會回 422 |
transcription_language | 否 | (已棄用,向後相容)等同 transcription_languages 首元素 |
translation_languages | 否 | 翻譯語言陣列 |
name | 否 | 頻道名稱(最大 100 字元,建立後無法修改;不會沿用為錄音名稱) |
access_type | 否 | public(預設)或 password |
pass_code | 條件 | 密碼(password 類型時必填,4-12 字元,限英文字母、數字與常見標點(不接受中文與空白)) |
max_viewers | 否 | 最大觀眾人數(1 ~ 帳戶觀眾上限;未指定時預設為該上限) |
speaker_diarization | 否 | 是否啟用說話者辨識(預設 false) |
tts_config | 否 | TTS 預設設定 |
summary_template | 否 | 摘要模板 slug(如 meeting、lecture) |
summary_language | 否 | 摘要輸出語言,須為支援語言清單中的語言代碼(不指定時使用第一個轉錄語言,即 transcription_languages 首元素) |
callback_url | 否 | Webhook 回呼 URL(錄音處理完成/失敗時通知) |
最佳實務:轉錄語言只列實際會出現的
指定多個轉錄語言時,系統會自動辨識每段語音的語言;候選語言越多、越容易誤判,尤其當內容含跨語言共通的詞(專有名詞、數字、外來語)時。建議:
- 只列出實際會出現的語言,不要為了保險列滿 10 個。
- 候選越少越準;確定只有單一語言時就只填一種(不需語言辨識、最準確)。
- 避免同時列入「同文字系統或同語系近親」的語言(如多種拉丁字母歐語、
zh-CN與zh-TW、同語言的不同地區變體),這類最容易互相誤判。
成功回應重點欄位:
| 欄位 | 說明 |
|---|---|
id | 廣播 ID(用於後續 REST API 操作) |
token | 分享 Token(4 字元短碼 a-z0-9,用於觀眾端連線) |
share_url | 預設分享網址(不是可開啟的觀眾頁面,見步驟 2) |
status | 初始狀態為 pending |
完整參數請參考 REST API - Broadcasts。
Webhook 通知:加入
callback_url參數後,廣播錄音處理完成時會收到recording.completed事件通知。詳見 Webhook 回呼指南。
步驟 2:分享連結給觀眾
觀眾頁面由您的應用程式提供。將回應中的 token 帶入您的觀眾頁面網址後分享給觀眾,例如:
https://your-app.example.com/broadcast/a3f9
觀眾頁面再依觀眾端流程以 token 取得廣播資訊並接收字幕。
回應中的
share_url不是可開啟的觀眾頁面,請勿直接分享給觀眾。
步驟 3:WebSocket 連線
主講者需建立 WebSocket 連線來傳送音訊。
3.1 取得 Ticket:
curl -X POST "https://vas-poc.vurbo.ai/api/v1/auth/ticket" \
-H "X-API-Key: YOUR_API_KEY"
3.2 建立 WebSocket 連線:
const ws = new WebSocket('wss://vas-poc.vurbo.ai/ws', [`ticket.${ticket}`]);
3.3 發送 start action:
{
"type": "voice-translation",
"data": {
"action": "start",
"type": "broadcast",
"broadcast_token": "a3f9",
"audio_format": "pcm",
"broadcast_phase": "standby",
"standby_message": "演講即將開始,請稍候..."
}
}
廣播模式 start 的關鍵參數:
| 參數 | 必填 | 說明 |
|---|---|---|
type | 是 | 必須為 "broadcast" |
broadcast_token | 是 | 建立廣播時取得的 Token |
audio_format | 是 | pcm 或 webm |
broadcast_phase | 否 | standby(預備)或 live(預設)。只接受小寫的這兩個值,空字串視同 live,其他值會被拒絕 |
standby_message | 否 | 預備階段觀眾看到的訊息 |
tts_config | 否 | 多語言 TTS 設定(可覆蓋建立時的設定) |
summary_template | 否 | 摘要模板 slug(可覆蓋建立時的設定,不指定時使用廣播頻道的預設值) |
注意:
broadcast_token只能搭配type: "broadcast";其他類型帶了broadcast_token會被拒絕(invalid_parameter),錄音不會開始。
注意:廣播模式的語言設定會自動從廣播頻道設定取得,無需在 start 中傳送
transcription_languages和translation_languages。summary_template和summary_language也會從廣播頻道設定取得,僅在需要覆蓋時才需傳入。
注意:主講端也會收到翻譯的中間結果(
is_final: false),處理方式與觀眾端相同,見步驟 4:接收即時內容。錄音名稱不會沿用頻道名稱,start未帶name時自動生成如Broadcast #1。
成功回應:
{
"type": "voice-translation",
"data": {
"action": "session_started",
"session_id": "550e8400-e29b-41d4-a716-446655440000",
"task_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"recording_type": "broadcast",
"recognition_mode": "multi_speaker",
"phase": "standby",
"viewer_count": 0,
"queue_count": 0,
"peak_viewers": 0,
"total_viewers": 0,
"message": "語音辨識已開始"
}
}
步驟 4:預備階段(standby)
預備階段讓主講者在正式開播前進行設備測試和 STT/翻譯熱機。
預備階段特性:
- STT/翻譯結果只給主講者,觀眾看不到
- 觀眾會看到
standby_message設定的等待訊息 - 主講者可確認麥克風、辨識準確度等是否正常
- 隨時可動態更新預備訊息
- 有時間上限(預設 30 分鐘),見 預備階段的時間上限
動態更新預備訊息:
{
"type": "voice-translation",
"data": {
"action": "set_standby_message",
"message": "講師正在準備中,預計 5 分鐘後開始..."
}
}
觀眾端會即時收到更新的 standby 事件,訊息會自動翻譯成所有翻譯語言。
詳細說明請參考 預備階段詳解。
步驟 5:正式直播(go_live)
確認設備正常後,切換到正式階段:
{
"type": "voice-translation",
"data": {
"action": "broadcast_go_live"
}
}
成功回應:
{
"type": "voice-translation",
"data": {
"action": "broadcast_phase_changed",
"phase": "live",
"message": "廣播已開始"
}
}
切換後:
- STT/翻譯結果開始廣播給觀眾
- 結果開始寫入逐字稿
- TTS 開始發送給觀眾
- 觀眾端收到
phase_changed事件
注意:如果 start 時設定
broadcast_phase: "live"(預設),則跳過預備階段直接進入正式直播。
開播之後才加入的觀眾,不會收到主講者在預備階段說的內容與翻譯。
由預備階段轉為直播時,計費從開播那一刻起算;預備階段不計費。
取得本場廣播的 task_id
正式開播後會收到 broadcast_recording_ready 事件,帶回本場廣播定案後的 task_id:
{
"type": "voice-translation",
"data": {
"action": "broadcast_recording_ready",
"task_id": "3f9a1c2e-..."
}
}
重要:
session_started帶的task_id是連線階段的初始值,不是本場廣播的最終 ID。 不論是經預備階段轉正式開播、或 start 時直接指定broadcast_phase: "live"(預設)開播, 都會在開播後收到本事件。後續操作(查詢任務、換取浮動字幕 Feed Token 等) 一律以本事件的task_id為準——使用初始值會查無資料。直接開播時,本事件一定在
session_started之後送達。主講者斷線後在寬限期內重新開播(接管)時,新連線會收到帶新task_id的本事件,見 斷線處理。
步驟 6:直播中操作
正式直播期間,主講者可進行以下操作:
傳送音訊:
{
"type": "voice-translation",
"data": {
"action": "audio",
"payload": "Base64 編碼的音訊資料"
}
}
暫停直播:
{
"type": "voice-translation",
"data": {
"action": "pause"
}
}
觀眾端會收到 paused 事件,並顯示 message(目前為「廣播已暫停」)。
恢復直播:
{
"type": "voice-translation",
"data": {
"action": "resume"
}
}
觀眾端會收到 resumed 事件。
步驟 7:動態更新設定
廣播進行中可透過 REST API 即時調整設定:
curl -X PATCH "https://vas-poc.vurbo.ai/api/v1/broadcasts/{id}" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"translation_languages": ["en-US", "ja-JP", "ko-KR"],
"max_viewers": 200
}'
可動態更新的設定:
| 設定 | 說明 |
|---|---|
access_type | 切換公開/密碼保護 |
pass_code | 更新密碼 |
max_viewers | 調整觀眾上限 |
transcription_languages | 變更轉錄語言(string[],最多 10、不可重複;speaker_diarization: true 時僅支援單一轉錄語言) |
transcription_language | 變更轉錄語言(已棄用,向後相容,等同 transcription_languages 首元素) |
translation_languages | 新增或移除翻譯語言 |
speaker_diarization | 開啟/關閉說話者辨識 |
tts_config | 更新 TTS 設定 |
summary_template | 摘要模板(空字串清除) |
summary_language | 摘要輸出語言,須為支援的語言代碼(空字串清除) |
只有
pending、active、paused狀態的廣播可以更新。頻道名稱建立後無法修改,帶name會被忽略。
步驟 8:觀眾管理
主講者可在直播期間監控觀眾人數。人數有變動時會推送 viewer_count 事件,更新頻率最快約每 3 秒一次:
{
"type": "voice-translation",
"data": {
"action": "viewer_count",
"viewer_count": 45,
"queue_count": 8,
"peak_viewers": 50,
"total_viewers": 123
}
}
| 欄位 | 說明 |
|---|---|
viewer_count | 目前在線觀眾數 |
queue_count | 排隊等待中的觀眾數 |
peak_viewers | 本次廣播峰值觀眾數 |
total_viewers | 累計曾連線的觀眾總數 |
步驟 9:停止廣播
{
"type": "voice-translation",
"data": {
"action": "stop"
}
}
停止後:
- 所有觀眾收到
ended事件 - 系統自動上傳音檔和逐字稿
- 廣播狀態變更為
ended - 主講者收到
task_complete事件(task_id與broadcast_recording_ready帶回的相同,可用於後續查詢)
觀眾端流程
觀眾頁可以放在您自己的網域:以下的觀眾端 API 與字幕串流都可從任何網域的網頁直接呼叫,請求不要帶憑證(例如 credentials: 'include')。詳見從瀏覽器呼叫。
步驟 1:取得廣播資訊
觀眾開啟分享連結後,先查詢廣播資訊。
方式 A:透過 Viewer API
curl -X GET "https://vas-poc.vurbo.ai/api/v1/viewer/broadcasts/{token}"
回應包含頻道名稱、存取類型、可用語言、TTS 語音列表等資訊。前端可據此顯示廣播資訊頁面。
Viewer API 回應重點欄位:
| 欄位 | 說明 |
|---|---|
name | 頻道名稱 |
access_type | public 或 password |
requires_password | 是否需要密碼驗證 |
is_live | 是否正在直播 |
translation_languages | 可用的翻譯語言 |
tts_languages | 支援 TTS 的語言 |
tts_voices | 各語言可用的 TTS 語音列表 |
步驟 2:密碼驗證(若需要)
如果廣播設定為密碼保護(requires_password: true),觀眾需先驗證密碼:
curl -X POST "https://vas-poc.vurbo.ai/api/v1/viewer/broadcasts/{token}/verify" \
-H "Content-Type: application/json" \
-d '{"password": "mySecret123"}'
成功回應:
{
"data": {
"viewer_access_token": "aB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vWaB3dE5fG7hI9jK1lM3nO5pQ7rS9tU1vW",
"expires_at": "2026-01-04T10:00:00.000Z"
}
}
取得的 viewer_access_token 在後續 SSE 連線時需帶入。Token 有效期 24 小時。
步驟 3:SSE 連線接收字幕
觀眾透過 SSE 連線即時接收字幕串流。
基本連線(公開廣播):
const eventSource = new EventSource(
'https://vas-poc.vurbo.ai/broadcast/{token}/text'
);
篩選特定翻譯語言:
const eventSource = new EventSource(
'https://vas-poc.vurbo.ai/broadcast/{token}/text?lang=en-US'
);
啟用 TTS:
const eventSource = new EventSource(
'https://vas-poc.vurbo.ai/broadcast/{token}/text?lang=en-US&tts=true'
);
密碼保護廣播:
const eventSource = new EventSource(
'https://vas-poc.vurbo.ai/broadcast/{token}/text?lang=en-US&viewer_access_token=aB3dE5fG7hI9jK1l...'
);
SSE 連線參數:
| 參數 | 必填 | 說明 |
|---|---|---|
token | 是 | 廣播分享 Token(路徑參數) |
lang | 否 | 篩選特定翻譯語言(如 en-US) |
tts | 否 | 是否啟用 TTS(true / false) |
viewer_access_token | 條件 | 密碼保護廣播時必填 |
步驟 4:接收即時內容
連線成功後,觀眾會依序收到以下事件:
4.1 connected(連線確認)
{
"session_id": "abc123",
"source_lang": "zh-TW",
"subscribed_lang": "en-US",
"available_langs": ["en-US", "ja-JP"],
"tts_languages": ["en-US"],
"phase": "live",
"recognition_mode": "single",
"client_id": "client_xyz"
}
4.2 origin(原文)
{
"sid": 1,
"text": "大家好,歡迎參加今天的技術研討會",
"speaker_id": "0",
"start_time": "00:05",
"is_final": true
}
4.3 translation(翻譯)
{
"sid": 1,
"language": "en-US",
"text": "Hello everyone, welcome to today's technical seminar",
"speaker_id": "Royx",
"is_final": true
}
同一句的翻譯可能先送出數次 is_final: false 的中間結果,最後才送出 is_final: true 的定稿版本,請以 sid 加 language 覆蓋顯示。
4.4 tts_ready(TTS 音訊,需啟用 tts=true)
{
"sid": 1,
"language": "en-US",
"transcript": "大家好,歡迎參加今天的技術研討會",
"text": "Hello everyone, welcome to today's technical seminar",
"audio": "Base64EncodedMP3...",
"format": "mp3",
"duration_ms": 3200,
"boundaries": [...]
}
步驟 5:選擇語言與 TTS
觀眾端可透過 SSE 連線參數選擇偏好的翻譯語言和 TTS 設定。
切換語言:需要斷開現有 SSE 連線,使用新的 lang 參數重新連線。
// 切換到日文
eventSource.close();
const newEventSource = new EventSource(
`https://vas-poc.vurbo.ai/broadcast/${token}/text?lang=ja-JP&tts=true`
);
完整觀眾端範例:
function connectBroadcast(token, lang, enableTts, viewerToken) {
let url = `https://vas-poc.vurbo.ai/broadcast/${token}/text`;
const params = new URLSearchParams();
if (lang) params.set('lang', lang);
if (enableTts) params.set('tts', 'true');
if (viewerToken) params.set('viewer_access_token', viewerToken);
if (params.toString()) url += `?${params.toString()}`;
const eventSource = new EventSource(url);
// 連線確認
eventSource.addEventListener('connected', (e) => {
const data = JSON.parse(e.data);
console.log(`已連線,可用語言:${data.available_langs.join(', ')}`);
});
// 預備階段
eventSource.addEventListener('standby', (e) => {
const data = JSON.parse(e.data);
const msg = data.translations?.[lang] || data.message;
showWaitingScreen(msg);
});
// 階段變更
eventSource.addEventListener('phase_changed', (e) => {
const data = JSON.parse(e.data);
if (data.phase === 'live') {
hideWaitingScreen();
}
});
// 原文
eventSource.addEventListener('origin', (e) => {
const data = JSON.parse(e.data);
displayOriginText(data.sid, data.text, data.speaker_id, data.start_time);
});
// 翻譯
eventSource.addEventListener('translation', (e) => {
const data = JSON.parse(e.data);
displayTranslation(data.sid, data.language, data.text);
});
// TTS
eventSource.addEventListener('tts_ready', (e) => {
const data = JSON.parse(e.data);
playTtsAudio(data.audio, data.boundaries, data.text);
});
// 暫停/恢復
eventSource.addEventListener('paused', (e) => {
showPausedOverlay();
});
eventSource.addEventListener('resumed', (e) => {
hidePausedOverlay();
});
// 公告
eventSource.addEventListener('announcement', (e) => {
const data = JSON.parse(e.data);
const msg = data.translations?.[lang] || data.message;
showAnnouncement(msg);
});
// 結束
eventSource.addEventListener('ended', (e) => {
const data = JSON.parse(e.data);
showEndedScreen(data.reason);
eventSource.close();
});
// 被踢除
eventSource.addEventListener('kicked', (e) => {
showKickedMessage();
eventSource.close();
});
// 排隊
eventSource.addEventListener('queued', (e) => {
const data = JSON.parse(e.data);
showQueuePosition(data.position, data.estimated_wait);
});
eventSource.addEventListener('admitted', (e) => {
hideQueueScreen();
});
// 錯誤
eventSource.addEventListener('error', (e) => {
if (e.data) {
const error = JSON.parse(e.data);
handleError(error.error_code, error.message);
}
eventSource.close();
});
return eventSource;
}
密碼保護
廣播可設定為密碼保護,觀眾需輸入正確密碼才能進入。
建立密碼保護廣播
curl -X POST "https://vas-poc.vurbo.ai/api/v1/broadcasts" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"transcription_languages": ["zh-TW", "en-US"],
"translation_languages": ["en-US"],
"access_type": "password",
"pass_code": "mySecret123"
}'
觀眾端驗證流程
[1] GET /api/v1/viewer/broadcasts/{token}
↓ 確認 requires_password: true
[2] 顯示密碼輸入畫面
↓ 觀眾輸入密碼
[3] POST /api/v1/viewer/broadcasts/{token}/verify
↓ 取得 viewer_access_token
[4] SSE 連線帶入 viewer_access_token
GET /broadcast/{token}/text?viewer_access_token=xxx
動態切換存取類型
廣播進行中可將公開廣播改為密碼保護,或反之:
# 改為密碼保護
curl -X PATCH "https://vas-poc.vurbo.ai/api/v1/broadcasts/{id}" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"access_type": "password",
"pass_code": "newPassword"
}'
# 改回公開
curl -X PATCH "https://vas-poc.vurbo.ai/api/v1/broadcasts/{id}" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"access_type": "public"
}'
容量管理與排隊機制
觀眾上限
每個廣播可設定最大觀眾人數(max_viewers),超過上限的觀眾會進入排隊佇列。
排隊流程
當觀眾人數已達上限時:
- 新觀眾連線 SSE 後收到
queued事件 - 系統告知排隊位置和預估等待時間
- 有觀眾離開時,佇列中的觀眾依序進入
- 觀眾進入直播時收到
admitted事件
queued 事件:
{
"position": 3,
"estimated_wait": "約 2 分鐘"
}
admitted 事件:
{
"message": "已進入直播"
}
排隊超時
排隊等待超過 5 分鐘仍未進入的觀眾,會收到 error 事件(error_code 為 broadcast_queue_timeout),連線隨即結束;要繼續等待請重新連線:
event: error
data: {"error_code":"broadcast_queue_timeout","severity":"error","message":"Queue timeout","context":"broadcast","request_id":"req_abc123xyz789","timestamp":"2026-09-26T10:30:45Z"}
動態調整上限
主講者可在直播期間動態調整觀眾上限:
curl -X PATCH "https://vas-poc.vurbo.ai/api/v1/broadcasts/{id}" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"max_viewers": 200}'
提高上限後,排隊中的觀眾會自動進入。
廣播狀態與生命週期
狀態流轉
pending ──(WebSocket start)──> active ──(pause)──> paused
| |
| <──(resume)───────
|
├──(stop)──> ended
|
pending ──(DELETE)──> revoked
狀態說明
| 狀態 | 說明 | 允許操作 |
|---|---|---|
pending | 已建立,等待主講者啟動 | 更新設定、撤銷 |
active | 直播中 | 暫停、停止、更新設定、傳送音訊 |
paused | 已暫停 | 恢復、停止、更新設定 |
ended | 已結束 | 無(唯讀) |
revoked | 已撤銷(僅 pending 狀態可撤銷) | 無(唯讀) |
is_live 欄位
is_live 為便利欄位,當狀態為 active 或 paused 時為 true,表示廣播正在進行中。
結束原因
廣播結束時(主講者結束,或斷線超過寬限期仍未重新連上),觀眾收到 ended 事件,之後連線關閉。ended 只帶提示文字(data.message),不帶結束原因,不同情況收到的都是同一個事件。
排隊逾時不是 ended,而是 error 事件(broadcast_queue_timeout),見上方 排隊超時。
預備階段(Standby)詳解
預備階段是廣播正式開始前的熱機時段,讓主講者在正式開播前測試設備和確認辨識品質。
啟用預備階段
在 WebSocket start action 中設定 broadcast_phase: "standby":
{
"type": "voice-translation",
"data": {
"action": "start",
"type": "broadcast",
"broadcast_token": "YOUR_TOKEN",
"audio_format": "pcm",
"broadcast_phase": "standby",
"standby_message": "演講即將開始,請稍候..."
}
}
預備階段行為
| 項目 | 行為 |
|---|---|
| STT 辨識 | 正常運作,結果只發給主講者 |
| 翻譯 | 正常運作,結果只發給主講者 |
| 觀眾字幕 | 不發送,觀眾看到 standby_message |
| TTS | 不發送給觀眾 |
| 逐字稿 | 不寫入 |
| 觀眾的歷史紀錄 | 不包含預備階段的內容(開播後才加入的觀眾也看不到) |
| 計費 | 不計費,從開播那一刻起算 |
| 時間上限 | 預設 30 分鐘,見下方 預備階段的時間上限 |
動態更新預備訊息
進入預備階段後,可隨時更新顯示給觀眾的訊息:
{
"type": "voice-translation",
"data": {
"action": "set_standby_message",
"message": "設備調整中,預計 3 分鐘後開始"
}
}
更新後,所有觀眾立即收到新的 standby 事件:
event: standby
data: {"message":"設備調整中,預計 3 分鐘後開始","translations":{"en-US":"Adjusting equipment, expected to start in 3 minutes","ja-JP":"機器の調整中、約3分後に開始予定"}}
注意:
set_standby_message只能在預備階段使用。已進入正式階段(live)時會回傳錯誤。
預備階段的時間上限
預備階段累計達上限(預設 30 分鐘)時,這一場會自動結束。
- 到期前約 2 分鐘,主講者會先收到一則
broadcast_standby_warning(error事件,severity: "warning"),廣播照常進行。details.standbySeconds為已經累計的秒數,details.remainingSeconds為剩下的秒數,details.limitSeconds為上限秒數。 - 到期時收到
broadcast_standby_timeout(severity: "fatal",details帶standbySeconds與limitSeconds),接著收到status: "ended"。預備階段沒有錄音,所以不會收到task_complete、不會送出 Webhook,也不計費。 - 開播(
broadcast_go_live)之後就不再計時。 - 斷線等待續接的期間照樣計時;續接之後接著原本的秒數計算,不會重新計時。
- 主講者斷線後在寬限期內重新
start(接管)時,如果原本那一場還在預備階段,新連線接著已經累計的秒數計算。 - 到期後要重新開始,請重新取得 Ticket 並送出
start。
{
"type": "error",
"data": {
"error_code": "broadcast_standby_warning",
"severity": "warning",
"message": "Standby is about to reach its time limit; the broadcast will end automatically",
"context": "broadcast",
"request_id": "req_abc123xyz789",
"timestamp": "2026-09-25T10:28:00.000Z",
"details": {
"standbySeconds": 1680,
"remainingSeconds": 120,
"limitSeconds": 1800
}
}
}
建議在收到預警時提示主講者「預備時間快用完了,請開播或重新開始」。
觀眾在預備階段的體驗
- 觀眾連線後收到
connected事件(phase: "standby") - 緊接著收到
standby事件,包含等待訊息和多語言翻譯 - 主講者切換到正式階段時,觀眾收到
phase_changed事件(phase: "live") - 之後開始接收字幕和翻譯
eventSource.addEventListener('standby', (e) => {
const data = JSON.parse(e.data);
// 根據觀眾選擇的語言顯示對應翻譯
const displayLang = 'en-US';
const msg = data.translations?.[displayLang] || data.message;
showWaitingScreen(msg);
});
eventSource.addEventListener('phase_changed', (e) => {
const data = JSON.parse(e.data);
if (data.phase === 'live') {
hideWaitingScreen();
// 開始顯示字幕區域
}
});
公告功能
主講者可在直播期間發送公告訊息給所有觀眾。
發送公告
{
"type": "voice-translation",
"data": {
"action": "broadcast_announcement",
"message": "會議將在 5 分鐘後結束"
}
}
觀眾端接收
觀眾透過 SSE 收到 announcement 事件,包含原文和多語言翻譯:
{
"message": "會議將在 5 分鐘後結束",
"translations": {
"en-US": "The meeting will end in 5 minutes",
"ja-JP": "会議は5分後に終了します"
}
}
前端可根據觀眾選擇的語言顯示對應翻譯:
eventSource.addEventListener('announcement', (e) => {
const data = JSON.parse(e.data);
const displayLang = 'en-US';
const msg = data.translations?.[displayLang] || data.message;
showAnnouncementPopup(msg);
});
錯誤處理
主講者端常見錯誤
| 錯誤碼 | 說明 | 處理建議 |
|---|---|---|
broadcast_token_required | 廣播模式需要 Token | 確認提供 broadcast_token |
broadcast_token_invalid | Token 無效 | 確認 Token 正確且未過期 |
broadcast_not_ready | 廣播服務尚未啟動 | 稍後重試 |
broadcast_not_enabled | 非廣播模式 | 確認 type: "broadcast" |
broadcast_not_in_standby | 不在預備階段 | 只能在 standby 階段使用 |
broadcast_cannot_revoke | 無法撤銷 | 只有 pending 狀態可撤銷 |
broadcast_standby_warning | 預備階段即將達到時間上限(廣播照常) | 提示主講者開播或重新開始 |
broadcast_standby_timeout | 預備階段已達時間上限,這一場已結束 | 重新取得 Ticket 並送出 start |
invalid_parameter | broadcast_phase 的值不合法,或非 broadcast 類型帶 broadcast_token | 依 details.field 修正參數 |
觀眾端常見錯誤
| 錯誤碼 | 說明 | 處理建議 |
|---|---|---|
broadcast_not_found | 找不到廣播 | 確認 Token 正確 |
broadcast_session_ended | 廣播已結束 | 提示觀眾廣播已結束 |
broadcast_capacity_full | 觀眾已達上限 | 加入排隊等待 |
broadcast_token_invalid | Token 無效 | 確認 Token 正確 |
broadcast_token_revoked | Token 已撤銷 | 廣播已被撤銷 |
broadcast_password_incorrect | 密碼錯誤 | 重新輸入密碼 |
斷線處理
主講者斷線:
- 觀眾收到
host_disconnected事件:廣播凍結但尚未結束,觀眾不需要斷線,顯示「主講者重連中」即可 - 主講者在寬限期內重新連上:觀眾收到
host_reconnected事件,data.resumed為true時同時恢復播放(主講者斷線前是自己暫停的則為false,維持暫停) - 如果主講者超時未重連,觀眾收到
ended事件
主講者在寬限期內重新開播(接管):
主講者斷線後,在寬限期內用同一個 broadcast_token 重新 start,會接管這個頻道:
- 新連線的
broadcast_recording_ready會帶新的task_id;舊連線已經斷開,收不到任何訊息。 - 同一場廣播因此分成兩筆錄音:接管前那一筆在舊場收尾時完成、接管後那一筆在新場結束時完成,各自送出一封
recording.completedWebhook(整場沒有音訊的那一筆改送recording.failed)。 - 接管期間頻道維持直播,觀眾的存取權不會被清除;新場照常計費。
- 使用浮動字幕的觀眾:舊錄音的字幕會結束,請改用新錄音的分享連結。
- 舊連線還連著(沒有斷線)時,同一個
broadcast_token的新start會被拒絕(broadcast_token_already_used)。要用同一個 Token 開新的一場,請先對舊連線送出stop,收到task_complete後再送新的start。
觀眾斷線:
- SSE 連線斷開後,瀏覽器會自動重連(EventSource 內建機制)
- 重連後會收到新的
connected事件 - SSE 使用 15 秒間隔的心跳保持連線活躍
相關 Reference 文件
- REST API - Broadcasts(建立 / 查詢 / 更新 / 撤銷)
- REST API - Speakers(離線說話者編輯)
- WebSocket - Voice Translation(start / broadcast_go_live / broadcast_announcement 等)
- SSE - Broadcast Viewer(觀眾即時字幕串流)
- TTS 語音合成指南
- 說話者管理指南
版本:V1.24.1 最後更新:2026-10-07