Media3 (ExoPlayer) + Compose: 비디오 및 오디오 플레이어 구현
I’m happy to translate the article for you, but I need the full text you’d like translated. Could you please paste the content (excluding the source line you already provided) here? Once I have it, I’ll translate it into Korean while preserving all formatting, markdown, and code blocks.
ExoPlayer.Builder 설정
ExoPlayer 인스턴스를 생성하고 구성합니다:
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
val exoPlayer = ExoPlayer.Builder(context)
.setTrackSelector(DefaultTrackSelector(context))
.build()
MediaItems 로드하기
재생할 미디어 아이템을 하나 또는 여러 개 추가합니다:
import androidx.media3.common.MediaItem
import androidx.media3.common.MimeTypes
// 단일 비디오
val mediaItem = MediaItem.Builder()
.setUri("https://example.com/video.mp4")
.setMimeType(MimeTypes.APPLICATION_MP4)
.build()
exoPlayer.setMediaItem(mediaItem)
// 여러 아이템이 포함된 재생 목록
val playlist = listOf(
MediaItem.fromUri("https://example.com/video1.mp4"),
MediaItem.fromUri("https://example.com/video2.mp4"),
MediaItem.fromUri("https://example.com/audio.mp3")
)
exoPlayer.setMediaItems(playlist)
exoPlayer.prepare()
AndroidView에서 PlayerView
AndroidView를 사용하여 Compose에 PlayerView를 임베드합니다:
import androidx.media3.ui.PlayerView
import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
AndroidView(
factory = { context ->
PlayerView(context).apply {
player = exoPlayer
useController = true
controllerShowTimeoutMs = 5000
controllerHideTimeoutMs = 3000
}
},
modifier = Modifier
.fillMaxWidth()
.height(300.dp)
)
Compose에서 사용자 정의 오디오 플레이어 UI
비디오 없이 오디오용 사용자 정의 플레이어 UI를 구축합니다:
import androidx.compose.foundation.layout.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.Alignment
val currentPosition by exoPlayer.currentPosition.collectAsState()
val duration by exoPlayer.duration.collectAsState()
val isPlaying by exoPlayer.isPlaying.collectAsState()
Column(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
// Play/Pause Button
Button(onClick = {
if (isPlaying) exoPlayer.pause() else exoPlayer.play()
}) {
Text(if (isPlaying) "⏸ Pause" else "▶ Play")
}
// Progress Slider
Slider(
value = currentPosition.toFloat(),
onValueChange = { exoPlayer.seekTo(it.toLong()) },
valueRange = 0f..duration.toFloat(),
modifier = Modifier.fillMaxWidth()
)
// Time Display
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(formatTime(currentPosition))
Text(formatTime(duration))
}
}
fun formatTime(ms: Long): String {
val seconds = (ms / 1000) % 60
val minutes = (ms / 60000) % 60
return "%02d:%02d".format(minutes, seconds)
}
라이프사이클 인식 재생
앱이 백그라운드로 전환될 때 재생을 일시 정지합니다:
import androidx.compose.runtime.*
import androidx.lifecycle.*
DisposableEffect(Unit) {
val lifecycleObserver = LifecycleEventObserver { _, event ->
when (event) {
Lifecycle.Event.ON_RESUME -> exoPlayer.play()
Lifecycle.Event.ON_PAUSE -> exoPlayer.pause()
else -> {}
}
}
val lifecycle = LocalLifecycleOwner.current.lifecycle
lifecycle.addObserver(lifecycleObserver)
onDispose {
lifecycle.removeObserver(lifecycleObserver)
}
}
DisposableEffect를 사용한 적절한 리소스 해제
Compose 컴포넌트가 폐기될 때 플레이어를 해제합니다:
DisposableEffect(Unit) {
onDispose {
exoPlayer.release()
}
}
재생 목록 관리
재생 목록 내에서 재생 흐름을 제어합니다:
// Move to next item
Button(onClick = { exoPlayer.seekToNextMediaItem() }) {
Text("Next Track")
}
// Move to previous item
Button(onClick = { exoPlayer.seekToPreviousMediaItem() }) {
Text("Previous Track")
}
// Repeat modes
IconButton(onClick = {
exoPlayer.repeatMode = when (exoPlayer.repeatMode) {
Player.REPEAT_MODE_OFF -> Player.REPEAT_MODE_ALL
Player.REPEAT_MODE_ALL -> Player.REPEAT_MODE_ONE
else -> Player.REPEAT_MODE_OFF
}
}) {
Text("Repeat: ${exoPlayer.repeatMode}")
}
모범 사례
- 플레이어 리소스를 해제하려면 항상
DisposableEffect를 사용하세요. - 배터리를 절약하기 위해 앱이 백그라운드로 전환될 때 재생을 일시 중지하세요.
- 적절한 상태 관리를 위해 라이프사이클 옵저버를 사용하세요.
- 네트워크 오류를 처리하고 로컬 재생으로 대체할 수 있도록 하세요.
- 다양한 미디어 포맷(MP4, WebM, DASH, HLS)으로 테스트하세요.
- 사용자에게 재생 제어와 진행 표시를 제공하세요.
Media3는 오디오 및 비디오 재생을 위한 견고한 기반을 제공합니다. Compose의 선언형 UI와 결합하면 최소한의 보일러플레이트로 현대적인 미디어 경험을 구축할 수 있습니다.
더 많은 Android 개발 패턴을 탐색하세요: 8 Android App Templates →