IVS 廣播 SDK:混音器指南 | 低延遲串流 - Amazon IVS

IVS 廣播 SDK:混音器指南 | 低延遲串流

混音器是一種音訊和影片處理裝置,可接受多種輸入來源並產生單一輸出。它是一個強大的功能,可以讓您定義和管理多個畫面 (影片) 元素和音軌。您可以結合來自多個來源的影片和音訊,例如相機、麥克風、螢幕擷取,以及您應用程式產生的音訊和影片。您可以使用轉換功能,在串流到 Amazon IVS 的影片周圍移動這些來源,並在中途新增和移除這些來源。

若要存取混音器,請呼叫:

BroadcastSession.getMixer() (在 Android 上)

IVSBroadcastSession.mixer (在 iOS 上)

術語

IVS 廣播混音器術語。
術語 描述

裝訂版

若要建立輸入裝置與插槽的關聯,裝置必須繫結至混音器插槽。這可使用 Mixer.bind() 方法完成。一個插槽一次可以繫結一個影像輸入和一個音訊輸入。您可以透過呼叫 Mixer.unbind(),從插槽取消繫結裝置。

Canvas

BroadcastSession 組態中定義之影片的顯示範圍。畫布的大小與您的影片設定相等,而且會以組態中指定的相同影格速率執行。

裝置

一種硬體或軟體元件,可對 BroadcastSession 產生音訊或影像輸入。裝置範例包括麥克風、相機、藍牙耳機和虛擬裝置,例如螢幕擷取或自訂影像輸入。除了自訂輸入之外,您通常不需要保留對裝置物件的參考,而是保留裝置描述項的複本。

裝置描述項

具有輸入裝置相關資訊的結構,例如,其類型、系統位址、人類可讀的「易記」名稱,以及行動裝置上的實體位置。此資訊可讓您決定是否要使用參考的裝置,並讓 Amazon IVS 存取該裝置。

Slot

一種容器,可定義視覺元素在畫面上的位置,以及音軌在混音中的屬性。混音器可以設定零個或多個插槽。插槽會被賦予一個字串名稱,可用於繫結裝置和執行轉換。上圖顯示四個插槽:

  • 左下角有相機輸入

  • 右上角有影片輸入

  • 右下角有 Amazon IVS 標誌

  • 全螢幕背景影像

設定工作階段之後,您可以使用 addSlotremoveSlot 混音器方法,新增和移除插槽。

轉換

若要將插槽移至新位置或變更其部分屬性,請使用 Mixer.transition()。此方法採用:

  • 新插槽結構,表示插槽的下一個狀態

  • 持續時間,指定相對於影片的時間軸,動畫應該花多久的時間。如果持續時間設定為 0,則會在下一個混音的影格上進行轉換。

  • 選用的回呼,可在動畫完成時通知您。回呼可能對鏈結動畫很有用。

畫布屬性

畫布屬性是根據您在建立 BroadcastSession 時所提供的 BroadcastConfiguration 設定的。AudioVideo 結構中的數個屬性會影響畫布:

名稱 Type 描述

Audio.channels

Integer

混音器的輸出聲道數目。有效值:1、2。1 聲道為單聲道音訊;2 聲道為立體聲音訊。預設:2。

Audio.sampleRate

AudioSampleRate

混音器每秒的音訊取樣數。此值至少應該是音訊訊號中最高頻率的兩倍。人類最高可以聽到大約 20 kHz,所以 44.1 kHz 和 48 kHz 一般就足夠了。預設:48 kHz。

Video.defaultAspectMode

AspectMode

插槽的預設長寬比模式。有效值:

  • Fill — 維持影像的長寬比,但填滿插槽。如果需要,將裁剪影像。

  • Fit — 維持影像的長寬比,但將整個影像調整至符合插槽大小。如有需要,插槽可以有一個信箱或郵筒。如果已設定該值,信箱/郵筒將是 fillColor;否則為透明 (如果影像後面的畫布顏色為黑色,則可能會顯示為黑色)。

  • None — 不維持影像的長寬比。影像將會調整以符合插槽的尺寸。

Video.size

Vec2

