IVS Android Broadcast SDK를 사용한 게시 및 구독 | 실시간 스트리밍 - Amazon IVS

IVS Android Broadcast SDK를 사용한 게시 및 구독 | 실시간 스트리밍

이 문서에서는 IVS Real-Time Streaming Android Broadcast SDK를 사용하여 스테이지에 게시하고 구독하는 단계를 안내합니다.

개념

실시간 기능의 3가지 핵심 개념은 스테이지, 전략렌더러입니다. 설계 목표는 작동하는 제품을 구축하는 데 필요한 클라이언트 측 로직의 수를 최소화하는 것입니다.

단계

Stage 클래스는 호스트 애플리케이션과 SDK 사이의 주요 상호 작용 지점입니다. 스테이지 자체를 나타내며 스테이지에 참가하고 나가는 데 사용됩니다. 스테이지를 만들고 참가하려면 제어 플레인에서 유효하고 만료되지 않은 토큰 문자열(token으로 표시됨)이 필요합니다. 스테이지 참가 및 나가기는 간단합니다.

Stage stage = new Stage(context, token, strategy); try { stage.join(); } catch (BroadcastException exception) { // handle join exception } stage.leave();

Stage 클래스는 StageRenderer가 첨부될 수 있는 위치이기도 합니다.

stage.addRenderer(renderer); // multiple renderers can be added

Strategy

Stage.Strategy 인터페이스는 호스트 애플리케이션이 원하는 스테이지 상태를 SDK에 전달하는 방법을 제공합니다. shouldSubscribeToParticipant, shouldPublishFromParticipantstageStreamsToPublishForParticipant 함수를 구현해야 합니다. 모든 함수를 아래에서 설명합니다.

참가자 구독

Stage.SubscribeType shouldSubscribeToParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo);

원격 참가자가 스테이지에 참가하면 SDK는 호스트 애플리케이션에 해당 참가자의 원하는 구독 상태를 쿼리합니다. 옵션은 NONE, AUDIO_ONLYAUDIO_VIDEO입니다. 이 함수의 값을 반환할 때 호스트 애플리케이션은 게시 상태, 현재 구독 상태 또는 스테이지 연결 상태에 대해 걱정할 필요가 없습니다. AUDIO_VIDEO가 반환되는 경우 SDK는 구독 전에 원격 참가자가 게시할 때까지 기다리고, 프로세스 전반에 걸쳐 렌더러를 통해 호스트 애플리케이션을 업데이트합니다.

다음은 샘플 구현입니다.

@Override Stage.SubscribeType shouldSubscribeToParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { return Stage.SubscribeType.AUDIO_VIDEO; }

항상 모든 참가자가 서로 보기를 원하는 호스트 애플리케이션(예: 비디오 채팅 애플리케이션)을 위한 이 기능의 전체 구현입니다.

고급 구현도 가능합니다. ParticipantInfouserInfo 속성을 사용하여 서버에서 제공하는 특성을 기반으로 참가자를 선택적으로 구독합니다.

@Override Stage.SubscribeType shouldSubscribeToParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { switch(participantInfo.userInfo.get(“role”)) { case “moderator”: return Stage.SubscribeType.NONE; case “guest”: return Stage.SubscribeType.AUDIO_VIDEO; default: return Stage.SubscribeType.NONE; } }

이를 통해 중재자가 직접 보거나 듣지 않고 모든 게스트를 모니터링할 수 있는 스테이지를 만들 수 있습니다. 호스트 애플리케이션은 추가 비즈니스 로직을 사용하여 중재자가 서로를 볼 수는 있지만 게스트에게는 보이지 않도록 할 수 있습니다.

참가자 구독 구성

SubscribeConfiguration subscribeConfigurationForParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo);

