Back to Blog

다중 플랫폼으로 다중 사용자와의 실시간 스트리밍

Live Streaming to Multiple Platforms with Multiple Users

다른 크리에이터와 협업하는 라이브 스트리밍을 위한 꽤 좋은 솔루션들이 있습니다. 하지만 저와 같은 분이라면 아마도 가격이 조금 너무 비싸다고 생각하시고, 더 맞춤형으로 설정할 수 있었으면 좋겠다고 생각하실 것입니다. 다행히 아고라가 존재하며, 이와 같은 서비스를 직접 구축할 수 있습니다!

필수 조건

이 프로젝트가 매우 크기 때문에, 가장 중요한 개념만 다루기 위해 단계별로 나누어 설명하겠습니다. 전체 코드를 원하신다면 여기에서 확인할 수 있습니다: https://github.com/tadaspetra/flutter-projects/tree/main/streamer

앱 개요

주요 내용을 다루기 전에, 우리가 달성하려는 목표와 그 방법을 살펴보겠습니다. 이 애플리케이션의 목표는 간단합니다: 호스트 애플리케이션을 통해 사용자들이 스트리밍 플랫폼에 비디오를 스트리밍하고, 사용자를 관리하며 스트리밍 플랫폼에 전송되는 내용을 제어할 수 있도록 하는 것입니다.

Live Streaming To Multiple Platforms With Multiple Users Agora screenshot 1

응용 프로그램의 흐름은 최대한 간단합니다. 메인 화면에서 시작하여 참여하려는 채널과 이름을 입력합니다. 그 다음 두 가지 옵션이 있습니다: 통화 관리자(Director)로 참여하거나 통화 내 참가자(Participant)로 참여할 수 있습니다:

  • 관리자(Director): 모든 사용자를 관리하고 스트림이 전송되는 위치와 방법을 제어합니다
  • 참가자(Participant): 통화에 참여하며 협업을 위한 간단한 통화 인터페이스를 제공합니다

이 기사에서는 아고라의 기본 기능이나 작동 방식에 대해 설명하지 않습니다. Agora에 익숙하지 않다면 이 튜토리얼을 계속하기 전에 다음 자료를 참고하세요:

참가자 보기

Live Streaming To Multiple Platforms With Multiple Users Agora screenshot 2

이 튜토리얼은 참가자 화면에 많은 시간을 할애하지 않습니다. 여기서의 논리는 다른 유형의 비디오 통화와의 차이가 네 가지 큰 점을 제외하면 매우 유사합니다:

  • 사용자가 처음 참여하면 로비에 있으며, 이 때 카메라와 오디오가 꺼져 있습니다.
  • 호스트가 사용자를 무대로 불러들이면, 해당 사용자의 비디오와 오디오가 켜집니다.
  • 비디오와 오디오는 호스트가 제어할 수 있습니다.
  • 모든 참가자를 보는 대신, 참가자는 무대에 있는 사람들만 볼 수 있습니다.

장치를 사용하는 사용자는 일반적인 비디오 통화와의 차이를 거의 느끼지 못합니다. 유일한 추가 기능은 통화 참여 전에 보는 로비 화면입니다. 무대에 사람이 있다면, 현재 참가자는 무대에 있지 않더라도 그들을 볼 수 있습니다. 스트리밍 플랫폼으로 전송되는 출력 통화를 확인할 수 있지만 맞춤형 트랜스코딩은 적용되지 않습니다. 이제 모든 복잡한 작업은 Agora의 RTM 메시지를 통해 처리됩니다.

RTM 메시징

이것은 단순한 비디오 통화 애플리케이션이 아니기 때문에 감독자가 참가자를 제어할 수 있는 방법이 필요합니다. 이를 위해 agora_rtm 패키지를 사용합니다. 이 패키지는 채널 내 모든 참가자 간 실시간 데이터를 전송할 수 있습니다. agora_rtc_engine 패키지는 agora_rtm 위에 구축되었습니다. 차이점은 RTM은 어떤 데이터도 전송할 수 있지만, RTC는 비디오 및 오디오 데이터 전송을 쉽게 처리한다는 점입니다. 이 애플리케이션에서는 감독만 RTM 메시지를 전송할 수 있으며, 참가자는 수신만 가능합니다. 감독이 수행할 수 있는 세 가지 유형의 기능이 필요합니다:

  • 오디오 음소거 또는 음소거 해제.
  • 비디오 활성화 또는 비활성화.
  • 활성 사용자 목록 전송.

