

# IVS Android 广播 SDK 的高级用例 \$1 低延迟直播功能
<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>

我们将通过开发工具包检测到的输入设备进行迭代。在 Android 7 (Nougat) 上，这将仅返回默认麦克风设备，因为 Amazon IVS 广播开发工具包不支持在此版本的 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;
   }
}
```

## 创建输入表面
<a name="broadcast-android-create-input-surface"></a>

若要输入应用程序生成的声音或图像数据，请使用 `createImageInputSource` 或者 `createAudioInputSource`。这两种方法都会创建和连接虚拟设备，这些设备可以像任何其他设备一样绑定到混合器。

`createImageInputSource` 返回的 `SurfaceSource` 具有 `getInputSurface` 方法，它会为您提供一个 `Surface`，可以将其与 Camera2 API、OpenGL 或 Vulkan 或可以写入到 Surface 的任何其他接口一起使用。

`createAudioInputSource` 返回的 `AudioDevice` 可以接收由音频录音机或其他方式生成的线性 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 Broadcast SDK for Android 含有一些帮助程序，可以简化捕获设备屏幕（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 Broadcast SDK for Android 提供了便利的方法 `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` 方法来运行一个简短的测试。在测试运行时，您将收到多个建议，从最推荐到最不推荐的顺序排列。在此版本的开发工具包中，无法重新配置当前 `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 次。它通过 `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);
```