全部產品
Search
文件中心

Alibaba Cloud Model Studio:HappyOyster iOS SDK API 參考

更新時間:Sep 23, 2026

HappyOyster iOS SDK 入口為處理程序級單例 HappyOysterEngine.shared,業務方法均為 async throws,失敗時拋出 OysterSDKError。一次體驗由 OysterTravel 控制代碼承載,支援 UIKit 與 SwiftUI。

本文檔是 HappyOyster iOS SDK 的對外功能描述與介面細節參考:逐一說明公開類型與方法的參數、使用時機、用法與簡單範例。

完整接入流程(工程建置、相依性設定、伺服器端配合、端到端跑通)請參閱 HappyOyster iOS SDK 接入指南,本文不再贅述。

核心概念

先確立幾個名詞,以便閱讀後文。

概念

說明

token

HTTP 驗證 token(百煉臨時 API Key)。由您的伺服器呼叫百煉介面換取後下發,經 updateToken(_:) 注入;SDK 以 HTTP Bearer 方式攜帶它請求閘道。SDK 僅保存最新一個,不持久化、不重新整理,過期後由您重新換取並再次注入。

ticket

一次性體驗憑證。由您的伺服器呼叫開放平台 get-travel-credential 換取(前綴 tk_,有效期 30 分鐘,一次性),作為 createTravel(ticket:) 的輸入參數;體驗結束或過期後即失效,不可重複使用。

World

AI 世界,含角色、場景。由您的伺服器建立與管理,SDK 不涉及。

Travel

一次即時體驗對應一個 OysterTravel 控制代碼。僅限單次使用,到達終止狀態後即失效,需透過 engine 重新 createTravel。

工作階段狀態

OysterTravelStatus:idle → prepare → running → pausing → paused(paused 經重新連線回到 prepare → running),外加兩個終止狀態 ended / failed(詳見「事件與狀態」章節)。

模式

adventure(世界探索,方向/視角/動作指令)、directing(即時導演,文字驅動劇情)或 acting(角色演繹,文字驅動人物演繹)。start() 返回的 mode 據此決定 UI,各模式可用的介面詳見「概覽」章節。

概覽

整合本 SDK 後,您的 App 可進入由 AI 即時生成的「世界」,以世界探索模式(adventure)、即時導演模式(directing)或角色演繹模式(acting)進行即時互動視訊體驗:開始體驗 → 即時播放 → 即時互動 → 過程控制(暫停/恢復/回溯/結束)→ 狀態與錯誤回調。

各模式可用的能力不同,請按 start() 傳回的 mode 決定 UI:

能力

adventure

directing

acting

sendCommand(方向/視角/動作)

支援

不支援

不支援

sendInstruct(文字指令)

不支援

支援

支援

pause() / resume()

不支援

支援

支援

rewind(toSec:)

不支援

支援

不支援,請隱藏入口

end()

支援

支援

支援

start(maxExperienceTimeSec:)

生效

忽略

忽略

說明acting 世界以直向螢幕優先:start() 返回的 aspectRatio 提供本次體驗的畫幅比例(9:16 / 16:9),建議在拉流前據此決定播放器方向與容器尺寸(詳見「資料模型」章節)。

import HappyOysterSDK,核心入口是兩個類型。

HappyOysterEngine —— 編排入口,處理程序級單例 HappyOysterEngine.shared。

方法 / 屬性

說明

initialize(config:)

初始化執行階段並自動註冊即時引擎(createTravel 前一次)

updateToken(_:)

注入 / 更新 HTTP 驗證 token

createTravel(ticket:)

使用一次性憑證建立工作階段控制代碼

cleanup()

釋放資源(可再 initialize)

version

目前 SDK 版本號

OysterTravel —— 單次體驗的工作階段控制代碼,由 createTravel(ticket:) 建立。

方法 / 屬性

說明

videoView / OysterVideoView(travel:)

播放檢視(UIKit / SwiftUI)

events / status

狀態變更 + 錯誤推送串流 / 可觀察的目前狀態

start() / start(maxExperienceTimeSec:)

建立連線播放(可選請求世界探索模式體驗的最大時長)

sendInstruct(content:)

即時導演模式文字指令

sendCommand(_:) / flushCommands()

世界探索模式操控

pause() / resume()

暫停 / 恢復(directing 與 acting 支援)

rewind(toSec:)

回溯(僅限 paused)

end()

結束(冪等,務必呼叫)

pauseLocalAudioCapture() / resumeLocalAudioCapture()

暫時釋放 / 恢復麥克風

此外還有幾個輔助匯出:OysterLog 用於接管 SDK 內部日誌;HappyOysterEngine.version 讀取版本號;OysterVideoView 是 SwiftUI 播放檢視(與 videoView 等效)。用法詳見後文。

生命週期:初始化 → 注入 token → 建立工作階段 → 掛載視訊與訂閱事件 → start 播放 → 互動 → end。SDK 不負責世界的建立與管理(由您的伺服器完成),也不公開底層即時通訊細節。

