音視訊通話
音視訊通話用於一對一呼叫和群裡的多人通話,由平臺部署的媒體服務承載。不依賴 IM 群的會議、語音房使用獨立 RTC 頻道。
| 要做什麼 | 看哪裡 |
|---|---|
| 開通音影片 | 在控制檯開啟執行策略 rtc_enabled(預設關閉),見執行策略 |
| 瞭解呼叫、接聽、結束通話的規則 | 一對一通話、群通話 |
| 實現客戶端的通話 | 客戶端需要做什麼 |
| 處理掉線、App 重啟 | 斷線與恢復 |
| 顯示通話的結果 | 結束原因、通話記錄 |
| 從服務端查詢、結束通話,或把人移出通話 | 通話管理 |
| 把通話結果同步到業務系統 | 事件回撥 |
| 核對用量和扣費 | 計費分鐘數與統計 |
使用條件:應用開啟了執行策略 rtc_enabled,應用狀態正常,沒有超過併發通話額度,媒體服務可用。新計費啟用後,應用還要有正的音影片標準時長餘額;切換到新計費之前,按原來套餐的開關。控制檯應用詳情的“音影片”頁面可以檢視通話,實際扣費在“音影片計費”中檢視。
基本規則
- 兩種通話:一對一通話(
single)在兩個使用者之間進行;群通話(group)在一個群內進行,參與者必須是這個群的成員。這套呼叫介面的多人通話需要 IM 群;不關聯群的會議或語音房使用獨立 RTC 頻道。 - 兩種媒體:語音通話(
audio)和視訊通話(video),發起時確定,通話中不能切換。視訊通話中客戶端可以自行關閉攝像頭,仍按視訊通話記錄和計費。 - 一人同時一個通話:一個使用者同時只能在一個通話中,發起一對一通話後等待對方接聽也算在通話中;他的多臺裝置中只有一臺在通話中。正在通話的人不會再收到來電:一對一來電判定為忙線,群通話的邀請記為忙線、不振鈴。只是正在振鈴、還沒有接聽的人可以同時收到多個來電,接聽其中一個後,要先結束通話它才能接聽另一個。
- 每個群同時一個群通話:群裡已有進行中的群通話時,再發起會得到這個通話的 ID,客戶端改為加入它。
- 以服務端為準:通話的狀態、誰在通話中、何時接通和結束,都以服務端的記錄為準。每次狀態變化,通話的版本號
version加一,客戶端收到的通知和響應都帶完整的通話,只應用比本地版本號大的即可。 - 通話 ID:每個通話有一個
call_id,查詢、結束通話和事件回撥都用它標識通話。
一對一通話
發起:主叫指定被叫的使用者名稱和媒體型別,可以附帶自定義欄位
ext(如業務訂單號),隨來電送達被叫,也會出現在通話物件和事件回撥中。發起時檢查:- 應用開啟了音影片、可以提供服務;
- 主叫沒有被全域性禁言單聊(
chat),否則返回user_muted。被禁言的使用者可以接聽; - 與傳送單聊訊息的規則相同:被叫把主叫加入了黑名單時返回
user_blocked;應用開啟了只能給好友發訊息,而雙方不是好友時返回not_friend; - 主叫此刻不在其他通話中,否則返回
call_in_progress; - 主叫的發起頻率,以及應用的併發通話額度。
被叫不存在或已刪除返回
not_found。被叫被封禁不影響發起:他收不到來電,振鈴超時後以“無人接聽”結束,不會因此暴露他被封禁。振鈴:被叫的全部線上裝置收到來電通知,不線上或 App 在後臺的裝置收到來電推送。主叫發起後就進入通話,連線媒體服務等待對方。振鈴時間由執行策略
rtc_ring_timeout_seconds決定,預設 60 秒。接聽:被叫在任意一臺裝置上接聽,這臺裝置進入通話,通話接通(
status變為active),他的其他裝置停止振鈴,顯示“已在其他裝置接聽”。
通話以下列方式結束,結束原因 end_reason 見結束原因:
| 情況 | 結束原因 |
|---|---|
| 接通後任一方結束通話,時長從接通算到結束通話 | completed |
| 接通前主叫結束通話(取消) | canceled |
| 被叫拒絕 | rejected |
發起時被叫正在另一個通話中:不振鈴,通話立即結束,發起的響應中通話已是 ended;或者被叫以“忙線”拒絕(SDK 發現裝置正在進行運營商電話等其他通話時自動發出) | busy |
| 振鈴超時,被叫沒有接聽 | no_answer |
| 任一方的裝置掉線超過 45 秒,見斷線與恢復 | connection_lost |
被叫只是在振鈴(正在收到別人的來電)不算忙線,新的來電同樣振鈴,由他選擇接聽哪一個。兩人幾乎同時呼叫對方時,先到達的呼叫照常振鈴,後到達的一方判定為對方忙線。被叫在接聽前拉黑主叫、解除好友關係,不影響已經發出的來電;通話中拉黑對方也不中斷通話。
群通話
- 發起:群成員在群裡發起群通話,指定語音或影片,並選擇要邀請的成員,最多為人數上限減一;也可以不邀請任何人,等其他成員自行加入。
- 發起人必須能在群裡發言:群沒有被封禁,他是群成員、沒有在群裡被禁言,群開啟了全員禁言時他是群主或管理員;他也不能被全域性禁言群聊(
group)。 - 被邀請的人必須能加入:是群成員、沒有在群裡被禁言、群開啟全員禁言時是群主或管理員。不滿足的不邀請,在發起的結果中逐個說明(
not_found、not_group_member、group_muted),以免響鈴之後卻接聽不了。正在其他通話中的人記為忙線,不振鈴。 - 群通話不檢查個人黑名單,與群訊息的規則相同。
- 群裡已有進行中的群通話時,返回
409 already_exists,details.call_id為那個通話,客戶端改為加入它。
- 發起人必須能在群裡發言:群沒有被封禁,他是群成員、沒有在群裡被禁言,群開啟了全員禁言時他是群主或管理員;他也不能被全域性禁言群聊(
- 群內提示:群通話開始、在通話中的人數變化和結束時,群的全部線上成員收到一條群通話狀態的通知,用於在群會話中顯示“3 人正在通話”的橫幅和加入入口。這條通知只有人數,可能有誤差;成員開啟群會話時可以查詢群當前的通話。
- 加入:被邀請的人接聽邀請,或其他群成員從橫幅加入,都是加入。加入的條件與發起相同,另外通話不能已滿:在通話中的人數加上正在振鈴的人數不超過人數上限(本人正在振鈴的不重複計算),正在振鈴的邀請為被邀請人預留了名額。拒絕過、未接聽、離開過的成員都可以再加入。
- 邀請:在通話中的成員可以繼續邀請其他群成員,被邀請的人同樣要能加入;拒絕過、未接聽、離開過的成員可以再次被邀請,重新振鈴。
- 拒絕與未接聽:被邀請的人可以拒絕,振鈴超時記為未接聽(時長與一對一相同),都不影響其他人繼續通話。
- 離開:在通話中的成員結束通話即離開,之後可以再加入。
- 結束:發起人、群主和群管理員可以結束群通話,其他人結束時返回
permission_denied(not_host)。結束時全部成員離開,正在振鈴的邀請取消。此外以下情況自動結束:- 在通話中的人數降為 0:接透過的為
completed,沒有接透過的為canceled;最後離開的人是掉線或裝置被踢下線的,為connection_lost; - 只剩一人在通話中、且沒有正在振鈴的邀請,持續 60 秒:接透過的為
completed,沒有接透過的為no_answer。這 60 秒留給其他成員從橫幅加入,期間有人加入或有新的邀請就取消計時; - 群被解散或封禁:
group_unavailable。
- 在通話中的人數降為 0:接透過的為
- 群的變化:
- 成員退出群、被移出群(包括被拉黑、賬號被刪除):在通話中的立即離開(
removed_from_group),也被移出媒體房間;正在振鈴的邀請取消; - 通話中被禁言、群開啟全員禁言:已在通話中的人不受影響,之後不能再發起、加入(包括離開後重新加入)和邀請,也不能再被邀請;已經在振鈴的邀請照常振鈴,接聽時被拒絕。
- 成員退出群、被移出群(包括被拉黑、賬號被刪除):在通話中的立即離開(
- 人數上限:同時在通話中的人數上限由套餐決定(含發起人,預設不限);執行策略
max_group_call_participants不為 0 時取兩者中較小的一個,發起時確定,之後修改策略不影響進行中的通話。一個群通話累計涉及的成員(包括已離開、拒絕、未接聽的)最多 100 人,達到後不能再邀請或加入新的人,已經參與過的人照常重新加入。
多臺裝置
- 來電送到全部裝置:來電通知推給被叫的全部線上裝置;不線上或 App 在後臺的裝置收到來電推送,有效期為剩餘的振鈴時間,超時沒有送達的不再送達。裝置在振鈴期間連上長連線時,會單獨補發一次來電通知。來電推送的通道、免打擾規則見離線推送。
- 在一臺裝置上接聽:這臺裝置進入通話,本人的其他裝置收到“已在其他裝置接聽”,停止振鈴;收到過來電推送的裝置上的來電通知被靜默清除(個別推送通道不支援清除,由 App 下次開啟時清除)。
- 在一臺裝置上拒絕:一對一通話隨之結束,群通話中本人的邀請結束,本人的其他裝置停止振鈴。
- 只有一臺裝置在通話中:媒體憑據只簽發給在通話中的那臺裝置。本人的其他裝置可以檢視通話狀態和結束通話,但不能同時進入同一個通話,接聽時返回
call_in_progress(joined_on_other_device)。暫不支援把通話轉移到本人的另一臺裝置。 - 主叫的其他裝置:主叫發起後,他的其他線上裝置收到通話狀態,可以顯示“正在通話”並提供結束通話。
- 同一臺裝置重複接聽(如網路重試)結果相同,不會被當成另一臺裝置。
斷線與恢復
- 心跳:在通話中的裝置每 15 秒傳送一次通話心跳。服務端 45 秒沒有收到心跳,就認為這個成員已經掉線:一對一通話以
connection_lost結束;群通話中他離開(connection_lost),可以重新加入。App 被強制關閉、裝置斷網後,對方最遲約 45 秒後得知。 - 網路切換:在 Wi-Fi 與行動網路之間切換時,只要 45 秒內恢復心跳,通話不受影響,雙方最多聽到幾秒的中斷。
- App 重啟後恢復:App 被系統關閉後 45 秒內重新啟動的,查詢本人正在進行的通話,本人仍在通話中且就是這臺裝置的(
self.joined_on_this_device為true),續期媒體憑據、重新連線媒體服務即可繼續通話;已經超時的,一對一通話已結束,群通話可以重新加入。 - 重新連線後的同步:客戶端登入、重新連線長連線後,查詢本人正在進行的通話和正在振鈴的來電,與本地核對。通知可能丟失、重複或亂序,以查詢結果和版本號為準。
- 賬號與裝置的變化:在通話中的裝置被踢下線或登入失效:一對一以
connection_lost結束,群通話中他離開(session_revoked)。賬號被封禁或刪除:他正在進行的一對一通話結束(user_unavailable),群通話中他離開(user_unavailable);賬號被刪除時,他正在振鈴的一對一來電隨之結束(user_unavailable),群通話的邀請隨之取消。被封禁的人正在振鈴的來電照常振鈴到超時。 - 最長時長:通話最長 12 小時,超過後自動結束(
max_duration)。
媒體服務與媒體憑據
- 音影片資料由本服務提供的媒體服務承載。每個通話一個媒體房間,房間在第一個人連入時建立,通話結束後服務端關閉房間,仍連著的裝置隨之斷開。
- 發起、接聽和加入成功後,響應中帶媒體憑據(
media),客戶端用它連線媒體服務。媒體憑據由 SDK 使用,App 的業務程式碼不需要解析其中的欄位。- 只簽發給在通話中的那臺裝置,正在振鈴的被叫拿不到,接聽之前不能提前收聽;
- 有效期 15 分鐘(
media.expires_at),快到期時由這臺裝置續期; - 只能進入本通話的房間。語音通話只能傳送麥克風的聲音,視訊通話可以傳送麥克風和攝像頭,都不能共享螢幕;可以收發資料訊息,客戶端可以用它同步“對方已靜音”等狀態。
- 成員離開群通話(結束通話、被移出、掉線)時,服務端同時把他移出媒體房間,他不能憑手中的憑據繼續收聽。
- 客戶端的 SDK 版本過舊、不支援當前的媒體服務時,發起、接聽和加入返回
permission_denied(media_provider_unsupported),請提示使用者升級 App。
客戶端需要做什麼
SDK 釋出後由 SDK 封裝
以下是客戶端與服務端之間的互動,客戶端 SDK 釋出後由 SDK 封裝,App 只需要處理介面。在 SDK 釋出前需要直接對接時,請聯絡我們獲取長連線協議。
客戶端的通話操作推薦透過長連線傳送,App 剛被來電推送喚醒、長連線還沒連上時使用客戶端介面。兩者的請求和響應相同,長連線上請求的 data 為客戶端介面的請求體加上 call_id:
| 操作 | 長連線 op | 客戶端介面 | 說明 |
|---|---|---|---|
| 發起通話 | rtc.start | POST /client/v1/calls | to_user(一對一的被叫)或 to_group(群 ID)二選一;media 為 audio 或 video;群通話的 usernames 為要邀請的成員,一次最多 1000 個;可選 ext、client_call_id,見下文 |
| 接聽、加入 | rtc.join | POST /client/v1/calls/{call_id}/join | 接聽一對一來電、接聽群通話邀請、從橫幅加入群通話、離開後重新加入 |
| 拒絕 | rtc.reject | POST /client/v1/calls/{call_id}/reject | 可選 reason:declined(預設)或 busy |
| 結束通話 | rtc.hangup | POST /client/v1/calls/{call_id}/hangup | 接通前的主叫為取消,在通話中為離開,正在振鈴的人等同拒絕。本人的任何一臺裝置都可以結束通話 |
| 結束群通話 | rtc.end | POST /client/v1/calls/{call_id}/end | 只有發起人、群主和群管理員可以;對一對一通話等同結束通話 |
| 邀請 | rtc.invite | POST /client/v1/calls/{call_id}/invite | 只用於群通話,usernames 為 1 到 1000 個其他成員 |
| 心跳 | rtc.heartbeat | POST /client/v1/calls/{call_id}/heartbeat | 在通話中每 15 秒一次;連上媒體服務後的第一次心跳帶 media_connected: true |
| 續期媒體憑據 | rtc.renew_token | POST /client/v1/calls/{call_id}/token | 只接受在通話中的那臺裝置 |
| 查詢通話詳情 | rtc.get | GET /client/v1/calls/{call_id} | 一對一通話只有雙方能查到;群通話是群的當前成員能查到,已離開群的人只能查到自己參與過、已結束的通話 |
| 查詢正在進行的通話和來電 | — | GET /client/v1/calls/active | current 為本人正在進行的通話,incoming 為正在振鈴的來電(最多 20 個)。登入和重新連線後呼叫 |
| 查詢通話歷史 | — | GET /client/v1/calls | 本人參與過的通話,包括未接聽、拒絕的,按時間倒序,limit 預設 20、最大 50 |
| 查詢群的當前通話 | — | GET /client/v1/groups/{group_id}/call | 開啟群會話時呼叫,用於顯示橫幅;沒有進行中的群通話時 call 為 null |
例如在長連線上發起一對一視訊通話:
{
"op": "rtc.start",
"id": "7",
"data": {
"client_call_id": "c-0001",
"to_user": "bob",
"media": "video",
"ext": { "order_id": "8812" }
}
}ext:字串鍵值對,最多 16 項,鍵為 1 到 64 個字元、不能包含控制字元,JSON 編碼後不超過 1 KB。不會放進來電推送。client_call_id:發起的去重 ID,1 到 64 個可見 ASCII 字元。網路錯誤、超時後用同一個 ID 重試,同一個發起人相同的 ID 只建立一個通話,重複提交返回第一次建立的通話(客戶端介面的狀態碼為200,第一次為201)。
發起、接聽和加入的響應帶 call(通話,另有 self)、media(媒體憑據)和 heartbeat_interval_seconds(15),發起時被叫忙線、通話立即結束的沒有 media;接聽、加入另有 changed。拒絕、結束通話和結束帶 call 和 changed。changed 為 false 表示沒有改變任何狀態(如同一臺裝置重複接聽、重複拒絕、結束通話已結束的通話),可以視為成功;群通話的發起和邀請另有每個被邀請人的結果 results。self 為本人在通話中的狀態:state 為本人的成員狀態(不是成員為 null),joined_on_this_device 表示本人是否就在這臺裝置上通話。客戶端收到的通話和成員沒有 ended_via、member_count、peak_joined、billable_minutes、invited_at、joined_platform、join_count、joined_ms、first_joined_at、media_joined_at、media_left_at 這幾個服務端欄位。
服務端透過長連線推送以下通知:
| 通知 | 推送給 | 內容 |
|---|---|---|
rtc.call_invite | 新振鈴的人的全部裝置 | call 為通話;inviter 為邀請人的 username、nickname、avatar_url;group 為群通話所在群的 group_id、name、avatar_url,一對一為 null |
rtc.call_changed | 正在振鈴和在通話中的成員,以及本次狀態有變化的成員的全部裝置 | call 為變化後的通話;change 說明變化:kind 為變化的型別,usernames 為狀態變化的成員,actor 為操作人(你的服務端、平臺和系統為 null),reason 為拒絕的原因、離開原因、取消原因或結束原因 |
rtc.group_call_changed | 群的全部線上成員 | 群通話開始、在通話中的人數變化和結束時推送,只有 group_id、call_id、media、status、joined_count、initiator、version,不含成員名單 |
change.kind 的取值:created(發起,只推給發起人的其他裝置)、invited(群通話中有新的邀請)、joined(有人接聽或加入)、answered_elsewhere(本人在另一臺裝置上接聽了,停止振鈴)、declined(有人拒絕)、missed(群通話的邀請超時未接聽)、left(有人離開或被移出)、canceled(群通話中正在振鈴的邀請被取消,如被移出群)、ended(通話結束)。一次操作引起的多個變化只發一條,kind 取最終的結果,例如拒絕導致群通話結束時為 ended。
{
"type": "event",
"event": "rtc.group_call_changed",
"at": "2026-10-04T19:22:32.957Z",
"data": {
"group_id": "100311544667045888",
"call_id": "100311615936659456",
"media": "audio",
"status": "ringing",
"joined_count": 1,
"initiator": "alice",
"version": 1
}
}客戶端需要:
- 按執行配置中的
rtc_enabled顯示入口,但以服務端的實際結果為準:新計費啟用後餘額不足也會被拒絕,餘額隨時可能變化,本地開關為開不代表一定能發起成功; - 為每次發起生成新的
client_call_id,失敗重試時沿用; - 登入、重新連線長連線後呼叫
GET /client/v1/calls/active,恢復本裝置正在進行的通話、為沒有收到通知的來電振鈴;本地有、查詢結果中沒有的通話按已結束處理; - 來電通知、來電推送和查詢結果可能重複到達,按
call_id去重;在ring_expires_at停止振鈴,等待服務端的結束通知; - 在通話中每 15 秒傳送心跳,長連線不可用時改用客戶端介面;心跳返回
call_ended時結束本地通話,返回version_conflict時說明本人已不在通話中(如被移出、掉線後已離開),斷開媒體; - 媒體憑據到期前續期;
- 裝置正在進行運營商電話等其他通話時,收到來電用
busy拒絕; - 渲染會話中的通話記錄訊息。
客戶端的通話操作按使用者限制頻率,超出時返回 429 rate_limited,details.reason 說明是哪一項,按 Retry-After 等待。這些請求同時計入應用的客戶端每秒請求數:
| 操作 | 上限 | details.reason |
|---|---|---|
| 發起通話(一對一和群通話合計) | 每個使用者每分鐘 10 次、每天 200 次 | call_rate |
| 同一主叫呼叫同一被叫 | 每小時 20 次 | pair_call_rate |
| 在同一個群裡發起群通話 | 每個群每分鐘 10 次 | group_call_rate |
| 在群通話中邀請 | 每個使用者每分鐘 30 人,超出的人在結果中單獨返回 rate_limited,其餘照常邀請 | invite_rate |
| 接聽、加入、拒絕、結束通話、結束 | 每個使用者合計每分鐘 60 次 | signal_rate |
| 續期媒體憑據 | 每個使用者每分鐘 10 次 | token_rate |
| 查詢(通話詳情、正在進行的通話、通話歷史、群的當前通話)和發起的重複提交 | 每個使用者合計每分鐘 120 次 | query_rate |
心跳只計入應用的客戶端請求數,同一個成員 5 秒內的第二次心跳直接忽略、仍返回成功。限制的計算方式見限流的計算方式。
客戶端的通話操作可能返回以下錯誤,SDK 按 code 和 details 提示使用者:
| HTTP 狀態碼 | code | 說明 |
|---|---|---|
| 400 | invalid_argument | details.reason 為 invalid_target(to_user、to_group 不是恰好一個)、invalid_media、self_call(呼叫自己)、invalid_ext、invalid_client_call_id、invalid_usernames(usernames 超過 31 個、包含本人,或邀請時為空)、too_many_invitees(邀請的人數超過人數上限減一,details.max 為可以邀請的人數)、group_call_only(對一對一通話邀請他人)、invalid_reject_reason |
| 403 | permission_denied | details.reason 為 rtc_disabled(應用策略未開啟、切換前套餐不允許,或新模式音影片餘額不足)、rtc_suspended(本應用的音影片已被平臺停用,details.suspend_reason 為原因)、rtc_not_configured(音影片服務暫不可用)、not_host(不是發起人、群主或群管理員卻結束群通話)、not_in_call(不在通話中卻邀請他人)、not_joined_device(不是在通話中的那臺裝置卻續期媒體憑據)、media_provider_unsupported(App 版本過舊) |
| 403 | user_muted、user_blocked、not_friend、not_group_member、group_muted、group_disabled | 發起、加入或邀請時,與傳送訊息相同的禁言、黑名單、好友和群成員檢查不透過 |
| 403 | app_unavailable | 應用處於只讀狀態(如欠費)時不能發起通話和邀請,其他操作照常 |
| 404 | not_found | 被叫不存在或已刪除;通話不存在或本人看不到;群不存在 |
| 409 | call_in_progress | 本人已在通話中,不能再發起、接聽或加入:details.reason 為 in_other_call(本人在另一個通話中,details.call_id 為那個通話)或 joined_on_other_device(本人已在另一臺裝置上接聽或加入了這個通話) |
| 409 | call_ended | 通話已經結束,不能再接聽、加入、邀請、續期或心跳;details.end_reason、details.ended_at 為結束的原因和時間 |
| 409 | already_exists | 群裡已有進行中的群通話,details.call_id 為那個通話,改為加入它 |
| 409 | version_conflict | 本人在通話中的狀態不允許這個操作,如接聽之後又拒絕、從未被邀請卻拒絕:details.reason 為 member_state,details.member_state 為本人當前的成員狀態(從未被邀請為 null) |
| 409 | limit_exceeded | details.reason 為 call_full(群通話人數已滿)、call_member_limit(群通話累計涉及的成員已達 100 人)或 app_concurrent_calls(應用進行中的通話數已達額度,details.limit 為額度) |
通話的狀態
通話狀態
通話的 status 只按以下順序前進:
status | 說明 |
|---|---|
ringing | 已發起,還沒有接通 |
active | 已接通:第一次有兩個人同時在通話中 |
ended | 已結束,end_reason 為結束原因 |
成員的狀態
通話的每個成員有一個角色 role 和狀態 state。角色:initiator 發起人、invitee 被邀請的人、joiner 沒有被邀請、自己加入群通話的人。
state | 說明 |
|---|---|
ringing | 正在振鈴 |
joined | 在通話中 |
left | 已離開,leave_reason 為離開原因。群通話中可以重新加入 |
declined | 拒絕了來電或邀請 |
busy | 忙線:被邀請時正在另一個通話中,沒有振鈴;或以忙線拒絕 |
missed | 振鈴超時,沒有接聽 |
canceled | 正在振鈴時邀請被取消,如通話結束、被移出 |
離開原因
成員離開通話時的 leave_reason:
leave_reason | 說明 |
|---|---|
hangup | 本人結束通話 |
ended | 通話結束時他仍在通話中 |
connection_lost | 心跳超時,裝置掉線 |
removed | 被你的服務端、控制檯或平臺移出通話 |
removed_from_group | 退出了群或被移出群 |
session_revoked | 在通話中的裝置被踢下線或登入失效 |
user_unavailable | 賬號被封禁或刪除 |
app_unavailable | 應用或租戶不能提供服務,或平臺停用了本應用的音影片 |
結束原因
通話結束時的 end_reason,一對一和群通話共用。介面上顯示的文字由 App 決定,下表的提示僅供參考:
end_reason | 場景 | 發起人一方的提示 | 被叫一方的提示 |
|---|---|---|---|
completed | 接通後結束通話;群通話最後的人離開,發起人、群主或管理員結束,或只剩一人超過 60 秒 | 通話時長 | 通話時長 |
canceled | 接通前發起人結束通話;群通話接通前在通話中的人全部離開,或接通前被結束 | 已取消 | 對方已取消 |
rejected | 一對一被叫拒絕 | 對方已拒絕 | 已拒絕 |
busy | 一對一被叫忙線,或被叫以忙線拒絕 | 對方忙線中 | 未接來電 |
no_answer | 一對一振鈴超時;群通話沒有接通,只剩發起人超過 60 秒 | 對方無應答 | 未接來電 |
connection_lost | 一對一任一方掉線或裝置被踢下線;群通話最後在通話中的人掉線或裝置被踢下線 | 通話中斷 | 通話中斷 |
server_ended | 你的服務端或控制檯結束了通話,或把一對一通話的一方移出 | 通話已結束 | 通話已結束 |
platform_ended | 平臺結束了通話 | 通話已結束 | 通話已結束 |
user_unavailable | 一對一通話中一方的賬號被封禁或刪除,或振鈴中的被叫賬號被刪除 | 通話已結束 | 通話已結束 |
group_unavailable | 群被解散或封禁 | 通話已結束 | 通話已結束 |
app_unavailable | 應用或租戶不能提供服務,或平臺停用了本應用的音影片 | 通話已結束 | 通話已結束 |
service_disabled | 平臺停用了承載這個通話的媒體服務,並要求立即結束進行中的通話 | 通話已結束 | 通話已結束 |
max_duration | 通話超過 12 小時 | 通話已結束 | 通話已結束 |
以後可能增加新的結束原因,遇到不認識的值時顯示為“通話已結束”。
通話的 ended_via 說明由誰結束:client 使用者在客戶端(ended_by 為結束通話的使用者)、openapi 你的服務端、console 控制檯、platform 平臺、system 系統(如振鈴超時、掉線、服務端判定的忙線)。
通話記錄
通話結束後,會話中自動寫入一條通話記錄訊息,每個通話恰好一條:
- 一對一:寫入兩人的單聊會話,傳送者為主叫。沒有接通的通話(如未接聽、忙線、取消)計入被叫的未讀數,讓他看到未接來電;接透過的通話和被叫主動拒絕(
rejected)的不計入未讀數。寫入時任一方的賬號已被刪除的不寫入; - 群通話:寫入群會話,傳送者為發起人(他已不在群裡時為系統),不計入未讀數。群已解散的不寫入;
- 通話記錄不發離線推送:來電、未接來電和取消已經推送過;
- 應用可以在執行策略中關閉通話記錄(
rtc_call_record_enabled),例如由業務系統自己展示通話。
通話記錄的訊息型別 type 為 call,客戶端和你的服務端都不能傳送這種型別的訊息。body 的欄位:
| 欄位 | 型別 | 說明 |
|---|---|---|
call_id | String | 通話 ID,可以用它查詢通話詳情 |
call_type | String | single 一對一 / group 群通話 |
media | String | audio 語音 / video 影片 |
result | String | 結束原因,取值同結束原因 |
duration_seconds | Number | 通話時長,秒,未接通為 0 |
started_at | String | 發起時間 |
群會話中的一條通話記錄(查詢歷史訊息時返回):
{
"conversation_id": "100311544667045888",
"conversation_type": "group",
"seq": 5,
"message_id": "100313815656169472",
"client_msg_id": "sys_rtc_100313666666102784",
"sender": "alice",
"sender_type": "user",
"recipient": null,
"type": "call",
"body": {
"call_id": "100313666666102784",
"call_type": "group",
"media": "video",
"result": "completed",
"duration_seconds": 32,
"started_at": "2026-10-04T19:30:41.871Z"
},
"ext": null,
"mentions": null,
"reply_to": null,
"need_receipt": false,
"exclude_from_unread": true,
"reactions": null,
"pinned": null,
"edited": null,
"recalled": null,
"erased": false,
"via": "system",
"created_at": "2026-10-04T19:31:17.393Z",
"message_version": 0
}通話記錄按普通訊息儲存,保留期由執行策略 message_retention_days 決定,與通話資料的保留期無關。
計費分鐘數與統計
本節說明介面中 duration_seconds、billable_minutes 等欄位怎麼統計。這些欄位不是新計費的扣費依據:新計費按實際收到的解析度分檔計算標準時長,同一應用的通話和頻道按月合計後取整,見音影片計費。
| 欄位 | 怎麼算 |
|---|---|
duration_seconds | 從接通到結束的時長,振鈴不計 |
billable_minutes | 歷史報表欄位:每個成員從接通或加入到離開的時長,各自向上取整到分鐘,再相加。例如兩人各通話 28 秒,為 2 分鐘 |
| 每天的語音、影片分鐘 | 計入通話結束的那一天,用於報表、歷史賬單和排查問題 |
不要用 billable_minutes × 新单价 推算新計費的扣費;新計費也不會在標準時長之外,再按這些分鐘另扣一次。
例如兩人各 28 秒的純語音通話,本月沒有其他用量時:報表中為 2 分鐘;新計費合計 56 標準秒,按月累計扣 1 標準分鐘。視訊通話還要先乘以實際接收檔位的係數。跨天、跨月的歸屬和更正見取整規則。
併發通話額度
應用同時進行(發起後還沒有結束)的通話數受套餐的併發通話額度 max_concurrent_calls 限制,為 0 表示不限,在控制檯應用的“套餐與用量”中檢視,見套餐與賬單。達到額度後再發起通話,返回 409 limit_exceeded,details.reason 為 app_concurrent_calls,details.limit 為額度:
{
"code": "limit_exceeded",
"message": "应用进行中的通话数已达上限",
"details": {
"limit": 1,
"reason": "app_concurrent_calls"
},
"request_id": "FW43EI3USADPEKRAFTOZWODLDX"
}額度只在發起時檢查,進行中的通話不受影響;同時發起的多個通話可能讓實際的通話數略超額度。每天同時進行的通話數峰值見查詢通話統計。
執行策略
音影片相關的執行策略:
| 欄位 | 說明 | 預設值 | 取值範圍 |
|---|---|---|---|
rtc_enabled | 是否開啟音視訊通話 | false | true、false |
rtc_ring_timeout_seconds | 來電的振鈴時間,單位為秒,一對一和群通話相同 | 60 | 20 到 120 |
max_group_call_participants | 群通話同時在通話中的人數上限,含發起人 | 0(不限,由套餐決定) | 0 或不小於 3 |
rtc_call_record_enabled | 通話結束後是否在會話中寫入通話記錄 | true | true、false |
- 關閉
rtc_enabled後不能發起、接聽、加入或邀請,返回permission_denied(rtc_disabled)。新計費啟用後餘額不足時同樣拒絕,但進行中的通話不會因為餘額用完而被強制結束,之後的用量照常結算。切換到新計費之前,套餐不允許音影片時同樣拒絕。 - 振鈴時間和人數上限在發起或邀請時確定,修改後隻影響之後的來電和通話。
應用狀態的影響
| 情況 | 影響 |
|---|---|
| 平臺停用了本應用的音影片 | 進行中的通話立即結束(app_unavailable);不能發起、接聽、加入和邀請,返回 permission_denied(rtc_suspended),details.suspend_reason 為平臺給出的原因。控制檯的“音影片”頁面顯示停用的原因和時間,如有異議請聯絡我們 |
| 音影片服務暫不可用 | 不能發起通話,返回 permission_denied(rtc_not_configured) |
| 應用停用、被暫停服務,租戶被暫停或處於登出冷靜期 | 進行中的通話結束(app_unavailable),使用者的請求返回 app_unavailable 或 tenant_unavailable,見應用和租戶的狀態 |
| 應用處於只讀狀態(如欠費) | 不能發起通話和在群通話中邀請,返回 app_unavailable(欠費時 details.reason 為 arrears);接聽、加入、拒絕、結束通話、心跳和續期照常,已經發起的通話可以正常完成。你的服務端查詢、結束通話和移出成員照常 |
| 應用刪除、租戶登出 | 進行中的通話結束(app_unavailable),之後通話資料被刪除 |
事件回撥
在控制檯配置事件回撥後,你的服務端可以收到以下音影片事件。事件中用使用者名稱錶示使用者,帶發起時的 ext,沒有承載通話的媒體服務的資訊:
| 事件 | 觸發時機 |
|---|---|
rtc.call_created | 發起通話,包括髮起時被叫忙線、立即結束的通話 |
rtc.call_answered | 通話接通 |
rtc.member_joined | 群通話中有人加入(發起人在發起時加入不另發) |
rtc.member_left | 群通話中有人離開(通話結束時離開的不另發) |
rtc.call_ended | 通話結束,帶結束原因、時長、計費分鐘數和每個成員的通話時長 |
每個通話的 rtc.call_created 和 rtc.call_ended 各一條。事件可能重複投遞、不保證順序,請以 rtc.call_ended 為準記錄通話的結果,例如按通話的計費分鐘數向你的使用者收費、把訂單號放在 ext 中對應業務。
資料保留
- 通話和成員在結束 180 天后刪除,之後查詢通話不再返回;
- 通話統計保留到應用刪除;
- 會話中的通話記錄訊息按
message_retention_days保留。
資料結構
通話物件
查詢通話詳情、結束通話等介面返回的通話。查詢通話列表返回的每一項沒有 members。
| 欄位 | 型別 | 說明 |
|---|---|---|
call_id | String | 通話 ID |
type | String | single 一對一 / group 群通話 |
group_id | String | 群通話所在的群 ID,一對一為 null |
media | String | audio 語音 / video 影片 |
status | String | 通話狀態:ringing / active / ended,見通話狀態 |
initiator | String | 發起人的使用者名稱,已刪除的使用者為 null |
max_participants | Number | 同時在通話中的人數上限:一對一為 2,群通話為發起時的 max_group_call_participants |
joined_count | Number | 此刻在通話中的人數,結束後為 0 |
ext | Object | 發起時附帶的自定義欄位,字串鍵值對,沒有為 null |
created_at | String | 發起時間 |
answered_at | String | 接通時間,未接通為 null |
ended_at | String | 結束時間,未結束為 null |
end_reason | String | 結束原因,見結束原因,未結束為 null |
ended_by | String | 在客戶端結束通話的使用者,如結束通話、拒絕、結束群通話的人;你的服務端、平臺和系統結束的,以及已刪除的使用者為 null |
duration_seconds | Number | 通話時長:從接通到結束(未結束時到現在)的秒數,向下取整;未接通為 0 |
version | Number | 版本號,通話或成員的狀態每變化一次加一 |
members | Array<Object> | 全部成員,包括已離開、拒絕、未接聽的,見通話成員物件;按 media_uid 升序,最多 100 個 |
ended_via | String | 結束方:client、openapi、console、platform、system,見結束原因;未結束為 null |
member_count | Number | 通話涉及的成員數,包括已離開、拒絕、未接聽的 |
peak_joined | Number | 同時在通話中的人數的最大值 |
billable_minutes | Number | 歷史統計分鐘,通話結束時按成員分別取整;未結束為 0。不是新模式實際扣減的標準分鐘,見口徑說明 |
一個已結束的群視訊通話:
{
"call_id": "100313666666102784",
"type": "group",
"group_id": "100311544667045888",
"media": "video",
"status": "ended",
"initiator": "alice",
"max_participants": 16,
"joined_count": 0,
"ext": {
"topic": "周会"
},
"created_at": "2026-10-04T19:30:41.871Z",
"answered_at": "2026-10-04T19:30:44.923Z",
"ended_at": "2026-10-04T19:31:17.049Z",
"end_reason": "completed",
"ended_by": "alice",
"duration_seconds": 32,
"version": 5,
"members": [
{
"username": "alice",
"role": "initiator",
"state": "left",
"media_uid": 1,
"invited_by": null,
"ring_expires_at": null,
"joined_at": "2026-10-04T19:30:41.871Z",
"left_at": "2026-10-04T19:31:17.049Z",
"leave_reason": "ended",
"invited_at": null,
"joined_platform": "ios",
"join_count": 1,
"joined_ms": 32126,
"first_joined_at": "2026-10-04T19:30:41.871Z",
"media_joined_at": "2026-10-04T19:30:41.898Z",
"media_left_at": null
},
{
"username": "bob",
"role": "invitee",
"state": "left",
"media_uid": 2,
"invited_by": "alice",
"ring_expires_at": "2026-10-04T19:31:41.871Z",
"joined_at": "2026-10-04T19:30:44.923Z",
"left_at": "2026-10-04T19:31:17.049Z",
"leave_reason": "ended",
"invited_at": "2026-10-04T19:30:41.871Z",
"joined_platform": "android",
"join_count": 1,
"joined_ms": 32126,
"first_joined_at": "2026-10-04T19:30:44.923Z",
"media_joined_at": "2026-10-04T19:30:44.947Z",
"media_left_at": null
},
{
"username": "carol",
"role": "invitee",
"state": "left",
"media_uid": 3,
"invited_by": "alice",
"ring_expires_at": "2026-10-04T19:31:41.871Z",
"joined_at": "2026-10-04T19:30:46.967Z",
"left_at": "2026-10-04T19:31:02.014Z",
"leave_reason": "hangup",
"invited_at": "2026-10-04T19:30:41.871Z",
"joined_platform": "web",
"join_count": 1,
"joined_ms": 15047,
"first_joined_at": "2026-10-04T19:30:46.967Z",
"media_joined_at": "2026-10-04T19:30:46.983Z",
"media_left_at": null
}
],
"ended_via": "client",
"member_count": 3,
"peak_joined": 3,
"billable_minutes": 3
}通話成員物件
| 欄位 | 型別 | 說明 |
|---|---|---|
username | String | 使用者名稱,已刪除的使用者為 null |
role | String | 角色:initiator 發起人 / invitee 被邀請的人 / joiner 自己加入群通話的人 |
state | String | 成員狀態,見成員的狀態 |
media_uid | Number | 成員在這個通話中的編號,從 1 開始,按成為成員的先後分配 |
invited_by | String | 最近一次邀請他的使用者名稱;發起人、自己加入的人為 null |
ring_expires_at | String | 最近一次邀請的振鈴截止時間,沒有被邀請過為 null |
joined_at | String | 最近一次加入的時間,沒有加入過為 null |
left_at | String | 最近一次離開的時間,沒有離開過為 null |
leave_reason | String | 最近一次離開的原因,見離開原因,沒有離開過為 null |
invited_at | String | 最近一次被邀請的時間,沒有被邀請過為 null |
joined_platform | String | 最近一次加入時所用裝置的平臺,如 ios、android、web,取值同登入時的 platform;沒有加入過為 null |
join_count | Number | 加入的次數,群通話中離開後可以重新加入 |
joined_ms | Number | 在通話中的累計時長,毫秒,只計接通之後的部分。仍在通話中的,本次加入的時長在離開時才計入 |
first_joined_at | String | 第一次加入的時間,沒有加入過為 null |
media_joined_at | String | 第一次連上媒體服務的時間,沒有連上為 null。接聽了卻一直為 null 的,通常是裝置的網路無法連線媒體服務 |
media_left_at | String | 最近一次斷開媒體服務的時間,重新連上後清空,沒有為 null |
與獨立頻道共享額度
通話和獨立 RTC 頻道共用兩項限制:
- 一個使用者同時只能在一個通話或頻道中。使用者在頻道里,或剛退出頻道、媒體還在清理時,不能發起或接聽通話;
- 應用的併發通話額度由通話和頻道合計。
通話的計費取整方式不受影響;頻道使用單獨的計量項,按天彙總取整,見頻道用量與更正。