사용자를 음소거하려면 감독자가 채널 전체에 “mute uid” 형식의 RTM 메시지를 전송합니다. 여기서 “uid”는 음소거할 사용자의 특정 uid로 대체됩니다. 이 메시지를 수신한 참가자는 이 uid가 자신의 uid인지 확인합니다. 해당 경우 사용자는 자신을 음소거합니다. 음소거 해제, 비디오 비활성화 및 활성화는 동일한 방식으로 작동하지만, 키워드 “unmute uid”, “enable uid”, “disable uid”를 사용합니다.

활성 사용자는 조금 더 복잡합니다. 일반적으로 Agora를 사용할 경우 해당 통화 내 모든 방송자를 표시합니다. 하지만 이번 경우 일부 사용자는 로비에 있으며 시청자에게 표시되지 않아야 합니다. 이를 처리하기 위해 다시 RTM 메시지를 사용해 표시해야 할 모든 사용자를 전송합니다. 형식은 “activeUsers uid,uid,uid”이며, 여기서 “uid”는 활성 사용자의 특정 uid로 대체됩니다.

현재까지 참가자 관점에서 대부분의 내용을 다루었습니다. 이제 대부분의 마법이 일어나는 감독자(Director)로 전환하겠습니다.

Director Controller

이 앱의 감독자는 많은 기능과 추적해야 할 사항을 가지고 있습니다. 이를 체계적으로 관리하기 위해 Flutter용 인기 있는 상태 관리 솔루션인 riverpod를 사용할 것입니다.

Riverpod를 사용해본 적이 없다면 여기에서 시작하는 것이 좋습니다: https://www.youtube.com/watch?v=8qzip8tVmqU

여기서 정의한 DirectorControllerStateNotifierProvider가 됩니다. 이는 애플리케이션의 UI 부분과 비즈니스 로직을 분리하는 데 도움이 됩니다.

이 컨트롤러는 우리 코드의 나머지 부분에서 액세스할 수 있는 기능을 포함합니다. 이 기능에는 joinCall(), leaveCall(), toggleUserAudio(), addUserToLobby(), promoteToActiveUser(), startStream() 등이 포함되며, 이 외에도 많은 기능이 있습니다. 이 컨트롤러는 앱에서 추적해야 하는 모든 데이터를 저장합니다.

참가자들은 RTM 메시지만 수신하기 때문에, 디렉터는 RTM 메시지만 전송합니다.

디렉터가 RTM 메시지를 전송하려면 RTM을 사용하여 클라이언트와 채널을 설정해야 합니다. 이는 RTC 엔진 뒤에서 발생하는 과정과 매우 유사합니다. 클라이언트를 생성하고, 클라이언트에 로그인한 후 채널을 생성하고 채널에 참여해야 합니다. 이 작업이 완료되면 RTM 메시지를 전송할 준비가 됩니다. 참가자들도 동일한 작업을 수행해야 onMessageReceived 콜백에서 메시지를 수신할 수 있습니다.

메시지를 전송하려면 해당 채널에 제공되는 sendMessage 함수를 사용해야 합니다. 메시지를 올바르게 형식화하려면 다음을 사용하세요:

모든 다른 메시지에도 동일한 방법을 적용하세요. 예를 들어 “mute uid”, “enable uid”, “disable uid”, “activeUsers uid,uid,uid” 등이 있습니다.

이것이 사용자와 스트림을 관리하기 위해 필요한 인프라 세부 사항입니다. 이제 이 앱의 디렉터 부분 작동 방식에 대해 자세히 살펴보겠습니다. 다루게 될 주요 세 가지 기능은 다음과 같습니다:

  • 다른 사용자의 비디오 음소거 및 비활성화
  • 사용자를 무대(Stage)와 로비(Lobby) 사이 이동
  • 각 비디오를 트랜스코딩하고 스트리밍 플랫폼에 전송

비디오 음소거 및 비활성화

RTM 메시징을 통해 인프라가 모두 설정되었기 때문에 이 섹션은 단순해 보일 수 있지만, 실제로는 고려해야 할 요소와 동기화해야 할 부분이 많습니다.