快速上手

一段從初始化到結束的完整流程,逐步註解。各 API 的細節見「API 參考」章節。

import HappyOysterSDK

// 1) 初始化(App 启动后尽早,createTravel 前一次)
let engine = HappyOysterEngine.shared
engine.initialize(config: OysterConfig(
    apiHost: "[workspace-id].[region].maas.aliyuncs.com",// 网关地址获取参考百炼文档
    model: "happyoyster-1.0-adventure"                  // 已开通的模型名(含版本),必填;取值见 HappyOyster 系列模型文档
))

// 2)(可选)接管日志:设置等级 + 自定义打印
OysterLog.setMinimumLevel(.info)
OysterLog.setHandler { level, tag, message in
    print("[Oyster][\(level)][\(tag)] \(message)")
}

// 3) 注入 HTTP 鉴权 token(百炼临时 API Key,由你的服务端下发)
engine.updateToken(temporaryApiKey)

// 4) 用一次性 ticket 创建会话句柄(尚未建连)
let travel = try engine.createTravel(ticket: ticket)

Task { @MainActor in
    // 5) 挂载视频(UIKit;SwiftUI 用 OysterVideoView(travel:))
    containerView.addSubview(travel.videoView)

    // 6) start 前就订阅事件,避免漏掉早期状态
    let eventTask = Task {
        for await event in travel.events {
            switch event {
            case .statusChanged(let status): render(status)   // 见"事件与状态"章节
            case .error(let error):          handle(error)     // 见"错误码"章节 error.code / error.kind
            }
        }
    }

    do {
        // 7) 建连播放
        let data = try await travel.start()

        // 8) 按模式互动:adventure 走操控指令,directing / acting 走文本指令
        if data.mode == .adventure {
            travel.sendCommand(OysterAdventureCommand(translation: .front))
        } else {
            _ = try await travel.sendInstruct(content: "突然下起了大雨")
        }
    } catch let error as OysterSDKError {
        handle(error)
    }

    // 9) 任意退出路径都收口到一次 end()
    _ = try? await travel.end()
    eventTask.cancel()
}

// 退出 SDK / 切换网关:await engine.cleanup()

說明工程建置、相依性設定(含必須引入的 AliRTC 適配器與廠商二進位檔)、伺服器配合與端到端跑通,請參閱範例工程文件。

環境需求

項目

要求

最低系統版本

iOS 15.0+(公開類型均標記 @available(iOS 15.0, *))

語言

Swift(async/await)

並行處理

主執行緒存取(入口類型標記 @MainActor)

網路

需可存取公網

權限

Info.plist 須設定 NSMicrophoneUsageDescription(見下文)

麥克風權限:即時互動視訊體驗需要建立一條雙向(上行 + 下行)即時影音通道,因此 SDK 運行期間會佔用本機麥克風——這並非錄音。Info.plist 必須提供 NSMicrophoneUsageDescription,否則啟動即時採集時會崩潰。當您需要獨佔麥克風(如語音辨識)時,請使用 pauseLocalAudioCapture() / resumeLocalAudioCapture() 暫時釋放與恢復(詳見「OysterTravel」章節)。

引入與驗證

引入與相依性

程式碼層面 import HappyOysterSDK 即可。SDK 以預編譯二進位檔(xcframework)按 CocoaPods subspec 分發,已發佈至 CocoaPods 公開 Trunk——請在 Podfile 中宣告相依性:

# HappyOysterSDK / AliVCSDK_ARTC 均发布在 CocoaPods 公开源。
pod 'HappyOysterSDK', '1.0.3'              # 聚合入口(Core + World)
pod 'HappyOysterSDK/UI', '1.0.3'           # 可选:默认 UI 组件(视频视图、操控 HUD)
pod 'HappyOysterSDK/StreamAliRTC', '1.0.3' # 视频流 + AliRTC 引擎适配器(已依赖 Stream)

# RTC 厂商二进制:SDK 弱引用、不随 SDK 分发,由集成方自行引入。
pod 'AliVCSDK_ARTC', '7.11.0'

說明引入 HappyOysterSDK/StreamAliRTC 時 AliVCSDK_ARTC 為必要項目:若缺失,SDK 會靜默回落 Loopback——能連上且狀態走到 running,但會黑屏且不報錯。

工程建置與端到端初始化流程由範例工程負責,請參閱範例工程文件。本文聚焦於介面本身。

驗證模型

SDK 不獲取、不重新整理 token,保持輕量。驗證分為兩層,整合方負責生命週期管理:

  1. HTTP 驗證 token(百煉臨時 API Key):由您的伺服器呼叫百煉介面換取後下發,經 updateToken(_:) 注入。SDK 內部部分服務直接呼叫百煉閘道,並攜帶該 token 進行驗證——因此它必須是百煉端簽發的臨時 API Key,而非您自有業務服務的 token。SDK 僅保存最新一個,不持久化、不重新整理;過期後由您重新換取並再次注入。
  2. 一次性體驗憑證 ticket:由您的伺服器呼叫開放平台 get-travel-credential 換取(前綴 tk_,有效期 30 分鐘,一次性),作為 createTravel(ticket:) 的輸入參數;體驗結束(正常或異常)或過期後即失效,不可重複使用。

