本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
IVS Broadcast SDK: Mixer Guide(低延迟流式传输)
混合器是一种音频和视频处理单元,它接收多个输入源并生成单个输出。它是一项强大的功能,可让您定义和管理多个屏幕上的(视频)元素和音轨。您可以组合来自多个来源的视频和音频,例如摄像头、麦克风、屏幕截图以及应用程序生成的音频和视频。您可以使用转换围绕流式传输到 Amazon IVS 的视频移动这些源,然后在流中添加和删除它们。
要访问混合器,请致电:
Android 上的 BroadcastSession.getMixer()
iOS 上的 IVSBroadcastSession.mixer
术语
租期 | 描述 |
---|---|
Binding |
要将输入设备与插槽关联,该设备必须绑定到混合器插槽。该操作通过 |
画布 |
在您的 |
设备 |
一种硬件或软件组件,用于生成到 |
设备描述符 |
包含有关输入设备的信息的结构,例如,其类型、系统地址、人类可读的“友好”名称以及移动设备上的物理位置。通过此信息,您可以决定是否要使用引用的设备并允许 Amazon IVS 访问该设备。 |
Slot |
定义视觉元素在屏幕上的位置以及音频混合中音轨属性的容器。混合器可以配置零个或多个插槽。系统将为插槽指定一个字符串名称,可用于绑定设备和执行转换。上图显示了四个插槽:
配置会话后,您可以使用 |
Transition |
要将插槽移动到新位置或更改其部分属性,请使用
|
画布属性
画布属性基于您在创建 BroadcastSession
时提供的 BroadcastConfiguration
设置。Audio
和 Video
结构中的几个属性会影响画布:
名称 | 类型 | 描述 |
---|---|---|
|
整数 |
音频混合器的输出通道数。有效值:1、2。1 通道为单声道音频;2 为立体声音频。默认值:2。 |
|
AudioSampleRate |
音频混合器每秒发出的音频样本数。该值应至少是音频信号中最高频率的两倍。人们最高可以听到约 20 kHz 的声音,因此 44.1 kHz 和 48 kHz 一般就足够了。默认值:48 kHz。 |
|
AspectMode |
插槽的默认宽高比模式。有效值:
|
|
Vec2 |
视频画布的大小。 |
|
整数 |
画布的每秒目标帧数。通常情况下,该值应能够得到满足,但是在某些情况下(例如 CPU 负载过高或网络拥塞),系统可能会丢弃帧。 |
插槽属性
插槽有几个可配置属性,您可以使用这些属性来自定义场景和制作动画。对于持续时间超过 0 秒的转换,任何为 Float 或 Vector 的值都将被使用线性插值制作动画。
名称 | 类型 | 描述 |
---|---|---|
|
AspectMode |
适用于插槽中渲染的任何图像的宽高比模式。有效值:
默认值:如果 |
|
Vec4 |
当插槽和图像的宽高比不匹配时,填充用于 Aspect Fit 的颜色。格式为(red、green、blue、alpha)。有效值(对于每个通道):0-1。默认值:(0, 0, 0, 0)。 |
|
Float |
音频增益。这是一个乘数,因此任何高于 1 的值都会提高增益;任何低于 1 的值都会减小增益。有效值:0 - 2。默认值:1。 |
|
布尔值 |
如果为真,请使用画布的 |
|
布尔值 |
如果为真,则插槽的大小调整为等于画布的大小,其位置设置为 (0, 0)。如果您设置插槽的 |
|
字符串 |
插槽的名称。该名称用于引用插槽以进行绑定和转换。默认值: |
|
Vec2 |
插槽相对于画布左上角的位置(以像素为单位)。插槽的原点也在左上角。 |
|
DeviceType |
首选的音频输入设备类型。如果解除此插槽的绑定并将指定类型的音频设备连接到会话,则该设备将自动绑定到此插槽。有效值:
|
|
DeviceType |
首选的视频输入设备。如果解除此插槽的绑定并将指定类型的视频设备连接到会话,则该设备将自动绑定到此插槽。有效值:
|
|
Vec2 |
插槽的大小(以像素为单位)。设置此值也会将 |
|
Float |
插槽的透明度。这是与图像中的任何 Alpha 值相乘的结果。不透明度为 1 - |
|
Float |
插槽的相对顺序。 |
配置广播会话以进行混合
在这里,我们创建了一个类似于本指南开头的场景,其中包含三个屏幕元素:
-
左下角的摄像头插槽。
-
右下角的徽标叠加插槽。
-
右上角用于电影的插槽。
请注意,画布的原点是左上角,插槽也是一样。因此,将插槽定位在 (0, 0) 会将其放在左上角,且整个插槽可见。
iOS
let config = IVSBroadcastConfiguration() try config.video.setSize(CGSize(width: 1280, height: 720)) try config.video.setTargetFramerate(60) config.video.enableTransparency = true // Bottom Left var cameraSlot = IVSMixerSlotConfiguration() cameraSlot.size = CGSize(width: 320, height: 180) cameraSlot.position = CGPoint(x: 20, y: 1280 - 200) cameraSlot.preferredVideoInput = .camera cameraSlot.preferredAudioInput = .microphone cameraSlot.matchCanvasAspectMode = false cameraSlot.zIndex = 2 try cameraSlot.setName("camera") // Top Right var streamSlot = IVSMixerSlotConfiguration() streamSlot.size = CGSize(width: 640, height: 320) streamSlot.position = CGPoint(x: 1280 - 660, y: 20) streamSlot.preferredVideoInput = .userImage streamSlot.preferredAudioInput = .userAudio streamSlot.matchCanvasAspectMode = false streamSlot.zIndex = 1 try streamSlot.setName("stream") // Bottom Right var logoSlot = IVSMixerSlotConfiguration() logoSlot.size = CGSize(width: 320, height: 180) logoSlot.position = CGPoint(x: 1280 - 340, y: 720 - 200) logoSlot.preferredVideoInput = .userImage logoSlot.preferredAudioInput = .unknown logoSlot.matchCanvasAspectMode = false logoSlot.zIndex = 3 try logoSlot.setTransparency(0.7) try logoSlot.setName("logo") config.mixer.slots = [ cameraSlot, streamSlot, logoSlot ]
Android
// Bottom Left val cameraSlot = BroadcastConfiguration.Mixer.Slot.with { s -> s.setSize(320, 180) s.position = BroadcastConfiguration.Vec2(20, 1280 - 200) s.preferredVideoInput = Device.Descriptor.DeviceType.CAMERA s.preferredAudioInput = Device.Descriptor.DeviceType.MICROPHONE s.matchCanvasAspectMode = false s.zIndex = 2 s.name = "camera" s } // Top Right val streamSlot = BroadcastConfiguration.Mixer.Slot.with { s -> s.setSize(640, 320) s.position = BroadcastConfiguration.Vec2(1280 - 660, 20) s.preferredVideoInput = Device.Descriptor.DeviceType.USER_IMAGE s.preferredAudioInput = Device.Descriptor.DeviceType.USER_AUDIO s.matchCanvasAspectMode = false s.zIndex = 1 s.name = "stream" s } // Bottom Right val logoSlot = BroadcastConfiguration.Mixer.Slot.with { s -> s.setSize(320, 180) s.position = BroadcastConfiguration.Vec2(1280 - 340, 720 - 200) s.preferredVideoInput = Device.Descriptor.DeviceType.USER_IMAGE s.preferredAudioInput = Device.Descriptor.DeviceType.UNKNOWN s.matchCanvasAspectMode = false s.zIndex = 3 s.name = "logo" s.transparency = 0.7 s } val config = BroadcastConfiguration.with { c -> c.mixer.slots = listOf(cameraSlot, streamSlot, logoSlot) c.video.targetFramerate = 60 c.video.setSize(1280, 720) c }
添加槽
一旦您使用配置创建了 BroadcastSession
,您可以在混合器中添加插槽和从中移除插槽。在这里,我们在混合器中添加了一个大的背景插槽用于图像。
iOS
// Background. We will use most of the defaults for this slot. var backgroundSlot = IVSMixerSlotConfiguration() backgroundSlot.preferredVideoInput = .userImage backgroundSlot.preferredAudioInput = .unknown backgroundSlot.matchCanvasAspectMode = false try backgroundSlot.setName("background") session.mixer.addSlot(backgroundSlot)
Android
// Background. We will use most of the defaults for this slot. val backgroundSlot = BroadcastConfiguration.Mixer.Slot.with { s -> s.preferredVideoInput = Device.Descriptor.DeviceType.USER_IMAGE s.preferredAudioInput = Device.Descriptor.DeviceType.UNKNOWN s.matchCanvasAspectMode = false s.name = "background" s } session.mixer.addSlot(backgroundSlot)
移除插槽
要移除插槽,请使用您想要移除的插槽的名称调用 BroadcastSession.Mixer.removeSlot
。绑定到该插槽的任何设备都会自动解除绑定,因此,您如果要继续使用它们,必须将它们重新绑定到不同的插槽。
动画转换
混合器转换方法用新配置替换插槽的配置。通过将持续时间设置为大于 0(以秒为单位),可以将此替换制作为随时间推移的动画。
哪些属性可以制作成动画?
插槽结构中并非所有属性都可以制作成动画。基于 Float 类型的任何属性都可以制作成动画;其他属性在动画的开始或结束时生效。
名称 | 可以制作成动画吗? | 影响点 |
---|---|---|
|
否 |
End |
|
是 |
已插入 |
|
是 |
已插入 |
|
否 |
Start |
|
否 |
Start |
注意:您不能更改插槽的名称。 |
否 |
不适用 |
|
是 |
已插入 |
|
否 |
End |
|
否 |
End |
|
是 |
已插入 |
|
是 |
已插入 |
注意: |
是 |
Unknown |
简单示例
以下是使用上述配置广播会话进行混合中定义的配置进行全屏摄像头接管的示例。将其在 0.5 秒以内的变化制作成动画。
iOS
// Bottom Left var bigCameraSlot = cameraSlot bigCameraSlot.size = CGSize(width: 1280, height: 720) bigCameraSlot.position = CGPoint(x: 0, y: 0) session.mixer.transition("camera", bigCameraSlot, 0.5) { println("animation completed!") }
Android
// Bottom Left val bigCameraSlot = cameraSlot.changing { s -> s.setSize(1280, 720) s.position = BroadcastConfiguration.Vec2(0, 0) s } session.mixer.transition("camera", bigCameraSlot, 0.5) { print("animation completed!") }
镜像广播
要在此方向镜像广播中附加的映像设备... | 使用负值表示... |
---|---|
水平 |
槽的宽度 |
垂直 |
槽的高度 |
水平和垂直方向 |
槽的宽度和高度 |
需要按相同的值调整位置,以便在镜像时将槽置于正确的位置。
以下是水平和垂直镜像广播的示例。
iOS
水平镜像:
var cameraSlot = IVSMixerSlotConfiguration cameraSlot.size = CGSize(width: -320, height: 720) // Add 320 to position x since our width is -320 cameraSlot.position = CGPoint(x: 320, y: 0)
垂直镜像:
var cameraSlot = IVSMixerSlotConfiguration cameraSlot.size = CGSize(width: 320, height: -720) // Add 720 to position y since our height is -720 cameraSlot.position = CGPoint(x: 0, y: 720)
Android
水平镜像:
cameraSlot = BroadcastConfiguration.Mixer.Slot.with { it.size = BroadcastConfiguration.Vec2(-320f, 180f) // Add 320f to position x since our width is -320f it.position = BroadcastConfiguration.Vec2(320f, 0f) return@with it }
垂直镜像:
cameraSlot = BroadcastConfiguration.Mixer.Slot.with { it.size = BroadcastConfiguration.Vec2(320f, -180f) // Add 180f to position y since our height is -180f it.position = BroadcastConfiguration.Vec2(0f, 180f) return@with it }
注意:此镜像与 ImagePreviewView
(Android)和 IVSImagePreviewView
(iOS)上的 setMirrored
方法不同。该方法仅影响设备上的本地预览视图,不会影响广播。