디렉터 앱

  • 사용자의 오디오 음소거/음소거 해제.
  • 사용자의 비디오 비활성화/활성화.
  • 각 사용자의 오디오 및 비디오 현재 상태.
  • 사용자가 자신의 상태를 변경할 경우 업데이트.

참가자 앱

  • 자신의 오디오 음소거/음소거 해제.
  • 자신의 비디오 비활성화/활성화.
  • 디렉터로부터 오디오 음소거/음소거 해제.
  • 디렉터로부터 비디오 비활성화/활성화.
  • 오디오 및 비디오 현재 상태.

이 모든 작업을 동기화하려면 오디오를 제어하는 많은 부분이 필요합니다. 이를 처리하는 가장 좋은 방법은 다양한 시나리오를 살펴보는 것입니다:

  • 참가자가 오디오를 음소거/음소거 해제합니다. 참가자가 자신을 음소거하기로 결정하면  muteLocalAudioStream()를 호출하고 자신의 버튼 상태를 업데이트하여 음소거 상태임을 표시해야 합니다. 디렉터 측에서는 remoteAudioStateChanged 이벤트가 트리거되어 해당 사용자의 현재 상태를 업데이트해야 합니다.
  • 참가자가 비디오를 비활성화/활성화합니다. 위와 동일한 프로세스이지만 muteLocalVideoStream() 함수를 호출합니다. 디렉터 측의 이벤트는 remoteVideoStateChanged입니다.
  • 디렉터가 사용자를 음소거/음소거 해제합니다. 디렉터는 “mute uid” 또는 “unmute uid”를 포함한 RTM 메시지를 전송해야 합니다. 그러면 해당 uid를 가진 사용자는 자신이 음소거를 한 것처럼 동일한 실행을 따릅니다. 감독자는 다시 remoteAudioStateChanged이벤트가 트리거되는 것을 확인하고 로컬 상태를 업데이트할 수 있습니다.
  • 감독자가 비디오를 비활성화/활성화합니다. 음소거와 동일한 프로세스이지만, 스트림 메시지는 “enable uid” 또는 “disable uid”로 변경됩니다.

이 프로세스에 추가 복잡성을 추가해 보겠습니다.

스테이지와 로비

이 아이디어는 너무 복잡하지는 않지만 몇 가지 주의해야 할 점이 있습니다. 로비와 스테이지를 모두 볼 수 있는 유일한 사람은 감독입니다. DirectorController는 활성 사용자 목록과 로비 사용자 목록을 별도로 유지합니다.

일반적인 참가자 사용자의 흐름은 채널에 가입하면 로비에 직접 추가됩니다. 이후 감독은 완전히 통제권을 가지고 스테이지로 이동하거나 로비로 되돌릴 수 있습니다.스테이지로 이동하거나 로비로 되돌리는 흐름은 매우 유사합니다. 먼저 이전 목록(로비 또는 활성)에서 사용자를 제거한 후 다른 목록에 추가합니다. 그런 다음 RTM 메시지를 사용하여 새로운

activeUsers

목록을 모든 사용자에게 전송합니다.

그다지 복잡하지는 않지만, 여기서 복잡성이 발생합니다. 로비 사용자가 무대 사용자와 중복으로 대화하지 않도록 로비에 있을 때는 음소거되어야 합니다. 또한 로비에 있기 때문에 그들의 비디오에 추가 대역폭을 할당할 필요도 없습니다. 이 때문에 오디오 및 비디오 제어에 몇 가지 추가 시나리오를 추가해야 합니다:

  • 참가자가 채널에 처음 가입합니다.로비에 직접 추가되기 때문에 즉시 음소거되고 비디오가 비활성화되어야 합니다. 참가자가 채널에 가입할 때마다 자동으로 음소거됩니다.
  • 참가자가 무대로 이동합니다.무대로 이동되면 비디오와 오디오가 활성화되어 관객이 볼 수 있도록 해야 합니다. 이는 감독이 음소거를 해제하거나 비디오를 활성화하는 논리와 동일해야 합니다.
  • 참가자가 로비로 이동합니다.로비로 이동하면 비디오와 오디오가 활성화되어 청중이 볼 수 있도록 해야 합니다. 이는 감독이 음소거하거나 비디오를 비활성화하는 것과 동일한 논리를 따라야 합니다.

