IVS 廣播 SDK:混合裝置
混合裝置是指採用多個輸入來源但產生單一輸出的音訊和視訊裝置。混合裝置是一個強大的功能,可以讓您定義和管理多個畫面 (視訊) 元素和音軌。您可以結合來自多個來源的影片和音訊,例如相機、麥克風、螢幕擷取,以及您應用程式產生的音訊和影片。您可以使用轉換功能,在串流到 IVS 的視訊周圍移動這些來源,並在串流中途新增和移除這些來源。
混合裝置可以實現不同的影像和音訊風格。若要建立混合影像裝置,請調用:
DeviceDiscovery.createMixedImageDevice()
(在 Android 上)
IVSDeviceDiscovery.createMixedImageDevice()
(在 iOS 上)
傳回的裝置可以像任何其他裝置一樣連接到 BroadcastSession
(低延遲串流) 或 Stage
(即時串流)。
術語

術語 | 描述 |
---|---|
裝置 |
一種產生音訊或影像輸入的硬體或軟體元件。裝置範例包括麥克風、相機、藍牙耳機和虛擬裝置,例如螢幕擷取或自訂影像輸入。 |
混合裝置 |
混合裝置可以實現不同的影像或音訊風格。 |
混合裝置組態 |
混合裝置的組態物件。對於混合影像裝置,此組態會設定維度和影格率等屬性。對於混合音訊裝置,此組態會設定聲道數量。 |
來源 |
一種容器,可定義視覺元素在畫面上的位置,以及音軌在混音中的屬性。混合裝置可以設定零個或多個來源。可以對來源進行設定,從而影響來源媒體的使用方式。上圖顯示四個影像來源:
|
來源組態 |
進入混合裝置的來源組態物件。完整的組態物件如下所述。 |
轉換 |
若要將插槽移至新位置或變更其部分屬性,請使用
|
混合音訊裝置
組態
MixedAudioDeviceConfiguration
(在 Android 上)
IVSMixedAudioDeviceConfiguration
(在 iOS 上)
名稱 | Type | 描述 |
---|---|---|
|
Integer |
混音器的輸出聲道數目。有效值:1、2。1 為單聲道音訊;2 為立體聲音訊。預設:2。 |
來源組態
MixedAudioDeviceSourceConfiguration
(在 Android 上)
IVSMixedAudioDeviceSourceConfiguration
(在 iOS 上)
名稱 | Type | 描述 |
---|---|---|
|
Float |
音訊增益。這是一個倍數,因此 1 以上的任何值都會增加增益;1 以下的任何值都會減少增益。有效值:0–2。預設:1。 |
混合影像裝置
組態
MixedImageDeviceConfiguration
(在 Android 上)
IVSMixedImageDeviceConfiguration
(在 iOS 上)
名稱 | Type | 描述 |
---|---|---|
|
Vec2 |
影片畫布的大小。 |
|
Integer |
混合裝置的每秒目標影格數。平均而言,應該達到此值,但系統在某些情況下 (例如高 CPU 或 GPU 負載時) 可能會捨棄影格。 |
|
Boolean |
這可實現在影像來源組態上使用 |
來源組態
MixedImageDeviceSourceConfiguration
(在 Android 上)
IVSMixedImageDeviceSourceConfiguration
(在 iOS 上)
名稱 | Type | 描述 |
---|---|---|
|
Float |
插槽的 Alpha。這是與影像中的任何 Alpha 值相乘。有效值:0–1,0 表示完全透明,1 表示完全不透明。預設:1。 |
|
AspectMode | 插槽中呈現的任何影像的長寬比模式。有效值:
預設: |
|
Vec4 |
如果插槽和影像的長寬比不相符,要搭配 |
|
Vec2 |
相對於畫布左上角的插槽位置 (像素)。插槽的原點也是左上角。 |
|
Vec2 |
插槽的大小 (像素)。設定此值時,也會將 |
|
Float |
插槽的相對順序。 |
建立和設定混合影像裝置

在這裡,我們建立一個與本指南開頭場景類似的場景,其中包含三個畫面元素:
-
左下角插槽用於相機。
-
右下角插槽用於標誌覆蓋。
-
右上角插槽用於影片。
請注意,畫布的原點是左上角,這對於插槽來說是相同的。因此,將一個插槽定位在 (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 類型為基礎的屬性都可以進行動畫處理;其他屬性會在動畫開始或結束時生效。
名稱 | 是否可以進行動畫處理? | 影響點 |
---|---|---|
|
是 |
已插補 |
|
是 |
已插補 |
|
否 |
結束 |
|
是 |
已插補 |
|
是 |
已插補 |
|
是 |
已插補 |
備註: |
是 |
不明 |
簡單範例
以下是使用上述建立和設定混合影像裝置章節中定義的組態接管全螢幕相機的範例。這個動畫處理超過 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
方法不同。該方法只會影響裝置上的本機預覽檢視,並不會影響廣播。