현재 안드로이드 기기용 라이브 스트리밍 앱은 Twitch, Facebook Live, YouTube Live 등 다양한 옵션이 있으며, 얼굴 필터나 음성 변환기 같은 재미있는 기능을 제공합니다. 하지만 스스로 라이브 스트리밍 앱을 만들어본 적 있나요? 일부 기능이 너무 화려하거나 복잡해 보여서 망설이고 계신가요?
다행히 Agora Video SDK와 Banuba Face AR SDK를 사용하면 얼굴 필터를 포함한 안드로이드 비디오 스트리밍 앱을 빠르게 개발할 수 있습니다. 이 단계별 가이드에서는 안드로이드용 라이브 스트리밍 앱을 만드는 방법을 보여드리겠습니다.
필수 조건
- 자바와 안드로이드 SDK에 대한 기본에서 중간 수준의 이해
- Agora 계정
- Banuba Face AR 데모 앱
- Android Studio와 안드로이드 기기 2대
참고 사항: 자바/안드로이드 지식이 없어도 따라할 수 있지만, 자바/안드로이드의 기본 개념은 설명되지 않습니다.
개요
이 가이드는 Agora SDK를 사용하여 안드로이드 라이브 스트리밍 애플리케이션을 구축하는 단계를 설명합니다. 다음은 앱에 포함될 핵심 기능 목록입니다:
- 사용자(스트리머와 시청자)는 계정을 생성하고 로그인할 수 있습니다. 사용자 계정 정보는 Google Firebase의 Realtime Database에 저장됩니다.
- 사용자는 라이브 스트리밍을 호스트하기 위해 가상 방을 설정하고 스트리머가 될 수 있습니다.
- 사용자는 모든 라이브 스트림을 검색하고 가상 방에 시청자로 참여할 수 있습니다.
- 스트리머는 얼굴 필터 기능을 사용하여 가상 마스크나 애니메이션과 함께 스트리밍할 수 있습니다.
- 스트리머는 음성 변경기를 통해 목소리를 변경할 수 있습니다.
- 가상 방 내의 시청자는 모든 사용자가 볼 수 있는 텍스트 메시지를 보낼 수 있습니다.
- 사용자는 이름으로 다른 사용자를 검색하고 개인 메시지를 보낼 수 있습니다.
이 기사 참고용으로 실시간 비디오 스트리밍 안드로이드 데모 앱을 확인할 수 있습니다.
Banuba FaceAR 데모 앱 다운로드
실시간 비디오 스트리밍 기능을 중점으로 개발하기 위해 Banuba FaceAR 데모 앱을 기반으로 안드로이드 비디오 스트리밍 애플리케이션을 구축해 보겠습니다.
먼저, Banuba FaceAR 데모 앱을 다운로드하고 Android Studio에서 열습니다. Banuba의 온라인 안내를 따라 프로젝트를 설정합니다.
참고: 프로젝트를 실행하려면 Banuba 클라이언트 토큰이 필요합니다. 자세한 정보는 info@banuba.com로 문의하세요.
설정이 완료되면 “app” 프로젝트를 실행하면 다음과 같은 화면이 표시되며 다양한 얼굴 필터 옵션이 제공됩니다:

이 화면은 스트리머가 라이브 스트리밍을 시작할 때 표시되는 화면입니다. 그러나 Banuba 데모는 로컬 카메라 뷰에만 얼굴 필터를 적용합니다. 이 화면을 시청자나 다른 방송자에게 전송하려면 Agora Video SDK가 유용합니다.
아고라 Video SDK 통합
프로젝트에 Agora Video SDK를 통합하려면 프로젝트의 /app/build.gradle 파일에 다음 줄을 추가해야 합니다.
//Agora RTC SDK for video call
implementation 'io.agora.rtc:full-sdk:3.3.0'
//Agora RTM SDK for chat messaging
implementation 'io.agora.rtm:rtm-sdk:1.2.2'
//Agora UIKit
implementation 'io.agora.uikit:agorauikit:2.0.1'
첫 번째 구현은 Agora Video SDK를 위한 것입니다. 두 번째는 Agora Real-Time Messaging SDK를 위한 것입니다. 마지막으로 Agora Android UIKit는 Agora Android UI 래퍼입니다. UIKit을 사용하면 비디오 통화나 비디오 방송을 시작하기 위해 몇 줄의 코드만 필요합니다. 이 가이드의 후반부에서 오디언스 뷰를 구현할 때 UIKit을 사용하는 방법을 시연하겠습니다.
프로젝트 권한 추가
다음으로 해야 할 일은 /app/src/main/AndroidManifest.xml 파일에 기기 액세스를 위해 필요한 권한을 추가하는 것입니다:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
...>
<uses-permission
android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.INTERNET"
/>
<uses-permission
android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission
android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission
android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- The Agora SDK requires Bluetooth permissions in case users are using Bluetooth devices. -->
<uses-permission android:name="android.permission.BLUETOOTH"
/>
<uses-permission
android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
...
</manifest>
Google Firebase 데이터베이스 설정
우리 애플리케이션은 사용자가 앱 내에서 다른 사용자와 검색 및 채팅을 할 수 있도록 설계되어 있으므로, 사용자의 계정 정보를 저장하기 위해 Firebase Realtime Database를 사용해야 합니다. Firebase Realtime Database에 애플리케이션을 연결하기 위한 단계는 다음과 같습니다:
- Android Studio에서 “Tools”를 클릭한 후 “Firebase”를 선택합니다.

2. 오른쪽에 Firebase 어시스턴트 탭이 표시됩니다. “실시간 데이터베이스”를 찾아 클릭한 후 “데이터 저장 및 검색”을 선택합니다.

3. 그런 다음 실시간 데이터베이스의 상세 페이지가 표시됩니다. “Firebase에 연결” 버튼을 클릭한 후 “앱에 실시간 데이터베이스 추가” 버튼을 클릭합니다. 화면에 표시되는 안내 사항이 있다면 따라주세요.

4. 이제 귀하의 애플리케이션이 Firebase 실시간 데이터베이스에 연결되었습니다. 마지막으로 해야 할 일은 Firebase 콘솔로 이동하여 데이터베이스 규칙을 변경하는 것입니다. Cloud Firestore 대신 Realtime Database를 선택해야 합니다. 읽기 및 쓰기 규칙을 “true”로 변경하여 데이터베이스에 액세스할 수 있도록 합니다.

사용자 로그인
먼저 첫 번째 페이지부터 애플리케이션을 구축해 보겠습니다. 대부분의 동영상 스트리밍 앱과 마찬가지로 우리 애플리케이션에도 사용자 로그인 페이지가 필요합니다. 그리고 그 모습은 다음과 같아야 합니다:

사용자가 “다음” 버튼을 클릭하면 사용자의 이름을 가져와 다음 활동인 HomeActivity로 전달해야 합니다. 다음은 해당 코드입니다.
public void onLoginNextClick(View view) {
EditText userNameEditText = findViewById(R.id.et_login_user_name);
String userName = userNameEditText.getText().toString();
if(userName == null || userName.equals("")) {
Toast.makeText(this, "user name cannot be empty", Toast.LENGTH_SHORT).show();
}else {
Intent intent = new Intent(this, HomeActivity.class);
intent.putExtra("userName", userName);
startActivity(intent);
}
}
홈 활동 만들기
HomeActivity에서 3개의 페이지로 구성된 하단 네비게이션 바를 만들고 각 페이지에 연결된 프래그먼트를 생성합니다. 사용자가 첫 번째 하단 네비게이션 메뉴를 탭하면 Live 프래그먼트로 이동합니다. 마찬가지로 두 번째 메뉴는 사용자를 Chat 프래그먼트로, 마지막 메뉴는 Other 프래그먼트로 이동시킵니다.
HomeActivity의 onCreate 메서드에서 이전 활동에서 전달된 사용자 이름을 가져와 Firebase 데이터베이스에 저장합니다.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
userName = getIntent().getStringExtra("userName");
saveUserOnFireDatabase(userName);
}
private void saveUserOnFireDatabase(String userName) {
mRef = FirebaseDatabase.getInstance().getReference("Users");
mRef.push();
mRef.child(userName).setValue(new DBUser(userName, false));
}
라이브 프래그먼트
라이브 프래그먼트는 모든 라이브 스트리머의 이름을 표시하며, 다른 사용자가 버튼을 클릭하여 스트리머의 가상 방에 참여할 수 있도록 합니다.