說明AK、簽名金鑰等高權限僅存在於您的伺服器,用戶端 SDK 永不接觸;用戶端取得的始終是短時效臨時 API Key。

說明Feature Gate(遠端開關 / 強制升級):伺服器可遠端停用 SDK 或設定最低支援版本。被停用時,受控呼叫(start / pause / resume / rewind / sendInstruct / sendCommand)會被拒絕並透出 108001,進行中的體驗會被 SDK 主動終止(詳見「錯誤碼」章節);OysterSDKError.raw 中帶有可讀的停用原因,版本過低時請引導使用者升級。

處理 token 過期:上述 HTTP 驗證 token 過期後,需立即向您的伺服器重新請求 token,再透過 updateToken(_:) 注入。有兩處需判斷 token 是否過期:

  1. 呼叫 engine.createTravel、travel.start 等 API 時,處理 error,判斷 token 過期或無效的 error 類型(101001 / 101002),注入新 token 後重新呼叫對應 API。
  2. 監聽 OysterTravelEvent 的 .error 事件時,判斷 token 過期或無效的 error 類型,重新請求 token 並注入。

API 參考

入口為兩個類型,均標註 @MainActor、@available(iOS 15.0, *)。業務方法為 async throws,失敗時拋出 OysterSDKError;有返回值的方法均標註 @discardableResult。

HappyOysterEngine

處理程序級單例,編排入口。init 非 public——請統一使用 HappyOysterEngine.shared,切勿自行實體化(底層即時引擎亦為處理程序單例)。

@MainActor @available(iOS 15.0, *)
public final class HappyOysterEngine {
    public static let shared: HappyOysterEngine            // 进程级唯一实例
    public static let version: String                      // 当前 SDK 版本号

    @discardableResult
    public func initialize(config: OysterConfig) -> Bool   // 初始化(createTravel 前一次)
    public func updateToken(_ token: String)               // 注入/更新 HTTP 鉴权 token
    public func createTravel(ticket: String) throws -> OysterTravel  // 用一次性凭证创建会话句柄
    public func cleanup() async                            // 释放资源(可再 initialize)
}

initialize(config:)

初始化執行階段並自動註冊即時引擎(無需宿主手動註冊)。

使用時機:createTravel 前呼叫一次,App 啟動後盡早呼叫。

注意:空閒時再次呼叫即以新 config 重新裝配(更換 apiHost / model 都不必先 cleanup(),已注入的 token 會保留);僅當有進行中的 Travel 時為 no-op 並發出警告,需先 end()。config 無效時維持現狀,已生效的運行環境不受影響。

簽章
@discardableResult
public func initialize(config: OysterConfig) -> Bool
傳回值

Bool —— 本次傳入的 config 是否已生效。false 有兩種情況:config 無效(apiHost / model 為空或無法組合出合法閘道 URL),或有進行中的 Travel 導致本次呼叫被忽略;這兩種情況下運行環境皆維持原狀。

說明更換 config 時請勿使用 isReady 判斷成敗:若重新 initialize 被拒,先前的 config 仍在生效,isReady 依舊為 true。isReady 回答的是「引擎現在可用嗎」,而本返回值回答的是「我剛傳的這份 config 生效了嗎」。

參數

欄位

類型

是否必填

描述

config

OysterConfig

是

config.apiHost 是百煉網關地址,不是您的業務伺服器,必填;預發/試用環境必須顯式傳入對應網關,否則請求失敗(如 106001 域名無法解析)。config.model 是已開通的模型名稱(含版本),同樣必填、無預設值——HappyOyster 按模式拆成了不同子模型,SDK 無從推斷該用哪個。兩者須與注入的 token 同帳號、同區域、成套使用。其餘欄位見「数据模型」章節 OysterConfig。

說明模型與 mode 一一對應:按模式拆分後,每個模型是一條獨立的閘道路由,一次 initialize 僅服務一種 mode 的世界。若您的 App 同時提供多種模式的世界,在進入不同模式的世界前使用對應模型再 initialize() 一次即可——空閒時以最新 config 為準,不需要 cleanup(),已注入的 token 也會保留;若有進行中的 Travel,該呼叫會被忽略,需先 end()。當模型與世界 mode 不匹配時,閘道會以 AccessDenied 拒絕並歸一為 106003。

OysterLog

設定並接管 SDK 內部的日誌,輸出至您自己的日誌模組。提供 setMinimumLevel(_:) 設定等級、setHandler(_:) 自訂列印(詳見「快速入門」範例)。

updateToken(_:)

注入 / 更新 HTTP 驗證 token(百煉臨時 API Key,見「驗證模型」章節)。