影片畫布的大小。

Video.targetFramerate

Integer

畫布的每秒目標影格數。平均而言,應該符合這個值,但系統在某些情況下可能會捨棄影格 (例如高 CPU 負載或網路壅塞)。

插槽屬性

插槽有數個可設定的屬性,您可以使用這些屬性來自訂場景以及進行動畫處理。對於持續時間超過 0 秒的轉換,任何 Float 或 Vector 值都會使用線性插值進行動畫處理。

名稱 Type 描述

aspect

AspectMode

插槽中呈現的任何影像的長寬比模式。有效值:

  • Fill — 維持影像的長寬比,但填滿插槽。如果需要,將裁剪影像。

  • Fit — 維持影像的長寬比,但將整個影像調整至符合插槽大小。如有需要,插槽可以有一個信箱或郵筒。如果已設定該值,信箱/郵筒將是 fillColor;否則為透明 (如果影像後面的畫布顏色為黑色,則可能會顯示為黑色)。

  • None — 不維持影像的長寬比。影像將會調整以符合插槽的尺寸。

預設:如果 matchCanvasAspectMode 為 true,則與畫布 aspect 相同;否則為 Fill。設定此值時,也會將 matchCanvasAspectMode 設為 false。

fillColor

Vec4

當插槽和影像的長寬比不相符時,要搭配 Aspect Fit (符合長寬比) 使用的填色顏色。格式為 (紅色、綠色、藍色、Alpha)。有效值 (針對每個聲道):0 - 1。預設:(0, 0, 0, 0)。

gain

Float

音訊增益。這是一個倍數,因此 1 以上的任何值都會增加增益;1 以下的任何值都會減少增益。有效值:0 - 2。預設:1。

matchCanvasAspectMode

Boolean

如果為 true,請使用畫布的 Video.defaultAspectMode 值。如果您設定插槽的 aspect 屬性,則此設定為 false。預設:true。

matchCanvasSize

Boolean

如果為 true,插槽的大小會調整為等於畫布的大小,且其位置設定為 (0, 0)。如果您設定插槽的 size 屬性,則此設定為 false。預設:true。

name

字串

插槽的名稱。這是用來參考用於繫結和轉換的插槽。預設:"default"

position

Vec2

相對於畫布左上角的插槽位置 (像素)。插槽的原點也是左上角。

preferredAudioInput

DeviceType

偏好的音訊輸入裝置類型。如果取消繫結此插槽,且指定類型的音訊裝置已連接至工作階段,則裝置會自動繫結至此插槽。有效值:

  • 麥克風 — 音訊硬體,例如內建麥克風、插入式耳機或藍牙耳機。

  • 系統音訊 — 從作業系統擷取的音訊,通常伴隨著畫面錄製。

  • 使用者音訊 — 您建立的自訂音訊輸入。

  • 未知 — 沒有偏好的裝置;一律以手動方式繫結插槽。

preferredVideoInput

DeviceType

偏好的影片輸入裝置。如果取消繫結此插槽,且指定類型的影片裝置已連接至工作階段,則裝置會自動繫結至此插槽。有效值:

  • 相機 — 內建相機裝置,例如前置相機、後置相機或廣角相機。

  • 螢幕 — 作業系統的螢幕擷取。

  • 使用者影像 — 您建立的自訂影像和影片輸入。

  • 未知 — 沒有偏好的裝置;一律以手動方式繫結插槽。

size

Vec2

插槽的大小 (像素)。設定此值時,也會將 matchCanvasSize 設為 false。預設:(0, 0);但是,因為 matchCanvasSize 預設為 true,因此插槽的呈現大小為畫布大小,而不是 (0, 0)。

transparency

Float

插槽的透明度。這是與影像中的任何 Alpha 值相乘。不透明度為 1 - transparency。有效值:0-1,其中 0 為完全不透明,1 為完全透明。預設:0.

zIndex

Float

插槽的相對順序。zIndex 值較高的插槽繪製在 zIndex 值較低的插槽上方。

設定用於混音的廣播工作階段

設定用於混音的廣播工作階段。