원격 참가자를 구독 중인 경우(참가자 구독 참조) SDK에서는 해당 참가자의 사용자 지정 구독 구성에 대한 호스트 애플리케이션을 쿼리합니다. 이 구성은 선택 사항이며, 호스트 애플리케이션에서 구독자 동작의 특정 측면을 제어할 수 있습니다. 구성할 수 있는 항목에 대한 내용은 SDK 참조 설명서의 SubscribeConfiguration을 참조하세요.

다음은 샘플 구현입니다.

@Override public SubscribeConfiguration subscribeConfigrationForParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { SubscribeConfiguration config = new SubscribeConfiguration(); config.jitterBuffer.setMinDelay(JitterBufferConfiguration.JitterBufferDelay.MEDIUM()); return config; }

구독한 모든 참가자의 지터-버퍼 최소 지연이 이 구현을 통해 MEDIUM 사전 설정으로 업데이트됩니다.

shouldSubscribeToParticipant와 마찬가지로 고급 구현도 가능합니다. 주어진 ParticipantInfo를 사용하여 특정 참가자에 대한 구독 구성을 선택적으로 업데이트할 수 있습니다.

기본 동작을 사용하는 것이 좋습니다. 변경하려는 특정 동작이 있는 경우에만 사용자 지정 구성을 지정합니다.

게시

boolean shouldPublishFromParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo);

스테이지에 연결되면 SDK는 호스트 애플리케이션을 쿼리하여 특정 참가자가 게시해야 하는지 여부를 확인합니다. 이는 제공된 토큰을 기반으로 게시할 권한이 있는 로컬 참가자에게만 호출됩니다.

다음은 샘플 구현입니다.

@Override boolean shouldPublishFromParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { return true; }

이는 사용자가 항상 게시하기를 원하는 표준 비디오 채팅 애플리케이션에 대한 구현입니다. 오디오 및 비디오를 음소거 또는 음소거 해제하여 즉시 숨기거나 보기/듣기가 가능하도록 할 수 있습니다. (게시/게시 취소를 사용할 수도 있지만 속도가 훨씬 느립니다. 음소거/음소거 해제는 가시성을 자주 변경하는 것이 바람직한 사용 사례에 적합합니다.)

게시할 스트림 선택

@Override List<LocalStageStream> stageStreamsToPublishForParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo); }

게시할 때 이를 사용하여 게시해야 하는 오디오 및 비디오 스트림을 결정합니다. 이에 대해서는 미디어 스트림 게시에서 자세히 설명합니다.

전략 업데이트

전략은 동적이어야 합니다. 위 함수 중에서 반환되는 값은 언제든지 변경될 수 있습니다. 예를 들어 호스트 애플리케이션이 최종 사용자가 버튼을 탭할 때까지 게시하지 않으려는 경우, shouldPublishFromParticipant에서 변수를 반환할 수 있습니다(예: hasUserTappedPublishButton). 최종 사용자의 상호 작용에 따라 변수가 변경되면 stage.refreshStrategy()를 호출하여 SDK에 최신 값에 대한 전략을 쿼리하고 변경된 사항만 적용하도록 신호를 보냅니다. SDK에서 shouldPublishFromParticipant 값이 변경된 것을 관찰하면 게시 프로세스가 시작됩니다. SDK 쿼리와 모든 함수가 이전과 동일한 값을 반환하는 경우 refreshStrategy 호출 시 스테이지가 수정되지 않습니다.

shouldSubscribeToParticipant의 반환 값이 AUDIO_VIDEO에서 AUDIO_ONLY로 변경된 경우 이전에 비디오 스트림이 존재했다면 반환된 값이 변경된 모든 참가자에 대한 비디오 스트림이 제거됩니다.

일반적으로 스테이지는 전략을 사용하여 이전 전략과 현재 전략 간의 차이를 가장 효율적으로 적용하므로 호스트 애플리케이션에서 이를 올바르게 관리하는 데 필요한 모든 상태에 대해 걱정할 필요가 없습니다. 이로 인해 stage.refreshStrategy() 호출은 전략이 변경되지 않는 한 아무 소용도 없으므로 소모량이 적은 작업이라고 생각하면 됩니다.