使用時機:initialize 之後可隨時呼叫;token 過期或收到驗證類錯誤(101001 / 101002)後重新換取並再次呼叫。

注意:未 initialize 時為 no-op 並發出警告。

簽章
public func updateToken(_ token: String)
參數

欄位

類型

是否必填

描述

token

String

是

HTTP 驗證 token(百煉臨時 API Key,見「驗證模型」章節)。

createTravel(ticket:)

使用一次性 ticket 建立一次工作階段控制代碼。

使用時機:每次開始新體驗前呼叫;返回的控制代碼尚未建立連線,需再呼叫 travel.start()。視訊畫面從返回的控制代碼取得(詳見「OysterTravel」章節)。

注意(同步 throws):未 initialize 時拋出 100001;上一個 Travel 未 end() 前再次呼叫則拋出 103004(每個 engine 同時僅允許一個 active Travel)。

簽章
public func createTravel(ticket: String) throws -> OysterTravel
參數

欄位

類型

是否必填

描述

ticket

String

是

一次性憑證,建立後即視為本次體驗佔用。

傳回值

返回 OysterTravel 工作階段控制代碼;尚未建立連線,需再呼叫 start()。

錯誤

code

說明

100001

未 initialize

103004

上一個 Travel 未 end() 前再次呼叫(每個 engine 同時僅允許一個 active Travel)

cleanup()

釋放 SDK 資源(結束 active travel、執行階段設定、token)。

使用時機:徹底退出 SDK 或需要更換 config 時。

注意:async——內部會先確定性地 end() 目前的 active travel,再拆解運行環境,不留 fire-and-forget。釋放後可再次 initialize。

簽章
public func cleanup() async

OysterTravel

由 createTravel 建立的工作階段控制代碼;僅限單次使用,到達終止狀態(end / 伺服器端結束 / 失敗)後即失效,需透過 engine 重新 createTravel。同時也是 ObservableObject(@Published status,可直接驅動 SwiftUI)。

@MainActor @available(iOS 15.0, *)
public final class OysterTravel: ObservableObject {
    @Published public private(set) var status: OysterTravelStatus   // 当前对外状态(可观察)
    public var isEnded: Bool { get }                                // 是否已到达终态(同步可读)
    public var videoView: UIView { get }                            // UIKit 播放视图;SwiftUI 用 OysterVideoView(travel:)
    public var events: AsyncStream<OysterTravelEvent> { get }       // 状态变更 + 错误,多订阅

    @discardableResult public func start() async throws -> OysterStartTravelData   // 建连播放
    @discardableResult public func start(maxExperienceTimeSec: Int?) async throws -> OysterStartTravelData  // 建连播放 + 请求最大体验时长(仅世界探索模式)
    @discardableResult public func pause() async throws -> OysterTravelStateData   // 暂停(directing / acting)
    @discardableResult public func resume() async throws -> OysterTravelStateData  // 恢复
    @discardableResult public func rewind(toSec: TimeInterval) async throws -> OysterRewindTravelData  // 回溯(仅 paused)
    @discardableResult public func end() async throws -> OysterEndTravelData       // 结束(幂等,务必调用)
    @discardableResult public func sendInstruct(content: String) async throws -> OysterSendInstructData  // 实时导演模式文本指令

    public func sendCommand(_ command: OysterAdventureCommand)      // 世界探索模式操控(fire-and-forget)
    public func flushCommands()                                     // 松键时补发最后一帧
    public func pauseLocalAudioCapture() async                      // 临时让出麦克风
    public func resumeLocalAudioCapture() async                     // 恢复麦克风占用
}

videoView / OysterVideoView(travel:)

遠端畫面渲染入口,「SDK 提供檢視、宿主負責擺放」。UIKit 取 travel.videoView;SwiftUI 使用 OysterVideoView(travel:)。

使用時機:建立控制代碼後即可取得(多次存取返回同一檢視),掛載至任意層級,引擎就緒後自動渲染,start() 前後掛載均可,不會黑屏。

注意:工作階段結束時 SDK 會自動釋放渲染繫結,您可按需將檢視移出層級。

簽章
public var videoView: UIView { get }

events / status / isEnded

events 是狀態變更與錯誤的推送串流;status 是可觀察的目前對外狀態;isEnded 可同步讀取是否為終止狀態。

使用時機:建議在 start() 之前就開始消費 events,避免遺漏早期狀態。

注意:每次存取 events 會返回一條獨立串流,支援多重訂閱;取消訂閱等同於結束 for await 迭代(或銷毀持有的 Task)。SwiftUI 可直接使用 @StateObject/@ObservedObject 觀察 status(錯誤仍走 events)。詳見「事件與狀態」章節。

簽章
@Published public private(set) var status: OysterTravelStatus
public var isEnded: Bool { get }
public var events: AsyncStream<OysterTravelEvent> { get }

start() / start(maxExperienceTimeSec:)

使用 create 時捕獲的 ticket 換取 travel 與 RTC 入會設定並建立連線播放。成功後 SDK 會自動建立即時連線並開始內部狀態輪詢,狀態經 events 透出。