트랜스코딩

activeUser 목록이 앱의 감독자 섹션과 참가자 섹션과 동기화되면 마지막 단계는 스트리밍 플랫폼에 방송하는 것입니다. 이를 위해 먼저 모든 입력 비디오를 원하는 레이아웃으로 트랜스코딩한 후 Real-Time Messaging Protocol (RTMP)을 사용하여 스트림을 원하는 플랫폼에 게시하고 비공개로 전환합니다.

먼저 출력 비디오의 레이아웃을 정의해야 합니다. 이 경우 한 통화당 최대 8명까지 지원합니다. 하지만 이 개념을 원하는 만큼의 통화 참여자로 확장할 수 있습니다. 또한 스트림이 1080p 스트림임을 고려해 1920x1080 픽셀을 사용할 수 있습니다. 이 정보를 바탕으로 레이아웃은 다음과 같이 됩니다:

Live Streaming To Multiple Platforms With Multiple Users Agora screenshot 3

이 정보를 전송하기 위해 TranscodingUser 목록을 생성해야 합니다. 각 사용자 레이아웃을 해당 목록에 맞게 설정합니다. 목록에 추가된 후에는 해당 목록을 사용하여 LiveTranscoding 객체를 생성하고 RTCEngine에 이 레이아웃이 적용되도록 지시합니다.

스트림 레이아웃을 설정했으니 이제 이를 전송해야 합니다. 이 앱을 사용하면 여러 위치로 스트림을 전송할 수 있습니다. 이를 위해 스트림을 전송할 대상 URL이 필요합니다. YouTube의 경우 매우 간단합니다. 스트림 URL + 역슬래시 (“/”) + 스트림 키가 필요합니다. 이 정보는 라이브 스트리밍 대시보드에서 확인할 수 있습니다.

Twitch도 유사한 개념을 사용하며, 자세한 내용은 여기에서 확인할 수 있습니다: https://help.twitch.tv/s/article/guide-to-broadcast-health-and-using-twitch-inspector?language=en_UShttps://help.twitch.tv/s/article/guide-to-broadcast-health-and-using-twitch-inspector?language=en_US이제 모든 링크를 준비하셨다면, 모든 링크를 순차적으로 순회하며 addPublishUrl()RTCEngine에 호출하고, transcodingEnabled 매개변수를 true로 설정해야 합니다.

이제 모든 작업이 완료되었습니다! 스트림이 플랫폼에 표시되어야 합니다.

마지막으로, 메인 스테이지에 사용자가 추가되거나 제거될 때 트랜스코딩을 업데이트하고 스트림을 종료해야 합니다. 업데이트를 위해 트랜스코딩 레이아웃을 해당 내용으로 업데이트한 후 setLiveTranscoding()를 다시 호출합니다. 스트림을 제거하려면 removePublishUrl()를 호출합니다.

결론

이 앱이 조금 복잡해 보인다면, 그 이유는 실제로 복잡하기 때문입니다. 하지만 이와 같은 것을 MVP(최소 기능 제품)로 개발하는 데 몇 달이 걸리는 기업들도 있습니다. 그리고 설령 개발하더라도 SD-RTN이 제공하는 인프라와 신뢰성에 근접하지 못합니다. 이 앱은 매우 복잡하지만, Agora를 사용하면 실현 가능합니다.

이 앱의 코드는 여기에서 확인할 수 있습니다.

기타 자료

아고라 Flutter SDK 및 기타 사용 사례에 대해 자세히 알아보려면 개발자 가이드 여기를 참조하세요.

위에서 논의된 기능과 더 많은 기능에 대한 완전한 문서는 여기에서 확인할 수 있습니다.

또한 Agora Developer Slack Community에 참여해 주시기를 초대합니다.

RTE Telehealth 2023
Join us for RTE Telehealth - a virtual webinar where we’ll explore how AI and AR/VR technologies are shaping the future of healthcare delivery.

Learn more about Agora's video and voice solutions

Ready to chat through your real-time video and voice needs? We're here to help! Current Twilio customers get up to 2 months FREE.

Complete the form, and one of our experts will be in touch.

Try Agora for Free

Sign up and start building! You don’t pay until you scale.
Try for Free