IVS 실시간 스트리밍 최적화
IVS 실시간 스트리밍을 사용하여 비디오를 스트리밍하고 볼 때 사용자가 최상의 경험을 할 수 있도록 현재 제공되는 기능을 사용하여 경험의 일부를 개선하거나 최적화할 수 있는 몇 가지 방법이 있습니다.
소개
사용자 경험의 품질을 최적화할 때 사용자가 원하는 환경을 고려하는 것이 중요하며, 이는 사용자가 시청하는 콘텐츠와 네트워크 상태에 따라 달라질 수 있습니다.
이 가이드에서는 스트림 게시자 또는 스트림 구독자인 사용자에게 초점을 맞추고 해당 사용자가 원하는 작업과 경험을 고려합니다.
IVS SDK를 사용하면 스트림의 최대 비트 전송률, 프레임 속도 및 해상도를 구성할 수 있습니다. 게시자에게 네트워크 정체가 발생하면 SDK는 비트 전송률, 프레임 속도 및 해상도를 낮추어 비디오 품질을 자동으로 조정하고 낮춥니다. Android 및 iOS에서는 정체가 발생할 때 성능 저하 기본 설정을 선택할 수 있습니다. 동시 방송으로 계층화된 인코딩을 활성화하거나 기본 구성을 유지하거나 관계없이 동일한 동작이 적용됩니다.
적응형 스트리밍: 동시 방송을 사용한 계층화된 인코딩
이 기능은 다음 클라이언트 버전에서만 지원됩니다.
-
iOS 및 Android 1.18.0 이상
-
웹 1.12.0 이상
IVS 실시간 Broadcast SDK를 사용할 때 게시자는 비디오의 여러 계층을 인코딩할 수 있으며, 구독자에게는 네트워크에 가장 적합한 품질로 자동으로 조정되거나 변경됩니다. 이를 동시 방송을 사용한 계층화된 인코딩이라고 합니다.
동시 방송을 사용한 계층화된 인코딩은 Android 및 iOS와 Chrome 및 Edge 데스크톱 브라우저(Windows 및 macOS용)에서 지원됩니다. 다른 브라우저에서는 계층화된 인코딩을 지원하지 않습니다.
아래 다이어그램에서 호스트는 3가지 비디오 품질(높음, 중간, 낮음)을 전송하고 있습니다. IVS는 사용 가능한 대역폭에 따라 각 시청자에게 최고 품질의 비디오를 전달합니다. 이는 각 시청자에게 최적의 경험을 제공합니다. 시청자 1의 네트워크 연결이 양호에서 불량으로 변경되면 IVS는 자동으로 시청자 1에게 더 낮은 품질의 비디오를 전송하기 시작하므로, 시청자 1은 가능한 최상의 품질로 스트림을 중단 없이 계속 시청할 수 있습니다.
기본 계층, 품질 및 프레임 속도
모바일 및 웹 사용자에게 제공되는 기본 품질과 계층은 다음과 같습니다.
| 모바일(Android, iOS) | 웹(Chrome) |
|---|---|
상위 계층 또는 사용자 지정:
|
상위 계층 또는 사용자 지정:
|
중간 계층: 없음(모바일에서 상위 계층 비트레이트와 하위 계층 비트레이트의 차이가 적기 때문에 필요 없음) |
중간 계층:
|
하위 계층:
|
하위 계층:
|
계층 해상도
중간 계층과 하위 계층의 해상도는 동일한 종횡비가 유지되도록 상위 계층에서 자동으로 스케일 다운됩니다.
중간 계층과 하위 계층은 해당 해상도가 위의 계층에 너무 가까우면 제외됩니다. 예를 들어, 구성된 해상도가 320x180인 경우 SDK에서는 해상도가 더 낮은 계층도 보내지 않습니다.
아래 표는 구성된 서로 다른 해상도에 대해 생성된 계층의 해상도를 보여줍니다. 나열된 값은 가로 방향이지만, 세로 콘텐츠의 경우 반대로 적용할 수 있습니다.
| 입력 해상도 | 출력 계층 해상도: 모바일 | 출력 계층 해상도: 웹 |
|---|---|---|
|
720p(1280x720) |
높음(1280x720) 낮음(320x180) |
높음(1280x720) 중간(640x360) 낮음(320x180) |
|
540p(960x540) |
높음(960x540) 낮음(320x180) |
높음(960x540) 낮음(320x180) |
|
360p(640x360) |
높음(640x360) 낮음(360x180) |
높음(640x360) 낮음(360x180) |
|
270p(480x270) |
높음(480x270) |
높음(480x270) |
|
180p(320x180) |
높음(320x180) |
높음(320x180) |
위에 매핑되지 않은 사용자 지정 입력 해상도는 다음과 같은 도구를 사용
동시 방송을 사용한 계층화된 인코딩 구성(게시자)
동시 방송을 사용한 계층화된 인코딩을 사용하려면 클라이언트에서 특성을 활성화해야 합니다. 활성화하면 게시자의 업로드 대역폭 사용량이 증가하여 시청자의 비디오 멈춤이 적어질 수 있다는 것을 알게 됩니다.
Android:
// Enable Simulcast StageVideoConfiguration config = new StageVideoConfiguration(); config.simulcast.setEnabled(true); ImageLocalStageStream cameraStream = new ImageLocalStageStream(frontCamera, config); // Other Stage implementation code
iOS:
// Enable Simulcast let config = IVSLocalStageStreamVideoConfiguration() config.simulcast.enabled = true let cameraStream = IVSLocalStageStream(device: camera, configuration: config) // Other Stage implementation code
웹
// Enable Simulcast let cameraStream = new LocalStageStream(cameraDevice, { simulcast: { enabled: true } }) // Other Stage implementation code
개별 계층 구성에 대한 자세한 내용은 각 브로드캐스트 SDK 가이드(Android, iOS, 웹)의 ‘계층형 인코딩(게시자) 구성’을 참조하세요.
동시 방송을 사용한 계층화된 인코딩 구성(구독자)
구독자가 수신하는 계층을 구성하려면 실시간 스트리밍 SDK 가이드의 '동시 방송을 사용한 계층화된 인코딩' 섹션을 참조하세요.
구독자 구성을 사용하면 InitialLayerPreference 항목을 정의할 수 있습니다. 이렇게 하면 처음에 전송되는 비디오의 품질과 preferredLayerForStream, 비디오 재생 중에 선택되는 계층이 무엇인지 결정됩니다. 계층이 변경되거나, 조정이 변경되거나, 계층이 선택될 때 이를 알리는 이벤트 및 스트리밍 방법이 있습니다.
스트리밍 구성
이 섹션에서는 비디오 및 오디오 스트림에 사용할 수 있는 기타 구성에 대해 알아봅니다.
스트림 비디오 비트레이트 변경
비디오 스트림의 비트레이트를 변경하려면 다음 구성 샘플을 사용합니다.
Android:
StageVideoConfiguration config = new StageVideoConfiguration(); // Update Max Bitrate to 1.5mbps config.setMaxBitrate(1500000); ImageLocalStageStream cameraStream = new ImageLocalStageStream(frontCamera, config); // Other Stage implementation code
iOS:
let config = IVSLocalStageStreamVideoConfiguration(); // Update Max Bitrate to 1.5mbps try! config.setMaxBitrate(1500000); let cameraStream = IVSLocalStageStream(device: camera, configuration: config); // Other Stage implementation code
웹
let cameraStream = new LocalStageStream(camera.getVideoTracks()[0], { // Update Max Bitrate to 1.5mbps or 1500kbps maxBitrate: 1500 }) // Other Stage implementation code
비디오 스트림 프레임 속도 변경
비디오 스트림의 프레임 속도를 변경하려면 다음 구성 샘플을 사용합니다.
Android:
StageVideoConfiguration config = new StageVideoConfiguration(); // Update target framerate to 10fps config.targetFramerate(10); ImageLocalStageStream cameraStream = new ImageLocalStageStream(frontCamera, config); // Other Stage implementation code
iOS:
let config = IVSLocalStageStreamVideoConfiguration(); // Update target framerate to 10fps try! config.targetFramerate(10); let cameraStream = IVSLocalStageStream(device: camera, configuration: config); // Other Stage implementation code
웹
// Note: On web it is also recommended to configure the framerate of your device from userMedia const camera = await navigator.mediaDevices.getUserMedia({ video: { frameRate: { ideal: 10, max: 10, }, }, }); let cameraStream = new LocalStageStream(camera.getVideoTracks()[0], { // Update Max Framerate to 10fps maxFramerate: 10 }) // Other Stage implementation code
오디오 비트레이트 및 스테레오 지원 최적화
오디오 스트림의 비트레이트 및 스테레오 설정을 변경하려면 다음 구성 샘플을 사용합니다.
웹
// Note: Disable autoGainControl, echoCancellation, and noiseSuppression when enabling stereo. const camera = await navigator.mediaDevices.getUserMedia({ audio: { autoGainControl: false, echoCancellation: false, noiseSuppression: false }, }); let audioStream = new LocalStageStream(camera.getAudioTracks()[0], { // Optional: Update Max Audio Bitrate to 96Kbps. Default is 64Kbps maxAudioBitrateKbps: 96, // Signal stereo support. Note requires dual channel input source. stereo: true }) // Other Stage implementation code
Android:
StageAudioConfiguration config = new StageAudioConfiguration(); // Update Max Bitrate to 96Kbps. Default is 64Kbps. config.setMaxBitrate(96000); AudioLocalStageStream microphoneStream = new AudioLocalStageStream(microphone, config); // Other Stage implementation code
iOS:
let config = IVSLocalStageStreamConfiguration(); // Update Max Bitrate to 96Kbps. Default is 64Kbps. try! config.audio.setMaxBitrate(96000); let microphoneStream = IVSLocalStageStream(device: microphone, config: config); // Other Stage implementation code
구독자 지터 버퍼 MinDelay 변경
구독 중인 참가자의 지터 버퍼 최소 지연을 변경하려면 사용자 지정 subscribeConfiguration을 사용할 수 있습니다. 재생이 시작되기 전에 저장되는 패킷 수가 지터 버퍼를 통해 결정됩니다. 최소 지연 시간은 저장해야 하는 최소 데이터 양의 대상을 나타냅니다. 최소 지연 시간을 변경하면 패킷 손실/연결 문제 발생 시 재생 복원력 향상에 도움이 될 수 있습니다.
지터 버퍼의 크기 증가 시 단점은 재생이 시작되기 전에 지연 시간도 증가한다는 점입니다. 최소 지연 시간을 늘리면 비디오 재생 시간에 영향을 미치는 대신 복원력이 향상됩니다. 참고로, 재생 중 최소 지연 시간을 늘리면 비슷한 효과가 발생합니다. 재생이 잠시 일시 중지되어 지터 버퍼가 채워질 수 있습니다.
복원력이 더 필요한 경우 재생을 시작하기 전에 최소 지연 시간 사전 설정을 MEDIUM으로 시작하고 구독 구성을 설정하는 것이 좋습니다.
참고로, 최소 지연 시간은 참가자가 구독 전용인 경우에만 적용됩니다. 참가자가 직접 게시하는 경우에는 최소 지연 시간이 적용되지 않습니다. 이는 여러 게시자가 추가 지연 시간 없이 서로 대화할 수 있도록 하기 위한 것입니다.
아래 예제에서는 의 최소 지연 시간 사전 설정으로 MEDIUM을 사용합니다. 가능한 모든 값은 SDK 참조 설명서를 참조하세요.
웹
const strategy = { subscribeConfiguration: (participant) => { return { jitterBuffer: { minDelay: JitterBufferMinDelay.MEDIUM } } // ... other strategy functions }
Android:
@Override public SubscribeConfiguration subscribeConfigrationForParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { SubscribeConfiguration config = new SubscribeConfiguration(); config.jitterBuffer.setMinDelay(JitterBufferConfiguration.JitterBufferDelay.MEDIUM()); return config; }
iOS:
func stage(_ stage: IVSStage, subscribeConfigurationForParticipant participant: IVSParticipantInfo) -> IVSSubscribeConfiguration { let config = IVSSubscribeConfiguration() try! config.jitterBuffer.setMinDelay(.medium()) return config }
단계 간 전환 시 비디오 시간 단축
애플리케이션을 사용하면 사용자가 라이브 스트림 피드를 탐색할 수 있습니다. 사용자는 한 단계에 조인하고, 다른 스트림으로 살짝 밀고, 현재 단계에서 나가고, 다음 단계에 빠르게 조인합니다. 각 단계 전환에 대해 새 연결을 생성하려면 새 네트워크 연결을 설정해야 하므로 비디오를 사용할 수 있을 때까지 걸리는 시간이 늘어날 수 있습니다.
RealTimeConnection을 사용하여 사용자가 스트림을 탐색하는 동안 하나의 네트워크 연결을 열어 둡니다. 연결을 한 번 생성하고, 선택적으로 첫 번째 단계 조인 전에 연결한 다음, 여러 단계에서 재사용합니다. 각 단계는 여전히 자체 참가자 토큰을 사용하고 자체 참가자, 스트림 및 수명 주기를 유지합니다.
애플리케이션 조정자, 탐색 컨트롤러 또는 뷰 모델과 같이 개별 단계보다 수명이 긴 객체에서 브라우징 세션 기간 동안 RealTimeConnection을 생성하고 유지합니다. 모든 단계 전환에 대해 생성하거나 연결을 해제하지 마십시오.
Android:
다음 예제에서는 Android 샘플에 사용되는 MainViewModel에 RealTimeConnection 수명 주기를 추가합니다. 뷰 모델은 Stage.Strategy 및 StageRenderer를 구현합니다.
private var realTimeConnection: RealTimeConnection? = null private var stage: Stage? = null private fun setupConnection(connectionToken: String) { try { val connection = RealTimeConnection(getApplication(), connectionToken) // Establish the shared transport before the first Stage join. connection.connect() realTimeConnection = connection } catch (error: BroadcastException) { Log.e("BasicRealTime", "Unable to create the RealTimeConnection", error) } } private fun joinStage(participantToken: String) { val realTimeConnection = realTimeConnection ?: return try { stage?.leave() stage?.release() val stage = Stage( getApplication(), participantToken, realTimeConnection, this ) stage.addRenderer(this) stage.join() this.stage = stage } catch (error: BroadcastException) { Log.e("BasicRealTime", "Unable to join the Stage", error) } } override fun onCleared() { stage?.leave() stage?.release() stage = null realTimeConnection?.disconnect() realTimeConnection?.release() realTimeConnection = null super.onCleared() }
connect() 호출은 선택 사항입니다. 단계에 조인할 때 연결이 열려 있지 않으면 SDK가 자동으로 연결을 엽니다. 다음 단계 조인 전에 connect()를 호출하여 연결을 미리 설정하고 비디오 시간을 단축합니다. 이 예제는 한 번에 한 스테이지에만 연결되지만 RealTimeConnection은 여러 스테이지에 동시에 연결할 수 있습니다.
애플리케이션에 RealTimeConnection이 더 이상 필요하지 않은 경우 release()를 호출합니다. 릴리스된 후에는 재사용할 수 없습니다.
iOS:
다음 예제에서는 사용자가 다단계 흐름을 시작할 때 애플리케이션이 연결 토큰을 획득하고, 사용자가 조인하는 각 단계에 대한 참가자 토큰을 획득한다고 가정합니다.
private var realTimeConnection: IVSRealTimeConnection? private var stage: IVSStage? private func setupConnection(connectionToken: String) throws { let connection = try IVSRealTimeConnection(token: connectionToken) // Establish the shared transport before the first Stage join. try connection.connect() realTimeConnection = connection } private func joinStage(participantToken: String) throws { guard let realTimeConnection else { return } stage?.leave() let stage = try IVSStage( token: participantToken, on: realTimeConnection, strategy: self ) stage.addRenderer(self) try stage.join() self.stage = stage } private func endStageTransitionFlow() { stage?.leave() stage = nil realTimeConnection?.disconnect() realTimeConnection = nil }
connect() 호출은 선택 사항입니다. 단계에 조인할 때 연결이 열려 있지 않으면 SDK가 자동으로 연결을 엽니다. 다음 단계 조인 전에 connect()를 호출하여 연결을 미리 설정하고 비디오 시간을 단축합니다. 이 예제는 한 번에 한 단계에만 연결되지만 IVSRealTimeConnection은 여러 단계에 동시에 연결할 수 있습니다.
단계에서 사용하는 동안 IVSRealTimeConnection에 대한 강력한 참조를 유지합니다. disconnect()를 호출하면 연결을 사용하는 모든 단계가 종료됩니다.
권장 최적화
| 시나리오 | 추천 |
|---|---|
| 프레젠테이션이나 슬라이드와 같이 텍스트나 느리게 움직이는 콘텐츠가 포함된 스트림 | 동시 방송을 사용한 계층화된 인코딩을 사용하거나 프레임 속도가 낮은 스트림을 구성할 수 있습니다. |
| 동작이 많거나 움직임이 많은 스트림 | 동시 방송을 사용한 계층화된 인코딩을 사용하세요. |
| 대화가 있거나 움직임이 적은 스트림 | 동시 방송을 사용한 계층화된 인코딩을 사용하거나 오디오 전용(실시간 스트리밍 Broadcast SDK 가이드: 웹, Android, iOS의 "참가자 구독" 참조)을 선택하세요. |
| 제한된 데이터로 스트리밍하는 사용자 | 동시 방송을 사용한 계층화된 인코딩을 사용하거나, 모든 사용자의 데이터 사용량을 줄이려면 프레임 속도를 낮추고, 비트레이트를 수동으로 낮추세요. |
| 사용자는 라이브 스트림 피드를 탐색하고 한 단계에서 다음 단계로 빠르게 이동합니다. | 브라우징 세션에 RealTimeConnection을 재사용합니다. 첫 번째 단계 조인 전에 connect()를 호출하여 연결을 미리 설정합니다. |