在這裡,我們建立一個與本指南開頭場景類似的場景,其中包含三個畫面元素:

  • 左下角插槽用於相機。

  • 右下角插槽用於標誌覆蓋。

  • 右上角插槽用於影片。

請注意,畫布的原點是左上角,這對於插槽來說是相同的。因此,將一個插槽定位在 (0, 0) 時,會將其放在左上角,且可以看到整個插槽。

iOS

let config = IVSBroadcastConfiguration() try config.video.setSize(CGSize(width: 1280, height: 720)) try config.video.setTargetFramerate(60) config.video.enableTransparency = true // Bottom Left var cameraSlot = IVSMixerSlotConfiguration() cameraSlot.size = CGSize(width: 320, height: 180) cameraSlot.position = CGPoint(x: 20, y: 1280 - 200) cameraSlot.preferredVideoInput = .camera cameraSlot.preferredAudioInput = .microphone cameraSlot.matchCanvasAspectMode = false cameraSlot.zIndex = 2 try cameraSlot.setName("camera") // Top Right var streamSlot = IVSMixerSlotConfiguration() streamSlot.size = CGSize(width: 640, height: 320) streamSlot.position = CGPoint(x: 1280 - 660, y: 20) streamSlot.preferredVideoInput = .userImage streamSlot.preferredAudioInput = .userAudio streamSlot.matchCanvasAspectMode = false streamSlot.zIndex = 1 try streamSlot.setName("stream") // Bottom Right var logoSlot = IVSMixerSlotConfiguration() logoSlot.size = CGSize(width: 320, height: 180) logoSlot.position = CGPoint(x: 1280 - 340, y: 720 - 200) logoSlot.preferredVideoInput = .userImage logoSlot.preferredAudioInput = .unknown logoSlot.matchCanvasAspectMode = false logoSlot.zIndex = 3 try logoSlot.setTransparency(0.7) try logoSlot.setName("logo") config.mixer.slots = [ cameraSlot, streamSlot, logoSlot ]

Android

// Bottom Left val cameraSlot = BroadcastConfiguration.Mixer.Slot.with { s -> s.setSize(320, 180) s.position = BroadcastConfiguration.Vec2(20, 1280 - 200) s.preferredVideoInput = Device.Descriptor.DeviceType.CAMERA s.preferredAudioInput = Device.Descriptor.DeviceType.MICROPHONE s.matchCanvasAspectMode = false s.zIndex = 2 s.name = "camera" s } // Top Right val streamSlot = BroadcastConfiguration.Mixer.Slot.with { s -> s.setSize(640, 320) s.position = BroadcastConfiguration.Vec2(1280 - 660, 20) s.preferredVideoInput = Device.Descriptor.DeviceType.USER_IMAGE s.preferredAudioInput = Device.Descriptor.DeviceType.USER_AUDIO s.matchCanvasAspectMode = false s.zIndex = 1 s.name = "stream" s } // Bottom Right val logoSlot = BroadcastConfiguration.Mixer.Slot.with { s -> s.setSize(320, 180) s.position = BroadcastConfiguration.Vec2(1280 - 340, 720 - 200) s.preferredVideoInput = Device.Descriptor.DeviceType.USER_IMAGE s.preferredAudioInput = Device.Descriptor.DeviceType.UNKNOWN s.matchCanvasAspectMode = false s.zIndex = 3 s.name = "logo" s.transparency = 0.7 s } val config = BroadcastConfiguration.with { c -> c.mixer.slots = listOf(cameraSlot, streamSlot, logoSlot) c.video.targetFramerate = 60 c.video.setSize(1280, 720) c }

加入插槽

一旦使用您的組態建立 BroadcastSession 之後,您就可以在混音器中新增插槽和移除插槽。在這裡,我們在混音器中,為影像新增一個大型背景插槽。

iOS

// Background. We will use most of the defaults for this slot. var backgroundSlot = IVSMixerSlotConfiguration() backgroundSlot.preferredVideoInput = .userImage backgroundSlot.preferredAudioInput = .unknown backgroundSlot.matchCanvasAspectMode = false try backgroundSlot.setName("background") session.mixer.addSlot(backgroundSlot)