파편의 중앙에는 모든 라이브 스트리머를 표시하는 리사이클러 뷰가 있습니다. Firebase 데이터베이스에 ChildEventListener를 등록하여 스트리머 데이터를 가져올 수 있습니다.
childEventListener = new ChildEventListener() {
@Override
public void onChildAdded(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
DBUser result = dataSnapshot.getValue(DBUser.class);
if (!result.getName().equals(userName) && result.getStreaming() == true) {
streamerList.add(result);
}
}
...
};
mRef.orderByChild("name").addChildEventListener(childEventListener);
스트리머 이름 옆에는 사용자가 해당 가상 방에 참여할 수 있는 버튼이 있습니다. 이 버튼은 Intent를 통해 사용자를 관객 활동으로 안내합니다.
private void joinStream(String streamerName) {
Intent intent = new Intent(getActivity(), AudienceActivity.class);
intent.putExtra("streamerName", streamerName);
intent.putExtra("userName", userName);
startActivity(intent);
}
사용자가 하단의 “GO LIVE” 버튼을 클릭하면 Intent를 통해 MainActivity로 이동합니다. MainActivity는 위에서 논의한 Banuba 얼굴 필터 기능을 활성화하는 활동입니다.
@Override
public void onClick(View v) {
Intent intent = new Intent(getActivity(), MainActivity.class);
intent.putExtra("userName", userName);
startActivity(intent);
}
채팅 조각
채팅 조각은 사용자가 다른 사용자를 검색하고 그들에게 개인 메시지를 보낼 수 있는 공간입니다. 따라서 UI를 다음과 같이 설계해야 합니다:

로컬 카메라 뷰를 원격 사용자에게 전송
MainActivity 클래스를 열겠습니다. 이전 단계에서 이 활동은 로컬 카메라 뷰에 얼굴 필터 기능을 추가하는 용도임을 알고 있습니다. 여기서의 작업은 얼굴 필터가 적용된 로컬 카메라 뷰를 원격 사용자에게 전송하는 것입니다. 이로써 실시간 비디오 스트림을 받는 사용자는 렌더링된 필터 효과를 확인할 수 있습니다.
RtcEngine 초기화 및 채널 가입
onCreate 콜백 내부에 홈 활동에서 전달된 사용자 이름을 가져와 아고라 RtcEngine을 초기화합니다.
userName = getIntent().getStringExtra("userName");
try {
mRtcEngine = RtcEngine.create(getBaseContext(), getString(appID), mRtcEventHandler);
} catch (Exception e) {
Log.e(TAG, Log.getStackTraceString(e));
throw new RuntimeException("NEED TO check rtc sdk init fatal error\n" + Log.getStackTraceString(e));
}
mRtcEngine.enableVideo();
여기서 RtcEngine을 초기화할 때 appID를 매개변수로 입력해야 합니다. 매개변수에 appID를 얻으려면 다음 단계를 따르세요:
- Agora Console에서 Agora 프로젝트를 생성합니다.
- 왼쪽 탐색 패널의 Project Management 탭을 클릭합니다.
- “Create”를 클릭하고 화면에 표시되는 안내에 따라 프로젝트 이름을 설정하고 인증 메커니즘을 선택한 후 “Submit”을 클릭합니다.
- 프로젝트 관리 페이지에서 프로젝트의 App ID를 찾습니다.
mRtcEventHandler는 RtcEngine에서 발생하는 다양한 이벤트를 관리하는 핸들러입니다.
그런 다음 setExternalVideoSource를 호출하여 Agora 비디오 입력을 Banuba SDK에서 제공하는 카메라 프레임으로 외부 비디오 소스로 전환합니다.
mRtcEngine.setExternalVideoSource(true, false, true);
그 다음에는 RtcEngine 인스턴스에 joinChannel() 메서드를 호출하여 채널에 참여할 준비가 됩니다. channelName은 이전 활동에서 받은 사용자 이름입니다. 이 경우 각 스트리머는 로그인 시 입력한 사용자 이름으로 비디오 통화 채널을 시작합니다.
mRtcEngine.joinChannel(token, channelName, "Extra Info", 0);
참고: 이 가이드에서는 생산 환경에서 실행되는 모든 RTE 앱에 권장되는 토큰 인증을 구현하지 않습니다. Agora 플랫폼 내 토큰 기반 인증에 대한 자세한 내용은 다음 가이드를 참조하세요: https://bit.ly/3sNiFRs.
외부 비디오 프레임 푸시
우리는 이미 Agora RtcEngine에 외부 비디오 소스를 비디오 입력으로 사용할 것임을 알렸습니다. 그러나 아직 비디오 프레임을 제공하지 않았습니다.
먼저 BanubaSDKManager 인스턴스에서 startForwardingFrames()를 호출하면 onFrameRendered() 콜백이 트리거됩니다.
mSdkManager.startForwardingFrames();
이제 onFrameRendered() 콜백에서 로컬 카메라 뷰와 AR 얼굴 필터 뷰를 포함하는 카메라 프레임 데이터를 얻을 수 있습니다. 그런 다음 AgoraVideoFrame 인스턴스를 생성하고 이 정보를 전달합니다. Agora RtcEngine 인스턴스에 pushExternalVideoFrame()를 호출하고 매개변수에 AgoraVideoFrame 인스턴스를 전달합니다.
@Override
public void onFrameRendered(@NonNull Data data, int width, int height) {
byte[] arr = new byte[data.data.remaining()];
data.data.get(arr);
AgoraVideoFrame agoraVideoFrame = new AgoraVideoFrame();
agoraVideoFrame.buf = arr;
agoraVideoFrame.stride = width;
agoraVideoFrame.height = height;
agoraVideoFrame.format = AgoraVideoFrame.FORMAT_RGBA;
agoraVideoFrame.timeStamp = System.currentTimeMillis();
mRtcEngine.pushExternalVideoFrame(agoraVideoFrame);
data.close();
}
이제 이 멋진 얼굴 필터와 애니메이션을 동일한 RTC 채널에 있는 원격 사용자에게 전송할 수 있습니다.
테스트를 위해 Agora Web Showcase를 사용해 보세요. 앱 ID, 토큰 및 채널 이름을 입력하면 동영상을 확인할 수 있습니다.
음성 변경기 추가
라이브 동영상 스트리밍 경험을 더 재미있게 만들기 위해 안드로이드 애플리케이션에 음성 변경기를 추가할 수 있습니다. MainActivity에 버튼을 생성하고 해당 버튼의 onClick 메서드를 설정합니다. 사용자가 해당 버튼을 클릭하면 setLocalVoiceChanger를 호출하여 음성 변경기를 설정합니다.
public void onVoiceChangerClick(View view) {
mRtcEngine.setLocalVoiceChanger(VOICE_CHANGER_BABYGIRL);
Toast.makeText(this, "Voice Changer ON", Toast.LENGTH_SHORT).show();
}
다양한 음성 변환기를 선택할 수 있습니다. 여기서는 아기 소녀의 음성을 사용했습니다. 더 많은 음성 변환기 옵션을 확인하려면 setLocalVoiceChanger API를 참고하세요.
청중 보기 설정
청중 보기 설정은 더 간단합니다. Audience 활동으로 이동하여 Agora Android UIKit를 사용합니다.
UIKit을 사용하려면 onCreate() 메서드에서 이 코드를 호출하면 됩니다.
@Override
protected void onCreate(Bundle savedInstanceState) {
...
AgoraRTC.instance().bootstrap(this, appID, channelName);
setContentView(R.layout.group_template);
}
여기서 appID는 MainActivity에서 입력한 동일한 appID이며, channelName은 LiveFragment에서 전달받은 스트리머의 이름입니다.
자세한 내용은 UIKit Github를 방문하세요.
2줄의 코드만으로 비디오 통화를 시작할 수 있습니다!
그러나 우리 경우, 시청자 화면에 표시되도록 일부 맞춤 설정이 필요합니다. 먼저, 시청자가 채널로 비디오 및 오디오 스트림을 전송하지 않도록 해야 합니다.
AgoraRTC.instance().muteLocalVideoStream(true);
AgoraRTC.instance().muteLocalAudioStream(true);
또한, 원격 뷰(스트리머 뷰)를 항상 메인 화면에 표시하고 싶습니다. 이를 위해 IRtcEnginEventHandler 인스턴스를 등록해야 합니다.
IRtcEngineEventHandler eventHandler = new IRtcEngineEventHandler() {
@Override
public void onRemoteVideoStateChanged(final int uid, int state, int reason, int elapsed) {
super.onRemoteVideoStateChanged(uid, state, reason, elapsed);
if (state == REMOTE_VIDEO_STATE_STARTING) {
runOnUiThread(new Runnable() {
@Override
public void run() {
agoraView.setUID(uid);
}
});
}
}
};@Override
protected void onCreate(Bundle savedInstanceState) {
...
agoraView = findViewById(R.id.max_view);
AgoraRTC.instance().registerListener(eventHandler);
...
}
마지막으로 수행해야 할 맞춤 설정은 종료 버튼에 버튼 클릭 리스너를 추가하여 사용자가 종료 버튼을 클릭할 때 사용자를 이전 활동으로 리디렉션하는 것입니다.
endCallButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
현재까지 우리 비디오 스트리밍 애플리케이션은 사용자가 얼굴 필터 기능을 사용해 스트리밍을 진행하거나 시청자로 스트리밍을 시청할 수 있습니다. Agora RTM SDK를 사용해 텍스트 메시징 시스템을 추가하여 기능을 강화해 보겠습니다.
텍스트 메시징
스트리밍 애플리케이션에서의 텍스트 메시징은 두 부분으로 나눌 수 있습니다: 채널 메시징과 개인 메시징. 첫 번째는 채널에 메시지를 전송하며 채널 내 모든 사용자가 해당 메시지를 수신할 수 있으며, 두 번째는 사용자 간에 개인 메시지를 전송합니다.
채널 메시징
스트리머의 방에서 모든 시청자가 메시지를 전송할 수 있도록 하고자 합니다. 이를 위해 Agora Messaging SDK를 사용하여 RtmClient를 생성해야 합니다.
mRtmclient = RtmClient.createInstance(mContext, appID, new RtmClientListener() {
@Override
public void onConnectionStateChanged(int state, int reason){
for (RtmClientListener listener : mListenerList) {
listener.onConnectionStateChanged(state, reason);
}
}@Override
public void onMessageReceived(RtmMessage rtmMessage, String peerId) { ... }
});
여기서 우리는 Agora Video 엔진을 초기화할 때 사용한 동일한 appID를 전달합니다.
LiveFragment의 onCreateView 메서드에서 RtmClient에 로그인해야 합니다.
mRtmClient.login(null, userName, new io.agora.rtm.ResultCallback() {
@Override
public void onSuccess(Void aVoid) {
...
}
@Override
public void onFailure(ErrorInfo errorInfo) {
...
}
});
우리는 비디오 통화 채널에 가입할 때 사용한 것과 동일한 userName을 사용합니다. 이 경우 사용자는 동일한 채널 이름으로 RTM 채널과 RTC 채널에 가입하게 됩니다.
AudienceActivity의 onCreate() 메서드에서, 이전에 초기화한 mRtmClient에 createChannel()을 호출하여 RtmChannel 인스턴스를 생성합니다.
mRtmChannel = mRtmClient.createChannel(mChannelName, new MyChannelListener());
우리는 채팅방에서 어떤 멤버로부터 메시지를 수신할 때 해당 논리를 처리하기 위해 RtmChannelListener 인터페이스를 구현하고 콜백 메서드 onMessageReceived()를 가진 MyChannelListener를 정의해야 합니다.
class MyChannelListener implements RtmChannelListener {
@Override
public void onMessageReceived(final RtmMessage message, final RtmChannelMember fromMember) {
runOnUiThread(new Runnable() {
@Override
public void run() {
String account = fromMember.getUserId();
String msg = message.getText();
MessageBean messageBean = new MessageBean(account, msg, false);
messageBean.setBackground(getMessageColor(account));
mMessageBeanList.add(messageBean);
mMessageAdapter.notifyItemRangeChanged(mMessageBeanList.size(), 1);
mRecyclerView.scrollToPosition(mMessageBeanList.size() - 1);
}
});
}
...
}
그런 다음 RtmChannel 인스턴스에 join() 메서드를 호출하여 채널에 참여합니다.
mRtmChannel.join(new ResultCallback() {
@Override
public void onSuccess(Void responseInfo) { ... }@Override
public void onFailure(ErrorInfo errorInfo) { ... }
});
onSuccess() 콜백에서 RtmChannel 인스턴스의 getMembers() 메서드를 호출하여 채팅방의 멤버 수를 얻을 수 있습니다.
사용자가 텍스트 메시지를 입력하고 채팅 기록을 확인할 수 있도록 일부 UI 구성 요소를 추가해야 합니다. 시청자 레이아웃은 다음과 유사해야 합니다:

사용자가 “보내기” 버튼을 클릭하면, 우리는 RtmChannel 인스턴스의 sendMessage() 메서드를 호출하여 사용자의 메시지를 채널로 전송합니다.
private void sendChannelMessage(String content) {
// step 1: create a message
RtmMessage message = mRtmClient.createMessage();
message.setText(content);
// step 2: send message to channel
mRtmChannel.sendMessage(message, new ResultCallback<Void>() {
@Override
public void onSuccess(Void aVoid) {
...
}
@Override
public void onFailure(ErrorInfo errorInfo) {
...
}
});
}
개인 메시지
ChatFragment에서 사용자는 친구의 이름을 검색하여 친구를 찾고 그들에게 개인 텍스트 메시지를 보낼 수 있습니다.
친구 이름을 검색하려면 ChildEventListener를 등록하고 Firebase 데이터베이스에서 데이터를 가져와야 합니다.
childEventListener = new ChildEventListener() {
@Override
public void onChildAdded(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
DBUser result = dataSnapshot.getValue(DBUser.class);
showFriendText.setText(result.getName());
mRef.orderByChild("name").startAt(friendName).endAt(friendName + "\uf8ff").removeEventListener(childEventListener);
}
...
};
mRef.orderByChild("name").startAt(friendName).endAt(friendName + "\uf8ff").addChildEventListener(childEventListener);
개인 채팅 메시지를 보내는 방법은 그룹 메시지를 보내는 방법과 매우 유사합니다. 주의해야 할 몇 가지 차이가 있습니다:
- RTM 채널을 생성하지 않고도 메시지를 보낼 수 있습니다.
- RtmClientListener를 등록하고 onMessageReceived 콜백에서 메시지를 수신해야 합니다.
class MyRtmClientListener implements RtmClientListener {
@Override
public void onMessageReceived(final RtmMessage message, final String peerId) {
runOnUiThread(new Runnable() {
@Override
public void run() {
String content = message.getText();
if (peerId.equals(mPeerId)) {
MessageBean messageBean = new MessageBean(peerId, content,false);
messageBean.setBackground(getMessageColor(peerId));
mMessageBeanList.add(messageBean);
mMessageAdapter.notifyItemRangeChanged(mMessageBeanList.size(), 1);
mRecyclerView.scrollToPosition(mMessageBeanList.size() - 1);
} else {
MessageUtil.addMessageBean(peerId, content);
}
}
});
}
...
}
메시지를 전송할 때, 채널 메시지를 전송하기 위해 사용했던 sendMessage() 대신 sendMessageToPeer()를 호출하십시오.
mRtmClient.sendMessageToPeer(mPeerId, message, mChatManager.getSendMessageOptions(), new ResultCallback() {
@Override
public void onSuccess(Void aVoid) {
...
}
@Override
public void onFailure(ErrorInfo errorInfo) {
...
}
});
기기에서 빌드 및 테스트
이제 애플리케이션을 실행해 보겠습니다!
Android Studio에서 Android 기기가 연결되어 있는지 확인한 후 Run을 클릭합니다.
완료!
축하합니다! 안드로이드용 라이브 스트리밍 앱을 얼굴 필터 기능과 채팅 메시징 기능을 포함해 직접 만들었습니다!
함께 따라와 주셔서 감사합니다. 가상 선물 기능이나 앱 내 게임과 같은 추가 기능을 보고 싶으신 분은 아래에 댓글을 남겨주세요! 질문이 있으시면 다음 이메일 주소로 연락 주시기 바랍니다: devrel@agora.io.
지난 10년간 라이브 스트리밍은 점차 가장 인기 있는 엔터테인먼트 형태 중 하나로 자리 잡았습니다. 점점 더 많은 사람들이 라이브 스트리밍을 통해 자신의 삶을 공개적으로 공유하는 것을 즐기고 있습니다.