IVS Broadcast SDK: 혼합 디바이스 - Amazon IVS

IVS Broadcast SDK: 혼합 디바이스

혼합 디바이스는 여러 개의 입력 소스로 단일 출력을 생성하는 오디오 및 비디오 디바이스입니다. 디바이스 혼합은 여러 화면(비디오) 요소와 오디오 트랙을 정의하고 관리할 수 있는 강력한 기능입니다. 카메라, 마이크, 화면 캡처, 앱에서 생성한 오디오 및 비디오와 같은 여러 소스의 비디오와 오디오를 결합할 수 있습니다. 전환을 사용하여 IVS로 스트리밍하는 비디오에서 이러한 소스를 이동하고 스트림 중간에 소스를 추가하거나 제거할 수 있습니다.

혼합 디바이스에는 이미지 버전과 오디오 버전이 있습니다. 혼합 이미지 디바이스를 생성하려면 다음을 직접적으로 호출합니다.

Android에서 DeviceDiscovery.createMixedImageDevice()

iOS에서 IVSDeviceDiscovery.createMixedImageDevice()

반환된 디바이스는 다른 디바이스와 마찬가지로 BroadcastSession(저지연 스트리밍) 또는 Stage(실시간 스트리밍)에 연결할 수 있습니다.

용어

IVS 브로드캐스팅 혼합 디바이스 용어입니다.
Term 설명

장치

오디오 또는 이미지 입력을 생성하는 하드웨어 또는 소프트웨어 구성 요소입니다. 디바이스의 예로는 마이크, 카메라, Bluetooth 헤드셋 및 화면 캡처 또는 사용자 정의 이미지 입력과 같은 가상 디바이스가 있습니다.

혼합 디바이스

다른 Device와 마찬가지로 BroadcastSession에 연결할 수 있지만, Source 객체를 추가할 수 있는 추가 API가 있는 Device입니다. 혼합 디바이스에는 오디오 또는 이미지를 합성하여 단일 출력 오디오와 이미지 스트림을 생성하는 내부 믹서가 있습니다.

혼합 디바이스에는 이미지 버전 또는 오디오 버전이 있습니다.

혼합 디바이스 구성

혼합 디바이스의 구성 객체입니다. 혼합 이미지 디바이스의 경우 차원과 프레임 속도 등 속성이 구성됩니다. 혼합 오디오 디바이스의 경우 채널 개수가 구성됩니다.

소스

화면상의 시각적 요소의 위치와 오디오 믹스에서 오디오 트랙의 속성을 정의하는 컨테이너. 혼합 디바이스는 0개 이상의 소스로 구성할 수 있습니다. 소스에는 소스의 미디어 사용 방식에 영향을 미치는 구성이 주어집니다. 위 이미지에서는 네 가지 이미지 소스를 보여줍니다.

  • 카메라 입력을 사용한 왼쪽 아래

  • 영화 입력을 사용한 오른쪽 위

  • Amazon IVS 로고를 사용한 오른쪽 아래

  • 전체 화면 백그라운드 이미지

소스 구성

혼합 디바이스에 들어가는 소스의 구성 객체입니다. 아래에 전체 구성 객체가 설명되어 있습니다.

Transition

슬롯을 새 위치로 이동하거나 일부 속성을 변경하려면 MixedDevice.transitionToConfiguration()을 사용합니다. 이 메서드는 다음을 수행합니다.

  • 소스의 다음 상태를 나타내는 새 소스 구성입니다.

  • 비디오 타임라인을 기준으로 애니메이션에 걸리는 시간을 지정하는 지속 시간 지속 시간이 0으로 설정된 경우 혼합된 다음 프레임에서 전환이 발생합니다.

  • 애니메이션이 완료되면 알려주는 선택적 콜백. 콜백은 애니메이션 체인에 유용할 수 있습니다.

혼합 오디오 디바이스

구성

Android에서 MixedAudioDeviceConfiguration

iOS에서 IVSMixedAudioDeviceConfiguration

명칭 유형 설명

channels

Integer

오디오 믹서의 출력 채널 수 유효한 값: 1, 2. 1은 모노 오디오, 2는 스테레오 오디오입니다. 기본값: 2.

소스 구성

Android에서 MixedAudioDeviceSourceConfiguration

iOS에서 IVSMixedAudioDeviceSourceConfiguration

명칭 유형 설명

gain

Float

오디오 게인. 이 값은 승수이므로 1을 초과하는 모든 값은 게인을 증가시키고 1 미만의 값은 게인을 감소시킵니다. 유효한 값은 0~2입니다. 기본값: 1.

혼합 이미지 디바이스

구성

Android에서 MixedImageDeviceConfiguration

iOS에서 IVSMixedImageDeviceConfiguration

명칭 유형 설명

size

Vec2

비디오 캔버스 크기.

targetFramerate

Integer

혼합 디바이스의 목표 초당 프레임 수입니다. 평균적으로 이 값을 충족해야 하지만, 특정 상황(예: 높은 CPU 또는 GPU 부하)에서는 시스템이 프레임을 떨어뜨릴 수 있습니다.

transparencyEnabled

그러면 이미지 소스 구성에서 alpha 속성을 사용하여 블렌딩할 수 있습니다. true로 설정하면 메모리와 CPU 소비가 증가합니다. 기본값: false.

