본문으로 건너뛰기
Version: 6.1

그룹 영상 통화 시작하기

PlanetKit을 사용하면 1대1 통화 또는 그룹 통화를 위한 음성 및 영상 통화 기능을 앱에 연동할 수 있습니다. 이 가이드에서는 Android 앱에서 그룹 영상 통화 구현을 시작하는 방법을 설명합니다.

Note

더욱 빠른 개발을 위해 빠른 시작을 기반으로 앱을 구현할 수 있습니다.

필수 조건

프로젝트 생성

Android Studio를 열고 아래와 같이 새 프로젝트를 만드세요.

  1. [Welcome to Android Studio] 창에서 [New Project]를 클릭하세요.
  2. [Empty Activity]를 선택하고 [Next]를 클릭하세요.
  3. [New Project] 창에서 아래와 같이 설정하세요.
    1. [Name] 필드에 프로젝트 이름을 입력하세요.
    2. [Minimum SDK]에서 API 레벨 21 이상을 선택하세요.
    3. [Build configuration language]에서 'Groovy DSL (build.gradle)'을 선택하세요.
    4. [Finish]를 클릭하세요. Android 프로젝트 생성하기

SDK 설치

Gradle을 사용해 SDK를 설치합니다. 모듈 수준 build.gradle 파일에 아래 종속성을 추가하세요.

build.gradle
dependencies {
...
implementation 'com.linecorp.planetkit:planetkit:6.1.0'
...
}
Note

Android용 PlanetKit은 버전 5.3.3부터 Maven Central Repository를 통해 배포됩니다.

프로젝트에 Maven Central Repository가 설정돼 있지 않은 경우 다음과 같이 설정하세요.

build.gradle
// Root-level build.gradle
allprojects {
repositories {
mavenCentral()
}
}

시스템 권한 요청

영상 통화 기능을 활성화하려면 Android용 PlanetKit에 시스템 권한이 필요합니다. PlanetKit의 AndroidManifest.xml에 다음 권한이 포함되어 있습니다.

AndroidManifest.xml
<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.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30"/>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

사용자는 처음 앱을 실행할 때 런타임 권한을 부여해야 합니다. 이를 위해 다음과 같이 RECORD_AUDIO, CAMERA, READ_PHONE_STATE, BLUETOOTH_CONNECT 권한을 요청하세요.

Permissions.kt
fun checkAllRequirePermissions(context: Context): MutableList<String> {
val permissions: MutableList<String> = mutableListOf()

if (ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO)
!= PackageManager.PERMISSION_GRANTED) {
permissions.add(Manifest.permission.RECORD_AUDIO)
}

if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
permissions.add(Manifest.permission.CAMERA)
}

if (ContextCompat.checkSelfPermission(context, Manifest.permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
permissions.add(Manifest.permission.READ_PHONE_STATE)
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
&& ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_CONNECT)
!= PackageManager.PERMISSION_GRANTED) {
permissions.add(Manifest.permission.BLUETOOTH_CONNECT)
}
return permissions
}

fun requestPermissions(activity: Activity, permissions: Array<String>) {
ActivityCompat.requestPermissions(activity, permissions,0)
}

val requirePermissions = checkAllRequirePermissions(this)
if (requirePermissions.isNotEmpty()) {
requestPermissions(this, requirePermissions.toTypedArray())
return
}
Note

런타임 권한 요청에 대한 자세한 내용은 Android 공식 문서를 참조하세요.

SDK 초기화

PlanetKit API를 호출하려면 먼저 PlanetKit을 초기화해야 합니다. PlanetKitConfiguration 객체와 함께 PlanetKit.initialize()를 사용해 PlanetKit을 초기화하세요.

PlanetKit.initialize()를 호출하기 전에 PlanetKitConfiguration 객체에 서버 URL(planet_base_url)을 설정해야 합니다. 사용 중인 개발 환경에 따라 적절한 planet_base_url을 사용하세요.

