IVS 廣播 SDK:混合裝置 - Amazon IVS

IVS 廣播 SDK:混合裝置

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

混合裝置可以實現不同的影像和音訊風格。若要建立混合影像裝置,請調用:

DeviceDiscovery.createMixedImageDevice() (在 Android 上)

IVSDeviceDiscovery.createMixedImageDevice() (在 iOS 上)

傳回的裝置可以像任何其他裝置一樣連接到 BroadcastSession (低延遲串流) 或 Stage (即時串流)。

術語

IVS 廣播混合裝置術語。
術語 描述

裝置

一種產生音訊或影像輸入的硬體或軟體元件。裝置範例包括麥克風、相機、藍牙耳機和虛擬裝置,例如螢幕擷取或自訂影像輸入。

混合裝置

Device 可以像任何其他 Device 一樣連接到 BroadcastSession,但需要有其他 API 允許新增 Source 物件。混合裝置具有合成音訊或影像的內部混合器,可產生單一輸出音訊和影像串流。

混合裝置可以實現不同的影像或音訊風格。

混合裝置組態

混合裝置的組態物件。對於混合影像裝置,此組態會設定維度和影格率等屬性。對於混合音訊裝置,此組態會設定聲道數量。

來源

一種容器,可定義視覺元素在畫面上的位置,以及音軌在混音中的屬性。混合裝置可以設定零個或多個來源。可以對來源進行設定,從而影響來源媒體的使用方式。上圖顯示四個影像來源:

  • 左下角有相機輸入

  • 右上角有影片輸入

  • 右下角有 Amazon IVS 標誌

  • 全螢幕背景影像

來源組態

進入混合裝置的來源組態物件。完整的組態物件如下所述。

轉換

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

  • 代表來源下一個狀態的新來源組態。

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

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

混合音訊裝置

組態

MixedAudioDeviceConfiguration (在 Android 上)

IVSMixedAudioDeviceConfiguration (在 iOS 上)

名稱 Type 描述

channels

Integer

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

來源組態

MixedAudioDeviceSourceConfiguration (在 Android 上)

IVSMixedAudioDeviceSourceConfiguration (在 iOS 上)

名稱 Type 描述

gain

Float

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

混合影像裝置

組態

MixedImageDeviceConfiguration (在 Android 上)

IVSMixedImageDeviceConfiguration (在 iOS 上)

名稱 Type 描述

size

Vec2

影片畫布的大小。

targetFramerate

Integer

混合裝置的每秒目標影格數。平均而言,應該達到此值,但系統在某些情況下 (例如高 CPU 或 GPU 負載時) 可能會捨棄影格。

transparencyEnabled

Boolean

這可實現在影像來源組態上使用 alpha 屬性進行混合。將此設定為 true 會增加記憶體和 CPU 耗用量。預設:false

來源組態

MixedImageDeviceSourceConfiguration (在 Android 上)

IVSMixedImageDeviceSourceConfiguration (在 iOS 上)

名稱 Type 描述

alpha

Float

插槽的 Alpha。這是與影像中的任何 Alpha 值相乘。有效值:0–1,0 表示完全透明,1 表示完全不透明。預設:1。

aspect

AspectMode

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

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

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

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

預設:Fit

fillColor

Vec4

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

position

Vec2

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

size

Vec2

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

zIndex

Float

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

建立和設定混合影像裝置

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

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

  • 左下角插槽用於相機。

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

  • 右上角插槽用於影片。

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

iOS

let deviceDiscovery = IVSDeviceDiscovery() let mixedImageConfig = IVSMixedImageDeviceConfiguration() mixedImageConfig.size = CGSize(width: 1280, height: 720) try mixedImageConfig.setTargetFramerate(60) mixedImageConfig.isTransparencyEnabled = true let mixedImageDevice = deviceDiscovery.createMixedImageDevice(with: mixedImageConfig) // Bottom Left let cameraConfig = IVSMixedImageDeviceSourceConfiguration() cameraConfig.size = CGSize(width: 320, height: 180) cameraConfig.position = CGPoint(x: 20, y: mixedImageConfig.size.height - cameraConfig.size.height - 20) cameraConfig.zIndex = 2 let camera = deviceDiscovery.listLocalDevices().first(where: { $0 is IVSCamera }) as? IVSCamera let cameraSource = IVSMixedImageDeviceSource(configuration: cameraConfig, device: camera) mixedImageDevice.add(cameraSource) // Top Right let streamConfig = IVSMixedImageDeviceSourceConfiguration() streamConfig.size = CGSize(width: 640, height: 320) streamConfig.position = CGPoint(x: mixedImageConfig.size.width - streamConfig.size.width - 20, y: 20) streamConfig.zIndex = 1 let streamDevice = deviceDiscovery.createImageSource(withName: "stream") let streamSource = IVSMixedImageDeviceSource(configuration: streamConfig, device: streamDevice) mixedImageDevice.add(streamSource) // Bottom Right let logoConfig = IVSMixedImageDeviceSourceConfiguration() logoConfig.size = CGSize(width: 320, height: 180) logoConfig.position = CGPoint(x: mixedImageConfig.size.width - logoConfig.size.width - 20, y: mixedImageConfig.size.height - logoConfig.size.height - 20) logoConfig.zIndex = 3 let logoDevice = deviceDiscovery.createImageSource(withName: "logo") let logoSource = IVSMixedImageDeviceSource(configuration: logoConfig, device: logoDevice) mixedImageDevice.add(logoSource)