렌더러

StageRenderer 인터페이스는 호스트 애플리케이션에 스테이지 상태를 전달하는 방법을 제공합니다. 호스트 애플리케이션의 UI 업데이트는 대체로 렌더러에서 제공하는 이벤트에 의해 전적으로 이루어질 수 있습니다. 렌더러는 다음과 같은 함수를 제공합니다.

void onParticipantJoined(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo); void onParticipantLeft(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo); void onParticipantPublishStateChanged(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull Stage.PublishState publishState); void onParticipantSubscribeStateChanged(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull Stage.SubscribeState subscribeState); void onStreamsAdded(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull List<StageStream> streams); void onStreamsRemoved(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull List<StageStream> streams); void onStreamsMutedChanged(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull List<StageStream> streams); void onError(@NonNull BroadcastException exception); void onConnectionStateChanged(@NonNull Stage stage, @NonNull Stage.ConnectionState state, @Nullable BroadcastException exception);

대부분의 메서드에서 해당 StageParticipantInfo가 제공됩니다.

렌더러에서 제공하는 정보가 전략의 반환 값에 영향을 미칠 것으로 예상되지는 않습니다. 예를 들어, shouldSubscribeToParticipant의 반환 값은 onParticipantPublishStateChanged가 호출될 때 변경되지 않을 것으로 예상됩니다. 호스트 애플리케이션이 특정 참가자를 구독하려는 경우 해당 참가자의 게시 상태와 무관하게 원하는 구독 유형을 반환해야 합니다. SDK는 원하는 전략 상태가 스테이지 상태를 기반을 정확한 시간에 실행되도록 하는 역할을 합니다.

StageRenderer는 스테이지 클래스에 연결될 수 있습니다.

stage.addRenderer(renderer); // multiple renderers can be added

게시 참가자만 onParticipantJoined를 트리거하고, 참가자가 게시를 중단하거나 스테이지 세션에서 나갈 때마다 onParticipantLeft가 트리거됩니다.

미디어 스트림 게시

내장 마이크 및 카메라와 같은 로컬 디바이스는 DeviceDiscovery를 통해 검색됩니다. 다음은 전면 카메라와 기본 마이크를 선택한 다음 SDK에 게시될 LocalStageStreams로 반환하는 예제입니다.

DeviceDiscovery deviceDiscovery = new DeviceDiscovery(context); List<Device> devices = deviceDiscovery.listLocalDevices(); List<LocalStageStream> publishStreams = new ArrayList<LocalStageStream>(); Device frontCamera = null; Device microphone = null; // Create streams using the front camera, first microphone for (Device device : devices) { Device.Descriptor descriptor = device.getDescriptor(); if (!frontCamera && descriptor.type == Device.Descriptor.DeviceType.Camera && descriptor.position = Device.Descriptor.Position.FRONT) { front Camera = device; } if (!microphone && descriptor.type == Device.Descriptor.DeviceType.Microphone) { microphone = device; } } ImageLocalStageStream cameraStream = new ImageLocalStageStream(frontCamera); AudioLocalStageStream microphoneStream = new AudioLocalStageStream(microphoneDevice); publishStreams.add(cameraStream); publishStreams.add(microphoneStream); // Provide the streams in Stage.Strategy @Override @NonNull List<LocalStageStream> stageStreamsToPublishForParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { return publishStreams; }

참가자 표시 및 제거

구독이 완료되면 렌더러의 onStreamsAdded 함수를 통해 StageStream 객체 배열을 받게 됩니다. ImageStageStream에서 미리 보기를 검색할 수 있습니다.

ImagePreviewView preview = ((ImageStageStream)stream).getPreview(); // Add the view to your view hierarchy LinearLayout previewHolder = findViewById(R.id.previewHolder); preview.setLayoutParams(new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT)); previewHolder.addView(preview);

