

# IVS Android 廣播 SDK 的進階使用案例 \| 低延遲串流
<a name="broadcast-android-use-cases"></a>

以下介紹一些進階使用案例，銜接上述基本設定繼續延伸。

## 建立廣播組態
<a name="broadcast-android-create-configuration"></a>

在這裡，我們建立有兩個混音器插槽的自訂組態，可讓我們將兩個影片來源綁定到混音器。一個 (`custom`) 是全螢幕，並配置於另一個 (`camera`) 後方，前方的插槽較小，位於右下角。請注意，對於 `custom` 插槽，我們並不設定位置、大小或長寬比模式。因為我們未設定這些參數，插槽將使用影片設定的大小和位置。

```
BroadcastConfiguration config = BroadcastConfiguration.with($ -> {
    $.audio.setBitrate(128_000);
    $.video.setMaxBitrate(3_500_000);
    $.video.setMinBitrate(500_000);
    $.video.setInitialBitrate(1_500_000);
    $.video.setSize(1280, 720);
    $.mixer.slots = new BroadcastConfiguration.Mixer.Slot[] {
            BroadcastConfiguration.Mixer.Slot.with(slot -> {
                // Do not automatically bind to a source
                slot.setPreferredAudioInput(
                           Device.Descriptor.DeviceType.UNKNOWN);
                // Bind to user image if unbound
                slot.setPreferredVideoInput(
                           Device.Descriptor.DeviceType.USER_IMAGE);
                slot.setName("custom");
                return slot;
            }),
            BroadcastConfiguration.Mixer.Slot.with(slot -> {
                slot.setzIndex(1);
                slot.setAspect(BroadcastConfiguration.AspectMode.FILL);
                slot.setSize(300, 300);
                slot.setPosition($.video.getSize().x - 350,
                        $.video.getSize().y - 350);
                slot.setName("camera");
                return slot;
            })
    };
    return $;
});
```

## 建立廣播工作階段 (進階版)
<a name="broadcast-android-create-session-advanced"></a>