Android

val deviceDiscovery = DeviceDiscovery(this /* context */) val mixedImageConfig = MixedImageDeviceConfiguration().apply { setSize(BroadcastConfiguration.Vec2(1280f, 720f)) setTargetFramerate(60) setEnableTransparency(true) } val mixedImageDevice = deviceDiscovery.createMixedImageDevice(mixedImageConfig) // Bottom Left val cameraConfig = MixedImageDeviceSourceConfiguration().apply { setSize(BroadcastConfiguration.Vec2(320f, 180f)) setPosition(BroadcastConfiguration.Vec2(20f, mixedImageConfig.size.y - size.y - 20)) setZIndex(2) } val camera = deviceDiscovery.listLocalDevices().firstNotNullOf { it as? CameraSource } val cameraSource = MixedImageDeviceSource(cameraConfig, camera) mixedImageDevice.addSource(cameraSource) // Top Right val streamConfig = MixedImageDeviceSourceConfiguration().apply { setSize(BroadcastConfiguration.Vec2(640f, 320f)) setPosition(BroadcastConfiguration.Vec2(mixedImageConfig.size.x - size.x - 20, 20f)) setZIndex(1) } val streamDevice = deviceDiscovery.createImageInputSource(streamConfig.size) val streamSource = MixedImageDeviceSource(streamConfig, streamDevice) mixedImageDevice.addSource(streamSource) // Bottom Right val logoConfig = MixedImageDeviceSourceConfiguration().apply { setSize(BroadcastConfiguration.Vec2(320f, 180f)) setPosition(BroadcastConfiguration.Vec2(mixedImageConfig.size.x - size.x - 20, mixedImageConfig.size.y - size.y - 20)) setZIndex(1) } val logoDevice = deviceDiscovery.createImageInputSource(logoConfig.size) val logoSource = MixedImageDeviceSource(logoConfig, logoDevice) mixedImageDevice.addSource(logoSource)

移除來源

若要移除來源,請對要移除的 Source 物件調用 MixedDevice.remove

具有轉換的動畫

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

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

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

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

Audio.gain

已插補

Image.alpha

已插補

Image.aspect

結束

Image.fillColor

已插補

Image.position

已插補

Image.size

已插補

Image.zIndex

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

不明

簡單範例

以下是使用上述建立和設定混合影像裝置章節中定義的組態接管全螢幕相機的範例。這個動畫處理超過 0.5 秒。

iOS

// Continuing the example from above, modifying the existing cameraConfig object. cameraConfig.size = CGSize(width: 1280, height: 720) cameraConfig.position = CGPoint.zero cameraSource.transition(to: cameraConfig, duration: 0.5) { completed in if completed { print("Animation completed") } else { print("Animation interrupted") } }

Android

// Continuing the example from above, modifying the existing cameraConfig object. cameraConfig.setSize(BroadcastConfiguration.Vec2(1280f, 720f)) cameraConfig.setPosition(BroadcastConfiguration.Vec2(0f, 0f)) cameraSource.transitionToConfiguration(cameraConfig, 500) { completed -> if (completed) { print("Animation completed") } else { print("Animation interrupted") } }

鏡射廣播

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

水平

插槽寬度

垂直

插槽高度

水平和垂直

插槽寬度和高度

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

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

iOS

水平鏡射:

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

垂直鏡射

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

Android

水平鏡射:

val cameraConfig = MixedImageDeviceSourceConfiguration().apply { setSize(BroadcastConfiguration.Vec2(-320f, 180f)) // Add 320f to position x since our width is -320f setPosition(BroadcastConfiguration.Vec2(320f, 0f)) }

垂直鏡射

val cameraConfig = MixedImageDeviceSourceConfiguration().apply { setSize(BroadcastConfiguration.Vec2(320f, -180f)) // Add 180f to position y since our height is -180f setPosition(BroadcastConfiguration.Vec2(0f, 180f)) }

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