컨트롤러 제어
웹의 playerRef.current.play() 처럼 명령형 제어나 커스텀 UI 가 필요할 때는 컨트롤러 VpePlayerController 를 직접 보유하고 뷰 VpePlayerView 로 렌더링합니다.
기본 예제
컨트롤러는 remember 로 보유하고, 상태는 collectAsStateWithLifecycle() 로 구독합니다. 직접 생성한 컨트롤러는 DisposableEffect 에서 destroy() 로 해제해야 합니다.
CustomPlayer.kt
import com.navercloud.vpe.player.VpePlayerController
import com.navercloud.vpe.player.ui.VpePlayerView
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.media3.common.util.UnstableApi
import androidx.compose.runtime.*
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
@OptIn(UnstableApi::class)
@Composable
fun CustomPlayer() {
val context = LocalContext.current
val controller = remember {
VpePlayerController.fromMap(
context = context,
options = mapOf(
"autostart" to false,
"controls" to false, // 내장 컨트롤바 끄고 커스텀 UI로만 제어
"aspectRatio" to "16:9",
"playlist" to listOf(mapOf("file" to "https://.../master.m3u8"))
),
accessKey = "YOUR_ACCESS_KEY"
)
}
val state by controller.state.collectAsStateWithLifecycle()
DisposableEffect(controller) { onDispose { controller.destroy() } }
Column {
VpePlayerView(controller, showBuiltinControls = false, modifier = Modifier.fillMaxWidth())
Row {
Button(onClick = { controller.play() }) { Text("재생") }
Button(onClick = { controller.pause() }) { Text("일시정지") }
Button(onClick = { controller.seekTo(state.currentTime + 10) }) { Text("+10s") }
}
Text(if (state.isPlaying) "재생중" else "정지")
}
}생성 (companion factory)
// Map 옵션 + 라이선스
VpePlayerController.fromMap(context, options: Map<String, Any?>, accessKey,
platform = "pub", stage = "prod", isDev = false)
// JSON 문자열 옵션 + 라이선스
VpePlayerController.fromJson(context, json: String, accessKey,
platform, stage, isDev)
// 라이선스 없이 (저수준)
VpePlayerController(context, initialOptions: PlayerOptions = PlayerOptions())해제 필수 —
VpePlayer(...) Composable 은 컨트롤러를 자동 해제하지만, 위처럼 fromMap / fromJson 으로 직접 만든 경우 controller.destroy() 를 직접 호출해야 합니다.