고급 사용 사례 - Amazon Interactive Video Service

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

고급 사용 사례

여기서는 몇 가지 고급 사용 사례를 제공합니다. 위의 기본 설정으로 시작하고 여기에서 계속합니다.

브로드캐스트 구성 생성

여기서는 두 개의 비디오 소스를 믹서에 바인딩할 수 있도록 두 개의 믹서 슬롯이 있는 사용자 지정 구성을 생성합니다. 하나(custom)는 전체 화면이고 다른 슬롯(camera) 뒤에 배치되며, 다른 슬롯은 더 작고 오른쪽 맨 아래에 있습니다. custom 슬롯의 경우 위치, 크기 또는 가로 세로 비율 모드를 설정하지 않습니다. 이러한 파라미터를 설정하지 않으므로 슬롯에서는 크기 및 위치에 비디오 설정을 사용합니다.

let config = IVSBroadcastConfiguration() try config.audio.setBitrate(128_000) try config.video.setMaxBitrate(3_500_000) try config.video.setMinBitrate(500_000) try config.video.setInitialBitrate(1_500_000) try config.video.setSize(CGSize(width: 1280, height: 720)) config.video.defaultAspectMode = .fit config.mixer.slots = [ try { let slot = IVSMixerSlotConfiguration() // Do not automatically bind to a source slot.preferredAudioInput = .unknown // Bind to user image if unbound slot.preferredVideoInput = .userImage try slot.setName("custom") return slot }(), try { let slot = IVSMixerSlotConfiguration() slot.zIndex = 1 slot.aspect = .fill slot.size = CGSize(width: 300, height: 300) slot.position = CGPoint(x: config.video.size.width - 400, y: config.video.size.height - 400) try slot.setName("camera") return slot }() ]

브로드캐스트 세션 생성(고급 버전)

기본 예제에서 했던 것처럼 IVSBroadcastSession을 생성하지만, 여기서는 사용자 지정 구성을 제공합니다. 또한 디바이스를 수동으로 추가할 것이므로 디바이스 배열에 nil을 입력합니다.

let broadcastSession = try IVSBroadcastSession( configuration: config, // The configuration we created above descriptors: nil, // We’ll manually attach devices after delegate: self)

카메라 디바이스 반복 및 연결

여기서는 SDK가 감지한 입력 디바이스를 반복합니다. SDK는 iOS의 기본 제공 디바이스만 반환합니다. Bluetooth 오디오 디바이스가 연결되어 있더라도 기본 제공 디바이스로 표시됩니다. 자세한 정보는 알려진 문제 및 해결 방법을 참조하세요.

사용할 디바이스를 찾으면 attachDevice를 호출하여 연결합니다.

let frontCamera = IVSBroadcastSession.listAvailableDevices() .filter { $0.type == .camera && $0.position == .front } .first if let camera = frontCamera { broadcastSession.attach(camera, toSlotWithName: "camera") { device, error in // check error } }

카메라 전환

// This assumes you’ve kept a reference called `currentCamera` that points to the current camera. let wants: IVSDevicePosition = (currentCamera.descriptor().position == .front) ? .back : .front // Remove the current preview view since the device will be changing. previewView.subviews.forEach { $0.removeFromSuperview() } let foundCamera = IVSBroadcastSession .listAvailableDevices() .first { $0.type == .camera && $0.position == wants } guard let newCamera = foundCamera else { return } broadcastSession.exchangeOldDevice(currentCamera, withNewDevice: newCamera) { newDevice, _ in currentCamera = newDevice if let camera = newDevice as? IVSImageDevice { do { previewView.addSubview(try finalCamera.previewView()) } catch { print("Error creating preview view \(error)") } } }

사용자 지정 입력 소스 생성

앱에서 생성하는 사운드 또는 이미지 데이터를 입력하려면 createImageSource 또는 createAudioSource를 사용합니다. 이 두 메서드 모두 가상 디바이스(IVSCustomImageSourceIVSCustomAudioSource)를 생성하며, 해당 디바이스는 다른 디바이스처럼 믹서에 바인딩될 수 있습니다.

이 두 메서드에서 반환되는 디바이스는 onSampleBuffer 함수를 통해 CMSampleBuffer를 수락합니다.

  • 비디오 소스의 경우 픽셀 형식은 kCVPixelFormatType_32BGRA, 420YpCbCr8BiPlanarFullRange 또는 420YpCbCr8BiPlanarVideoRange여야 합니다.

  • 오디오 소스의 경우 버퍼에 선형 PCM 데이터가 포함되어야 합니다.

브로드캐스트 SDK에서 제공하는 카메라 디바이스도 사용하는 중 사용자 지정 이미지 소스를 공급하기 위해 카메라 입력으로 AVCaptureSession을 사용할 수 없습니다. 여러 카메라를 동시에 사용하려면 AVCaptureMultiCamSession을 사용하여 두 개의 사용자 지정 이미지 소스를 제공합니다.

사용자 지정 이미지 소스는 주로 이미지와 같은 정적 콘텐츠나 비디오 콘텐츠에 사용해야 합니다.

let customImageSource = broadcastSession.createImageSource(withName: "video") try broadcastSession.attach(customImageSource, toSlotWithName: "custom")

네트워크 연결 모니터링

모바일 디바이스에서는 이동 중에 네트워크 연결을 일시적으로 끊었다가 다시 연결하는 일이 흔합니다. 따라서 앱의 네트워크 연결을 모니터링하고 상황이 바뀌면 적절하게 대응하는 일이 중요합니다.

브로드캐스터의 연결이 끊어지면 브로드캐스트 SDK의 상태가 errordisconnected로 차례로 바뀝니다. 이러한 변경 사항에 대한 알림은 IVSBroadcastSessionDelegate를 통해 받습니다. 이러한 상태 변경 알림을 받으면 다음을 수행합니다.

  1. 브로드캐스트 앱의 연결 상태를 모니터링하고 연결이 복원되면 엔드포인트 및 스트림 키를 사용하여 start를 호출합니다.

  2. 중요: 상태 대리자 콜백을 모니터링하고 start를 다시 호출한 후 상태가 connected로 변경되는지 확인합니다.

디바이스 분리

디바이스를 분리만 하고 교체하지 않으려는 경우 IVSDevice 또는 IVSDeviceDescriptor을(를) 사용하여 디바이스를 분리하세요.

broadcastSession.detachDevice(currentCamera)

ReplayKit 통합

iOS에서 장치의 화면 및 시스템 오디오를 스트리밍하려면 와 통합해야 합니다 ReplayKit. Amazon IVS 브로드캐스트 SDK를 사용하면 다음을 사용하여 쉽게 ReplayKit 통합할 수 있습니다. IVSReplayKitBroadcastSession RPBroadcastSampleHandler 하위 클래스에서 IVSReplayKitBroadcastSession의 인스턴스를 생성하고 다음을 수행합니다.

  • broadcastStarted에서 세션 시작

  • broadcastFinished에서 세션 중지

세션 객체에 화면 이미지, 앱 오디오 및 마이크 오디오를 위한 세 가지 사용자 지정 소스가 제공됩니다. processSampleBuffer에 제공된 CMSampleBuffers를 해당 사용자 지정 소스에 전달합니다.

디바이스 방향을 처리하려면 샘플 버퍼에서 ReplayKit 특정 메타데이터를 추출해야 합니다. 다음 코드를 사용합니다.

let imageSource = session.systemImageSource; if let orientationAttachment = CMGetAttachment(sampleBuffer, key: RPVideoSampleOrientationKey as CFString, attachmentModeOut: nil) as? NSNumber, let orientation = CGImagePropertyOrientation(rawValue: orientationAttachment.uint32Value) { switch orientation { case .up, .upMirrored: imageSource.setHandsetRotation(0) case .down, .downMirrored: imageSource.setHandsetRotation(Float.pi) case .right, .rightMirrored: imageSource.setHandsetRotation(-(Float.pi / 2)) case .left, .leftMirrored: imageSource.setHandsetRotation((Float.pi / 2)) } }

IVSBroadcastSession대신 IVSReplayKitBroadcastSession 를 ReplayKit 사용하여 통합할 수 있습니다. 하지만 ReplayKit 특정 버전에는 내부 메모리 사용량을 줄여 Apple의 방송 확장 프로그램 메모리 한도 내에서 사용할 수 있도록 몇 가지 수정 사항이 적용되었습니다.

브로드캐스트를 시작하기 전에 사용자 연결을 평가하려면 IVSBroadcastSession.recommendedVideoSettings를 사용하여 간단한 테스트를 실행합니다. 테스트가 실행되면 가장 권장되는 것에서 가장 권장되지 않는 것 순서로 여러 권장 사항이 표시됩니다. 이 버전의 SDK에서는 현재 IVSBroadcastSession을 다시 구성할 수 없으므로, 해당 세션을 할당 취소한 다음 권장 설정으로 새 세션을 생성해야 합니다. result.statusSuccess 또는 Error가 될 때까지 계속 IVSBroadcastSessionTestResults가 표시됩니다. result.progress를 사용하여 진행률을 확인할 수 있습니다.

Amazon IVS에서는 최대 8.5Mbps의 비트 전송률(typeSTANDARD 또는 ADVANCED인 채널의 경우)을 지원하므로 이 메서드에서 반환되는 maximumBitrate는 8.5Mbps를 초과하지 않습니다. 네트워크 성능의 작은 변동을 고려하기 위해 이 메서드에서 반환되는 권장 initialBitrate는 테스트에서 측정된 실제 비트 전송률보다 약간 작습니다. (일반적으로 사용 가능한 대역폭의 100%를 사용하는 것은 바람직하지 않습니다.)

func runBroadcastTest() { self.test = session.recommendedVideoSettings(with: IVS_RTMPS_URL, streamKey: IVS_STREAMKEY) { [weak self] result in if result.status == .success { this.recommendation = result.recommendations[0]; } } }

백그라운드 비디오 사용

애플리케이션이 백그라운드에 있더라도 RelayKit 브로드캐스트를 하지 않고 계속할 수 있습니다.

전력을 절약하고 포그라운드 애플리케이션의 반응성을 유지하기 위해 iOS는 한 번에 하나의 애플리케이션만 GPU에 액세스하도록 합니다. Amazon IVS 브로드캐스트 SDK는 여러 입력 소스 합성, 이미지 크기 조정, 이미지 인코딩 등 비디오 파이프라인의 여러 단계에서 GPU를 사용합니다. 브로드캐스팅 애플리케이션이 백그라운드에 있는 동안 SDK가 이러한 작업을 수행할 수 있다는 보장은 없습니다.

이것을 해결하려면 createAppBackgroundImageSource 메서드를 사용합니다. 메서드를 통해 SDK는 백그라운드에서 비디오와 오디오 모두를 계속 브로드캐스팅할 수 있습니다. IVSBackgroundImageSource를 반환하며 이 결과는 추가 finish 함수를 가진 정상 IVSCustomImageSource입니다. 백그라운드 이미지 소스에 제공된 모든 CMSampleBuffer는 원본 IVSVideoConfiguration에서 제공하는 프레임 속도로 인코딩됩니다. CMSampleBuffer에서 타임스탬프는 무시됩니다.

그다음 SDK는 그런 이미지를 크기 조정하고, 인코딩하고, 캐싱하여 애플리케이션이 백그라운드로 이동할 때 해당 피드를 자동으로 반복합니다. 애플리케이션을 포그라운드로 반환하면 연결된 이미지 디바이스가 다시 활성화되고 미리 인코딩된 스트림이 반복을 중지합니다.

이 프로세스를 실행 취소하려면 removeImageSourceOnAppBackgrounded를 사용하세요. SDK의 백그라운드 동작을 명시적으로 되돌리려는 경우가 아니라면 이것을 호출할 필요는 없습니다. 그렇지 않으면 IVSBroadcastSession을 할당 해제할 때 자동으로 정리됩니다.

참고: 세션이 실행되기 전에 브로드캐스트 세션 구성의 일부로 이 메서드를 호출할 것을 적극 권장합니다. 해당 메서드는 비디오를 인코딩해 비용이 많이 들기 때문에 메서드가 실행되는 동안 라이브 브로드캐스트 성능이 저하될 수 있습니다.

예: 백그라운드 비디오에 대한 정적 이미지 생성

백그라운드 소스에 단일 이미지를 제공하면 해당 정적 이미지의 전체 GOP가 생성됩니다.

다음은 CIImage를 사용하는 예입니다.

// Create the background image source guard let source = session.createAppBackgroundImageSource(withAttemptTrim: true, onComplete: { error in print("Background Video Generation Done - Error: \(error.debugDescription)") }) else { return } // Create a CIImage of the color red. let ciImage = CIImage(color: .red) // Convert the CIImage to a CVPixelBuffer let attrs = [ kCVPixelBufferCGImageCompatibilityKey: kCFBooleanTrue, kCVPixelBufferCGBitmapContextCompatibilityKey: kCFBooleanTrue, kCVPixelBufferMetalCompatibilityKey: kCFBooleanTrue, ] as CFDictionary var pixelBuffer: CVPixelBuffer! CVPixelBufferCreate(kCFAllocatorDefault, videoConfig.width, videoConfig.height, kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, attrs, &pixelBuffer) let context = CIContext() context.render(ciImage, to: pixelBuffer) // Submit to CVPixelBuffer and finish the source source.add(pixelBuffer) source.finish()

또는 단색 CIImage를 만드는 대신 번들 이미지를 사용할 수 있습니다. 여기에 표시된 유일한 코드는 UIImage를 CIImage로 변환하여 이전 샘플과 함께 사용하는 방법입니다.

// Load the pre-bundled image and get it’s CGImage guard let cgImage = UIImage(named: "image")?.cgImage else { return } // Create a CIImage from the CGImage let ciImage = CIImage(cgImage: cgImage)

예: AV가 포함된 비디오 AssetImageGenerator

AVAssetImageGenerator를 사용하여 HLS 스트림 AVAsset을 제외한 AVAsset으로부터 CMSampleBuffers를 생성할 수 있습니다.

// Create the background image source guard let source = session.createAppBackgroundImageSource(withAttemptTrim: true, onComplete: { error in print("Background Video Generation Done - Error: \(error.debugDescription)") }) else { return } // Find the URL for the pre-bundled MP4 file guard let url = Bundle.main.url(forResource: "sample-clip", withExtension: "mp4") else { return } // Create an image generator from an asset created from the URL. let generator = AVAssetImageGenerator(asset: AVAsset(url: url)) // It is important to specify a very small time tolerance. generator.requestedTimeToleranceAfter = .zero generator.requestedTimeToleranceBefore = .zero // At 30 fps, this will generate 4 seconds worth of samples. let times: [NSValue] = (0...120).map { NSValue(time: CMTime(value: $0, timescale: CMTimeScale(config.video.targetFramerate))) } var completed = 0 let context = CIContext(options: [.workingColorSpace: NSNull()]) // Create a pixel buffer pool to efficiently feed the source let attrs = [ kCVPixelBufferPixelFormatTypeKey: kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, kCVPixelBufferCGImageCompatibilityKey: kCFBooleanTrue, kCVPixelBufferCGBitmapContextCompatibilityKey: kCFBooleanTrue, kCVPixelBufferMetalCompatibilityKey: kCFBooleanTrue, kCVPixelBufferWidthKey: videoConfig.width, kCVPixelBufferHeightKey: videoConfig.height, ] as CFDictionary var pool: CVPixelBufferPool! CVPixelBufferPoolCreate(kCFAllocatorDefault, nil, attrs, &pool) generator.generateCGImagesAsynchronously(forTimes: times) { requestTime, image, actualTime, result, error in if let image = image { // convert to CIImage then CVpixelBuffer let ciImage = CIImage(cgImage: image) var pixelBuffer: CVPixelBuffer! CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pool, &pixelBuffer) context.render(ciImage, to: pixelBuffer) source.add(pixelBuffer) } completed += 1 if completed == times.count { // Mark the source finished when all images have been processed source.finish() } }

AVPlayerAVPlayerItemVideoOutput을 사용하여 CVPixelBuffers를 생성할 수 있습니다. 그러나 이를 위해서 CADisplayLink를 사용해야 하고, 실시간에 가깝게 실행하는 반면 AVAssetImageGenerator는 프레임을 훨씬 빠르게 처리할 수 있습니다.

제한 사항

애플리케이션이 백그라운드로 이동한 후 일시 중단되는 것을 방지하려면 백그라운드 오디오 권한 부여가 필요합니다.

createAppBackgroundImageSource를 완료하려면 GPU에 액세스해야 하므로 애플리케이션이 포그라운드에 있는 동안에만 호출할 수 있습니다.

createAppBackgroundImageSource는 항상 전체 GOP로 인코딩합니다. 예를 들어 키프레임 간격이 2초(기본값)이고 30fps로 실행 중인 경우 60프레임의 배수를 인코딩합니다.

  • 60프레임 미만이 제공되면 트림 옵션의 값에 관계없이 60프레임에 도달할 때까지 마지막 프레임이 반복됩니다.

  • 60프레임 이상이 제공되고 트림 옵션이 true면 마지막 N 프레임이 삭제됩니다. 여기서 N은 제출된 총 프레임 수의 나머지 부분을 60으로 나눈 값입니다.

  • 60프레임 이상이 제공되고 트림 옵션이 false면 60프레임의 다음 배수에 도달할 때까지 마지막 프레임이 반복됩니다.