

# IVS 廣播 SDK：混合裝置
<a name="broadcast-mixed-devices"></a>

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

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

`DeviceDiscovery.createMixedImageDevice()` (在 Android 上)

`IVSDeviceDiscovery.createMixedImageDevice()` (在 iOS 上)

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

## 術語
<a name="broadcast-mixed-devices-terminology"></a>

![\[IVS 廣播混合裝置術語。\]](http://docs.aws.amazon.com/zh_tw/ivs/latest/LowLatencyUserGuide/images/Broadcast_SDK_Mixer_Glossary.png)



| 術語 | Description | 
| --- | --- | 
| 裝置 | 一種產生音訊或影像輸入的硬體或軟體元件。裝置範例包括麥克風、相機、藍牙耳機和虛擬裝置，例如螢幕擷取或自訂影像輸入。 | 
| 混合裝置 | `Device` 可以像任何其他 `Device` 一樣連接到 `BroadcastSession`，但需要有其他 API 允許新增 `Source` 物件。混合裝置具有合成音訊或影像的內部混合器，可產生單一輸出音訊和影像串流。 混合裝置可以實現不同的影像或音訊風格。  | 
| 混合裝置組態 | 混合裝置的組態物件。對於混合影像裝置，此組態會設定維度和影格率等屬性。對於混合音訊裝置，此組態會設定聲道數量。 | 
|  來源 | 一種容器，可定義視覺元素在畫面上的位置，以及音軌在混音中的屬性。混合裝置可以設定零個或多個來源。可以對來源進行設定，從而影響來源媒體的使用方式。上圖顯示四個影像來源： [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/ivs/latest/LowLatencyUserGuide/broadcast-mixed-devices.html)  | 
| 來源組態 |  進入混合裝置的來源組態物件。完整的組態物件如下所述。  | 
| 轉換 | 若要將插槽移至新位置或變更其部分屬性，請使用 `MixedDevice.transitionToConfiguration()`。此方法採用： [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/ivs/latest/LowLatencyUserGuide/broadcast-mixed-devices.html) | 

## 混合音訊裝置
<a name="broadcast-mixed-audio-device"></a>

### 組態
<a name="broadcast-mixed-audio-device-configuration"></a>

`MixedAudioDeviceConfiguration` (在 Android 上)

`IVSMixedAudioDeviceConfiguration` (在 iOS 上)


| 名稱 | 類型 | 說明 | 
| --- | --- | --- | 
| `channels` | Integer | 混音器的輸出聲道數目。有效值：1、2。1 為單聲道音訊；2 為立體聲音訊。預設：2。 | 

### 來源組態
<a name="broadcast-mixed-audio-device-source-configuration"></a>

`MixedAudioDeviceSourceConfiguration` (在 Android 上)

`IVSMixedAudioDeviceSourceConfiguration` (在 iOS 上)


| 名稱 | 類型 | 說明 | 
| --- | --- | --- | 
| `gain` | Float | 音訊增益。這是一個倍數，因此 1 以上的任何值都會增加增益；1 以下的任何值都會減少增益。有效值：0–2。預設：1。 | 

## 混合影像裝置
<a name="broadcast-mixed-image-device"></a>

### 組態
<a name="broadcast-mixed-image-device-configuration"></a>

`MixedImageDeviceConfiguration` (在 Android 上)

`IVSMixedImageDeviceConfiguration` (在 iOS 上)


| 名稱 | 類型 | 說明 | 
| --- | --- | --- | 
| `size` | Vec2 | 影片畫布的大小。 | 
| `targetFramerate` | Integer | 混合裝置的每秒目標影格數。平均而言，應該達到此值，但系統在某些情況下 (例如高 CPU 或 GPU 負載時) 可能會捨棄影格。 | 
| `transparencyEnabled` | Boolean | 這可實現在影像來源組態上使用 `alpha` 屬性進行混合。將此設定為 `true` 會增加記憶體和 CPU 耗用量。預設：`false`。 | 

### 來源組態
<a name="broadcast-mixed-image-device-source-configuration"></a>

`MixedImageDeviceSourceConfiguration` (在 Android 上)

`IVSMixedImageDeviceSourceConfiguration` (在 iOS 上)


| 名稱 | 類型 | 說明 | 
| --- | --- | --- | 
| `alpha` | Float | 插槽的 Alpha。這是與影像中的任何 Alpha 值相乘。有效值：0–1，0 表示完全透明，1 表示完全不透明。預設：1。 | 
| `aspect` | AspectMode | 插槽中呈現的任何影像的長寬比模式。有效值： [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/ivs/latest/LowLatencyUserGuide/broadcast-mixed-devices.html) 預設：`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` 值較低的插槽上方。 | 

## 建立和設定混合影像裝置
<a name="broadcast-mixed-image-device-creating-configuring"></a>

![\[設定用於混音的廣播工作階段。\]](http://docs.aws.amazon.com/zh_tw/ivs/latest/LowLatencyUserGuide/images/Broadcast_SDK_Mixer_Configuring.png)


在這裡，我們建立一個與本指南開頭場景類似的場景，其中包含三個畫面元素：
+ 左下角插槽用於相機。
+ 右下角插槽用於標誌覆蓋。
+ 右上角插槽用於影片。

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

### iOS
<a name="broadcast-mixed-image-device-creating-configuring-ios"></a>

```
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
<a name="broadcast-mixed-image-device-creating-configuring-android"></a>

```
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)
```

## 移除來源
<a name="broadcast-mixed-devices-removing-sources"></a>

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

## 具有轉換的動畫
<a name="broadcast-mixed-devices-animations-transitions"></a>

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

### 哪些屬性可以進行動畫處理？
<a name="broadcast-mixed-devices-animations-properties"></a>

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


| 名稱 | 是否可以進行動畫處理？ | 影響點 | 
| --- | --- | --- | 
| `Audio.gain` | 是 | 已插補 | 
| `Image.alpha` | 是 | 已插補 | 
| `Image.aspect` | 否 | 結束 | 
| `Image.fillColor` | 是 | 已插補 | 
| `Image.position` | 是 | 已插補 | 
| `Image.size` | 是 | 已插補 | 
| `Image.zIndex` 備註：`zIndex` 會將 2D 平面移動到 3D 空間，因此，當兩個平面在動畫中間的某個點相交時，會發生轉換。這可以計算出來，但它取決於開始和結束 `zIndex` 值。為了轉換更順暢，請將其與 `alpha` 組合在一起。  | 是 | 不明 | 

### 簡單範例
<a name="broadcast-mixed-devices-animations-examples"></a>

以下是使用上述[建立和設定混合影像裝置](#broadcast-mixed-image-device-creating-configuring)章節中定義的組態接管全螢幕相機的範例。這個動畫處理超過 0.5 秒。

#### iOS
<a name="broadcast-mixed-devices-animations-examples-ios"></a>

```
// 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
<a name="broadcast-mixed-devices-animations-examples-android"></a>

```
// 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")
    }
}
```

## 鏡射廣播
<a name="broadcast-mixed-devices-mirroring"></a>


| 若要在廣播中以此方向鏡射連接的影像裝置... | 以下項目使用負值... | 
| --- | --- | 
| 水平 | 插槽寬度 | 
| 垂直 | 插槽高度 | 
| 水平和垂直 | 插槽寬度和高度 | 

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

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

### iOS
<a name="broadcast-mixed-devices-mirroring-ios"></a>

水平鏡射：

```
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
<a name="broadcast-mixed-devices-mirroring-android"></a>

水平鏡射：

```
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` 方法不同。該方法只會影響裝置上的本機預覽檢視，並不會影響廣播。