In This Chapter
• Using the background audio player with an audio player agent
• Creating a UI for controlling the background audio player
• The Universal Volume Control (UVC)
• Responding to user actions in an audio player agent
• Unit testing and the background audio player
• Monitoring audio playback progress
• Creating audio streaming agents
• Using a custom audio streaming agent to play audio stored as assembly resources
Windows Phone allows you to create apps that respond to the Universal Volume Control (UVC) on the phone and, in conjunction with an onscreen menu, coordinate the playback of audio, even when your app is not running in the foreground.
This chapter begins with an overview of the background audio player and shows how it is used to control the playback of local or remote audio files. You look at how audio file information is represented by the AudioTrack
class and examine the role of background audio agents, which are used to coordinate audio playback while your app is not running in the foreground.
The chapter then demonstrates how to build a UI that leverages the background audio agent for controlling playback while your app is in the foreground.
Finally, the chapter examines audio streaming agents and how they are used to provide custom streaming and decoding of audio files. You see how a custom audio streaming agent can be employed to play audio directly from an assembly resource, something that is not possible using the built-in streaming and decoding system of the phone.
In Chapter 27, “Scheduled Actions,” you learned that Windows Phone supports the following three types of BackgroundAgents
:
• ScheduledTaskAgent
(presented in Chapter 27)
• AudioPlayerAgent
• AudioStreamingAgent
Background agents are classes invoked by the OS at certain times to carry out activities in the background (from another process) while your app may not actually be running in the foreground.
This chapter examines the last two agent types: AudioPlayerAgent
and AudioStreamingAgent
.
The following comprise the three main classes used for background audio, which are explored in this chapter:
• BackgroundAudioPlayer
• AudioPlayerAgent
• AudioStreamingAgent
BackgroundAudioPlayer
is a class that allows you to specify an audio file for playback and to carry out playback activities such as play, pause, fast-forward, and rewind. BackgroundAudioPlayer
allows you to specify track information, such as the title of the track and its location. The built-in playback capabilities of the BackgroundAudioPlayer
allow audio files to be streamed from either a remote server or locally from isolated storage.
Out of the box, BackgroundAudioPlayer
supports playback formats such as MP3, WMA, and WAV. For a complete list of supported audio formats see http://bit.ly/pnnT8C. BackgroundAudioPlayer
offers little in the way of coordinating the playback of a list of audio tracks. This is the task of the AudioPlayerAgent
.
AudioPlayerAgent
classes are specialized agents used to retrieve audio files and coordinate the playback of background audio via the BackgroundAudioPlayer
. AudioPlayerAgent
provides a number of method callbacks that are automatically called by the OS based on user actions.
An AudioStreamingAgent
can be used to customize how audio data is procured by your BackgroundAudioPlayer
. You can supplant the BackgroundAudioPlayer
’s media sourcing capabilities by providing your own logic to download and decode an audio file.
The BackgroundAudioPlayer
class implements the singleton pattern. The single instance of the BackgroundAudioPlayer
is accessed in your foreground app via its static Instance
property, as shown:
BackgroundAudioPlayer.Instance.Track
= new AudioTrack(
new Uri("http://example.com/Audio.mp3", UriKind.Absolute),
"Track Name", "Artist", "Album", null);
BackgroundAudioPlayer.Instance.Play();
The behavior of the BackgroundAudioPlayer
changes depending on whether it is being used in your foreground app or from an AudioPlayerAgent
. When the BackgroundAudioPlayer
is used in your foreground app, all calls are relayed to your AudioPlayerAgent
. When used from an AudioPlayerAgent
, the BackgroundAudioPlayer
affects the playback of audio directly. Without an AudioPlayerAgent
, the BackgroundAudioPlayer
does nothing.
This dual behavior can be best understood by looking at the internal workings of the BackgroundAudioPlayer
. The BackgroundAudioPlayer
is actually a proxy that relies on a native implementation of an internal IAudioPlaybackManager
interface. When the BackgroundAudioPlayer
is used in your foreground app, the IAudioPlaybackManager
relays all audio requests to the app’s AudioPlayerAgent
. Conversely, when used from your AudioPlayerAgent
, the IAudioPlaybackManager
uses the native audio playback system instead.
BackgroundAudioPlayer
contains several public methods that allow you to control the playback of audio (see Table 29.1).
BackgroundAudioPlayer
contains several public audio playback related properties, which are shown in Table 29.2.
BackgroundAudioPlayer
contains a principle event, PlayStateChanged
, which is used to detect, for example, when a track begins playing. You see later in the chapter how this event can be used to hide and show UI elements based on the current state of the player.
The AudioTrack
class includes information about the location of an audio file, as well as track metadata such as the track’s title. Table 29.3 describes each of the AudioTrack
properties.
AudioTrack
implements IEditableObject
to enable batching of property change requests. Once an AudioTrack
object has been instantiated, it cannot be changed without putting it in edit mode first. AudioTrack
uses an IAudioTrack
backing field, which links the managed AudioTrack
object to the native audio system via Com Interop. To make changes to an AudioTrack
object, its BeginEdit
method must be called. Once all changes have been made, EndEdit
should be called. This commits the property changes to the OS via the native IAudioTrack
object.
An audio player agent can be created by selecting the Windows Phone Audio Playback Agent project type from the Visual Studio New Project dialog (see Figure 29.1). This produces a new project that includes a class that derives from AudioPlayerAgent
.
When you link to an Audio Player Agent project from your main project, a BackgroundServiceAgent
task definition is added to your main project’s WMAppManifest.xml file, resembling the following:
<Tasks>
<DefaultTask Name="_default" NavigationPage="MainPage.xaml"/>
<ExtendedTask Name="BackgroundTask">
<BackgroundServiceAgent Specifier="AudioPlayerAgent"
Name="<Unique name within the app>"
Source="<Assembly>"
Type="<Namespace.Class>" />
</ExtendedTask>
</Tasks>
It is not necessary to use Visual Studio’s Audio Player Agent project template to create a custom background audio agent. You can create the class and add the task definition to the WMAppManifest.xml file manually if you so choose.
The custom audio player agent for this section includes a simple playlist, which includes both locally stored and remote audio files. The class is called BackgroundAudioPlayerAgent
, and it is located in the WindowsPhone7Unleashed.BackgroundAudio project, within the BackgroundAudio solution in the downloadable sample code.
A static list of audio tracks defines the playlist, shown in the following excerpt:
static readonly List<AudioTrack> playList
= new List<AudioTrack>
{
new AudioTrack(new Uri("AudioFiles/Audio01.wma",
UriKind.Relative),
"Ringtone 1", // title
"Windows Phone", // artist
"Windows Phone Ringtones", // album
null),
/* ... */
/* A remote URI. */
new AudioTrack(new Uri("http://example.com/track.mp3",
UriKind.Absolute),
"Episode 29",
"Windows Phone Radio",
"Windows Phone Radio Podcast",
null)
};
AudioPlayerAgents
remain active during playback. This allows the creation of dynamic playlists. During playback, you could retrieve a playlist on a background thread from a remote server.
If providing playback of local media files, the files must be copied to isolated storage before they can be played by the background audio player. The IIsolatedStorageUtility
is a custom interface that abstracts access to isolated storage. It is used by the BackgroundAudioPlayerAgent
to detect whether the media files have already been written to isolated storage; if not, it is used to copy the sample files. The downloadable sample code contains all the code for the IIsolatedStorageUtility
and its default implementation IsolatedStorageUtility
. See the following excerpt:
internal static void CopyAudioToIsolatedStorage(
IIsolatedStorageUtility isolatedStorageUtility)
{
ArgumentValidator.AssertNotNull(
isolatedStorageUtility, "isolatedStorageUtility");
/* If a file exists, do not copy. */
if (isolatedStorageUtility.FileExists("AudioFiles/Audio01.wma"))
{
return;
}
string[] files = new[] { "Audio01.wma", "Audio02.wma", "Audio03.wma" };
var sourceToDestinations = from file in files
let path = "AudioFiles/" + file
select new KeyValuePair<string, string>(path, path);
isolatedStorageUtility.CopyApplicationResourcesToIsolatedStorage(
sourceToDestinations);
}
The custom BackgroundAudioPlayerAgent
keeps track of the current track number using a static int
field. This is made possible because, unlike its ScheduledTaskAgent
counterpart, the process for an AudioPlayerAgent
is not terminated when NotifyComplete
is called.
The track number field is used to select the correct track to play when the user requests the previous or next track, as shown:
void PlayNextTrack(BackgroundAudioPlayer player)
{
if (++trackNumber >= playList.Count)
{
trackNumber = 0;
}
PlayTrack(player);
}
void PlayPreviousTrack(BackgroundAudioPlayer player)
{
if (--trackNumber < 0)
{
trackNumber = playList.Count - 1;
}
PlayTrack(player);
}
A PlayTrack
method is used to toggle the play state of the BackgroundAudioPlayer
, as shown:
void PlayTrack(BackgroundAudioPlayer player)
{
if (player.PlayerState == PlayState.Paused)
{
player.Play();
}
else
{
player.Track = playList[trackNumber];
}
}
During playback, when the BackgroundAudioPlayer.Track
property is set, the track does not begin immediate playback, but rather the BackgroundAudioPlayer
is placed into a TrackReady state, and the AudioPlayerAgent.OnPlayStateChanged
method is called. You see how this fits together in a moment.
AudioPlayerAgent
has the following three virtual methods that can be overridden in your implementation:
• OnPlayStateChanged
• OnUserAction
• OnError
The following sections explore each method in detail.
OnPlayStateChanged
allows you to respond to changes in the playback state of an audio track. For example, it allows you to transition the play state of a track once it has completed downloading or to move to the next track when a track finishes, as shown in the following excerpt:
protected override void OnPlayStateChanged(
BackgroundAudioPlayer player, AudioTrack track, PlayState playState)
{
switch (playState)
{
case PlayState.TrackReady:
/* The track to play is set in the PlayTrack method. */
player.Play();
break;
case PlayState.TrackEnded:
PlayNextTrack(player);
break;
}
NotifyComplete();
}
Table 29.4 describes the PlayState
enum values that can be handled in the OnPlayStateChanged
method.
The OnPlayStateChanged
method is used to respond to playback state changes, but not to errors that occur during playback. Use the OnError
to respond to such errors.
The name of the OnUserAction
method is somewhat misleading. The task of the OnUserAction
method is to handle BackgroundAudioPlayer
actions that occur in your foreground app.
Two actions cause the AudioPlayerAgent.OnUserAction
method to be called. The first action occurs when the user presses the UVC and an onscreen menu is displayed allowing the user to play, pause, and skip forward and backward between tracks (see Figure 29.2). User actions do not directly affect the BackgroundAudioPlayer
, but rather your AudioPlayerAgent
is responsible for manipulating the player via the OnUserAction
method.
The second action occurs when your app is in the foreground and it calls one of the playback related methods on the BackgroundAudioPlayer.Instance
object, such as Play
. This type of action may or may not have been directed by the user.
The volume-up and volume-down buttons and the play/pause button of the UVC can be simulated in the Windows Phone emulator using the F9, F10, and F11 keys, respectively.
While a function key simulates pressing the play/pause button, the play/pause button is not actually a required hardware button on Windows Phone devices and therefore may not be present on every device.
OnUserAction
is passed a UserAction
enum value that allows you to determine the behavior of the audio player agent. In some cases the player can be put into action immediately. See the following excerpt:
protected override void OnUserAction(
BackgroundAudioPlayer player, AudioTrack track, UserAction action,
object param)
{
/* User actions can be initiated from the main application
* or from the UVC (Universal Volume Control). */
switch (action)
{
case UserAction.Play:
PlayTrack(player);
break;
case UserAction.Pause:
player.Pause();
break;
case UserAction.SkipPrevious:
PlayPreviousTrack(player);
break;
case UserAction.SkipNext:
PlayNextTrack(player);
break;
case UserAction.Stop:
player.Stop();
break;
}
NotifyComplete();
}
Table 29.5 lists the UserAction
enum values that can be handled in the OnUserAction
method.
The param object argument, of the OnUserAction
method, is used only with the action argument UserAction.Seek
, in which case the param argument is a TimeSpan
that indicates the requested track position.
OnError
is called when an Exception
is raised during playback of an audio track. The method is generally called when the agent encounters a connection error when downloading an audio file from a remote server and offers the opportunity to transition to a different track, one that is potentially stored locally if the error is due to an absence of network connectivity.
In the following excerpt, you see that when an error is determined to be fatal, the Abort
method is called, signaling to the OS that playback should be disabled. The method implementation is identical to the base class implementation and need not be implemented if you do not want to provide explicit error handling:
protected override void OnError(BackgroundAudioPlayer player,
AudioTrack track,
Exception error, bool isFatal)
{
if (isFatal)
{
Abort();
}
else
{
NotifyComplete();
}
}
Most apps employing background audio are going to want to provide an enhanced interface within the foreground app for controlling playback and displaying track information. This section looks at the MainPage
and associated classes of the WindowsPhone7Unleashed.BackgroundAudio project. The page allows the user to play audio via the BackgroundAudioPlayer
class. It provides application bar buttons for play and pause, stop, rewind, and forward, and several fields for displaying the current track information.
It is a Windows Phone Marketplace certification requirement that when the user is already playing background music on the phone when your app is launched, your app must ask the user for consent to stop playing or to adjust the background music. This prompt must occur each time the app launches, unless there is an opt-in setting provided to the user, and the user has used this setting to opt-in.
The BackgroundAudioPlayer
class is inherently difficult to use in unit tests because of the cross process interaction with the AudioPlayerAgent
and its dependence on native phone components. For this reason, I have provided a wrapper called BackgroundAudioPlayerProxy
, which implements a custom IBackgroundAudioPlayer
interface. In addition, there is a second implementation: MockBackgroundAudioPlayer
, which allows you to test whether your code called the background audio player as expected; it allows you to test, for example, whether your code attempted to play a track.
The code for IBackgroundAudioPlayer
and its associated class implementations is not shown in this chapter because it is lengthy, and the methods, properties, and events of the IBackgroundAudioPlayer
coincide with those of the built-in BackgroundAudioPlayer
. You can, of course, find the code in the downloadable sample code for the book.
The viewmodel is declared as a field of the MainPage, as shown:
readonly MainPageViewModel viewModel = new MainPageViewModel(
new BackgroundAudioPlayerProxy(),
new IsolatedStorageUtility());
The MainPageViewModel
class constructor accepts an IBackgroundAudioPlayer
as well as an IIsolatedStorageUtility
instance (see Listing 29.1). A subscription to the PlayStateChanged
event of the background audio player allows the viewmodel to update various properties whenever the play state changes.
The viewmodel contains five commands for controlling the BackgroundAudioPlayer
while the app is in the foreground, which provide for playing, pausing, stopping, and skipping between tracks.
The viewmodel’s Refresh
method is called to update the viewmodel, which relies on the state of the BackgroundAudioPlayer
. A Timer
is used to monitor the progress of the track while it is being played.
public MainPageViewModel(
IBackgroundAudioPlayer backgroundAudioPlayer,
IIsolatedStorageUtility isolatedStorageUtility)
{
player = ArgumentValidator.AssertNotNull(
backgroundAudioPlayer, "backgroundAudioPlayer");
ArgumentValidator.AssertNotNull(
isolatedStorageUtility, "isolatedStorageUtility");
player.PlayStateChanged += HandlePlayStateChanged;
BackgroundAudioPlayerAgent.CopyAudioToIsolatedStorage(isolatedStorageUtility);
playCommand = new DelegateCommand(obj => player.Play());
pauseCommand = new DelegateCommand(obj => player.Pause());
stopCommand = new DelegateCommand(obj => player.Stop());
previousTrackCommand = new DelegateCommand(obj => player.SkipPrevious());
nextTrackCommand = new DelegateCommand(obj => player.SkipNext());
Refresh(false);
timer = new Timer(HandleTimerTick, null, 3000, 1000);
}
The viewmodel’s TrackArtist
and TrackTitle
properties reflect the Artist
and Title
properties of the current AudioTrack
.
The viewmodel’s VisualState
property, which is of type string
, determines the visual state of the view and the visibility of various application bar buttons. The VisualState
value is set to the PlayerState
property of the BackgroundAudioPlayer
, as shown:
void Refresh(bool setVisualState = true)
{
switch (player.PlayerState)
{
case PlayState.Playing:
CanPause = true;
TrackArtist = player.Track.Artist;
TrackTitle = player.Track.Title;
break;
case PlayState.Paused:
CanPause = false;
break;
}
BufferingProgress = player.BufferingProgress;
if (setVisualState)
{
VisualState = player.PlayerState.ToString("G");
}
if (player.Error != null)
{
MessageService.ShowError("A problem occured:" + player.Error);
}
}
When the viewmodel’s VisualState
property changes, the view responds by refreshing the VisualStateManager
state. This is performed from the MainPage
constructor, as shown:
public MainPage()
{
InitializeComponent();
DataContext = viewModel;
viewModel.PropertyChanged
+= (sender, args) =>
{
if (args.PropertyName == "VisualState")
{
SetVisualState();
}
};
}
The view’s custom AppBar
includes icon buttons bound to the viewmodel’s commands, as shown:
<u:AppBar>
<u:AppBarIconButton
Command="{Binding PreviousTrackCommand}"
Text="Previous"
IconUri="/Images/ApplicationBarIcons/Previous.png" />
<!-- A toggle button is used rather than visual state to avoid
repopulating the app bar when the user taps play or pause. -->
<u:AppBarToggleButton
x:Name="Button_Play"
Command1="{Binding PlayCommand}"
Text1="Play"
Icon1Uri="/Images/ApplicationBarIcons/Play.png"
Command2="{Binding PauseCommand}"
Text2="Pause"
Icon2Uri="/Images/ApplicationBarIcons/Pause.png"
Toggled="{Binding CanPause}"/>
<u:AppBarIconButton
x:Name="Button_Stop"
Command="{Binding StopCommand}"
Text="Stop"
IconUri="/Images/ApplicationBarIcons/Stop.png" />
<u:AppBarIconButton
Command="{Binding NextTrackCommand}"
Text="Previous"
IconUri="/Images/ApplicationBarIcons/Next.png" />
</u:AppBar>
The viewmodel’s ViewState
property determines the visibility of each button. The XAML that defines the VisualStateGroups
is not shown, but if you are interested, see the downloadable sample code.
Two TextBlock
controls are used to display the viewmodel’s TrackArtist
and TrackTitle
properties, like so:
<TextBlock Text="{Binding TrackArtist}"
Style="{StaticResource PhoneTextTitle3Style}"
TextWrapping="Wrap" />
<TextBlock Text="{Binding TrackTitle}"
Style="{StaticResource PhoneTextTitle2Style}"
TextWrapping="Wrap" />
Whenever the track changes, these fields are updated according to the new track.
The viewmodel contains a Position
property that reflects the progress of the current AudioTrack
. Because the BackgroundAudioPlayer
does not have facility to monitor the progress of a track directly, the viewmodel uses a Timer
to periodically raise a property changed event for the Position
property. The tick handler is shown in the following excerpt:
void HandleTimerTick(object state)
{
if (player.PlayerState == PlayState.Playing)
{
OnPropertyChanged(() => Position);
}
}
When a Position
property change is detected in the view, it prompts a Slider
control to reread the property. The Position
get accessor calculates the position value, which is returned as a value between 0 and 1, as shown:
public double Position
{
get
{
if (player.Track == null || player.Track.Duration.TotalSeconds < 1)
{
return 0;
}
double result = player.Position.TotalSeconds
/ player.Track.Duration.TotalSeconds;
return result;
}
set
{
if (player.Track != null)
{
double newSeconds = player.Track.Duration.TotalSeconds * value;
TimeSpan newPosition = TimeSpan.FromSeconds(newSeconds);
player.Position = newPosition;
}
OnPropertyChanged(() => Position);
}
}
Raising a property changed event for the Position
property causes the Slider
, which is bound to the property, to be updated. The Slider
is defined like so:
<Slider Value="{Binding Position, Mode=TwoWay}" Minimum="0" Maximum="1" />
Figure 29.3 shows the MainPage
with the slider indicating the progress of the current track and the application bar icon buttons for controlling playback.
There are numerous possibilities for extending an app such as this. For example, it could be extended to include a view for the playlist or an image for the album art—the sky’s the limit!
Audio streaming agents allow you to customize how audio files are downloaded and decoded by your app, and they enable you to support live streaming scenarios not natively supported by the phone.
An audio streaming agent can be created in the same manner as an audio player agent, by using the “Windows Phone Audio Streaming Agent” Visual Studio project template (shown previously in Figure 29.1).
The Visual Studio project template produces a class that derives from AudioStreamingAgent
. The agent must then be registered with your main projects WMAppManifest.xml file, which is done automatically when linking to an Audio Streaming Agent project. The XML definition can be seen alongside the AudioPlayerAgent
, as shown:
<Tasks>
<DefaultTask Name="_default" NavigationPage="MainPage.xaml"/>
<ExtendedTask Name="BackgroundTask">
<BackgroundServiceAgent Specifier="AudioPlayerAgent"
Name="<Unique name within the app>"
Source="<Assembly>"
Type="<Namespace.Class>" />
<BackgroundServiceAgent Specifier="AudioStreamingAgent"
Name="<Unique name within the app>"
Source="<Assembly>"
Type="<Namespace.Class>" />
</ExtendedTask>
</Tasks>
Registering a custom AudioStreamingAgent
does not disable the built-in streaming and decoding of the phone. The OS determines whether to use the built-in audio streaming or your custom AudioStreamingAgent
, based on the AudioTrack
’s Source
property, each time a new AudioTrack
is played. To have your custom AudioStreamingAgent
called for a particular track, assign the Source
property of the AudioTrack
to null, as shown in the following excerpt:
new AudioTrack(null, /* Uri is null so that it is sent
* to the AudioStreamingAgent
* called AssemblyAudioStreamingAgent. */
"Assembly Track!",
"Acquired using AudioStreamingAgent",
"Unleashed",
null,
"AudioFiles/Audio04.mp3", // Tag parameter.
EnabledPlayerControls.All),
If the Uri
is not null, the OS performs all streaming and decoding. By using this technique, you can use a combination of both the inbuilt streaming and decoding for some tracks, and custom streaming and decoding for others.
The AudioTrack
class contains a Tag
property, which can be used to pass information to an AudioStreamingAgent
. Notice from the previous excerpt that this property is set to the relative path of an audio file resource. You see, in the next section, how this value is used to resolve an audio file resource from the current assembly.
AudioStreamingAgent
contains a virtual method named OnBeginStreaming
, which is overridden to handle streaming and decoding of an audio track. The method has two parameters: the AudioTrack
and an AudioStreamer
. The AudioStreamer
allows you to attach your own MediaStreamSource
. MediaStreamSource
provides you with direct access to APIs for manipulating encoded elementary audio and video streams. It enables you to implement your own mechanism for processing the contents of a media file, enabling support beyond the native built-in media formats.
Creating a custom MediaStreamSource
is a large topic and one that is outside the scope of this book. A good starting point for learning about the MediaStreamSource
is the MSDN ManagedMediaHelpers project located at http://archive.msdn.microsoft.com/ManagedMediaHelpers.
While not covering MediaStreamSource
in detail, this section does, however, look at applying a specialized MediaStreamSource
from the ManagedMediaHelpers project to enable the playback of audio files that are located in an app’s assembly, something that is not supported by the phone’s built-in streaming and decoding system.
The built-in streaming and decoding system on the phone only supports playback of local audio files when they are located in isolated storage. This section looks at enabling the direct playback of audio files located within an app assembly.
The code for this section resides in the AssemblyAudioStreamingAgent
class of the WindowsPhone7Unleashed.BackgroundAudio project in the downloadable sample code. AssemblyAudioStreamingAgent
is a subclass of AudioStreamingAgent
and overrides the base type’s OnBeginStreaming
method (see Listing 29.2). The custom IsolatedStorageUtility
is used to retrieve the byte stream for an audio file whose location is specified by the AudioTrack.Tag
property. An Mp3MediaStreamSource
, from the ManagedMediaHelpers
library, is created using the stream. The Mp3MediaStreamSource
is then assigned to the AudioStreamer
, which then relies on the Mp3MediaStreamSource
to provide format-independent audio data.
protected override void OnBeginStreaming(AudioTrack track, AudioStreamer streamer)
{
IsolatedStorageUtility utility = new IsolatedStorageUtility();
Uri uri = new Uri(track.Tag, UriKind.Relative);
Stream stream = utility.GetApplicationResourceStream(uri);
Mp3MediaStreamSource mediaStreamSource
= new Mp3MediaStreamSource(stream, stream.Length);
streamer.SetSource(mediaStreamSource);
/* Not to be called. */
//NotifyComplete();
}
Do not call NotifyComplete
within the OnBeginStreaming
method. Doing so causes the process running the AudioStreamingAgent
to be terminated, which halts playback of the file.
Having the ability to provide your own custom streaming means that you can support other third-party services or retrieve files from, for example, a WCF service, which is not supported natively by the OS.
Listing 29.3 demonstrates how to use a WebRequest
to manually stream an audio file from the Web.
protected override void OnBeginStreaming(AudioTrack track, AudioStreamer streamer)
{
HttpWebRequest request
= WebRequest.CreateHttp("http://localhost:4864/Audio.mp3");
request.AllowReadStreamBuffering = true;
request.BeginGetResponse(asyncResult =>
{
HttpWebResponse response
= request.EndGetResponse(asyncResult) as HttpWebResponse;
if (response != null)
{
Stream stream = response.GetResponseStream();
mediaStreamSource = new Mp3MediaStreamSource(
stream, response.ContentLength);
streamer.SetSource(mediaStreamSource);
}
}, null);
}
While not something you will need every day, audio streaming agents provide the means to overcome any limitations imposed by the built-in streaming and decoding system on the phone.
This chapter began with an overview of the background audio player and showed how it is used to control the playback of local or remote audio files. You looked at how audio file information is represented by the AudioTrack
class and examined the role of background audio agents, which are used to coordinate audio playback while your app is not running in the foreground. You saw that when used in the foreground, a BackgroundAudioPlayer
forwards all calls to the registered AudioPlayerAgent
. Conversely, when a BackgroundAudioPlayer
is used in an AudioPlayerAgent
, it directly affects playback on the device.
The chapter then demonstrated how to build a UI that leverages the background audio agent for controlling playback while your app is in the foreground.
Finally, the chapter examined audio streaming agents, and you saw how they are used to provide custom streaming and decoding of audio files. You also saw how a custom audio streaming agent can be employed to play audio directly from an assembly resource, something not possible using the built-in streaming and decoding system of the phone.
18.219.213.27