Resuming streaming audio from Dormant/Tombstone state in Windows Phone
This article demonstrates how to resume MediaElement streaming audio from Dormant/Tombstone state in Silverlight on Windows Phone 7.
Article Metadata
Code Example
Tested with
Compatibility
Platform Security
Article
Contents |
Introduction
In this article we will show how we can resume streaming audio when the application comes back from Dormant/Tombstone state. In regular cases when we press the Window Key on the device, the application calls the Application_Deactivated() event and goes into the Dormant/Tombstone state. And then when we press the Back Key of the device the application comes back, but it leaves the states. To work on this scenario we will play a streaming audio and while the audio is playing we will press the Windows Key on the device, which will stop playing the audio and will take the application in the Dormant/Tombstone state. Now when we press the Back Key on the device the application will come up and the streaming audio should continue to play from the position where it left while deactivating the application.
Implementation
Let’s create an empty WP7 project. To archive this scenario, first we will add MediaElement in MainPage.xaml to play the streaming audio.
<MediaElement Name="mediaElement" BufferingTime="0:0:5" AutoPlay="True" Margin="419,1,1,611" />A ProgressBar to show the audio playing progress, two Button (play and pause) and a TextBlock is being added to display the track time left.
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0" >
<MediaElement Name="mediaElement" BufferingTime="0:0:5" AutoPlay="True" Margin="419,1,1,611" />
<ProgressBar IsHitTestVisible="True" IsIndeterminate="True" Height="3" HorizontalAlignment="Right" Margin="0,235,0,0" Name="progressBarAPlayerPage" VerticalAlignment="Top" Width="460" />
<TextBlock Height="30" HorizontalAlignment="Center" TextAlignment="Center" Margin="0,199,0,0" Name="progressBarTextAPlayerPage" Text="loading" Foreground="White" VerticalAlignment="Top" Width="444" />
<ProgressBar Height="60" Name="progressBar" Margin="12,256,6,337" />
<Button Content="play" Visibility="Collapsed" Height="72" HorizontalAlignment="Left" Margin="12,353,0,0" Name="play_btn" VerticalAlignment="Top" Width="438" Click="play_btn_Click" />
<Button Content="pause" Visibility="Collapsed" Height="72" HorizontalAlignment="Left" Margin="12,353,0,0" Name="pause_btn" VerticalAlignment="Top" Width="438" Click="pause_btn_Click" />
<TextBlock Height="58" Foreground="White" HorizontalAlignment="Left" Margin="226,146,0,0" Name="songTime" Text="" TextAlignment="Center" VerticalAlignment="Top" Width="224" Style="{StaticResource PhoneTextTitle1Style}" FontFamily="Segoe WP SemiLight" FontSize="42" />
<TextBlock Height="58" HorizontalAlignment="Left" Margin="12,146,0,0" Name="textBlock1" Text="Track Time :" VerticalAlignment="Top" Width="208" Style="{StaticResource PhoneTextTitle1Style}" FontFamily="Segoe WP SemiLight" FontSize="42" />
</Grid>
When we start the application first we check the network connectivity and assign the mediaElement.Source with a remote MP3 file in playSong() function.
void playSong()
{
play_btn.Opacity = 0.5;
pause_btn.Opacity = 0.5;
play_btn.IsHitTestVisible = false;
pause_btn.IsHitTestVisible = false;
songTime.Text = "00.00";
mediaElement.MediaOpened += new RoutedEventHandler(mediaElement_MediaOpened);
mediaElement.MediaEnded += new RoutedEventHandler(mediaElement_MediaEnded);
mediaElement.CurrentStateChanged += new RoutedEventHandler(mediaElement_CurrentStateChanged);
currentPosition.Tick += new EventHandler(currentPosition_Tick);
//enter url : http://www.yourdomain.com/audio.mp3
mediaElement.Source =new Uri( "REPLACE_THIS_WITH_REMOTE_MP3",UriKind.RelativeOrAbsolute);
}//end of funt
This will start streaming the audio. There are options to play and pause the streaming in between.
private void play_btn_Click(object sender, RoutedEventArgs e)
{
mediaElement.Play();
}
private void pause_btn_Click(object sender, RoutedEventArgs e)
{
mediaElement.Pause();
}//end of funt
Now when user clicks on the Windows Key of the device, it calls OnNavigatedFrom() method where we get the current track position of the MediaElement.
protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
thisApp.iCurrentTrackPositionText = mediaElement.Position.TotalMinutes.ToString();
mediaElement.Stop();
}//end of funt
And finally it calls the Application_Deactivated() event, where we save the current track information into IsolatedStorage.
private void Application_Deactivated(object sender, DeactivatedEventArgs e)
{
SaveCurrentTrackPosition();
}
void SaveCurrentTrackPosition()
{
//read file
//Obtain a virtual store for application
IsolatedStorageFile fileStorage = IsolatedStorageFile.GetUserStoreForApplication();
//Create a new StreamReader
StreamReader fileReader = null;
try
{
//Read the file from the specified location.
fileReader = new StreamReader(new IsolatedStorageFileStream("CurrentTrackPosition.txt", FileMode.Open, fileStorage));
//Read the contents of the file (the only line we created).
textFile = fileReader.ReadToEnd();
fileReader.Close();
}
catch
{
// MessageBox.Show("error saving data");
}
//replace text
textFile = textFile.Replace(textFile, iCurrentTrackPositionText);
//write file
//Obtain a virtual store for application
IsolatedStorageFile fileStorageUpdate = IsolatedStorageFile.GetUserStoreForApplication();
//Create a new StreamWriter, to write the file to the specified location.
StreamWriter fileWriter = new StreamWriter(new IsolatedStorageFileStream("CurrentTrackPosition.txt", FileMode.OpenOrCreate, fileStorageUpdate));
fileWriter.Write(textFile);
//Close the StreamWriter.
fileWriter.Close();
}
This will take the application to the Dormant/Tombstone state. Now when user clicks on the Back Key of the device, it will call the Application_Activated() event followed by the OnNavigatedTo() method.
protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
{
base.OnNavigatedTo(e);
ReadCurrentTrackLastPosition();
CurrentTrackLastKnowPosition = Convert.ToDouble(textFile);
initializeSection();
}
Where ReadCurrentTrackLastPosition() function reads the current track information from IsolatedStorage about the streaming audio.
void ReadCurrentTrackLastPosition()
{
//read file
//Obtain a virtual store for application
IsolatedStorageFile fileStorage = IsolatedStorageFile.GetUserStoreForApplication();
//Create a new StreamReader
StreamReader fileReader = null;
try
{
//Read the file from the specified location.
fileReader = new StreamReader(new IsolatedStorageFileStream("CurrentTrackPosition.txt", FileMode.Open, fileStorage));
//Read the contents of the file (the only line we created).
textFile = fileReader.ReadToEnd();
fileReader.Close();
}
catch
{
MessageBox.Show("error loading data");
}
}
And initializeSection() method starts streaming the audio from the position where it left.
private void initializeSection()
{
progressBarTextAPlayerPage.Text = "loading";
progressBarAPlayerPage.IsHitTestVisible = true;
progressBarAPlayerPage.IsIndeterminate = true;
progressBarAPlayerPage.Visibility = Visibility.Visible;
progressBar.Visibility = Visibility.Collapsed;
play_btn.Visibility = Visibility.Collapsed;
pause_btn.Visibility = Visibility.Collapsed;
songTime.Visibility = Visibility.Collapsed;
string enternetConectivity = Microsoft.Phone.Net.NetworkInformation.NetworkInterface.NetworkInterfaceType.ToString();
if (enternetConectivity != "None")
{
playSong();
}
else
{
//MessageBox.Show("No internet connection");
}
}//end of funt
I have not checked the status of IsApplicationInstancePreserved in the Application_Activated() event, so that what ever the current state of the application is (Dormant/Tombstone) we should be able to stream the audio from where it left behind.
Summary
With the use of IsApplicationInstancePreserved parameter we can put a restriction whether to come back from Dormant or from Tombstone state only. This application also shows the behavior of Fast Application Switching, where application recovers to its last know state from Dormant/Tombstone state.
Source Code
- The full source code of the example is available here: File:AudioResumeWP7.zip


