IVS Broadcast SDK: Mixer Guide(低延迟流式传输) - Amazon Interactive Video Service

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

IVS Broadcast SDK: Mixer Guide(低延迟流式传输)

混合器是一种音频和视频处理单元,它接收多个输入源并生成单个输出。它是一项强大的功能,可让您定义和管理多个屏幕上的(视频)元素和音轨。您可以组合来自多个来源的视频和音频,例如摄像头、麦克风、屏幕截图以及应用程序生成的音频和视频。您可以使用转换围绕流式传输到 Amazon IVS 的视频移动这些源,然后在流中添加和删除它们。

要访问混合器,请致电:

Android 上的 BroadcastSession.getMixer()

iOS 上的 IVSBroadcastSession.mixer

术语

IVS 广播混合器术语。
租期 描述

Binding

要将输入设备与插槽关联,该设备必须绑定到混合器插槽。该操作通过 Mixer.bind() 方法完成。一个插槽一次可以绑定一个图像输入和一个音频输入。您可以通过调用 Mixer.unbind() 来解除设备与插槽的绑定。

画布

在您的 BroadcastSession 配置中定义的视频的显示范围。画布的大小与您的视频设置相等,并以配置中指定的相同帧速率运行。

设备

一种硬件或软件组件,用于生成到 BroadcastSession 的音频或图像输入。例如,设备包括麦克风、摄像头、蓝牙耳机和屏幕截图或自定义图像输入等虚拟设备。除了自定义输入之外,通常不需要保留对设备对象的引用;相反,您可以保留设备描述符的副本。

设备描述符

包含有关输入设备的信息的结构,例如,其类型、系统地址、人类可读的“友好”名称以及移动设备上的物理位置。通过此信息,您可以决定是否要使用引用的设备并允许 Amazon IVS 访问该设备。

Slot

定义视觉元素在屏幕上的位置以及音频混合中音轨属性的容器。混合器可以配置零个或多个插槽。系统将为插槽指定一个字符串名称,可用于绑定设备和执行转换。上图显示了四个插槽:

  • 左下带摄像头输入

  • 右上带电影输入

  • 右下带有 Amazon IVS 徽标

  • 全屏背景图像

配置会话后,您可以使用 addSlotremoveSlot 混合器方法添加和删除插槽。

Transition

要将插槽移动到新位置或更改其部分属性,请使用 Mixer.transition()。此方法需要:

  • 代表插槽下一个状态的新插槽结构

  • 一个持续时间,该持续时间指定动画相对于视频的时间轴应该花费多长时间。如果持续时间设置为 0,则转换发生在混合的下一帧上。

  • 一个可选的回调函数,通知您动画的完成时间。回调可能对于链接动画很有用。

画布属性

画布属性基于您在创建 BroadcastSession 时提供的 BroadcastConfiguration 设置。AudioVideo 结构中的几个属性会影响画布:

名称 类型 描述

Audio.channels

整数

音频混合器的输出通道数。有效值:1、2。1 通道为单声道音频;2 为立体声音频。默认值:2。

Audio.sampleRate

AudioSampleRate

音频混合器每秒发出的音频样本数。该值应至少是音频信号中最高频率的两倍。人们最高可以听到约 20 kHz 的声音,因此 44.1 kHz 和 48 kHz 一般就足够了。默认值:48 kHz。

Video.defaultAspectMode

AspectMode

插槽的默认宽高比模式。有效值:

  • Fill — 保持图像的宽高比但填满插槽。必要时,图像将被裁剪。

  • Fit — 保持图像的宽高比但将整个图像放入插槽中。必要时,插槽可能有一个宽银幕效果或左右黑边。如果设置了该值,宽银幕效果/左右黑边将为 fillColor,否则,将为透明(如果图像后的画布颜色为黑色,它看起来可能是黑色)。

  • None — 请勿保持图像的宽高比。图像将被缩放,以匹配插槽的尺寸。

Video.size

Vec2

视频画布的大小。

Video.targetFramerate

整数

画布的每秒目标帧数。通常情况下,该值应能够得到满足,但是在某些情况下(例如 CPU 负载过高或网络拥塞),系统可能会丢弃帧。

插槽属性

插槽有几个可配置属性,您可以使用这些属性来自定义场景和制作动画。对于持续时间超过 0 秒的转换,任何为 Float 或 Vector 的值都将被使用线性插值制作动画。

名称 类型 描述

aspect

AspectMode