注意(無推流自動結束):伺服器下發「無推流逾時」(預設約 30s)。若建立連線後在該時長內仍未收到推流(遲遲未進入 running),SDK 會自動結束本次體驗,狀態轉為 failed、經 events 的 .error 透出 105006(致命,請按回到開始前介面處理,無需自行計時)。

簽章
@discardableResult public func start() async throws -> OysterStartTravelData
@discardableResult public func start(maxExperienceTimeSec: Int?) async throws -> OysterStartTravelData
參數

欄位

類型

是否必填

描述

maxExperienceTimeSec

Int?

否

(選用)——請求本次世界探索(adventure)體驗的最大持續時長(秒)。參數會原樣傳送給伺服器,允許值、實際生效時長與到期自動結束時機均由伺服器決定,SDK 不做本機驗證;傳 nil(或呼叫無參數的 start())時使用伺服器預設時長。即時導演(directing)模式會忽略此參數。到期後由伺服器結束體驗,宿主經 events 收到 ended 終止狀態(與伺服器主動結束相同,詳見「事件與狀態」章節)。

傳回值

OysterStartTravelData(mode / version / encryptedTravelId 等,詳見「資料模型」章節),據此決定互動 UI。

說明ticket 的世界 mode 必須與 initialize 傳入的 model 匹配(由呼叫方保證):模型按 mode 拆分後,每個模型是一條獨立的閘道路由,start() 是將 ticket 傳往目前模型那條路由。SDK 不會也無法在呼叫前替您驗證這一點——mode 由本次 start() 的回應下發(即 OysterStartTravelData.mode),呼叫前 SDK 手中只有不透明的 ticket 和模型名稱,沒有可比對的 mode;而從模型名稱反推 mode 屬於對伺服器命名的猜測,SDK 不會執行此操作。

因此:切換至另一種 mode 的世界之前,請使用對應模型再 initialize() 一次(空閒時以最新 config 為準,無需 cleanup(),token 會保留;若有進行中的 Travel,該呼叫會被忽略,需先 end())。若不匹配,本次 start() 會在閘道端失敗,排查時請優先核對「目前 model 與本次 ticket 所屬世界的 mode 是否成套」,再查看下表的憑證類錯誤碼。

錯誤

code

說明

401010

憑證無效(亦包括:憑證有效,但所屬世界的 mode 與目前 model 並非同一條閘道路由)

401011

憑證已使用

403002

世界未就緒

403007

目前規格未開通(如角色演繹(acting)規格)

429001 / 429002

併發已滿 / 可用容量不足

500001

資源/服務端失敗

103004

並行啟動

pause() / resume()

暫停 / 恢復體驗(冪等)。

使用時機:directing(即時導演)與 acting(角色演繹)世界支援,adventure 不支援。可使用 start() 返回的 mode 提前決定是否顯示暫停按鈕。pause 要求目前為 running;resume 要求為 paused。

簽章
@discardableResult public func pause() async throws -> OysterTravelStateData
@discardableResult public func resume() async throws -> OysterTravelStateData
返回值

返回 OysterTravelStateData(欄位見「数据模型」章節)。

錯誤

code

說明

103001

無活躍體驗

103002

狀態/版本不允許

103003

模式不匹配

rewind(toSec:)

回溯至指定秒數。成功後 SDK 會自動使用原始 rtcConfig 重新加入會議並回到播放。

使用時機:僅 paused 狀態可發起,且只有 directing(即時導演)世界支援——acting 與 adventure 均不支援回溯,請在這兩種模式下隱藏回溯入口,切勿呼叫。

簽章
@discardableResult public func rewind(toSec: TimeInterval) async throws -> OysterRewindTravelData
參數

欄位

類型

是否必填

描述

toSec

TimeInterval

是

回溯到的目標秒數。

返回值

返回 OysterRewindTravelData(欄位見「数据模型」章節)。

錯誤

code

說明

103001

無活躍體驗

103002

狀態不允許

end()

結束體驗(具冪等性,可重複呼叫)。呼叫成功或異常退出後,SDK 會自動斷開即時連線、停止輪詢並釋放所有工作階段資源,ticket 同時失效,控制代碼進入終止狀態。

使用時機與注意事項:無論使用者主動退出或被動結束(計時到期、收到 .ended/.failed、頁面銷毀),都必須確保執行一次 end(),否則遠端資源釋放可能不及時。建議將所有退出路徑收斂至同一個冪等清理方法。

簽章
@discardableResult public func end() async throws -> OysterEndTravelData
返回值

返回 OysterEndTravelData(欄位見「数据模型」章節)。

sendInstruct(content:)(即時導演模式)

傳送文字指令驅動劇情。

使用時機:即時導演模式;running 時直接傳送,paused 時暫存、待 resume 重新連入 running 後隨首幀補傳。

簽章
@discardableResult public func sendInstruct(content: String) async throws -> OysterSendInstructData
參數