GroupVideoCallApplication.kt
class GroupVideoCallApplication : Application() {
override fun onCreate() {
val config = PlanetKit.PlanetKitConfiguration.Builder(getApplicationContext())
.setServerUrl(planet_base_url)
.build()

PlanetKit.initialize(config) { isSuccessful, isVideoHwCodecSupport, userAgent ->
// Implement the completion callback if necessary
}
}
}
Note

앱에서 PlanetKit.initialize() 메서드를 최초에 한 번 호출해야 합니다. Application 인스턴스의 onCreate() 메서드에서 SDK를 초기화하는 것이 좋습니다.

액세스 토큰 획득

클라이언트 앱에서 앱 서버에 액세스 토큰 생성을 요청해 받으세요.

Note

PlanetKit.joinConference()를 호출할 때마다 새로운 액세스 토큰을 받아서 사용해야 합니다.

그룹 영상 통화 참여

그룹 영상 통화에 참여하려면 다음 인자와 함께 PlanetKit.joinConference()를 호출하세요.

  • 아래 속성을 포함하는 PlanetKitConferenceParam 객체
    • myId: 로컬 사용자의 사용자 ID
    • roomId: 방 ID
    • myServiceId: 로컬 사용자의 서비스 ID
    • roomServiceId: 방의 서비스 ID
    • mediaType: 영상 통화를 위해 PlanetKitMediaType.AUDIOVIDEO로 설정
    • accessToken: 액세스 토큰
  • 이벤트 콜백이 구현된 ConferenceListener 객체
GroupVideoCallActivity.kt
fun joinConferenceExample(userId: String, roomId: String, serviceId: String, accessToken: String)
{
val param = PlanetKitConferenceParam.Builder()
.myId(userId)
.roomId(roomId)
.myServiceId(serviceId)
.roomServiceId(serviceId)
.accessToken(accessToken)
.mediaType(PlanetKitMediaType.AUDIOVIDEO)
.build()

val result = PlanetKit.joinConference(param, conferenceListener = object : ConferenceListener {

override fun onConnected(conference: PlanetKitConference,
isVideoHwCodecEnabled: Boolean,
isVideoShareModeSupported: Boolean) {
// This is called when the call is connected.
// Write your own code here.
}

override fun onDisconnected(conference: PlanetKitConference,
param: PlanetKitDisconnectedParam) {
// This is called when the call is disconnected.
// Write your own code here.
}

override fun onPeerListUpdated(param: PlanetKitConferencePeerListUpdatedParam) {
// This is called when the list of peers is updated.
// Write your own code here.
}

override fun onPeersVideoUpdated(conference: PlanetKitConference, param: PlanetKitConferenceVideoUpdateParam) {
// This is called when the video of one or more peers is updated.
// Write your own code here.
}
})

if (result.reason == PlanetKitStartFailReason.NONE && result.conference != null) {
// The "result.conference" instance is the main instance to call APIs from now on.
// You must keep it to control this call.
}
else {
// Handle an error by referring to result.reason.
}
}
Note

사용자가 클라이언트 앱에서 그룹 통화 방에 입장하려면 방 ID가 필요하므로, 애플리케이션에서 정의한 통신 채널을 통해 방 ID를 다른 사용자와 공유해야 합니다.

로컬 사용자에 대한 비디오 뷰 렌더링

로컬 사용자의 비디오를 표시하려면 PlanetKitMyView 컴포넌트를 아래와 같이 사용하세요.

PlanetKitMyView란?

PlanetKitMyView는 PlanetKit에서 제공하는 UI 컴포넌트로, 로컬 사용자의 비디오 스트림을 렌더링합니다. 이 컴포넌트는 다음과 같은 기능을 처리합니다.

  • 로컬 사용자의 비디오 렌더링
  • 첫 프레임 렌더링 감지
  • 비디오 상태 변경 알림
  • 리스너 인터페이스를 통한 라이프사이클 이벤트 관리

레이아웃에서 PlanetKitMyView 선언

