使用 IVS iOS 廣播 SDK 發布和訂閱 | 即時串流
本文件將帶您了解開始使用 IVS 即時串流 iOS 廣播 SDK 發布和訂閱階段的相關步驟。
概念
以下是三個以即時功能為基礎的核心概念:階段、策略和轉譯器。設計目標是盡可能減少打造工作產品所需的用戶端邏輯數量。
階段
IVSStage
類別是主持人應用程式和 SDK 之間的主要交互點。該類別代表階段本身,用於加入和離開階段。建立或加入階段需有效且未過期的控制平面字符字串 (表示為 token
)。加入和離開階段並不難。
let stage = try IVSStage(token: token, strategy: self) try stage.join() stage.leave()
IVSStage
類別也可連接 IVSStageRenderer
和 IVSErrorDelegate
:
let stage = try IVSStage(token: token, strategy: self) stage.errorDelegate = self stage.addRenderer(self) // multiple renderers can be added
策略
IVSStageStrategy
協定為主持人應用程式提供了將所需階段狀態傳送至 SDK 的管道。您必須實作以下三項函數:shouldSubscribeToParticipant
、shouldPublishParticipant
、和 streamsToPublishForParticipant
。以下將討論所有內容。
訂閱參與者
func stage(_ stage: IVSStage, shouldSubscribeToParticipant participant: IVSParticipantInfo) -> IVSStageSubscribeType
遠端參與者加入階段時,SDK 會向主持人應用程式查詢該參與者所需的訂閱狀態。選項包括 .none
、.audioOnly
和 .audioVideo
。傳回此函數的值時,主持人應用程式不需要擔心發布狀態、目前的訂閱狀態或階段連線狀態。若傳回 .audioVideo
,SDK 會等到遠端參與者發布時才會訂閱,然後在整個程序中透過轉譯器更新主持人應用程式。
以下是實作範例:
func stage(_ stage: IVSStage, shouldSubscribeToParticipant participant: IVSParticipantInfo) -> IVSStageSubscribeType { return .audioVideo }
對於一律希望所有參與者互相看到彼此的主持人應用程式 (例如影片聊天應用程式),這是此函數的完整實作程序。
您也可以採用更進階的實作方式。在 IVSParticipantInfo
上使用 attributes
屬性,以根據伺服器提供的屬性選擇性訂閱參與者:
func stage(_ stage: IVSStage, shouldSubscribeToParticipant participant: IVSParticipantInfo) -> IVSStageSubscribeType { switch participant.attributes["role"] { case "moderator": return .none case "guest": return .audioVideo default: return .none } }
這可以用來建立一個階段,版主可以在不會被看到或聽到自己聲音的情況下監控所有訪客。主持人應用程式可以使用其他商業邏輯,讓版主看到彼此,但仍維持訪客看不到他們的狀態。
參與者訂閱組態
func stage(_ stage: IVSStage, subscribeConfigurationForParticipant participant: IVSParticipantInfo) -> IVSSubscribeConfiguration
如果正在訂閱遠端參與者 (請參閱訂閱參與者),則 SDK 會查詢主機應用程式關於該參與者的自訂訂閱組態。此組態為選用功能,允許主機應用程式控制某些層面的訂閱用戶行為。如需有關可設定內容的詳細資訊,請參閱 SDK 參考文件中的 SubscribeConfiguration
以下是實作範例:
func stage(_ stage: IVSStage, subscribeConfigurationForParticipant participant: IVSParticipantInfo) -> IVSSubscribeConfiguration { let config = IVSSubscribeConfiguration() try! config.jitterBuffer.setMinDelay(.medium()) return config }
此實作會將所有訂閱參與者的抖動緩衝區最低延遲更新為預設的 MEDIUM
。
您也可以透過 shouldSubscribeToParticipant
採用更進階的實作方式。指定的 ParticipantInfo
可用來專門更新特定參與者的訂閱組態。
我們建議您使用預設行為。只有在您想要變更特定行為時,才指定自訂組態。
發布
func stage(_ stage: IVSStage, shouldPublishParticipant participant: IVSParticipantInfo) -> Bool
連線到階段後,SDK 會查詢主持人應用程式,看看特定參與者是否應發布。系統僅會根據提供的字符為具有發布許可的本機參與者調用此函數。
以下是實作範例:
func stage(_ stage: IVSStage, shouldPublishParticipant participant: IVSParticipantInfo) -> Bool { return true }
這是針對一個使用者總是想發布內容的標準影片聊天應用程式。他們可以靜音和取消靜音其音訊和影片內容,以立即隱藏起來,或看到/聽見內容。(他們也可以使用發布/取消發布,但這種方式速度較慢。建議在需經常變更可見性的使用案例中使用靜音/取消靜音。)
選擇要發布的串流
func stage(_ stage: IVSStage, streamsToPublishForParticipant participant: IVSParticipantInfo) -> [IVSLocalStageStream]
發布時,這會用來決定應發布哪些音訊和影片串流。稍後會在發布媒體串流中進行詳細說明。
更新策略
策略應處於動態狀態:從上述任何函數返回的值可以隨時進行修改。例如,若主持人應用程式在終端使用者按下按鈕前都不想發布,您可以從 shouldPublishParticipant
傳回一個變數 (例如 hasUserTappedPublishButton
)。當該變數根據終端使用者的互動而變更時,請呼叫 stage.refreshStrategy()
向 SDK 傳送信號,表示它應查詢策略中的最新值,並僅套用已變更的項目。若 SDK 發現 shouldPublishParticipant
值已變更,它便會開始發布程序。若 SDK 查詢後所有函數傳回與之前相同的值,則 refreshStrategy
呼叫將不會對階段進行任何修改。
若 shouldSubscribeToParticipant
傳回的值從 .audioVideo
變更為 .audioOnly
,則系統將會針對傳回值已變更的所有參與者移除影片串流 (若之前存有影片串流)。
一般而言,階段會採用策略,以最有效率的方式套用先前與目前策略之間的差異,主持人應用程式不必擔心正確進行管理所需的所有狀態。因此,請將呼叫 stage.refreshStrategy()
視為低成本的操作,因為除非策略發生變化,否則它什麼都不會執行。
轉譯器
IVSStageRenderer
協定會將階段狀態傳送給主持人應用程式。主持人應用程式的 UI 更新通常可以完全由轉譯器提供的事件提供支援。轉譯器會提供以下函數:
func stage(_ stage: IVSStage, participantDidJoin participant: IVSParticipantInfo) func stage(_ stage: IVSStage, participantDidLeave participant: IVSParticipantInfo) func stage(_ stage: IVSStage, participant: IVSParticipantInfo, didChange publishState: IVSParticipantPublishState) func stage(_ stage: IVSStage, participant: IVSParticipantInfo, didChange subscribeState: IVSParticipantSubscribeState) func stage(_ stage: IVSStage, participant: IVSParticipantInfo, didAdd streams: [IVSStageStream]) func stage(_ stage: IVSStage, participant: IVSParticipantInfo, didRemove streams: [IVSStageStream]) func stage(_ stage: IVSStage, participant: IVSParticipantInfo, didChangeMutedStreams streams: [IVSStageStream]) func stage(_ stage: IVSStage, didChange connectionState: IVSStageConnectionState, withError error: Error?)
轉譯器提供的資訊應該不會對策略的傳回值造成影響。例如,呼叫 participant:didChangePublishState
時,shouldSubscribeToParticipant
的傳回值應該不會變更。若主持人應用程式想要訂閱特定參與者,則無論該參與者的發布狀態為何,它都應傳回所需的訂閱類型。SDK 負責確保根據階段狀態,在正確的時間點執行策略的所需狀態。
請注意,當發布參與者觸發 participantDidJoin
,且參與者停止發布或離開階段工作階段時,participantDidLeave
才會觸發。
發布媒體串流
您可以透過 IVSDeviceDiscovery
找到本地裝置 (例如內建的麥克風和攝影機)。以下是選擇前置攝影機和預設麥克風,然後將其以 IVSLocalStageStreams
返回並由 SDK 發布的範例:
let devices = IVSDeviceDiscovery().listLocalDevices() // Find the camera virtual device, choose the front source, and create a stream let camera = devices.compactMap({ $0 as? IVSCamera }).first! let frontSource = camera.listAvailableInputSources().first(where: { $0.position == .front })! camera.setPreferredInputSource(frontSource) let cameraStream = IVSLocalStageStream(device: camera) // Find the microphone virtual device and create a stream let microphone = devices.compactMap({ $0 as? IVSMicrophone }).first! let microphoneStream = IVSLocalStageStream(device: microphone) // Configure the audio manager to use the videoChat preset, which is optimized for bi-directional communication, including echo cancellation. IVSStageAudioManager.sharedInstance().setPreset(.videoChat) // This is a function on IVSStageStrategy func stage(_ stage: IVSStage, streamsToPublishForParticipant participant: IVSParticipantInfo) -> [IVSLocalStageStream] { return [cameraStream, microphoneStream] }
顯示和移除參與者
訂閱完成後,您會透過轉譯器的 didAddStreams
函數收到 IVSStageStream
物件陣列。若要預覽或接收有關此參與者的音訊層級統計資料,您可以從串流中存取基礎 IVSDevice
物件:
if let imageDevice = stream.device as? IVSImageDevice { let preview = imageDevice.previewView() /* attach this UIView subclass to your view */ } else if let audioDevice = stream.device as? IVSAudioDevice { audioDevice.setStatsCallback( { stats in /* process stats.peak and stats.rms */ }) }
當參與者停止發布或取消訂閱時,系統會呼叫 didRemoveStreams
函數,並傳回遭移除的串流。主持人應用程式應將此視為從檢視階層中移除參與者影片串流的信號。
didRemoveStreams
會在串流可能遭移除的所有情況下調用,其中包括:
-
遠端參與者停止發布。
-
本機裝置取消訂閱,或將訂閱從
.audioVideo
變更為.audioOnly
。 -
遠端參與者離開階段。
-
本機參與者離開階段。
由於 didRemoveStreams
會在所有情況下調用,因此在遠端或本機離開操作期間,不需要使用自訂商業邏輯從 UI 移除參與者。
靜音和取消靜音媒體串流
IVSLocalStageStream
物件具備控制是否將串流靜音的 setMuted
函數。此函數可以在從 streamsToPublishForParticipant
策略函數傳回之前或之後在串流上呼叫。
重要:如果呼叫 refreshStrategy
後由 streamsToPublishForParticipant
傳回新的 IVSLocalStageStream
物件執行個體,則新串流物件的靜音狀態會套用至階段。建立新 IVSLocalStageStream
執行個體時請務必小心,以確保維持預期的靜音狀態。
監控遠端參與者媒體靜音狀態
當參與者變更其影片或音訊串流的靜音狀態時,會以已變更的串流陣列調用轉譯器 didChangeMutedStreams
函數。使用 IVSStageStream
上的 isMuted
屬性來據此更新您的 UI:
func stage(_ stage: IVSStage, participant: IVSParticipantInfo, didChangeMutedStreams streams: [IVSStageStream]) { streams.forEach { stream in /* stream.isMuted */ } }
建立階段組態
若要自訂階段影片組態的值,請使用 IVSLocalStageStreamVideoConfiguration
:
let config = IVSLocalStageStreamVideoConfiguration() try config.setMaxBitrate(900_000) try config.setMinBitrate(100_000) try config.setTargetFramerate(30) try config.setSize(CGSize(width: 360, height: 640)) config.degradationPreference = .balanced
取得 WebRTC 統計資料
若要取得發布串流或訂閱串流的最新 WebRTC 統計資料,請在 IVSStageStream
上使用 requestRTCStats
。收集完成後,您將透過可以在上 IVSStageStream
設定的 IVSStageStreamDelegate
收到統計資料。若要持續收集 WebRTC 統計資料,請透過 Timer
呼叫此函數。
func stream(_ stream: IVSStageStream, didGenerateRTCStats stats: [String : [String : String]]) { for stat in stats { for member in stat.value { print("stat \(stat.key) has member \(member.key) with value \(member.value)") } } }
取得參與者屬性
如果您在 CreateParticipantToken
端點要求中指定屬性,您可以在 IVSParticipantInfo
屬性中看到屬性:
func stage(_ stage: IVSStage, participantDidJoin participant: IVSParticipantInfo) { print("ID: \(participant.participantId)") for attribute in participant.attributes { print("attribute: \(attribute.key)=\(attribute.value)") } }
在背景繼續工作階段
當應用程式進入背景時,您可以在聽到遠端音訊的同時繼續待在階段,不過無法繼續傳送自己的影像和音訊。您必須更新您的 IVSStrategy
實作以停止發布,並訂閱 .audioOnly
(或 .none
,如適用)。
func stage(_ stage: IVSStage, shouldPublishParticipant participant: IVSParticipantInfo) -> Bool { return false } func stage(_ stage: IVSStage, shouldSubscribeToParticipant participant: IVSParticipantInfo) -> IVSStageSubscribeType { return .audioOnly }
然後呼叫 stage.refreshStrategy()
。
啟用/停用使用 Simulcast 進行分層編碼
發布媒體串流時,SDK 會傳輸高品質和低品質的影片串流,因此即使下行頻寬有限,遠端參與者也可訂閱串流。預設會啟用使用 Simulcast 進行分層編碼。您可以使用 IVSSimulcastConfiguration
將其停用:
// Disable Simulcast let config = IVSLocalStageStreamVideoConfiguration() config.simulcast.enabled = false let cameraStream = IVSLocalStageStream(device: camera, configuration: config) // Other Stage implementation code
將階段廣播到 IVS 頻道
若要廣播階段,請建立一個獨立 IVSBroadcastSession
,然後按照使用 SDK 進行廣播的一般指示操作 (如上所述)。IVSStageStream
的 device
屬性會是 IVSImageDevice
或 IVSAudioDevice
,如上述程式碼片段所示;這些屬性可以連線至 IVSBroadcastSession.mixer
,以可自訂的版面配置廣播整個階段。
或者,您可以複合階段並將其廣播到 IVS 低延遲通道,以吸引更多受眾。請參閱《IVS 低延遲串流使用者指南》中的在 Amazon IVS 串流上啟用多位主持人。