AudioStageStream에서 오디오 수준 통계를 검색할 수 있습니다.

((AudioStageStream)stream).setStatsCallback((peak, rms) -> { // handle statistics });

참가자가 게시를 중단하거나 참가자 구독이 취소되면 제거된 스트림과 함께 onStreamsRemoved 함수가 호출됩니다. 호스트 애플리케이션은 이를 신호로 사용하여 보기 계층 구조에서 참가자의 비디오 스트림을 제거해야 합니다.

onStreamsRemoved는 다음을 포함하여 스트림이 제거될 수 있는 모든 시나리오에서 호출됩니다.

  • 원격 참가자가 게시를 중단합니다.

  • 로컬 디바이스가 구독을 취소하거나 구독을 AUDIO_VIDEO에서 AUDIO_ONLY로 변경합니다.

  • 원격 참가자가 스테이지를 나갑니다.

  • 로컬 참가자가 스테이지를 나갑니다.

모든 시나리오에서 onStreamsRemoved가 호출되므로 원격 또는 로컬 나가기 작업 중에 UI에서 참가자를 제거하는 사용자 지정 비즈니스 로직이 필요하지 않습니다.

미디어 스트림 음소거 및 음소거 해제

LocalStageStream 객체에는 스트림의 음소거 여부를 제어하는 setMuted 함수가 있습니다. 이 함수는 streamsToPublishForParticipant 전략 함수에서 반환되기 전이나 후에 스트림에서 호출할 수 있습니다.

중요: refreshStrategy 호출 이후 streamsToPublishForParticipant에 의해 새 LocalStageStream 객체 인스턴스가 반환되면 새 스트림 객체의 음소거 상태가 스테이지에 적용됩니다. 새 LocalStageStream 인스턴스를 만들 때는 예상되는 음소거 상태가 유지되도록 주의해야 합니다.

원격 참가자 미디어 음소거 상태 모니터링

참가자가 비디오 또는 오디오 스트림의 음소거 상태를 변경할 때 변경된 스트림 목록과 함께 렌더러 onStreamMutedChanged 함수가 호출됩니다. StageStreamgetMuted 메서드를 사용하여 UI를 적절히 업데이트합니다.

@Override void onStreamsMutedChanged(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo, @NonNull List<StageStream> streams) { for (StageStream stream : streams) { boolean muted = stream.getMuted(); // handle UI changes } }

WebRTC 통계 가져오기

게시 스트림 또는 구독 스트림에 대한 최신 WebRTC 통계를 가져오려면 StageStream에서 requestRTCStats를 사용합니다. 수집이 완료되면 StageStream에서 설정할 수 있는 StageStream.Listener를 통해 통계를 받습니다.

stream.requestRTCStats(); @Override void onRTCStats(Map<String, Map<String, String>> statsMap) { for (Map.Entry<String, Map<String, string>> stat : statsMap.entrySet()) { for(Map.Entry<String, String> member : stat.getValue().entrySet()) { Log.i(TAG, stat.getKey() + “ has member “ + member.getKey() + “ with value “ + member.getValue()); } } }

참가자 특성 가져오기

CreateParticipantToken 엔드포인트 요청에서 특성을 지정하는 경우 ParticipantInfo 속성에서 특성을 볼 수 있습니다.

@Override void onParticipantJoined(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { for (Map.Entry<String, String> entry : participantInfo.userInfo.entrySet()) { Log.i(TAG, “attribute: “ + entry.getKey() + “ = “ + entry.getValue()); } }

백그라운드에서 세션 계속하기

앱이 백그라운드로 전환되면 게시를 중단하거나 다른 원격 참가자의 오디오만 구독하고 싶을 수 있습니다. 이렇게 하려면 Strategy 구현을 업데이트하여 게시를 중단하고 AUDIO_ONLY를 구독합니다(또는 해당하는 경우 NONE).

// Local variables before going into the background boolean shouldPublish = true; Stage.SubscribeType subscribeType = Stage.SubscribeType.AUDIO_VIDEO; // Stage.Strategy implementation @Override boolean shouldPublishFromParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { return shouldPublish; } @Override Stage.SubscribeType shouldSubscribeToParticipant(@NonNull Stage stage, @NonNull ParticipantInfo participantInfo) { return subscribeType; } // In our Activity, modify desired publish/subscribe when we go to background, then call refreshStrategy to update the stage @Override void onStop() { super.onStop(); shouldPublish = false; subscribeTpye = Stage.SubscribeType.AUDIO_ONLY; stage.refreshStrategy(); }

동시 방송을 사용한 계층화된 인코딩 활성화/비활성화

미디어 스트림을 게시할 때 SDK는 고품질 및 저품질 비디오 스트림을 전송하므로 원격 참가자는 다운링크 대역폭이 제한되어 있어도 스트림을 구독할 수 있습니다. 동시 방송을 사용한 계층화된 인코딩이 기본적으로 켜져 있습니다. StageVideoConfiguration.Simulcast 클래스를 사용하여 이를 비활성화할 수 있습니다.

// Disable Simulcast StageVideoConfiguration config = new StageVideoConfiguration(); config.simulcast.setEnabled(false); ImageLocalStageStream cameraStream = new ImageLocalStageStream(frontCamera, config); // Other Stage implementation code

비디오 구성 제한

SDK는 StageVideoConfiguration.setSize(BroadcastConfiguration.Vec2 size)를 사용하는 세로 모드 또는 가로 모드 강제 사용을 지원하지 않습니다. 세로 방향에서는 작은 치수가 너비로 사용되고 가로 방향에서는 높이가 너비로 사용됩니다. 즉, setSize에 대한 다음 두 호출은 비디오 구성에 동일한 영향을 미칩니다.

StageVideo Configuration config = new StageVideo Configuration(); config.setSize(BroadcastConfiguration.Vec2(720f, 1280f); config.setSize(BroadcastConfiguration.Vec2(1280f, 720f);

네트워크 문제 처리

로컬 디바이스의 네트워크 연결이 끊어지면 SDK는 사용자 작업 없이 내부적으로 재연결을 시도합니다. SDK에서 재연결이 성공적이지 않아 사용자 작업이 필요한 경우도 있습니다. 네트워크 연결 끊김과 관련된 두 가지 주요 오류가 있습니다.

  • 오류 코드 1400, 메시지: “알 수 없는 네트워크 오류로 인해 PeerConnection이 손실됨”

  • 오류 코드 1300, 메시지: “재시도 횟수가 모두 소진됨”

첫 번째 오류가 수신되었지만 두 번째 오류는 수신되지 않은 경우 SDK가 여전히 스테이지에 연결되어 있으며 자동으로 연결 재설정을 시도합니다. 예방 조치로 전략 메서드의 반환 값을 변경하지 않고 refreshStrategy를 호출하여 수동 재연결 시도를 트리거할 수 있습니다.

두 번째 오류가 수신되면 SDK의 재연결 시도가 실패하고 로컬 디바이스가 더 이상 스테이지에 연결되지 않습니다. 이 경우 네트워크 연결이 다시 설정된 후 join을 호출하여 스테이지에 다시 참여해 보세요.

일반적으로 스테이지에 성공적으로 참가한 후 오류가 발생하면 SDK가 연결 재설정에 실패했음을 나타냅니다. 새 Stage 객체를 만들고 네트워크 상태가 개선되면 참가를 시도합니다.

Bluetooth 마이크 사용

Bluetooth 마이크 장치를 사용하여 게시하기 위해서는 Bluetooth SCO 연결을 시작해야 합니다.

Bluetooth.startBluetoothSco(context); // Now bluetooth microphones can be used … // Must also stop bluetooth SCO Bluetooth.stopBluetoothSco(context);