适用于插槽中渲染的任何图像的宽高比模式。有效值:

  • Fill — 保持图像的宽高比但填满插槽。必要时,图像将被裁剪。

  • Fit — 保持图像的宽高比但将整个图像放入插槽中。必要时,插槽可能有一个宽银幕效果或左右黑边。如果设置了该值,宽银幕效果/左右黑边将为 fillColor,否则,将为透明(如果图像后的画布颜色为黑色,它看起来可能是黑色)。

  • None — 请勿保持图像的宽高比。图像将被缩放,以匹配插槽的尺寸。

默认值:如果 matchCanvasAspectMode 为真,与画布 aspect 相同,否则为 Fill。设置此值也会将 matchCanvasAspectMode 设置为假。

fillColor

Vec4

当插槽和图像的宽高比不匹配时,填充用于 Aspect Fit 的颜色。格式为(red、green、blue、alpha)。有效值(对于每个通道):0-1。默认值:(0, 0, 0, 0)。

gain

Float

音频增益。这是一个乘数,因此任何高于 1 的值都会提高增益;任何低于 1 的值都会减小增益。有效值:0 - 2。默认值:1。

matchCanvasAspectMode

布尔值

如果为真,请使用画布的 Video.defaultAspectMode 值。如果您设置插槽的 aspect 属性,则该值被设置为假。默认值:真。

matchCanvasSize

布尔值

如果为真,则插槽的大小调整为等于画布的大小,其位置设置为 (0, 0)。如果您设置插槽的 size 属性,则该值被设置为假。默认值:真。

name

字符串

插槽的名称。该名称用于引用插槽以进行绑定和转换。默认值: "default"

position

Vec2

插槽相对于画布左上角的位置(以像素为单位)。插槽的原点也在左上角。

preferredAudioInput

DeviceType

首选的音频输入设备类型。如果解除此插槽的绑定并将指定类型的音频设备连接到会话,则该设备将自动绑定到此插槽。有效值:

  • 麦克风 — 音频硬件,例如板载麦克风、可插拔耳机或蓝牙耳机。

  • 系统音频 — 从操作系统捕获的音频,通常伴随屏幕录制。

  • 用户音频 — 您创建的自定义音频输入。

  • 未知 — 没有首选设备;插槽将始终需要手动绑定。

preferredVideoInput

DeviceType

首选的视频输入设备。如果解除此插槽的绑定并将指定类型的视频设备连接到会话,则该设备将自动绑定到此插槽。有效值:

  • 摄像头 — 板载摄像头设备,例如前置、后置或广角摄像头。

  • 屏幕 — 来自操作系统的屏幕截图。

  • 用户图像 — 您创建的自定义图像和视频输入。

  • 未知 — 没有首选设备;插槽将始终需要手动绑定。

size

Vec2

插槽的大小(以像素为单位)。设置此值也会将 matchCanvasSize 设置为假。默认值:(0, 0);但是,因为 matchCanvasSize 默认为真,插槽的渲染大小就是画布大小,而不是 (0, 0)。

transparency

Float

插槽的透明度。这是与图像中的任何 Alpha 值相乘的结果。不透明度为 1 - transparency。有效值:0-1,其中 0 表示完全不透明,1 表示完全透明。默认值:0。

zIndex

Float

插槽的相对顺序。zIndex 值较高的插槽绘制在 zIndex 值较低的插槽之上。

配置广播会话以进行混合

配置广播会话以进行混合。

在这里,我们创建了一个类似于本指南开头的场景,其中包含三个屏幕元素:

  • 左下角的摄像头插槽。

  • 右下角的徽标叠加插槽。

  • 右上角用于电影的插槽。

请注意,画布的原点是左上角,插槽也是一样。因此,将插槽定位在 (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 类型的任何属性都可以制作成动画;其他属性在动画的开始或结束时生效。

名称 可以制作成动画吗? 影响点

aspect

End

fillColor

已插入

gain

已插入

matchCanvasAspectMode

Start

matchCanvasSize

Start

name

注意:您不能更改插槽的名称。

不适用

position

已插入

preferredAudioInput

End

preferredVideoInput

End

size

已插入

transparency

已插入

zIndex

注意:zIndex 在 3D 空间中移动 2D 平面,因此当两个平面在动画中间的某个点交叉时,就会发生转换。这可以计算出来,但具体取决于开始和结束 zIndex 值。为了实现更顺畅的转换,请将其与 transparency 结合使用。

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 方法不同。该方法仅影响设备上的本地预览视图,不会影响广播。