按照您在[基本範例](broadcast-android-getting-started.md#broadcast-android-create-session)中的作法建立 `BroadcastSession`，但在此提供自訂組態。同時為裝置陣列提供 `null`，因為我們將手動新增。

```
// Create a broadcast-session instance and sign up to receive broadcast
// events and errors.
Context ctx = getApplicationContext();
broadcastSession = new BroadcastSession(ctx,
                       broadcastListener,
                       config, // The configuration we created above
                       null); // We’ll manually attach devices after
```

## 逐一查看和連接攝影機裝置
<a name="broadcast-android-attach-camera"></a>

在這裡，我們逐一查看 SDK 偵測到的輸入裝置。在 Android 7 (Nougat) 上，這只會傳回預設的麥克風裝置，因為 Amazon IVS 廣播 SDK 不支援在此版本的 Android 上選擇非預設裝置。

一旦我們找到要使用的裝置，就呼叫 `attachDevice` 予以連接。輸入裝置連接完成時，會在主執行緒上呼叫 Lambda 函數。如果呼叫失敗，您會在接聽程式中收到錯誤。

```
for(Device.Descriptor desc: BroadcastSession.listAvailableDevices(getApplicationContext())) {
    if(desc.type == Device.Descriptor.DeviceType.CAMERA &&
            desc.position == Device.Descriptor.Position.FRONT) {
        session.attachDevice(desc, device -> {
            LinearLayout previewHolder = findViewById(R.id.previewHolder);
            ImagePreviewView preview = ((ImageDevice)device).getPreviewView();
            preview.setLayoutParams(new LinearLayout.LayoutParams(
                    LinearLayout.LayoutParams.MATCH_PARENT,
                    LinearLayout.LayoutParams.MATCH_PARENT));
            previewHolder.addView(preview);
            // Bind the camera to the mixer slot we created above.
            session.getMixer().bind(device, "camera");
        });
        break;
    }
}
```

## 交換攝影機
<a name="broadcast-android-swap-cameras"></a>

```
// This assumes you’ve kept a reference called "currentCamera" that points to
// a front facing camera
for(Device device: BroadcastSession.listAvailableDevices()) {
   if(device.type == Device.Descriptor.DeviceType.CAMERA &&
          Device.position != currentCamera.position) {
        // Remove the preview view for the old device.
        // setImagePreviewTextureView is an example function 
        // that handles your view hierarchy.
        setImagePreviewView(null);
        session.exchangeDevices(currentCamera, device, camera -> {
             // Set the preview view for the new device.
             setImagePreviewView(camera.getPreviewView());
             currentCamera = camera;
        });
        break;
   }
}
```

## 建立 Input Surface
<a name="broadcast-android-create-input-surface"></a>

若要輸入應用程式產生的聲音或影像資料，請使用 `createImageInputSource` 或 `createAudioInputSource`。這兩種方法都會建立並附加像任何其他裝置一樣可以綁定到混音器的虛擬裝置。

`createImageInputSource` 傳回的 `SurfaceSource` 具有 `getInputSurface` 方法，所提供的 `Surface` 可與 Camera2 API、OpenGL、Vulkan 或其他任何能寫入 Surface 者搭配使用。

`AudioDevice` 傳回的 `createAudioInputSource` 可接收由 AudioRecorder 或其他方式產生的線性 PCM 資料。

```
SurfaceSource source = session.createImageInputSource();
Surface surface = source.getInputSurface();
session.getMixer().bind(source, “custom”);
```

## 分離裝置
<a name="broadcast-android-detach-device"></a>

如果您要分離 (而不是取代) 裝置，請使用 `Device` 或 `Device.Descriptor`。

```
session.detachDevice(currentCamera);
```

## 螢幕和系統音訊擷取
<a name="broadcast-android-screen-audio-capture"></a>

Amazon IVS 廣播 Android SDK 內含一些可簡化裝置螢幕 (Android 6 和更高版本) 和系統音訊 (Android 10 和更高版本) 擷取的協助程式。如果您想要手動管理這些協助程式，可以建立自訂影像輸入來源和自訂音訊輸入來源。

若要建立螢幕和系統音訊擷取工作階段，您必須先建立權限請求意圖：

```
public void startScreenCapture() {
    MediaProjectionManager manager =
                         (MediaProjectionManager) getApplicationContext()
                         .getSystemService(Context.MEDIA_PROJECTION_SERVICE);
    if(manager != null) {
        Intent intent = manager.createScreenCaptureIntent();
        startActivityIfNeeded(intent, SCREEN_CAPTURE_REQUEST_ID);
    }
}
```

若要使用此功能，您必須提供擴展 `com.amazonaws.ivs.broadcast.SystemCaptureService` 的類別。您不必覆寫其任何方法，但必須提供類別，以免製造服務之間的任何潛在衝突。

此外，您必須將幾個元素加入 Android 資訊清單中：

```
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application ...>
    <service android:name=".ExampleSystemCaptureService"
         android:foregroundServiceType="mediaProjection" 
         android:isolatedProcess="false" />
</application>
...
```

您必須在 `<service>` 元素中為擴展 `SystemCaptureService` 的類別命名。在 Android 9 和更高版本上，`foregroundServiceType` 必須為 `mediaProjection`。

權限意圖傳回後，您就可以繼續建立螢幕和系統音訊擷取工作階段。在 Android 8 和更高版本上，您必須提供要在使用者的通知面板中顯示的通知。Amazon IVS 廣播 Android SDK 提供了方便的方法 `createServiceNotificationBuilder`。或者，您也可以自行提供通知。

```
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if(requestCode != SCREEN_CAPTURE_REQUEST_ID
       || Activity.RESULT_OK != resultCode) {
        return;
    }
    Notification notification = null;
    if(Build.VERSION.SDK_INT >= 26) {
        Intent intent = new Intent(getApplicationContext(),
                                   NotificationActivity.class);
        notification = session
                         .createServiceNotificationBuilder("example",
                                            "example channel", intent)
                         .build();
    }
    session.createSystemCaptureSources(data,
                  ExampleSystemCaptureService.class,
                  Notification,
                  devices -> {
        // This step is optional if the mixer slots have been given preferred
        // input device types SCREEN and SYSTEM_AUDIO
        for (Device device : devices) {
            session.getMixer().bind(device, "game");
        }
    });
}
```

## 取得建議的廣播設定
<a name="broadcast-android-recommended-settings"></a>

若要在開始廣播之前評估使用者的連線，請使用 `recommendedVideoSettings` 方法來執行簡短的測試。在測試執行時，您會收到數個建議，依建議強烈程度 (從高到低) 排序。在此版本的 SDK 中，您無法重新設定目前的 `BroadcastSession`，所以需要將其設為 `release()`，然後使用建議的設定重新建立。您將持續收到 `BroadcastSessionTest.Results`，直到 `Result.status` 為 `SUCCESS` 或 `ERROR` 為止。您可以使用 `Result.progress` 檢查進度。

Amazon IVS 支援的最大位元速率為 8.5 Mbps (適用於 `type` 為 `STANDARD` 或 `ADVANCED` 的頻道)，所以此方法傳回的 `maximumBitrate` 絕不會超過 8.5 Mbps。由於網路效能可能有微小波動，此方法傳回之建議的 `initialBitrate` 會稍微小於測試中測得的真實位元速率。(通常不建議使用 100% 的可用頻寬)。

```
void runBroadcastTest() {
    this.test = session.recommendedVideoSettings(RTMPS_ENDPOINT, RTMPS_STREAMKEY,
        result -> {
            if (result.status == BroadcastSessionTest.Status.SUCCESS) {
                this.recommendation = result.recommendations[0];
            }
        });
}
```

## 使用自動重新連線
<a name="broadcast-android-auto-reconnect"></a>

如果廣播在未呼叫 `stop` API 的情況下意外停止，IVS 會支援自動重新連線至廣播 (例如，網路連線暫時中斷)。若要啟用自動重新連線，請呼叫 `BroadcastConfiguration.autoReconnect` 上的 `setEnabled(true)`。

當某些情況導致串流意外停止時，SDK 會按照線性退避策略重試最多 5 次。SDK 會透過 `BroadcastSession.Listener.onRetryStateChanged` 方法通知您的應用程式重試狀態的相關資訊。

自動重新連線會在背景透過將優先順序號碼 (從 1 開始) 附加至所提供之串流金鑰的結尾，以使用 IVS [串流接收](streaming-config.md#streaming-config-stream-takeover)功能。在 `BroadcastSession` 執行個體的持續時間內，每次嘗試重新連線時，該數字會遞增 1。這表示如果裝置在廣播期間中斷連線 4 次，且每次中斷皆需要重試 1 到 4 次，則上次串流上傳的優先順序可能介於 5 到 17 之間。因此，*我們建議您不要在 SDK 中針對相同頻道啟用自動重新連線時，使用其他裝置的 IVS 串流接管*。我們無法保證 SDK 目前使用的優先順序，如果其他裝置接管，SDK 將會嘗試以更高的優先順序重新連線。

## 使用藍牙麥克風
<a name="broadcast-android-bluetooth-microphones"></a>

要使用藍牙麥克風設備進行廣播，您必須啟動藍牙 SCO 連接：

```
Bluetooth.startBluetoothSco(context);
// Now bluetooth microphones can be used
…
// Must also stop bluetooth SCO
Bluetooth.stopBluetoothSco(context);
```