Overview: Background audio (Windows Phone Store apps)
You can write apps for Windows Phone 8.1 that play audio in the background. This means that even after the user has left your app by pressing the Back button or the Start button on their device, your app can continue to play audio. This article discusses the components of a background audio app and how they work together.
Scenarios for background audio playback include:
- Long-running playlists The user briefly brings up a foreground app to select and start a playlist, after which the user expects the playlist to continue playing in the background.
- Using task switcher The user briefly brings up a foreground app to start playing audio, then switches to another open app using the task switch. The user expects the audio to continue playing in the background.
A background audio app uses background agents. However, playing audio in the background in Windows Phone 8.1 is different from the way background audio is played in Windows 8. The model also differs from background audio agents used in earlier versions of Windows Phone.
The Windows.Media.Playback namespace introduces generic audio APIs and can be used to play music even in the foreground, but their primary purpose is to play audio in the background. When using this API, there is a single global MediaPlayer through which all playback occurs. Your background audio app sends commands to the media player to set the current track, start playback, pause, fast forward, rewind, and so on. You do this by calling methods on the MediaPlayer class. The media player instance object, accessed through the BackgroundMediaPlayer.Current property, communicates with the global media player to manipulate audio playback.
You cannot create new instances of MediaPlayer.
Use the SystemMediaTransportControls class to raise events that are used by the Universal Volume Control (UVC). The UVC is the UI that appears when the app user presses the volume control on their device. You use the SystemMediaTransportControls to manipulate the media player. In addition to start playing audio from your app, you can control the audio playback. For example, use the SystemMediaTransportControls to send events to the IBackgroundTask in your app, and you can implement playlist logic. There is more discussion of the IBackgroundTask later in this topic.
The following diagram is a very simple view of how the system is designed. An app doing background playback consists of two processes. The first process is the main app, which contains the UI, running in the foreground. The second process is the background playback task, which contains the audio playback machinery, and optionally, some app logic. The foreground process is suspended or terminated by the operating system based on its resource needs. The background process continues to run.
While audio playback occurs in the background process, the foreground process has access to all the information via proxy objects. The foreground process can manage the properties of the MediaPlayer instance in the background process. The foreground app can receive notification of media-specific events like MediaOpened, MediaEnded, and MediaFailed. When the foreground process terminates or the app is suspended, the media continues to play.
SystemMediaTransportControls is a new API set introduced in Windows 8.1. Windows Phone 8.1 also implements this class, but because Windows Phone has one global volume control, only one process can interact with it at a time. In context of MediaPlayer APIs, it is extremely important to define the instance and all handlers in the background process. This will ensure the connection is bound to the right process in case the foreground app is terminated.
There are times when you will want to communicate between the two processes of a background audio app. For example, you might want the background task to notify the foreground task when a new track starts playing, and then send the new song title to the foreground task to display on the screen. A simple communication mechanism raises events in both the foreground and background processes. The SendMessageToForeground and SendMessageToBackground methods each invoke events in the corresponding task. Data can be passed as an argument to the event handler in the receiving task. Pass data using a new class called ValueSet. This class is a dictionary that contains a string as a key and other value types as values. You can pass simple value types such as int, string, bool, and so on.
The lifetime of a background task is closely tied to the ability of the app to play music. For example, when a user pauses audio playback, the system may terminate or cancel your app depending on the circumstances.
Your background task starts the first time your app accesses BackgroundMediaPlayer.Current in foreground app code or when you register a handler for the MessageReceivedFromBackground event, whichever occurs first. To ensure that communications channels are established when the IBackgroundTask.Run method is called, you must register the MessageReceivedFromBackground event before accessing the BackgroundMediaPlayer.Current property for the first time. Your app should wait for your background task to be running before attempting to start any audio playback. This way you will able to subscribe to media events.
To keep the background task alive, your app will have to get a BackgroundTaskDeferral in the Run method and call BackgroundTaskDeferral.Complete when the task instance receives the Canceled or Completed events. Do not wait in the Run method because this uses resources and could cause your app's background task to be terminated.
Your background task gets the Completed event when the Run method is completed and deferral is not requested. In some cases, when your app gets the Canceled event, it can be also followed by the Completed event.
A background task can be cancelled in the following situations:
- A new app with audio playback capabilities starts.
- A background task has been launched but music is not yet playing, and then the foreground app is suspended.
- Some time after the background task has been launched, playback is paused and the foreground app is suspended. Playback can be paused by the user or by other media interruptions, such as incoming phone calls or VoIP calls. If the phone call or VoIP call ends within five minutes, your app will receive the Run notification with the SystemMediaTransportControlsButton.Play button indicated in the SystemMediaTransportControlsButtonPressedEventArgs. If not, the user will have to explicitly initiate playback by using the UVC. The UVC does not lose its state. However, when the user presses the Play button, the background task is relaunched. You then get a Run method call and a SystemMediaTransportControlsButton.Play notification.
A background task can be terminated without warning under the following circumstances:
- A VoIP call comes in and there is not enough memory.
- A resource policy is violated.
- Task cancellation or completion does not end gracefully.
The media pipeline is asynchronous in nature, which means the events fired only guarantees the occurrence of those events but not the order of them. For example, when your app fetches an audio file from a remote source, the app will get various state changed events, such as Starting, Paused, Closed, and so on. These handlers are not necessarily called in the same order each time. You should not rely heavily on the CurrentState values in your CurrentStateChanged handler.
MediaOpened is a very important event and plays multiple roles. If you have set AutoPlay to false and you set source for MediaPlayer, you will still receive MediaOpened. This indicates that the media pipeline is initiated and your media is ready to play. When you set a source, media playback starts automatically. You don’t need to call Play after setting the source. Another very good technique is to play explicitly when media is ready. This can be done by setting AutoPlay to false and specifically calling Play in your MediaOpened handler.
You can set the media source to several different types of content by calling SetUriSource, SetFileSource, SetMediaSource, or SetStreamSource. MediaPlayer also has the ability to play protected content. Other than setting a URI source, the system depends on app objects or app code running in memory. Also, the background process will not be aware of process memory in the foreground task. So, make sure you define all objects to set their source in the background process only. The system will throw an InvalidCastException if your app tries to set a source other than a URI in the foreground process.
BackgroundMediaPlayer.Shutdown closes the media pipeline and release the MediaPlayer object from memory. If you try to access a reference to BackgroundMediaPlayer.Current again after calling Shutdown, you will get an error. Shutdown is meant for an app to clean the media pipeline when its task is cancelled.
When your app gets suspended, remember to unsubscribe from MediaPlayer events or you may experience undesired activity which could potentially lead to foreground process termination. However, this does not mean the foreground process will go away from the task switcher and the user will still be able to go back to the app. When your app resumes, if you are still holding a reference to a suspended MediaPlayer, it will throw an error if the background audio task was cancelled and the media pipeline was shut down.