소스 구성

Android에서 MixedImageDeviceSourceConfiguration

iOS에서 IVSMixedImageDeviceSourceConfiguration

명칭 유형 설명

alpha

Float

슬롯의 알파입니다. 이미지에 알파 값이 있는 배수입니다. 유효한 값: 0~1. 0은 완전히 투명하고, 1은 완전히 불투명합니다. 기본값: 1.

aspect

AspectMode

슬롯에 렌더링된 모든 이미지에 대한 종횡비 모드. 유효한 값:

  • Fill은 이미지의 종횡비는 유지하지만 슬롯을 채웁니다. 필요한 경우 이미지가 잘립니다.

  • Fit은 이미지의 종횡비는 유지하지만 전체 이미지를 슬롯에 맞춥니다. 필요한 경우 슬롯은 레터박스 또는 필러박스를 가질 수 있습니다. 레터박스/필러박스는 값이 설정된 경우 fillColor이(가) 됩니다. 그렇지 않으면 투명하게 되어 이미지 뒤의 캔버스 색상이 검은색이면 검은색으로 나타날 수 있습니다.

  • None에는 이미지 종횡비를 유지하지 마세요. 슬롯의 치수와 일치하도록 이미지 규모가 조정됩니다.

기본값: Fit

fillColor

Vec4

슬롯 및 이미지 종횡비가 일치하지 않을 때 aspect Fit이 사용되어 색상을 채웁니다. 형식은 (빨간색, 녹색, 파란색, 알파) 입니다. 각 채널에 대해 유효한 값: 0~1. 기본값: (0, 0, 0, 0).

position

Vec2

캔버스의 왼쪽 위 모서리를 기준으로 한 슬롯 위치(단위: 픽셀). 슬롯의 원점도 왼쪽 위입니다.

size

Vec2

슬롯의 크기(픽셀) 이 값 설정도 matchCanvasSizefalse로 설정합니다. 기본값: (0, 0). 하지만 matchCanvasSize 기본값이 true이므로 슬롯의 렌더링된 크기는 (0, 0)이 아닌 캔버스 크기입니다.

zIndex

Float

슬롯의 상대적 순서. zIndex 값이 높은 슬롯은 zIndex 값이 낮은 슬롯 위에 그려집니다.

혼합 이미지 디바이스 생성 및 구성

믹싱을 위한 브로드캐스트 세션 구성.

다음은 가이드의 시작 부분에 있는 것과 비슷한 장면을 만들 때 사용하는 화면에 표시되는 3가지 요소입니다.

  • 카메라용 왼쪽 하단 슬롯.

  • 로고 오버레이용 오른쪽 하단 슬롯.

  • 영화용 오른쪽 상단 슬롯.

캔버스의 원점은 왼쪽 상단 모서리이며 슬롯에 대해 동일합니다. 따라서 (0, 0)에 슬롯을 배치하면 전체 슬롯이 보이는 왼쪽 상단 모서리에 배치됩니다.

iOS

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

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)

소스 제거

소스를 제거하려면 제거하려는 Source 객체로 MixedDevice.remove를 직접적으로 호출합니다.

애니메이션 전환

전환 메서드는 슬롯의 구성을 새 구성으로 대체합니다. 이 대체에 지속 시간을 0보다 높은 시간(초)을 설정하여 애니메이션을 적용할 수 있습니다.

애니메이션할 수 있는 속성은 무엇인가요?

슬롯 구조의 모든 속성에 애니메이션이 적용될 수 있는 것은 아닙니다. Float 유형을 기반으로 하는 모든 속성에 애니메이션을 적용할 수 있으며, 다른 속성은 애니메이션의 시작 또는 종료 시점에 적용됩니다.

명칭 애니메이션으로 만들 수 있나요? 임팩트 포인트

Audio.gain

보간

Image.alpha

보간

Image.aspect

아니요

종료

Image.fillColor

보간

Image.position

보간

Image.size

보간

Image.zIndex

참고: zIndex는 3D 공간을 통해 2D 평면을 이동하므로 두 평면이 애니메이션 중간 지점에서 교차할 때 전환이 발생합니다. 이 값은 계산될 수 있지만 zIndex 값은 시작과 종료에 따라 다릅니다. 보다 원활한 전환을 위해 이 값을 alpha와 결합합니다.

알 수 없음

간단한 예제

아래는 혼합 이미지 디바이스 생성 및 구성의 위에서 정의된 구성을 사용하는 전체 화면 카메라 전환 예제입니다. 0.5초에 걸쳐 애니메이션이 표시됩니다.

iOS

// 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

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

브로드캐스트 미러링

다음 방향으로 브로드캐스트에 연결된 이미지 디바이스를 미러링하려면 …에 음수 값 사용

수평

슬롯의 너비

수직

슬롯의 높이

수평 및 수직 모두

슬롯의 너비 및 높이 모두

미러링 시 슬롯을 올바른 위치에 놓으려면 위치를 같은 값으로 조정해야 합니다.

다음은 브로드캐스트를 수평 및 수직으로 미러링하는 예입니다.

iOS

수평 미러링:

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

수평 미러링:

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 메서드와 다릅니다. 해당 메서드는 디바이스의 로컬 미리 보기에만 영향을 주며 브로드캐스트에는 영향을 주지 않습니다.