레이아웃 파일에서 PlanetKitMyView를 선언합니다. 이 뷰는 일반적으로 다음과 같은 UI 오버레이로 감싸져 있습니다.

  • 비디오 없음(no_video)
  • 비디오 재개(video_resume)
simple_my_view.xml
<com.linecorp.planetkit.ui.PlanetKitMyView
android:id="@+id/my_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- Overlays for video status -->
<LinearLayout android:id="@+id/no_video" ... />
<LinearLayout android:id="@+id/video_resume" ... />
...

뷰에 컨퍼런스를 바인딩

현재 컨퍼런스 인스턴스를 PlanetKitMyView에 바인딩하고 리스너를 등록합니다.

SimpleMyView.kt
fun setMe() {
showNoVideoView() // Default state before rendering
PlanetKit.getConference()?.let {
binding.myView.setMe(it, myViewListener)
}
}

PlanetKitMyView의 리스너 구현

초기화, 첫 프레임 렌더링, 비디오 상태 업데이트를 처리하기 위해 PlanetKitMyView.MyViewListener를 구현합니다.

  • onInitializedPlanetKitMyView가 준비되었을 때 호출됩니다.
  • onRenderFirstFrame은 첫 비디오 프레임이 렌더링될 때 호출됩니다.
  • onVideoStatusUpdated는 비디오가 활성화되었는지 비활성화되었는지를 알립니다.
SimpleMyView.kt
private val myViewListener = object : PlanetKitMyView.MyViewListener {
override fun onInitialized(userData: Any?) {
val status = binding.myView.myVideoStatus
when (status.videoState) {
VideoState.DISABLED -> showNoVideoView()
VideoState.ENABLED -> showResumedView()
else -> {}
}
}

override fun onRenderFirstFrame() {
binding.videoResume.isVisible = false
}

override fun onVideoStatusUpdated(videoStatus: PlanetKitVideoStatus) {
when (videoStatus.videoState) {
VideoState.DISABLED -> showNoVideoView()
VideoState.ENABLED -> showResumedView()
else -> {}
}
}
}

비디오 상태에 따른 UI 관리

비디오 상태에 따라 오버레이를 표시하거나 숨깁니다.

  • 비디오가 꺼지면 noVideo 오버레이를 표시합니다.
  • 비디오가 켜지면 첫 프레임이 렌더링될 때까지 videoResume 오버레이를 표시한 후 숨깁니다.
SimpleMyView.kt
private fun showNoVideoView() {
binding.videoResume.isVisible = false
binding.noVideo.isVisible = true
}

private fun showResumedView() {
binding.videoResume.isVisible = true
binding.noVideo.isVisible = false
binding.myView.resetFirstFrameRendered()
}

액티비티에서 PlanetKitMyView 처리

그룹 통화가 연결되면 커스텀 뷰의 setMe()를 호출하여 PlanetKitMyView를 초기화합니다.

GroupVideoCallActivity.kt
override fun onConnected(conference: PlanetKitConference, ...) {
binding.simpleMyView.setMe()
}

피어에 대한 비디오 뷰 렌더링

피어의 비디오를 표시하려면 PlanetKitPeerView 컴포넌트를 아래와 같이 사용하세요.

PlanetKitPeerView란?

PlanetKitPeerView는 PlanetKit에서 제공하는 UI 컴포넌트로, 그룹 통화에서 피어의 비디오 스트림을 렌더링합니다. 이 컴포넌트는 다음과 같은 기능을 처리합니다.

  • 특정 PlanetKitConferencePeer의 비디오 렌더링
  • 첫 프레임 렌더링 감지
  • 비디오 상태 변경 알림
  • 리스너 인터페이스를 통한 라이프사이클 이벤트 관리

레이아웃에서 PlanetKitPeerView 선언

레이아웃 파일에서 PlanetKitPeerView를 선언합니다. 이 뷰는 일반적으로 다음과 같은 UI 오버레이로 감싸져 있습니다.

  • 비디오 없음(no_video)
  • 비디오 일시 중지(video_pause)
  • 비디오 재개(video_resume)
