- Android Media Playback
- Implementing Media Session and Media Player in Android
- Media Playback Life Cycle
- Comparing Media Players
- Media Formats
- Steps to Implement Media Playback (ExoPlayer) in Android
- Customizing SimpleExoPlayerView
- Other ExoPlayer Features
- Other Android Media Framework Features
- Conclusion
- Media Playback
- In this document
- Key classes
- See also
- The Basics
- Manifest Declarations
- Using MediaPlayer
- Asynchronous Preparation
- Managing State
- Releasing the MediaPlayer
- Using a Service with MediaPlayer
- Running asynchronously
- Handling asynchronous errors
- Using wake locks
- Running as a foreground service
- Handling audio focus
- Performing cleanup
- Handling the AUDIO_BECOMING_NOISY Intent
- Retrieving Media from a Content Resolver
Android Media Playback
Using media in Android apps is a common task and nearly every use case shares common features.These common features are:
- Media player: This is a component that can take in digital media and render it as audio or video.
- Media controller: This is the piece of the UI that controls the media player and includes all of the UI buttons such play, pause, rewind and forward.
There are many options when it comes to these components including building from the scratch for your specific needs , but the nature of their interaction is nearly the same for every application.
That is why the Android Framework provides two classes that impose a well defined structure for building media player apps. The two classes are media session and media controller. These two classes communicate with each other so that the UI stays in sync with the player.They communicate using predefined callbacks that correspond to standard player actions such as play, stop, pause, rewind and forward. They also allow you to include extensible custom calls that you can use to define special behaviors that are unique to your app.
Implementing Media Session and Media Player in Android
Implementing media session and media player in Android depends on the use case. Apps that play audio exclusively have different requirements to apps that play both audio and video. When playing both audio and video, the UI is expected to remain visible for the duration of the playback since the user is watching the video. So , navigating away from the UI should normally terminate the playback. However, when playing only video, you will most likely want the playback to continue even if the user navigates away from the UI.
Media Playback Life Cycle
For video apps, the life cycle of the player is completely tied to the life cycle of the UI. Therefore, all components of the media player can be implemented in a single activity which contains both the media controller the media session.
For audio apps, the player may out live the activity that started it if the user navigates away .Therefore, your media session should run in a service, which updates your UI activity when the player state changes.
Comparing Media Players
The Android framework provides a number of options on the types of media players to use in your app.
- Media Player: Media player provides the basic functionality for a bare bones player that supports the most common audio and video formats and data sources. This player supports very little customization but is very straightforward to use and is good enough for many use cases.
- ExoPlayer: This is an open source library that exposes the lower-level Android audio APIs. ExoPlayer supports high performance features like DASH and HLS (adaptive streaming technologies) which are not found Media Player. Further, you can customize code hence making it easy to add new components.
- YouTube: If your use case is playing video specifically from YouTube, you can checkout the YouTube developer APIs.
- Custom Media Player: You can also build a custom media player from low-level media APIs such as MediaCodec, AudoTrack and MediaDRM, that meets the exact needs of your media app.
Media Formats
Media exists in a variety of different formats at different levels. From highest level to the lowest level, these include:
- Sample Formats: These are the formats of the individual media samples. Example include: H.264 for video and AAC for audio.
- Container Formats: This houses the media samples and the associated metadata. Example include: MP4 for video and MP3 for audio.
- Adaptive Streaming Technologies: These are not media formats as such, however, it is necessary to define the level of support ExoPlayer supports. Examples include DASH, smooth streaming and HLS.
Steps to Implement Media Playback (ExoPlayer) in Android
Before, you implement ExoPlayer in your Android app, ensure that the following requirements:
- A recent version of Android Studio (>=3.0)
- An Android device with Android 4.1 or greater.
Once you have confirmed, proceed to implement ExoPlayer by following these steps on Codelabs.
Customizing SimpleExoPlayerView
ExoPlayer comes with two notable out of the box UI elements:
- PlaybackControlView: This is a view for controlling ExoPlayer instances. It displays standard playback controls including a play/pause button, fast-forward and rewind buttons, and a seek bar.
- SimpleExoPlayerView: It is a high level view for SimpleExoPlayer media playbacks. It displays video (or album art in our case) and displays playback controls using a PlaybackControlView.
These ExoPlayer UI components can be customized in the following ways: follows:
- Attributes: The XML items support a variety of XML attributes that customize the look of the UI. Take a look at the documentation for the UI element to see the list of possible attributes (and their corresponding Java methods).
- Overriding Layout Files: When these views are inflated, they use specific layout files to determine what the UI looks like. For SimpleExoPlayerView, this file is called: exo_simple_player_view.xml. This layout file includes a PlayBackControlView (once it’s inflated, it replaces the exo_controller_placeholder item) which also uses its own layout file: exo_playback_control_view.xml. If you include any layout files with the same names, ExoPlayer will use them instead of these default ones. This allows you to fully customize what the UI looks like.
Other ExoPlayer Features
- Subtitle Side Loading: Given a video file and a separate subtitle file, MergingMediaSource can be used to merge them into a single source for playback.
- Looping a Video: A video can be seamlessly looped using a LoopingMediaSource. The following example loops a video indefinitely. It’s also possible to specify a finite loop count when creating a LoopingMediaSource.
Other Android Media Framework Features
- Audio Focus: This is how the Android framework knows about different applications using audio. If you want your app to fade out when other important notifications (such as navigation) occur, you’ll need to learn how your app can “hop in line” to be the one in charge of audio playback, until another app requests focus.
- Noisy Intent: There are certain conditions that you will want to check for. For example, imagine you are blasting your favorite song at full volume. Right when it’s about to get to the best part, you trip and yank out the headphones from the audio port. Suddenly the whole world knows your secret. Not the best experience right? Luckily the android framework sends out the ACTION_AUDIO_BECOMING_NOISY intent when this occurs. This allows you to register a broadcast receiver and take a specific action when this occurs (like pausing the music and saving yourself of embarrassment).
- Audio Stream: Android uses separate audio streams for playing music, alarms, notifications, the incoming call ringer, system sounds, in-call volume, and DTMF tones. This allows users to control the volume of each stream independently. By default, pressing the volume control modifies the volume of the active audio stream. If your app isn’t currently playing anything, hitting the volume keys adjusts the ringer volume. To ensure that volume controls adjust the correct stream, you should call setVolumeControlStream() passing in AudioManager.STREAM_MUSIC.
Conclusion
You should choose the media player that makes the most sense for your use case. But generally the customizability of ExoPlayer makes it the preferred choice since its support of many different formats and extensible modular features makes it usable in almost every use case. Further, because ExoPlayer is a Library that you include in your APK, you have control over which version you use and you can easily update to a newer version as part of a regular application update.
Источник
Media Playback
In this document
Key classes
See also
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 using MediaPlayer APIs.
This document shows you how 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.
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.
- Wake Lock Permission — If your player application needs to keep the screen from dimming or the processor from sleeping, or uses the MediaPlayer.setScreenOnWhilePlaying() or MediaPlayer.setWakeMode() methods, you must request this permission.
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 the Android Supported Media Formats document.
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):
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):
Playing from a remote URL via HTTP streaming looks like this:
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 and IOException when using setDataSource() , because the file you are referencing might not exist.
Asynchronous Preparation
Using MediaPlayer 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 to prepare() 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 should never call it from your application’s UI thread. Doing that will cause 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 will cause a noticeable pause and will give the user the impression that your application is slow.
To avoid hanging your UI thread, spawn another thread to prepare the MediaPlayer and notify the main thread when done. However, while you could write the threading logic yourself, this pattern is so common when using MediaPlayer 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, the onPrepared() method of the MediaPlayer.OnPreparedListener , configured through setOnPreparedListener() is called.
Managing State
Another aspect of a MediaPlayer that you should keep in mind is that it’s state-based. That is, the MediaPlayer has an internal state that you must always be aware of when writing your code, because certain operations are only valid when then player is in specific states. If you perform an operation while in the wrong state, the system may throw an exception or cause other undesireable behaviors.
The documentation in the MediaPlayer class shows a complete state diagram, that clarifies which methods move the MediaPlayer from one state to another. For example, when you create a new MediaPlayer , it is in the Idle state. At that point, you should initialize it by calling setDataSource() , bringing it to the Initialized state. After that, you have to prepare it using either the prepare() or prepareAsync() method. When the MediaPlayer is done preparing, it will then enter the Prepared state, which means you can call start() to make it play the media. At that point, as the diagram illustrates, you can move between the Started, Paused and PlaybackCompleted states by calling such methods as start() , pause() , and seekTo() , amongst others. When you call stop() , however, notice that you cannot call start() again until you prepare the MediaPlayer again.
Always keep the 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
A MediaPlayer can consume valuable system resources. Therefore, you should always take extra precautions to make sure you are not hanging on to a MediaPlayer 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 a MediaPlayer and your activity receives a call to onStop() , you must release the MediaPlayer , 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 new MediaPlayer and prepare it again before resuming playback.
Here’s how you should release and then nullify your MediaPlayer :
As an example, consider the problems that could happen if you forgot to release the MediaPlayer 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 new MediaPlayer that you never release. (For more information about runtime restarts, see Handling 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 a MediaPlayer controlled by a Service , as discussed in Using a Service with MediaPlayer.
Using a Service with MediaPlayer
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 should be careful about this setup, because the user and the system have expectations about how an application running a background service should interact with the rest of the system. If your application does not fulfil those expectations, the user may have a bad experience. This section describes the main issues that you should be aware of and offers suggestions about how to approach them.
Running asynchronously
First of all, like an Activity , 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 a MediaPlayer from your main thread, you should call prepareAsync() rather than prepare() , and implement a MediaPlayer.OnPreparedListener in order to be notified when the preparation is complete and you can start playing. For example:
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 a MediaPlayer , you can accomplish this by implementing a MediaPlayer.OnErrorListener and setting it in your MediaPlayer instance:
It’s important to remember that when an error occurs, the MediaPlayer moves to the Error state (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 your MediaPlayer is playing, call the setWakeMode() method when initializing your MediaPlayer . Once you do, the MediaPlayer holds the specified lock while playing and releases the lock when paused or stopped:
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:
When you pause or stop your media, or when you no longer need the network, you should release the lock:
Running as a foreground service
Services are often used for performing background tasks, such as fetching emails, synchronizing data, downloading content, amongst other possibilities. In these cases, the user is not actively aware of the service’s execution, and probably wouldn’t even notice if some of these services were interrupted and later restarted.
But consider the case of a service that is playing music. Clearly this is a service that the user is actively aware of and the experience would be severely affected by any interruptions. Additionally, it’s a service that the user will likely wish to interact with during its execution. In this case, the service should run as a «foreground service.» A foreground service holds a higher level of importance within the system—the system will almost never kill the service, because it is of immediate importance to the user. When running in the foreground, the service also must provide a status bar notification to ensure that users are aware of the running service and allow them to open an activity that can interact with the service.
In order to turn your service into a foreground service, you must create a Notification for the status bar and call startForeground() from the Service . For example:
While your service is running in the foreground, the notification you configured is visible in the notification area of the device. If the user selects the notification, the system invokes the PendingIntent you supplied. In the example above, it opens an activity ( MainActivity ).
Figure 1 shows how your notification appears to the user:
Figure 1. Screenshots of a foreground service’s notification, showing the notification icon in the status bar (left) and the expanded view (right).
You should only hold on to the «foreground service» status while your service is actually performing something the user is actively aware of. Once that is no longer true, you should release it by calling stopForeground() :
For more information, see the documentation about Services and Status Bar Notifications.
Handling audio focus
Even though only one activity can run at any given time, Android is a multi-tasking environment. This poses a particular challenge to applications that use audio, because there is only one audio output and there may be several media services competing for its use. Before Android 2.2, there was no built-in mechanism to address this issue, which could in some cases lead to a bad user experience. For example, when a user is listening to music and another application needs to notify the user of something very important, the user might not hear the notification tone due to the loud music. Starting with Android 2.2, the platform offers a way for applications to negotiate their use of the device’s audio output. This mechanism is called Audio Focus.
When your application needs to output audio such as music or a notification, you should always request audio focus. Once it has focus, it can use the sound output freely, but it should always listen for focus changes. If it is notified that it has lost the audio focus, it should immediately either kill the audio or lower it to a quiet level (known as «ducking»—there is a flag that indicates which one is appropriate) and only resume loud playback after it receives focus again.
Audio Focus is cooperative in nature. That is, applications are expected (and highly encouraged) to comply with the audio focus guidelines, but the rules are not enforced by the system. If an application wants to play loud music even after losing audio focus, nothing in the system will prevent that. However, the user is more likely to have a bad experience and will be more likely to uninstall the misbehaving application.
To request audio focus, you must call requestAudioFocus() from the AudioManager , as the example below demonstrates:
The first parameter to requestAudioFocus() is an AudioManager.OnAudioFocusChangeListener , whose onAudioFocusChange() method is called whenever there is a change in audio focus. Therefore, you should also implement this interface on your service and activities. For example:
The focusChange parameter tells you how the audio focus has changed, and can be one of the following values (they are all constants defined in AudioManager ):
- AUDIOFOCUS_GAIN : You have gained the audio focus.
- AUDIOFOCUS_LOSS : You have lost the audio focus for a presumably long time. You must stop all audio playback. Because you should expect not to have focus back for a long time, this would be a good place to clean up your resources as much as possible. For example, you should release the MediaPlayer .
- AUDIOFOCUS_LOSS_TRANSIENT : You have temporarily lost audio focus, but should receive it back shortly. You must stop all audio playback, but you can keep your resources because you will probably get focus back shortly.
- AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK : You have temporarily lost audio focus, but you are allowed to continue to play audio quietly (at a low volume) instead of killing audio completely.
Here is an example implementation:
Keep in mind that the audio focus APIs are available only with API level 8 (Android 2.2) and above, so if you want to support previous versions of Android, you should adopt a backward compatibility strategy that allows you to use this feature if available, and fall back seamlessly if not.
You can achieve backward compatibility either by calling the audio focus methods by reflection or by implementing all the audio focus features in a separate class (say, AudioFocusHelper ). Here is an example of such a class:
You can create an instance of AudioFocusHelper class only if you detect that the system is running API level 8 or above. For example:
Performing cleanup
As mentioned earlier, a MediaPlayer 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 the MediaPlayer , 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 the MediaPlayer :
You should always look for other opportunities to release your MediaPlayer 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 existing MediaPlayer 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 your MediaPlayer to avoid the overhead of creating and preparing it again.
Handling the AUDIO_BECOMING_NOISY Intent
Many well-written applications that play audio automatically stop playback when an event occurs that causes the audio to become noisy (ouput through external speakers). For instance, this might happen when a user is listening to music through headphones and accidentally disconnects the headphones from the device. However, this behavior does not happen automatically. If you don’t implement this feature, audio plays out of the device’s external speakers, which might not be what the user wants.
You can ensure your app stops playing music in these situations by handling the ACTION_AUDIO_BECOMING_NOISY intent, for which you can register a receiver by adding the following to your manifest:
This registers the MusicIntentReceiver class as a broadcast receiver for that intent. You should then implement this class:
Retrieving Media from a Content Resolver
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 the ContentResolver for external media:
To use this with the MediaPlayer , you can do this:
Источник