欄位

類型

是否必填

描述

content

String

是

要傳送的文字指令內容。

返回值

返回 OysterSendInstructData(欄位見「数据模型」章節)。

錯誤

code

說明

103001

無活躍體驗

103002

狀態不允許

103003

在世界探索模式下呼叫

403004

輸入內容違規(內容安全審查)

404000

Travel 不存在

sendCommand(_:) / flushCommands()(世界探索模式)

  • sendCommand:傳送方向/視角/動作控制指令(詳見「資料模型」章節 OysterAdventureCommand,fire-and-forget,無返回值、不 throws)。僅於世界探索模式且為 running 時有效。外部可每幀高頻持續輸入,SDK 內部會進行節流(latest-wins 取樣、按 RTC 線速合幀),宿主無需自行節流。請注意伺服器對指令的回應本身有延遲,實際生效時間並不固定。
  • flushCommands:於「放開按鍵 / 輸入釋放」瞬間呼叫,立即補傳佇列中已等待的最後一條指令;若無待傳指令則為純 no-op,不會產生新指令。
簽章
public func sendCommand(_ command: OysterAdventureCommand)
public func flushCommands()
錯誤

code

說明

103001

無活躍體驗

103003

在即時導演模式下呼叫

105004

即時通道未就緒/傳送失敗

(均透過 events 的 .error 透出,非 throws。)

pauseLocalAudioCapture() / resumeLocalAudioCapture()

暫時釋放 / 恢復 SDK 對本機麥克風採集的佔用。

使用時機:語音辨識等需要獨佔麥克風時先 pause,結束後 resume。

簽章
public func pauseLocalAudioCapture() async
public func resumeLocalAudioCapture() async

事件與狀態

事件掛在 OysterTravel.events 上,是 SDK 對您的主動推送通道,用於透出非您主動呼叫所觸發的情況(例如內部自動維護的即時連線或狀態輪詢出現問題)。

var events: AsyncStream<OysterTravelEvent> { get }

@available(iOS 15.0, *)
public enum OysterTravelEvent {
    case statusChanged(OysterTravelStatus)
    // SDK 内部流程出错,例如内部接口请求出错、推流出错,注意这里必须判断 token 过期并重新请求 token
    case error(OysterSDKError)
}

消費方式二選一:

  • SwiftUI:OysterTravel 是 ObservableObject,可直接透過 @StateObject/@ObservedObject 觀察 status 驅動 UI;錯誤仍從 events 取得。
  • 命令式 / UIKit:在 Task 中使用 for await event in travel.events { ... },並以 switch 處理 .statusChanged / .error;結束時取消持有的 Task。
let task = Task {
    for await event in travel.events {
        switch event {
        case .statusChanged(let status): render(status)   // 见下表
        case .error(let error):          handle(error)     // error.code / error.kind,见"错误码"章节
        }
    }
}
// 结束时:task.cancel()

OysterTravelStatus(5 個過程態 + 2 個終態):

狀態

說明

典型處理

idle

create 後、start 前

—

prepare

建立連線 / 重新連線中(內部 connecting / reconnecting)

顯示連線 / 重新連線提示

running

串流已就緒、可互動(內部 playing)

顯示畫面與操控

pausing

暫停已受理、等待伺服器確認

顯示「暫停中…」

paused

已暫停(確認)

顯示暫停狀態(僅 directing 顯示回溯入口)

ended

已結束(主動 end 或伺服器端結束)。終態

收尾並关闭頁面

failed

失敗。終態

顯示錯誤並收尾

說明進入 ended / failed 後工作階段即終止,所有工作階段操作(pause/resume/sendCommand…)均不再生效。回調可能在主執行緒觸發,可直接用於更新 UI。

說明SDK 內部狀態輪詢時會自動上報用戶端拉流與播放狀態心跳(connecting / playing / paused / reconnecting / disconnected),供伺服器區分推流端與用戶端狀態——此為純 SDK 內部行為,宿主無需感知或參與。

資料模型

說明所有公開類型均標註 @available(iOS 15.0, *)。下列返回值與參數為 SDK 輸出參數,由內部就地建構並使用 Swift 原生類型(Date / TimeInterval / OysterTravelStatus),並非 Codable、也不公開 wire(snake_case)細節——wire 解碼發生在 SDK 內部。

// 配置
public struct OysterConfig: Sendable {
    public let apiHost: String             // 百炼网关地址(非业务 Server),必填、由宿主显式传入
    public let model: String               // 模型名含版本(如 "happyoyster-1.0-adventure"),必填、无默认值
    public let logLevel: OysterLogLevel?   // .debug/.info/.warning/.error;nil 时默认 .warning
    public let callbackTimeoutMs: Int      // 全局回调超时,默认 30000;超时按 105005 透出
    public init(apiHost: String, model: String, logLevel: OysterLogLevel? = nil, callbackTimeoutMs: Int = 30_000)
}