simple_peer_view.xml
<com.linecorp.planetkit.ui.PlanetKitPeerView
android:id="@+id/peer_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- Overlays for video status -->
<LinearLayout android:id="@+id/no_video" ... />
<LinearLayout android:id="@+id/video_pause" ... />
<LinearLayout android:id="@+id/video_resume" ... />
...

피어를 뷰에 바인딩

PlanetKitConferencePeerPlanetKitPeerView에 바인딩하고 리스너를 등록합니다.

SimplePeerView.kt
fun setUser(newConferencePeer: PlanetKitConferencePeer, userData: Any? = null) {
PlanetKit.getConference()?.let {
conferencePeer = newConferencePeer
if (!binding.peerView.setPeer(it, newConferencePeer, peerViewListener, userData)) {
Log.e(TAG, "Failed to set peer video")
}
}
}

PlanetKitPeerView의 리스너 구현

비디오 상태 및 피어 연결 해제와 같은 상태 변경을 관리하기 위해 PlanetKitPeerView.PeerViewListener를 구현합니다.

  • onInitializedPlanetKitPeerView가 준비되었을 때 호출됩니다.
  • onRenderFirstFrame은 첫 비디오 프레임이 렌더링될 때 호출됩니다.
  • onDisconnected는 피어가 통화에서 연결이 끊어졌을 때 호출됩니다.
  • onVideoUpdated는 비디오가 활성화되었는지, 일시 중지되었는지, 비활성화되었는지를 알립니다.
SimplePeerView.kt
private val peerViewListener = object : PlanetKitPeerView.PeerViewListener {
override fun onInitialized(peer, userData) {
updateVideo(peer.getVideoStatus(...).videoStatus)
}

override fun onRenderFirstFrame(peer) {
// Hide all overlays
}

override fun onDisconnected(peer) {
clearUser()
}

override fun onVideoUpdated(peer, videoStatus, subgroupName) {
updateVideo(videoStatus)
}
}

비디오 및 UI 제어

피어의 비디오 상태에 따라 UI를 업데이트합니다.

SimplePeerView.kt
private fun updateVideo(videoStatus: PlanetKitVideoStatus) {
when (videoStatus.videoState) {
VideoState.DISABLED -> showNoVideoView()
VideoState.ENABLED -> {
startVideoImpl(binding.peerView)
}
VideoState.PAUSED -> showPausedView()
}
}

비디오가 활성화되면 권장 해상도로 비디오 렌더링을 시작합니다.

SimplePeerView.kt
private fun startVideoImpl(peerView: PlanetKitPeerView) {
showResumedView()
peerView.startVideo(PlanetKitVideoResolution.RECOMMENDED, ..., callbacks)
...
}

액티비티에서 피어 뷰 처리

비디오를 렌더링할 피어가 선택되면 커스텀 뷰의 setUser()를 호출합니다.

GroupVideoCallActivity.kt
binding.simplePeerView.setUser(selectedPeer)

피어를 제거하고 뷰 상태를 초기화하려면 커스텀 뷰의 clearUser()를 호출합니다.

GroupVideoCallActivity.kt
binding.simplePeerView.clearUser()

다음 단계

아래 문서를 참조해 PlanetKit에서 제공하는 다양한 기능과 각 기능의 사용 방법을 자세히 살펴보세요.

  • 통화 흐름: 통화 유형별 통화 흐름을 살펴보세요.
  • 서브그룹: 여러 개의 하위 그룹이 있는 방이나 통역 방과 같은 고급 기능을 구현할 수 있는 서브그룹 기능을 살펴보세요.
  • 확장 기능: 화면 공유, 데이터 세션 등 다양한 확장 기능을 살펴보세요.
  • 예제 코드: 앱을 구현하는 데 참고할 수 있는 예제 코드를 확인하세요.
  • 참조 문서: API 레퍼런스, API 변경 내역, 릴리스 노트를 확인하세요.