MediaPlayer: Simplified Video Playback on Android
Playing videos is a common requirement in Android apps. In this tutorial learn about handling video playback via the MediaPlayer API on Android. By Bhavesh Misri.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
MediaPlayer: Simplified Video Playback on Android
25 mins
- Getting Started
- Understanding the code
- MediaPlayer
- Playing Video From Local Resources
- MediaPlayer is prepared
- Improving the UX
- Interacting with the SeekBar
- Playing Video from the Gallery
- Playing the video after it has been downloaded
- Releasing the Resources
- Executing Asynchronously
- Streaming a Video From a URL
- Digital Right Management
- DRM MediaPlayer
- Where To Go From Here?
MediaPlayer is prepared
When MediaPlayer is prepared, it invokes onPrepared(). This is the point where you will start playing the video.
To do that, replace // TODO (2) inside onPrepared() with:
// 1
progress_bar.visibility = View.GONE
//2
mediaPlayer?.start()
Here you:
- Make the
progressBarinvisible. - Tell
MediaPlayerto start the video.
Now Build and Run.
Tada! Now you can play the video from your raw or resource directory, but still, something is missing. The seekBar isn’t updating, and you can’t pause or play the video.
Next, you’ll implement the missing functionality and make the app more intuitive.
Improving the UX
Before you add the functionality to play, pause or fast-forward video using SeekBar, you need to create a few functions and extension properties.
At the bottom of VideoActivity.kt replace // TODO (3) with:
// 1
private val MediaPlayer.seconds: Int
get() {
return this.duration / SECOND
}
// 2
private val MediaPlayer.currentSeconds: Int
get() {
return this.currentPosition / SECOND
}
These extension properties help you implement those functionalities. MediaPlayer itself provides you with the video duration and currentPosition, therefore you don’t have to worry for tracking them.
-
secondsreturns the total duration of the video in seconds. -
currentSecondsreturns the current playback position in seconds of the video.
Next, convert the seconds to a more readable format by replacing // TODO (4) with:
private fun timeInString(seconds: Int): String {
return String.format(
"%02d:%02d",
(seconds / 3600 * 60 + ((seconds % 3600) / 60)),
(seconds % 60)
)
}
In this function you convert seconds to MM:SS format. If the video is more than 60 seconds long, it’s better to show 2:32 minutes rather than 152 seconds.
In adition, you’ll create three functions that initialize and periodically update seekbar and convert the seconds to a more readable format.
To initialize seekbar, replace // TODO (5) with:
private fun initializeSeekBar() {
// 1
seek_bar.max = mediaPlayer.seconds
// 2
text_progress.text = getString(R.string.default_value)
text_total_time.text = timeInString(mediaPlayer.seconds)
// 3
progress_bar.visibility = View.GONE
// 4
play_button.isEnabled = true
}
When MediaPlayer prepares to play the video this function is executed. The code performs the following:
- Sets the maximum value for
SeekBar - Sets default values for
TextViewswhich shows the progress and the total duration of the video. - Hides the
ProgressBar. - Enables the
play button.
Next, to periodically update the seekbar as the video plays, replace // TODO (6) with:
private fun updateSeekBar() {
runnable = Runnable {
text_progress.text = timeInString(mediaPlayer.currentSeconds)
seek_bar.progress = mediaPlayer.currentSeconds
handler.postDelayed(runnable, SECOND.toLong())
}
handler.postDelayed(runnable, SECOND.toLong())
}
In this function, you use Runnable to execute the code periodically after every one second. Runnable is a Java interface and executes on a separate thread. Since it executes on a separate thread, it won't block your UI and the SeekBar and TextViews will update periodically.
Instead of playing the video when MediaPlayer is ready, it would be better if the user could play and pause the video by using the imageButton.
Inside the onCreate() function replace // TODO (7) with:
play_button.setOnClickListener {
// 1
if (mediaPlayer.isPlaying) {
// 2
mediaPlayer.pause()
play_button.setImageResource(android.R.drawable.ic_media_play)
} else {
// 3
mediaPlayer.start()
play_button.setImageResource(android.R.drawable.ic_media_pause)
}
}
Here you:
- Check if
MediaPlayeris playing any video. - If it is, you pause the video and change the button icon to play.
- If not, you play the video and change the button icon to pause.
Next, replace all the code added to the onPrepared()'s body with a call to initalizeSeekBar() and updateSeekBar() which you created earlier:
initializeSeekBar()
updateSeekBar()
Now your app is more intuitive.
Build and Run. You can play or pause the video and see the progress on seekBar and TextView.
Interacting with the SeekBar
Now the app is more intuitive for the user, except for the SeekBar. Even though the SeekBar updates with time, you can't fast-forward or rewind the video by tapping or dragging it.
For that, you'll use the onProgressChanged() method of SeekBar. Whenever there's a change in the SeekBar's progress, it will invoke this function.
The SeekBar change listener is already in the code, so navigate to onProgressChanged() and replace // TODO (8) with:
if (fromUser){
mediaPlayer.seekTo(progress * SECOND)
}
The function onProgressChanged() has three parameters:
You use this parameter to update MediaPlayer's progress if the seekbar's progress level is manually changed.
-
seekBar: Instance of the
seekBar. -
progress: Progress of
seekBarin seconds. -
fromUser: Boolean which tells you if the change is because of user interaction. If the change in progress is due to user interaction, it'll be true. If not, it'll be false.
You use this parameter to update
MediaPlayer's progress if theseekbar's progress level is manually changed.
Now your user can fast-forward or rewind the video using seekBar. Build and run to give it a try. :]
Playing Video from the Gallery
The app works great now, but it would be better if users could select a video from their gallery and play it. You'll implement that next.
The options button in the toolbar and its functionality are already in the starter project.
Before you add new code, you need to understand what's happening in the existing code. When the user selects an option the app invokes onOptionItemSelected() with the menuItem as a parameter.
Now, inside the when statement replace // TODO (9) with:
// 1
val intent = Intent()
// 2
intent.type = "video/*"
// 3
intent.action = Intent.ACTION_GET_CONTENT
// 4
startActivityForResult(Intent.createChooser(intent, getString(R.string.select_file)), GET_VIDEO)
Here you use an intent to get the URI for the file the user selected from the gallery. In the code:
- You get an Intent which is a messaging object in Android used to request different action types.
- You ensure that the intent type is a video format.
- Then you specify this is an Intent with an action of GET content type.
- Finally, you trigger the intent waiting for a result.
Playing the video after it has been downloaded
Once the activity returns something from the intent, startActivityForResult() invokes onActivityResult(), which passes GET_VIDEO as a request_code.
Inside the startActivityForResult() function replace // TODO (10) with:
// 1
if (resultCode == Activity.RESULT_OK) {
// 2
if (requestCode == GET_VIDEO) {
// 3
selectedVideoUri = data?.data!!
// 4
video_view.holder.addCallback(this)
}
}
Here you:
- Check the
resultCode. If the operation was executed successfully, it returnsActivity.RESULT_OK. - Check the
requestCodeto identify the caller and define if the requestCode was actually a video. - Assign URI to the
selectedVideoUrivariable you declared earlier. - Invoke
surfaceCreated()by callingvideo_view.holder.addCallback(this).
It will probably ask you to import Activity, so add it at the top with your other inputs with:
import android.app.Activity
Next, update setDataSource() so you pass the correct URI in the parameter. Navigate to surfaceCreated() and replace the function body with:
mediaPlayer.apply {
setDataSource(applicationContext, selectedVideoUri)
setDisplay(surfaceHolder)
prepareAsync()
}
Voila! Now your user can open the gallery, select a video and play it in your app. Build and run to see how it works now.



