DEV Community

myougaTheAxo
myougaTheAxo

Posted on

Media3 + Compose Complete Guide — Video Player/Music Player/PiP

What You'll Learn

Media3 + Compose (ExoPlayer, video player UI, music player, PiP support, media notification) explained.


Setup

dependencies {
    implementation("androidx.media3:media3-exoplayer:1.5.1")
    implementation("androidx.media3:media3-ui-compose:1.5.1")
    implementation("androidx.media3:media3-session:1.5.1")
}
Enter fullscreen mode Exit fullscreen mode

Video Player

@Composable
fun VideoPlayer(uri: String) {
    val context = LocalContext.current
    val player = remember {
        ExoPlayer.Builder(context).build().apply {
            setMediaItem(MediaItem.fromUri(uri))
            prepare()
        }
    }

    DisposableEffect(Unit) {
        onDispose { player.release() }
    }

    AndroidView(
        factory = { ctx ->
            PlayerView(ctx).apply {
                this.player = player
                useController = true
            }
        },
        modifier = Modifier.fillMaxWidth().aspectRatio(16f / 9f)
    )
}
Enter fullscreen mode Exit fullscreen mode

Custom Controller

@Composable
fun CustomVideoPlayer(uri: String) {
    val context = LocalContext.current
    val player = remember {
        ExoPlayer.Builder(context).build().apply {
            setMediaItem(MediaItem.fromUri(uri))
            prepare()
        }
    }
    var isPlaying by remember { mutableStateOf(false) }
    var progress by remember { mutableFloatStateOf(0f) }
    var duration by remember { mutableLongStateOf(0L) }

    LaunchedEffect(player) {
        while (true) {
            isPlaying = player.isPlaying
            progress = if (player.duration > 0) player.currentPosition.toFloat() / player.duration else 0f
            duration = player.duration
            delay(200)
        }
    }

    DisposableEffect(Unit) { onDispose { player.release() } }

    Column {
        AndroidView(
            factory = { PlayerView(it).apply { this.player = player; useController = false } },
            modifier = Modifier.fillMaxWidth().aspectRatio(16f / 9f)
        )

        Row(Modifier.fillMaxWidth().padding(8.dp), verticalAlignment = Alignment.CenterVertically) {
            IconButton(onClick = { if (isPlaying) player.pause() else player.play() }) {
                Icon(if (isPlaying) Icons.Default.Pause else Icons.Default.PlayArrow, "Play/Pause")
            }
            Slider(
                value = progress,
                onValueChange = { player.seekTo((it * duration).toLong()) },
                modifier = Modifier.weight(1f)
            )
            Text(formatTime(player.currentPosition), style = MaterialTheme.typography.labelSmall)
        }
    }
}

fun formatTime(ms: Long): String {
    val seconds = (ms / 1000) % 60
    val minutes = (ms / 1000 / 60) % 60
    return "%d:%02d".format(minutes, seconds)
}
Enter fullscreen mode Exit fullscreen mode

Music Player (MediaSession)

class MusicService : MediaSessionService() {
    private var mediaSession: MediaSession? = null

    override fun onCreate() {
        super.onCreate()
        val player = ExoPlayer.Builder(this).build()
        mediaSession = MediaSession.Builder(this, player).build()
    }

    override fun onGetSession(controllerInfo: MediaSession.ControllerInfo) = mediaSession

    override fun onDestroy() {
        mediaSession?.run {
            player.release()
            release()
        }
        super.onDestroy()
    }
}
Enter fullscreen mode Exit fullscreen mode

PiP (Picture-in-Picture)

@Composable
fun PipVideoPlayer(uri: String) {
    val context = LocalContext.current
    val activity = context as Activity

    Button(onClick = {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            activity.enterPictureInPictureMode(
                PictureInPictureParams.Builder()
                    .setAspectRatio(Rational(16, 9))
                    .build()
            )
        }
    }) { Text("PiP Mode") }

    VideoPlayer(uri)
}
Enter fullscreen mode Exit fullscreen mode

Summary

Feature Implementation
Video player ExoPlayer + PlayerView
Custom UI AndroidView + Compose
Music service MediaSessionService
PiP enterPictureInPictureMode
Notification MediaSession
  • ExoPlayer for video/audio playback
  • AndroidView integrates PlayerView into Compose
  • MediaSession auto-generates media notification
  • PiP for background viewing

Ready-Made Android App Templates

8 production-ready Android app templates with Jetpack Compose, MVVM, Hilt, and Material 3.

Browse templatesGumroad

Top comments (0)