본문으로 건너뛰기
Version: 7.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:7.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를 구현합니다.

  • onInitialized는 PlanetKitMyView가 준비되었을 때 호출됩니다.
  • 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" ... />
...

피어를 뷰에 바인딩​

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

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를 구현합니다.

  • onInitialized는 PlanetKitPeerView가 준비되었을 때 호출됩니다.
  • 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 변경 내역, 릴리스 노트를 확인하세요.