public enum OysterLogLevel: Int, Comparable, CaseIterable, Sendable {
    case debug, info, warning, error
}

// 对外会话状态(见"事件与状态"章节)
public enum OysterTravelStatus: String, Equatable, Sendable, CustomStringConvertible {
    case idle      // create 后、start 前
    case prepare   // 建连 / 重连中
    case running   // 流已就绪、可交互
    case pausing   // 暂停已受理、等待服务端确认
    case paused    // 已暂停(确认)
    case ended     // 终态:主动 end 或服务端结束
    case failed    // 终态:失败
}

// 开放字符串值(保留未知值,便于服务端扩展;编解码为裸 JSON 字符串 "running")
public struct OysterRawValue: RawRepresentable, Equatable, Hashable, Codable, Sendable {
    public let rawValue: String
    public init(rawValue: String) { self.rawValue = rawValue }
}
public typealias OysterModeValue = OysterRawValue
public extension OysterModeValue {
    static let adventure = OysterModeValue(rawValue: "adventure")
    static let directing  = OysterModeValue(rawValue: "directing")
    static let acting     = OysterModeValue(rawValue: "acting")
}

// start() 返回(SDK 出参,非 Codable)
public struct OysterStartTravelData: Equatable, Sendable {
    public let encryptedTravelId: String  // 本次体验标识
    public let encryptedWorldId: String
    public let mode: OysterModeValue       // adventure / directing / acting
    public let playUrl: String?            // 当前服务端固定返回 null
    public let firstFrame: String?         // 首帧图地址,可能为 null(异步产生)
    public let version: String             // 世界版本标识(诊断用;互动 UI 请按 mode 决策)
    public let aspectRatio: String?        // 画幅("9:16" / "16:9");仅 acting 有值,其余模式为 nil
}

// 控制接口返回(SDK 出参,非 Codable;status 为对外枚举 OysterTravelStatus)
public struct OysterTravelStateData: Equatable, Sendable { public let encryptedTravelId: String; public let status: OysterTravelStatus }
public struct OysterRewindTravelData: Equatable, Sendable { public let encryptedTravelId: String; public let status: OysterTravelStatus; public let resumedAtSec: TimeInterval }
public struct OysterEndTravelData: Equatable, Sendable { public let encryptedTravelId: String; public let status: OysterTravelStatus; public let endedAt: Date; public let duration: TimeInterval }
public struct OysterSendInstructData: Equatable, Sendable { public let encryptedTravelId: String; public let content: String; public let accepted: Bool }

// 世界探索模式操控指令(强类型枚举;取值与内部 WorldControlParams 对齐)
public struct OysterAdventureCommand: Equatable, Sendable {
    public enum Translation: String { case none, front, back, left, right, frontLeft, frontRight, backLeft, backRight }
    public enum Rotation: String { case none, mouseUp, mouseDown, mouseLeft, mouseRight, mouseUpLeft, mouseUpRight, mouseDownLeft, mouseDownRight }
    public enum Interaction: String { case none, jump, attack, crouch, sprint }
    public let translation: Translation   // 本条指令的移动方向;持续移动时按帧提交,松开后停止提交
    public let rotation: Rotation          // 本条指令的视角方向;持续转动时按帧提交,松开后停止提交
    public let interaction: Interaction    // 本条指令的单次动作;调用一次即可
    public init(translation: Translation = .none, rotation: Rotation = .none, interaction: Interaction = .none)
    public init(_ params: WorldControlParams)   // 由默认控件 WorldControlParams 便捷桥接(rawValue 一致)
}

// 统一错误(见"错误码"章节)
public struct OysterSDKError: Error {
    public let code: Int
    public let raw: Any?                  // 原始信息(内部 OysterError、网关 request_id 等),不承诺结构稳定
    public var kind: OysterErrorKind      // 数字 code 的类型化视图,便于穷尽 switch(计算属性)
    public init(code: Int, raw: Any? = nil)
    // 错误码常量见嵌套的 OysterSDKError.Code(如 .notInitialized = 100001)
}

// 错误码的类型化语义视图:本地码为具名 case,服务端 4xxxxx/5xxxxx 收敛为 .server(code:)
public enum OysterErrorKind: Equatable, Sendable {
    case notInitialized, tokenMissing, tokenInvalid, noActiveTravel, invalidState, sendCommandInDirecting
    case concurrentTravel, realtimeConnectFailed, realtimeJoinTimeout, firstFrameTimeout
    case channelNotReady, callbackTimeout, localNetwork, responseDecodeFailed
    case streamAutoEnd, proxyOrUnrecognized, featureGateDisabled
    case server(code: Int), unknown(code: Int)
}

說明

  • 操控指令 rawValue 為小寫駝峰命名(如 front / mouseLeft / jump),以上述列舉值為準。
  • mode 對外取值為 adventure(世界探索)/ directing(即時導演)/ acting(角色演繹),以 OysterModeValue 定義為準。
  • aspectRatio 為開放字串(目前為 9:16 / 16:9,後續可能擴充)。判斷橫向或直向螢幕時請按 宽:高 解析比例,請勿窮舉已知值。

