Implementing Video Playback on Android Jetpack Compose
A Hands-On Tutorial to ExoPlayer #
As an Android developer you might come across the task of having to implement video-playback in your Jetpack Compose application. I was in the same situation, started research on this topic and found a nice solution which I present in this tutorial.
When researching this topic I came across two solutions:
- MediaPlayer and
- ExoPlayer
I have decided to use ExoPlayer because it seemed to me the more flexible and more feature-rich framework. Additionally, I got the impression that this framework is more widely-used and therefore I was able to find more examples and discussions on it.
There are quite a few and easy to find tutorials out there already, but even the most recent ones used code that is already deprecated. I have used this as a starting point and updated the deprecated pieces. Then I have stripped away all the unnecessary and distracting pieces as always to come up with the most minimal working solution.
The result is an up-to-data and fully-working Android Jetpack Compose application that takes a URL of a video file and displays this video in the application.
Let’s start by creating a new project in Android Studio and chose the “Empty Compose Activity”.
As always, we need to fill out all the necessary bits for the new application.
As a very first step, we need to add the dependency for ExoPlayer in the build.gradle
:
implementation 'com.google.android.exoplayer:exoplayer:2.18.1'
Next, I have created a new composable called VideoView
. This view encapsulates all the ExoPlayer related code.
As you can see, it’s very little that has to be added. Considering on your specific use case, you might have to add a few things here and there. More about this later on in this tutorial.
@Composable
fun VideoView(videoUri: String) {
val context = LocalContext.current
val exoPlayer = ExoPlayer.Builder(LocalContext.current)
.build()
.also { exoPlayer ->
val mediaItem = MediaItem.Builder()
.setUri(videoUri)
.build()
exoPlayer.setMediaItem(mediaItem)
exoPlayer.prepare()
}
DisposableEffect(
AndroidView(factory = {
StyledPlayerView(context).apply {
player = exoPlayer
}
})
) {
onDispose { exoPlayer.release() }
}
}
Let’s stay here for a while and have a look at this code.
There are four points that needs to be mentioned.
- We need to setup our ExoPlayer-instance
exoPlayer
by calling the builderExoPlayer.Builder
and providing our currentLocalContext
- After that, we create the
mediaItem
from the videos URI by also using the builder-pattern with ``MediaItem.Builder()``` - Then we create a
DisposableEffect
that does two things. When our composable gets shown it will create theStyledPlayerView
from ourexoPlayer
- instance. Then, when the view is disposed, it will release ourexoPlayer
-instance.
Please check the resources section at the end of this article where I have provided some links about Jetpack Compose Side-Effects and in specific the DisposableEffect.
Let’s change our MainActivity
to use the VideoView
. The final result looks like this:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
VideoApplicationTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
VideoView(videoUri = "https://storage.googleapis.com/exoplayer-test-media-0/BigBuckBunny_320x180.mp4")
}
}
}
}
}
You can also use the preview which then need to be changed as follows:
@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
VideoApplicationTheme {
VideoView(videoUri = "https://storage.googleapis.com/exoplayer-test-media-0/BigBuckBunny_320x180.mp4")
}
}
Everything is in place to start the application. Wait!
Almost everything as you might already have found out. Trying to start the application like this will result in an exception:
E/LoadTask: Unexpected exception loading stream
java.lang.SecurityException: Permission denied (missing INTERNET permission?)
at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:150)
at java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:103)
at java.net.InetAddress.getAllByName(InetAddress.java:1152)
at com.android.okhttp.Dns$1.lookup(Dns.java:41)
Because we are trying to access the video from the Internet, we first need to allow our application to access the Internet.
The following line needs to be added to the file AndroidManifest.xml
(under app/manifests
)
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
The file then looks like:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mycompany.videoapplication">
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.VideoApplication">
<activity
android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.VideoApplication">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Let’s start the app and you should see the video playing as shown below.
Hopefully, everything worked out as it should. If not, please let me know in the comments.
As promised, I will provide you with some more info on how to tailor the code to your potential needs with several configuration options.
Here are some use-cases that you might find useful:
- Auto-start the video
- Hide the controls
- Repeat the video
- Change the aspect ratio
You can auto-start the video right after the view has been loaded with
exoPlayer.playWhenReady = true
That’s it. Nothing more to say. On to the next one.
In case you don’t want to show the controls of the player you can hide them by adding two lines to the setup in the SyledPlayerView
.
StyledPlayerView(context).apply {
hideController()
useController = false
player = exoPlayer
}
The exoPlayer
-instance can be configured to repeat the video infinitely as well.
exoPlayer.repeatMode = Player.REPEAT_MODE_ALL
As a last hint, and for me a very important one, the aspect ratio can also be changed by setting it in the exoPlayer
-instance. The code below will fill and crop the video into the view.
exoPlayer.videoScalingMode = C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
I sincerely hope this tutorial was of any help for you. Please let me know in the comments how you have liked it and especially if any code is deprecated at the time you are trying it.
Thank you for reading!
- If you enjoyed this, please follow me on Medium
- Buy me a coffee to keep me going
- Support me and other Medium writers by signing up here
https://twissmueller.medium.com/membership