Hamishwillee - Useful - some suggestions
Hi
As a standalone article this is very useful. My only thought about design was that you save the settings on Application_Deactivated, while best practice would actually be to save when the page is navigated away from (doesn't make much difference in this case, but would in a multi page app. I also thought that it would be worth adding Silverlight category because this is a Sliverlight rather than XNA app, and also that you should provide links to MediaElement.
Other than that, I think that it would be useful to provide links to other articles which do "similar things" either on this wiki or MSDN. Specifically, stopping the audio on dormant is only one option - you can also make it continue to play in the background (see http://msdn.microsoft.com/en-us/library/hh202978(v=VS.92).aspx ) as a local file and anecdotally as a streaming file. Lastly, XNA uses a different approach again - its worth noting that too. Might be worth even having a short section to cover these options. Can you do that?
Nice job
Regards
Hhamishwillee 07:43, 30 April 2012 (EEST)
Somnathbanik - Thank you
Hi Hamish,
This article was created keeping in mind the FAS feature of WP7, where we resume the state of the application when it comes back from Dormant/Tombstone mode. But if you observe the code you will see that we have not followed the actual rule to implement the FAS, we have not put a check IsApplicationInstancePreserved. So according to me this is behaving like a FAS but not exactly a FAS process. We are resuming the state whether or not the app is in Dormant/Tombstone state. Also we didn't have any plan to play the audio in background, so didn't add any background audio playback agent, rather paused the audio when the app goes in background and resume when it comes back.
Thank you for this feedback
Somnathsomnathbanik 10:22, 30 April 2012 (EEST)