Android

// Background. We will use most of the defaults for this slot. val backgroundSlot = BroadcastConfiguration.Mixer.Slot.with { s -> s.preferredVideoInput = Device.Descriptor.DeviceType.USER_IMAGE s.preferredAudioInput = Device.Descriptor.DeviceType.UNKNOWN s.matchCanvasAspectMode = false s.name = "background" s } session.mixer.addSlot(backgroundSlot)

移除插槽

若要移除插槽,請使用您想要移除之插槽的名稱呼叫 BroadcastSession.Mixer.removeSlot。繫結到該插槽的任何裝置都會自動取消繫結,因此,如果您想要繼續使用它們,則必須將它們重新繫結到不同的插槽。

具有轉換的動畫

混音器轉換方法會以新的組態取代插槽的組態。透過將持續時間設定為大於 0 (秒),可以隨時間對此取代進行動畫處理。

哪些屬性可以進行動畫處理?

插槽結構中並非所有屬性都可以進行動畫處理。任何以 Float 類型為基礎的屬性都可以進行動畫處理;其他屬性會在動畫開始或結束時生效。

名稱 可以進行動畫處理嗎? 影響點

aspect

結束

fillColor

已插補

gain

已插補

matchCanvasAspectMode

Start

matchCanvasSize

Start

name

備註:您無法變更插槽的名稱。

N/A

position

已插補

preferredAudioInput

結束

preferredVideoInput

結束

size

已插補

transparency

已插補

zIndex

備註:zIndex 會將 2D 平面移動到 3D 空間,因此,當兩個平面在動畫中間的某個點相交時,會發生轉換。這可以計算出來,但它取決於開始和結束 zIndex 值。為了轉換更順暢,請將其與 transparency 組合在一起。

不明

簡單範例

以下是在設定用於混音的廣播工作階段中,使用以上定義的組態接管全螢幕相機的範例。這個動畫處理超過 0.5 秒。

iOS

// Bottom Left var bigCameraSlot = cameraSlot bigCameraSlot.size = CGSize(width: 1280, height: 720) bigCameraSlot.position = CGPoint(x: 0, y: 0) session.mixer.transition("camera", bigCameraSlot, 0.5) { println("animation completed!") }

Android

// Bottom Left val bigCameraSlot = cameraSlot.changing { s -> s.setSize(1280, 720) s.position = BroadcastConfiguration.Vec2(0, 0) s } session.mixer.transition("camera", bigCameraSlot, 0.5) { print("animation completed!") }

鏡射廣播

若要在廣播中以此方向鏡射連接的影像裝置... 為以下項目使用負值...

水平

插槽寬度

垂直

插槽高度

水平和垂直

插槽寬度和高度

需使用相同的值調整位置,才能在鏡射時將插槽放在正確的位置。

以下是水平和垂直鏡射廣播的範例。

iOS

水平鏡射:

var cameraSlot = IVSMixerSlotConfiguration cameraSlot.size = CGSize(width: -320, height: 720) // Add 320 to position x since our width is -320 cameraSlot.position = CGPoint(x: 320, y: 0)

垂直鏡射

var cameraSlot = IVSMixerSlotConfiguration cameraSlot.size = CGSize(width: 320, height: -720) // Add 720 to position y since our height is -720 cameraSlot.position = CGPoint(x: 0, y: 720)

Android

水平鏡射:

cameraSlot = BroadcastConfiguration.Mixer.Slot.with { it.size = BroadcastConfiguration.Vec2(-320f, 180f) // Add 320f to position x since our width is -320f it.position = BroadcastConfiguration.Vec2(320f, 0f) return@with it }

垂直鏡射

cameraSlot = BroadcastConfiguration.Mixer.Slot.with { it.size = BroadcastConfiguration.Vec2(320f, -180f) // Add 180f to position y since our height is -180f it.position = BroadcastConfiguration.Vec2(0f, 180f) return@with it }

請注意:此鏡射與 ImagePreviewView (Android) 和 IVSImagePreviewView (iOS) 的 setMirrored 方法不同。該方法只會影響裝置上的本機預覽檢視,並不會影響廣播。