錯誤碼

SDK 統一以 OysterSDKError 回報錯誤,類型一律透過 code 區分,請勿依據「從哪條路徑取得的錯誤」來判斷類型——同一個 code 既可能從業務方法(async throws)拋出,也可能經 events 的 .error 透出。需要型別化匹配時請使用 error.kind(詳見「資料模型」章節 OysterErrorKind)。

錯誤碼:伺服器端4xxxxx/5xxxxx,用戶端本機1xxxxx。

伺服器錯誤碼(常見)

code

含義

建議處理方式

400000

參數非法(列舉非法等)

檢查請求參數或 SDK 版本

401010

體驗憑證(ticket)無效或已過期

讓伺服器端重新下發憑證

401011

體驗憑證(ticket)已被使用

憑證一次性,重新下發

403001

World 不存在、已刪除或不屬於目前開發者(含憑證內世界已刪除)

重新選擇有效 World

403002

世界狀態非就緒

等世界就緒後再開始

403003

當前介面僅允許主 API Key

臨時 Key 不可用於此介面

403004

輸入內容違規(內容安全審查);適用於 sendInstruct 文字指令

修改輸入內容後重試

403007

目前規格未開通

不可依容量已滿重試;請更換已開通的規格或聯絡開通(典型情況:帳號未開通角色演繹(acting)規格)

403008

容量配置暫不可用

稍後重試

404000

資源不存在(世界 / Travel 歸屬或無產物)

核對 ID / 狀態

409000

請求與目前資源狀態衝突

檢查體驗狀態

429001

目前規格併發已滿

待現有工作階段結束後重試(請勿與 500001 混淆)

429002

目前可用容量不足

稍後重試

500000

系統內部錯誤

稍後重試 / 回饋

500001

推理資源分配/服務內部失敗

稍後重試

用戶端本機錯誤碼

code

含義

SDK 是否自動終止工作階段

建議處理方式

100001

SDK 未初始化即呼叫;亦包括 initialize 因 apiHost/model 為空或無效而未生效的情況

否(同步擲回,拒絕此次呼叫)

先 initialize,並確認 apiHost 與 model 皆已正確填寫

101001

未注入 HTTP 驗證 token

否

updateToken 後重試

101002

HTTP 驗證 token 無效 / 被拒

否

重新換取 token 後 updateToken 重試

103001

目前無活躍體驗

否(拒絕該次呼叫)

先 createTravel + start

103002

目前狀態/版本不允許此操作

否(拒絕該次呼叫)

檢查體驗狀態 / version

103003

模式不匹配(例如非 adventure 世界呼叫 sendCommand)

否(拒絕該次呼叫)

按 mode 選擇對應介面,見「概覽」章節能力表

103004

並行建立/開始體驗

否(同步擲回)

序列化呼叫,先 end 舊工作階段

105001

即時連線失敗

是

結束並重新開始

105002

即時入會逾時

是

結束並重新開始

105003

等待視訊首幀逾時

是

結束並重新開始

105004

即時通道未就緒/傳送失敗

依場景而定(主動傳送失敗;心跳僅上報)

待 running 後再傳送

105005

回呼逾時(預設 30s)

否

重試,必要時調大 callbackTimeoutMs

105006

加入會議後無推流,SDK 自動結束體驗

是

結束並重新開始

106001

本機網路錯誤

否

可重試

106002

回應解析失敗

依場景

升級 SDK / 回饋

106003

未攜帶可識別錯誤碼 / 代理字串錯誤碼

否

可重試

108001

被伺服器功能開關遠端停用(整體關閉或版本過低;原因見 raw)

是

按 raw 提示使用者;版本過低時引導升級

如何判斷致命性:致命性不再以布林欄位公開(OysterSDKError 沒有 isFatal)。語義上「致命」專指 SDK 是否主動終止工作階段(斷開 RTC、釋放整次工作階段)——

  • 會自動終止工作階段的錯誤(如 105001/105002/105003/105006/108001):宿主從狀態機終止狀態感知(status → failed,經 events 的 .statusChanged 透出),據此回到「開始體驗」前的介面即可,無需自行判定致命性。
  • 呼叫拒絕類錯誤(100001/103001/103002/103003/103004):在您主動呼叫時同步拋出或拒絕該次呼叫,不會終止工作階段。
  • 其餘非致命錯誤(如 101001/101002/105005/106001/106003):不會終止工作階段,請依建議重試或重新注入 token 後繼續。

說明106001 可能有兩種原因:本機網路錯誤,或 apiHost 設定錯誤。若重試無法恢復,請檢查 apiHost 是否設定正確。

說明106003 若在填寫 model 後出現,請優先檢查模型名稱及版本是否正確、是否已為目前帳號開通,並確認 apiHost、token 與模型屬於同一帳號和區域——不匹配時閘道會以 AccessDenied 拒絕並歸一至此錯誤碼。