The Android multimedia framework includes support for playing variety of common media types, so
that you can easily integrate audio, video and images into your applications. You can play audio or
video from media files stored in your application's resources (raw resources), from standalone files
in the filesystem, or from a data stream arriving over a network connection, all usingMediaPlayer
APIs.
This document shows you how to use
MediaPlayer
to write a media-playing
application that interacts with the user and the system in order to obtain good performance and a
pleasant user experience. Alternatively, you might want
to useExoPlayer,which is a customizable open source
library that supports high-performance features not available inMediaPlayer
Note:You can play back the audio data only to the standard output device. Currently, that is the mobile device speaker or a Bluetooth headset. You cannot play sound files in the conversation audio during a call.
The basics
The following classes are used to play sound and video in the Android framework:
MediaPlayer
- This class is the primary API for playing sound and video.
AudioManager
- This class manages audio sources and audio output on a device.
Manifest declarations
Before starting development on your application using MediaPlayer, make sure your manifest has the appropriate declarations to allow use of related features.
- Internet Permission- If you are using MediaPlayer to stream network-based
content, your application must request network access.
<uses-permissionandroid:name= "android.permission.INTERNET"/>
- Wake Lock Permission- If your player application needs to keep the screen
from dimming or the processor from sleeping, or uses the
MediaPlayer.setScreenOnWhilePlaying()
orMediaPlayer.setWakeMode()
methods, you must request this permission.<uses-permissionandroid:name= "android.permission.WAKE_LOCK"/>
Using MediaPlayer
One of the most important components of the media framework is the
MediaPlayer
class. An object of this class can fetch, decode, and play both audio and video
with minimal setup. It supports several different media sources such as:
- Local resources
- Internal URIs, such as one you might obtain from a Content Resolver
- External URLs (streaming)
For a list of media formats that Android supports, see theSupported Media Formatspage.
Here is an example
of how to play audio that's available as a local raw resource (saved in your application's
res/raw/
directory):
Kotlin
varmediaPlayer=MediaPlayer.create(context,R.raw.sound_file_1) mediaPlayer.start()// no need to call prepare(); create() does that for you
Java
MediaPlayermediaPlayer=MediaPlayer.create(context,R.raw.sound_file_1); mediaPlayer.start();// no need to call prepare(); create() does that for you
In this case, a "raw" resource is a file that the system does not try to parse in any particular way. However, the content of this resource should not be raw audio. It should be a properly encoded and formatted media file in one of the supported formats.
And here is how you might play from a URI available locally in the system (that you obtained through a Content Resolver, for instance):
Kotlin
valmyUri:Uri=....// initialize Uri here valmediaPlayer=MediaPlayer().apply{ setAudioAttributes( AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setUsage(AudioAttributes.USAGE_MEDIA) .build() ) setDataSource(applicationContext,myUri) prepare() start() }
Java
UrimyUri=....;// initialize Uri here MediaPlayermediaPlayer=newMediaPlayer(); mediaPlayer.setAudioAttributes( newAudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setUsage(AudioAttributes.USAGE_MEDIA) .build() ); mediaPlayer.setDataSource(getApplicationContext(),myUri); mediaPlayer.prepare(); mediaPlayer.start();
Playing from a remote URL via HTTP streaming looks like this:
Kotlin
valurl="http://........"// your URL here valmediaPlayer=MediaPlayer().apply{ setAudioAttributes( AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setUsage(AudioAttributes.USAGE_MEDIA) .build() ) setDataSource(url) prepare()// might take long! (for buffering, etc) start() }
Java
Stringurl="http://........";// your URL here MediaPlayermediaPlayer=newMediaPlayer(); mediaPlayer.setAudioAttributes( newAudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setUsage(AudioAttributes.USAGE_MEDIA) .build() ); mediaPlayer.setDataSource(url); mediaPlayer.prepare();// might take long! (for buffering, etc) mediaPlayer.start();
Note: If you're passing a URL to stream an online media file, the file must be capable of progressive download.
Caution:You must either catch or pass
IllegalArgumentException
andIOException
when using
setDataSource()
,because
the file you are referencing might not exist.
Asynchronous preparation
UsingMediaPlayer
can be straightforward in
principle. However, it's important to keep in mind that a few more things are
necessary to integrate it correctly with a typical Android application. For
example, the call toprepare()
can
take a long time to execute, because
it might involve fetching and decoding media data. So, as is the case with any
method that may take long to execute, you shouldnever call it from your
application's UI thread.Doing that causes the UI to hang until the method returns,
which is a very bad user experience and can cause an ANR (Application Not Responding) error. Even if
you expect your resource to load quickly, remember that anything that takes more than a tenth
of a second to respond in the UI causes a noticeable pause and gives
the user the impression that your application is slow.
To avoid hanging your UI thread, spawn another thread to
prepare theMediaPlayer
and notify the main thread when done. However, while
you could write the threading logic
yourself, this pattern is so common when usingMediaPlayer
that the framework
supplies a convenient way to accomplish this task by using the
prepareAsync()
method. This method
starts preparing the media in the background and returns immediately. When the media
is done preparing, theonPrepared()
method of theMediaPlayer.OnPreparedListener
,configured through
setOnPreparedListener()
is called.
Managing state
Another aspect of aMediaPlayer
that you should keep in mind is
that it's state-based. That is, theMediaPlayer
has an internal state
that you must always be aware of when writing your code, because certain operations
are only valid when the player is in specific states. If you perform an operation while in the
wrong state, the system may throw an exception or cause other undesirable behaviors.
The documentation in the
MediaPlayer
class shows a complete state diagram,
that clarifies which methods move theMediaPlayer
from one state to another.
For example, when you create a newMediaPlayer
,it is in theIdle
state. At that point, you should initialize it by calling
setDataSource()
,bringing it
to theInitializedstate. After that, you have to prepare it using either the
prepare()
or
prepareAsync()
method. When
theMediaPlayer
is done preparing, it enters thePrepared
state, which means you can callstart()
to make it play the media. At that point, as the diagram illustrates,
you can move between theStarted,PausedandPlaybackCompletedstates by
calling such methods as
start()
,
pause()
,and
seekTo()
,
amongst others. When you
callstop()
,however, notice that you
cannot callstart()
again until you
prepare theMediaPlayer
again.
Always keepthe state diagram
in mind when writing code that interacts with a
MediaPlayer
object, because calling its methods from the wrong state is a
common cause of bugs.
Releasing the MediaPlayer
AMediaPlayer
can consume valuable
system resources.
Therefore, you should always take extra precautions to make sure you are not
hanging on to aMediaPlayer
instance longer than necessary. When you
are done with it, you should always call
release()
to make sure any
system resources allocated to it are properly released. For example, if you are
using aMediaPlayer
and your activity receives a call toonStop()
,you must release theMediaPlayer
,
because it
makes little sense to hold on to it while your activity is not interacting with
the user (unless you are playing media in the background, which is discussed in the next section).
When your activity is resumed or restarted, of course, you need to
create a newMediaPlayer
and prepare it again before resuming playback.
Here's how you should release and then nullify yourMediaPlayer
:
Kotlin
mediaPlayer?.release() mediaPlayer=null
Java
mediaPlayer.release(); mediaPlayer=null;
As an example, consider the problems that could happen if you
forgot to release theMediaPlayer
when your activity is stopped, but create a
new one when the activity starts again. As you may know, when the user changes the
screen orientation (or changes the device configuration in another way),
the system handles that by restarting the activity (by default), so you might quickly
consume all of the system resources as the user
rotates the device back and forth between portrait and landscape, because at each
orientation change, you create a newMediaPlayer
that you never
release. (For more information about runtime restarts, seeHandling Runtime Changes.)
You may be wondering what happens if you want to continue playing
"background media" even when the user leaves your activity, much in the same
way that the built-in Music application behaves. In this case, what you need is
aMediaPlayer
controlled by a Service, as
discussed in the next section
Using MediaPlayer in a service
If you want your media to play in the background even when your application
is not onscreen—that is, you want it to continue playing while the user is
interacting with other applications—then you must start a
Service and control the
MediaPlayer
instance from there.
You need to embed the
MediaPlayer in aMediaBrowserServiceCompat
service and have
it interact with a
MediaBrowserCompat
in another activity.
You should be careful about this client/server setup. There are expectations about how a player running in a background service interacts with the rest of the system. If your application does not fulfill those expectations, the user may have a bad experience. Read Building an Audio App for the full details.
This section describes special instructions for managing a MediaPlayer when it is implemented inside a service.
Running asynchronously
First of all, like anActivity
,all work in a
Service
is done in a single thread by
default—in fact, if you're running an activity and a service from the same application, they
use the same thread (the "main thread" ) by default. Therefore, services need to
process incoming intents quickly
and never perform lengthy computations when responding to them. If any heavy
work or blocking calls are expected, you must do those tasks asynchronously: either from
another thread you implement yourself, or using the framework's many facilities
for asynchronous processing.
For instance, when using aMediaPlayer
from your main thread,
you should callprepareAsync()
rather than
prepare()
,and implement
aMediaPlayer.OnPreparedListener
in order to be notified when the preparation is complete and you can start playing.
For example:
Kotlin
privateconstvalACTION_PLAY:String="com.example.action.PLAY" classMyService:Service(),MediaPlayer.OnPreparedListener{ privatevarmMediaPlayer:MediaPlayer?=null overridefunonStartCommand(intent:Intent,flags:Int,startId:Int):Int{ ... valaction:String=intent.action when(action){ ACTION_PLAY->{ mMediaPlayer=...// initialize it here mMediaPlayer?.apply{ setOnPreparedListener(this@MyService) prepareAsync()// prepare async to not block main thread } } } ... } /** Called when MediaPlayer is ready */ overridefunonPrepared(mediaPlayer:MediaPlayer){ mediaPlayer.start() } }
Java
publicclassMyServiceextendsServiceimplementsMediaPlayer.OnPreparedListener{ privatestaticfinalStringACTION_PLAY="com.example.action.PLAY"; MediaPlayermediaPlayer=null; publicintonStartCommand(Intentintent,intflags,intstartId){ ... if(intent.getAction().equals(ACTION_PLAY)){ mediaPlayer=...// initialize it here mediaPlayer.setOnPreparedListener(this); mediaPlayer.prepareAsync();// prepare async to not block main thread } } /** Called when MediaPlayer is ready */ publicvoidonPrepared(MediaPlayerplayer){ player.start(); } }
Handling asynchronous errors
On synchronous operations, errors would normally
be signaled with an exception or an error code, but whenever you use asynchronous
resources, you should make sure your application is notified
of errors appropriately. In the case of aMediaPlayer
,
you can accomplish this by implementing a
MediaPlayer.OnErrorListener
and
setting it in yourMediaPlayer
instance:
Kotlin
classMyService:Service(),MediaPlayer.OnErrorListener{ privatevarmediaPlayer:MediaPlayer?=null funinitMediaPlayer(){ //...initialize the MediaPlayer here... mediaPlayer?.setOnErrorListener(this) } overridefunonError(mp:MediaPlayer,what:Int,extra:Int):Boolean{ //... react appropriately... // The MediaPlayer has moved to the Error state, must be reset! } }
Java
publicclassMyServiceextendsServiceimplementsMediaPlayer.OnErrorListener{ MediaPlayermediaPlayer; publicvoidinitMediaPlayer(){ //...initialize the MediaPlayer here... mediaPlayer.setOnErrorListener(this); } @Override publicbooleanonError(MediaPlayermp,intwhat,intextra){ //... react appropriately... // The MediaPlayer has moved to the Error state, must be reset! } }
It's important to remember that when an error occurs, theMediaPlayer
moves to theErrorstate (see the documentation for the
MediaPlayer
class for the full state diagram)
and you must reset it before you can use it again.
Using wake locks
When designing applications that play media in the background, the device may go to sleep while your service is running. Because the Android system tries to conserve battery while the device is sleeping, the system tries to shut off any of the phone's features that are not necessary, including the CPU and the WiFi hardware. However, if your service is playing or streaming music, you want to prevent the system from interfering with your playback.
In order to ensure that your service continues to run under those conditions, you have to use "wake locks." A wake lock is a way to signal to the system that your application is using some feature that should stay available even if the phone is idle.
Notice:You should always use wake locks sparingly and hold them only for as long as truly necessary, because they significantly reduce the battery life of the device.
To ensure that the CPU continues running while yourMediaPlayer
is
playing, call thesetWakeMode()
method when initializing yourMediaPlayer
.Once you do,
theMediaPlayer
holds the specified lock while playing and releases the lock
when paused or stopped:
Kotlin
mediaPlayer=MediaPlayer().apply{ //... other initialization here... setWakeMode(applicationContext,PowerManager.PARTIAL_WAKE_LOCK) }
Java
mediaPlayer=newMediaPlayer(); //... other initialization here... mediaPlayer.setWakeMode(getApplicationContext(),PowerManager.PARTIAL_WAKE_LOCK);
However, the wake lock acquired in this example guarantees only that the CPU remains awake. If
you are streaming media over the
network and you are using Wi-Fi, you probably want to hold a
WifiLock
as
well, which you must acquire and release manually. So, when you start preparing the
MediaPlayer
with the remote URL, you should create and acquire the Wi-Fi lock.
For example:
Kotlin
valwifiManager=getSystemService(Context.WIFI_SERVICE)asWifiManager valwifiLock:WifiManager.WifiLock= wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL,"mylock") wifiLock.acquire()
Java
WifiLockwifiLock=((WifiManager)getSystemService(Context.WIFI_SERVICE)) .createWifiLock(WifiManager.WIFI_MODE_FULL,"mylock"); wifiLock.acquire();
When you pause or stop your media, or when you no longer need the network, you should release the lock:
Kotlin
wifiLock.release()
Java
wifiLock.release();
Performing cleanup
As mentioned earlier, aMediaPlayer
object can consume a significant
amount of system resources, so you should keep it only for as long as you need and call
release()
when you are done with it. It's important
to call this cleanup method explicitly rather than rely on system garbage collection because
it might take some time before the garbage collector reclaims theMediaPlayer
,
as it's only sensitive to memory needs and not to shortage of other media-related resources.
So, in the case when you're using a service, you should always override the
onDestroy()
method to make sure you are releasing
theMediaPlayer
:
Kotlin
classMyService:Service(){ privatevarmediaPlayer:MediaPlayer?=null //... overridefunonDestroy(){ super.onDestroy() mediaPlayer?.release() } }
Java
publicclassMyServiceextendsService{ MediaPlayermediaPlayer; //... @Override publicvoidonDestroy(){ super.onDestroy(); if(mediaPlayer!=null)mediaPlayer.release(); } }
You should always look for other opportunities to release yourMediaPlayer
as well, apart from releasing it when being shut down. For example, if you expect not
to be able to play media for an extended period of time (after losing audio focus, for example),
you should definitely release your existingMediaPlayer
and create it again
later. On the
other hand, if you only expect to stop playback for a very short time, you should probably
hold on to yourMediaPlayer
to avoid the overhead of creating and preparing it
again.
Digital Rights Management (DRM)
Starting with Android 8.0 (API level 26),MediaPlayer
includes APIs that
support the playback of DRM-protected material. They are similar to the low-level API provided by
MediaDrm
,but they operate at a higher level and do not
expose the underlying extractor, drm, and crypto objects.
Although the MediaPlayer DRM API does not provide the full functionality of
MediaDrm
,it supports the most common use cases. The
current implementation can handle the following content types:
- Widevine-protected local media files
- Widevine-protected remote/streaming media files
The following code snippet demonstrates how to use the new DRM MediaPlayer methods in a simple synchronous implementation.
To manage DRM-controlled media, you need to include the new methods alongside the usual flow of MediaPlayer calls, as shown below:
Kotlin
mediaPlayer?.apply{ setDataSource() setOnDrmConfigHelper()// optional, for custom configuration prepare() drmInfo?.also{ prepareDrm() getKeyRequest() provideKeyResponse() } // MediaPlayer is now ready to use start() //...play/pause/resume... stop() releaseDrm() }
Java
setDataSource(); setOnDrmConfigHelper();// optional, for custom configuration prepare(); if(getDrmInfo()!=null){ prepareDrm(); getKeyRequest(); provideKeyResponse(); } // MediaPlayer is now ready to use start(); //...play/pause/resume... stop(); releaseDrm();
Start by initializing theMediaPlayer
object and setting
its source usingsetDataSource()
,
as usual. Then, to use DRM, perform these steps:
- If you want your app to perform custom configuration, define an
OnDrmConfigHelper
interface, and attach it to the player usingsetOnDrmConfigHelper()
. - Call
prepare()
. - Call
getDrmInfo()
.If the source has DRM content, the method returns a non-nullMediaPlayer.DrmInfo
value.
IfMediaPlayer.DrmInfo
exists:
- Examine the map of available UUIDs and choose one.
- Prepare the DRM configuration for the current source by calling
prepareDrm()
.
- If you created and registered an
OnDrmConfigHelper
callback, it is called whileprepareDrm()
is executing. This lets you perform custom configuration of the DRM properties before opening the DRM session. The callback is called synchronously in the thread that calledprepareDrm()
.To access the DRM properties, callgetDrmPropertyString()
andsetDrmPropertyString()
. Avoid performing lengthy operations. - If the device has not yet been provisioned,
prepareDrm()
also accesses the provisioning server to provision the device. This can take a variable amount of time, depending on the network connectivity.
getKeyRequest()
.provideKeyResponse()
.The result depends on the type of key request:
- If the response is for an offline key request, the result is a key-set identifier. You can use
this key-set identifier with
restoreKeys()
to restore the keys to a new session. - If the response is for a streaming or release request, the result is null.
RunningprepareDrm()
asynchronously
By default,prepareDrm()
runs synchronously, blocking until preparation is finished. However, the very
first DRM preparation on a new device may also require provisioning, which is
handled internally by
prepareDrm()
,and
may take some time to finish due to the network operation involved. You can
avoid blocking on
prepareDrm()
by
defining and setting aMediaPlayer.OnDrmPreparedListener
.
When you set anOnDrmPreparedListener
,
prepareDrm()
performs the provisioning (if needed) and preparation in the background. When
provisioning and preparation have finished, the listener is called. You should
not make any assumption about the calling sequence or the thread in which the
listener runs (unless the listener is registered with a handler thread).
The listener can be called before or after
prepareDrm()
returns.
Setting up DRM asynchronously
You can initialize the DRM asynchronously by creating and registering the
MediaPlayer.OnDrmInfoListener
for DRM preparation and the
MediaPlayer.OnDrmPreparedListener
to start the player.
They work in conjunction with
prepareAsync()
,as shown below:
Kotlin
setOnPreparedListener() setOnDrmInfoListener() setDataSource() prepareAsync() //... // If the data source content is protected you receive a call to the onDrmInfo() callback. overridefunonDrmInfo(mediaPlayer:MediaPlayer,drmInfo:MediaPlayer.DrmInfo){ mediaPlayer.apply{ prepareDrm() getKeyRequest() provideKeyResponse() } } // When prepareAsync() finishes, you receive a call to the onPrepared() callback. // If there is a DRM, onDrmInfo() sets it up before executing this callback, // so you can start the player. overridefunonPrepared(mediaPlayer:MediaPlayer){ mediaPlayer.start() }
Java
setOnPreparedListener(); setOnDrmInfoListener(); setDataSource(); prepareAsync(); //... // If the data source content is protected you receive a call to the onDrmInfo() callback. onDrmInfo(){ prepareDrm(); getKeyRequest(); provideKeyResponse(); } // When prepareAsync() finishes, you receive a call to the onPrepared() callback. // If there is a DRM, onDrmInfo() sets it up before executing this callback, // so you can start the player. onPrepared(){ start(); }
Handling encrypted media
Starting with Android 8.0 (API level 26)MediaPlayer
can also decrypt
Common Encryption Scheme (CENC) and
HLS sample-level encrypted media (METHOD=SAMPLE-AES) for the elementary stream types
H.264, and AAC. Full-segment encrypted media (METHOD=AES-128) was previously supported.
Retrieving media from a ContentResolver
Another feature that may be useful in a media player application is the ability to
retrieve music that the user has on the device. You can do that by querying theContentResolver
for external media:
Kotlin
valresolver:ContentResolver=contentResolver valuri=android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI valcursor:Cursor?=resolver.query(uri,null,null,null,null) when{ cursor==null->{ // query failed, handle error. } !cursor.moveToFirst()->{ // no media on the device } else->{ valtitleColumn:Int=cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE) validColumn:Int=cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID) do{ valthisId=cursor.getLong(idColumn) valthisTitle=cursor.getString(titleColumn) //...process entry... }while(cursor.moveToNext()) } } cursor?.close()
Java
ContentResolvercontentResolver=getContentResolver(); Uriuri=android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; Cursorcursor=contentResolver.query(uri,null,null,null,null); if(cursor==null){ // query failed, handle error. }elseif(!cursor.moveToFirst()){ // no media on the device }else{ inttitleColumn=cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE); intidColumn=cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID); do{ longthisId=cursor.getLong(idColumn); StringthisTitle=cursor.getString(titleColumn); //...process entry... }while(cursor.moveToNext()); }
To use this with theMediaPlayer
,you can do this:
Kotlin
valid:Long=/* retrieve it from somewhere */ valcontentUri:Uri= ContentUris.withAppendedId(android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,id) mediaPlayer=MediaPlayer().apply{ setAudioAttributes( AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setUsage(AudioAttributes.USAGE_MEDIA) .build() ) setDataSource(applicationContext,contentUri) } //...prepare and start...
Java
longid=/* retrieve it from somewhere */; UricontentUri=ContentUris.withAppendedId( android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,id); mediaPlayer=newMediaPlayer(); mediaPlayer.setAudioAttributes( newAudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setUsage(AudioAttributes.USAGE_MEDIA) .build() ); mediaPlayer.setDataSource(getApplicationContext(),contentUri); //...prepare and start...
Learn more
These pages cover topics relating to recording, storing, and playing back audio and video.