×

First time here?

You are looking at the most recent posts. You may also want to check out older archives. Please leave a comment, ask a question and consider subscribing to the latest posts via RSS or email. Thank you for visiting!

In talking with a friend about some Windows Phone 7 and Silverlight stuff recently.  He was giving me some great feedback about a few things (all of which I’ve passed along).  One of the things was what I felt was a common task that might exist in the mobile space but admittedly isn’t as clear if you are just coming to WP7 development.  The scenario is that of downloading media files and storing them for later playback.

WP7 does not have a storage mechanism like SQLLite on the device, but since it is Silverlight, you do have Isolated Storage you can use leveraging the same .NET class libraries from the full framework.  Here’s a sample app that demonstrates downloading an MP3 and storing for later playback on Windows Phone 7.

The Scenario

First, to make the scenario clear, say you are building a specific app for your brand’s media archives (audio and/or video).  You want to enable the user to selectively (or automatically) download the media to their device.  You want the user to be able to playback the downloaded media while offline later.  The media in this scenario is an MP3 file, a common audio format.

The Pieces

In order to do this we’ll assume the following:

  • You have an absolute URI to the MP3 file.
  • You have some type of UI to display to the user a list of media for your application
  • Some type of UI to control the playback (play/stop/etc.)

For demonstration purposes my UI will not be very ‘user friendly’ as it is meant to be diagnostic in explaining the task.  I will have a ListBox that I’ll use to bind to the list of downloaded items, a TextBox to give an option to download an MP3 file, and three (3) buttons: Download (to fetch the file and store for later), Play and Stop.  Here is the XAML I’ve used starting with the blank Windows Phone Application using the Windows Phone Developer Tools available to developers.

   1: <!--LayoutRoot is the root grid where all page content is placed-->
   2: <Grid x:Name="LayoutRoot" Background="Transparent">
   3:     <Grid.RowDefinitions>
   4:         <RowDefinition Height="Auto"/>
   5:         <RowDefinition Height="*"/>
   6:     </Grid.RowDefinitions>
   7:  
   8:     <!--TitlePanel contains the name of the application and page title-->
   9:     <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,9,0,40">
  10:         <TextBlock x:Name="ApplicationTitle" Text="AUDIO SAMPLES" Style="{StaticResource PhoneTextNormalStyle}"/>
  11:         <TextBlock x:Name="PageTitle" Text="MP3" Margin="9,-8,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
  12:     </StackPanel>
  13:  
  14:     <!--ContentPanel - place additional content here-->
  15:     <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
  16:         <TextBlock Height="67" HorizontalAlignment="Left" Margin="6,6,0,0" Name="textBlock1" Text="Showing how to store and playback MP3 files on the device." VerticalAlignment="Top" TextWrapping="Wrap" />
  17:         <ListBox Height="150" HorizontalAlignment="Left" Margin="6,79,0,0" Name="listBox1" VerticalAlignment="Top" Width="444" />
  18:         <ProgressBar x:Name="DownloadProgress" IsIndeterminate="False" Style="{StaticResource PerformanceProgressBar}" Margin="6,-60,0,0"/>
  19:         <TextBlock Height="30" HorizontalAlignment="Left" Margin="12,298,0,0" Name="textBlock2" Text="URL to download" VerticalAlignment="Top" Width="175" />
  20:         <TextBox Height="72" HorizontalAlignment="Left" Margin="0,334,0,0" Name="mpsUri" VerticalAlignment="Top" Width="460" Text="http://files.sparklingclient.com/099_2010.07.09_WP7_Phones_In_The_Wild.mp3" />
  21:         <Button Content="DOWNLOAD" Height="72" HorizontalAlignment="Left" Margin="12,399,0,0" Name="button1" VerticalAlignment="Top" Width="438" Click="button1_Click" />
  22:         <Button Content="PLAY" Height="72" HorizontalAlignment="Left" Margin="12,477,0,0" Name="button2" VerticalAlignment="Top" Width="220" Click="button2_Click" />
  23:         <MediaElement Height="43" HorizontalAlignment="Left" Margin="6,555,0,0" Name="mediaPlayback" VerticalAlignment="Top" Width="438" />
  24:         <Button Content="STOP" Height="72" HorizontalAlignment="Left" Margin="230,477,0,0" Name="button3" VerticalAlignment="Top" Width="220" Click="button3_Click" />
  25:     </Grid>
  26: </Grid>

You can see that I’m not using any advanced pattern development here for two reasons: 1) it doesn’t need it and 2) I want to focus on specific tasks to isolate this learning.  Therefore if you don’t like Click handlers in your Button XAML, then you can flog me later :-).

Downloading the media

The first step in my sample is to download some media.  I’ve chosen a favorite podcast, the Sparkling Client as my sample file.  I hope they don’t mind.  In fact you may want to pick a small MP3 file to test on one of your servers so you don’t have to wait a while for the download to complete.

SIDE NOTE: I like how Sparkling Client has become more of short review of the times with regard to Silverlight.  Good to see some perspectives (and rumors).  It’s like a live version of Dave Campbell’s Silverlight Cream.

Because typing long things in the emulator is, well, annoying, I’ve prepopulated the URI TextBox (mpsUrl) with a specific URL.  Feel free to change this or enter a new MP3 URI in the emulator.  The next step is to execute the download.  I’m choosing to use WebClient.OpenReadAsync in this regard.  It’s simple and gets the job done.  Admittedly I wish that there was an OpenReadDownloadProgress argument available (as there is for DownloadStringAsync) but there is not. 

Because there is no actual progress value provided for me, I do want to provide the user some feedback that something is happening.  To do this in my sample I am using Jeff Wilcox’s “High Performance ProgressBar” for Windows Phone 7.  I’m not going to go into the details of why and how to hook it up – read his post.  I followed the exact same instructions. 

NOTE: When using the progress bar, don’t (only) set Visibility to collapsed if that is one of your mechanisms for displaying/hiding it, but be sure to set IsIndeterminate to false when not using it.

When I start the download, I start the ProgressBar to show the user some type of feedback that something is happening.  Here’s the code for the function:

   1: private void button1_Click(object sender, RoutedEventArgs e)
   2: {
   3:     string fileName = System.IO.Path.GetFileName(mpsUri.Text);
   4:  
   5:     // start the download of the MP3
   6:     WebClient wc = new WebClient();
   7:  
   8:     wc.OpenReadCompleted += ((s, args) =>
   9:         {
  10:             DownloadProgress.IsIndeterminate = false;
  11:  
  12:             // once get the streams, put in isolated storage
  13:  
  14:         });
  15:  
  16:     wc.OpenReadAsync(new System.Uri(mpsUri.Text, System.UriKind.RelativeOrAbsolute));
  17:     DownloadProgress.IsIndeterminate = true;
  18: }

And here’s a quick screenshot of what it looks like running:

Windows Phone 7 progress bar

Simple enough…let’s store the downloaded bits now.

Storing the media to IsolatedStorage

The result of OpenReadAsync is a Stream.  Using IsolatedStorage and specifically IsolatedStorageFileStream, I can write out those bits to a file that is stored in my device’s storage location.  Normally in the browser Silverlight world I would have to calculate the amount of storage needed, see if it is available and, if not, request a quota increase to the user.  I don’t have to do that in the phone world.  I can just begin to write out the data.  Ideally, however, I should check for available space since it is entirely possible the user has used all their storage.  This sample does not accommodate that logic.

In my OpenReadCompleted event I add the following logic:

   1: // snipped
   2: wc.OpenReadCompleted += ((s, args) =>
   3: {
   4:     DownloadProgress.IsIndeterminate = false;
   5:  
   6:     // once get the streams, put in isolated storage
   7:     using (var store = IsolatedStorageFile.GetUserStoreForApplication())
   8:     {
   9:         if (store.FileExists(fileName))
  10:         {
  11:             store.DeleteFile(fileName);
  12:         }
  13:  
  14:         using (var fs = new IsolatedStorageFileStream(fileName, FileMode.Create, store))
  15:         {
  16:             byte[] bytesInStream = new byte[args.Result.Length];
  17:             args.Result.Read(bytesInStream, 0, (int)bytesInStream.Length);
  18:             fs.Write(bytesInStream, 0, bytesInStream.Length);
  19:             fs.Flush();
  20:         }
  21:     }
  22:  
  23:     RefreshIsoFiles();
  24:  
  25: });
  26: // snipped

You can see that I’m writing out the file stream to a file using the same name as the MP3 file literally (which may not make sense to the user, so again this is one of those ‘polish’ areas you’d want to make better and perhaps organize the files in IsolatedStorage better).

The last step you see is a call to RefreshIsoFiles.  This is a function that I also call when the first user interface page is loaded.  It traverses the IsolatedStorage for the app to display the already stored media in the ListBox in our XAML:

   1: private void RefreshIsoFiles()
   2: {
   3:     string[] fileList;
   4:  
   5:     using (var store = IsolatedStorageFile.GetUserStoreForApplication())
   6:     {
   7:         fileList = store.GetFileNames();
   8:     }
   9:     listBox1.ItemsSource = fileList;
  10: }

Now we have our data downloaded and ready for playback.

Playing back the stored media

So great, now you have a stored MP3 file and you want to play it back.  Remember the XAML above and that I have a MediaElement there.  MediaElement is so simple at playing back media files from a URI.  Simply set the source of MediaElement and you can then call Play() and other functions. 

UPDATE: Well, you learn something new always.  Corrado pointed out below in comments that in WP7 you can SetSource directly to an IsolatedStorageFileStream...so while the following is interesting, it doesn't appear to be required in WP7 :-).

The challenge is that we now have our media in IsolatedStorage and there isn’t a URI scheme for IsolatedStorage that is predictable to the developer.  What we are left with is opening the media as a Stream and feeding that to the MediaElement.  This introduces MediaStreamSource.  If you aren’t familiar with this API, you’re probably not alone.  This is the API that enables a few scenarios, namely Smooth Streaming playback for Silverlight.  It is an extensible API so that you could wrap your own decode logic, etc. as needed.  Now given that MP3 is a common format you’d think it would be simple to do this…but there isn’t one built-in method for these various different codecs.

When MediaStreamSource was introduced, the program manager on that feature had written some helper files as code samples for developers to use.  One of them was an MP3 MediaStreamSource helper.  I wrote about them and where you can get them: MediaStreamSource for Silverlight.  Here’s where some awesome code re-use comes in to play.

I downloaded the ManagedMediaHelpers project and built the Mp3MediaStreamSource project (which also builds MediaHelpers).  In my WP7 project I simply added a reference to these in my project.  I was able to use these Silverlight binaries directly in my WP7 project! (I’ve included the compiled binaries in this sample for convenience but you can also see the source link in the article above.)

Now I need to read the media from IsolatedStorage as a stream, feed that Stream into my MediaStreamSource, and set that as the source for my MediaElement.  Here’s the relevant code on the Play button on my sample:

   1: private void button2_Click(object sender, RoutedEventArgs e)
   2: {
   3:     if (listBox1.SelectedItem == null)
   4:     {
   5:         MessageBox.Show("choose an item to play back!");
   6:     }
   7:     else
   8:     {
   9:         using (var store = IsolatedStorageFile.GetUserStoreForApplication())
  10:         {
  11:             audio = store.OpenFile(listBox1.SelectedItem.ToString(), FileMode.Open);
  12:             // play it back as a MSS
  13:             mss = new Media.Mp3MediaStreamSource(audio);
  14:             mediaPlayback.SetSource(mss);
  15:         }
  16:     }
  17: }

The media now plays back on the phone.  My Stop button basically closes the stream (audio and mss are member variables of the project) and nulls out references.

Summary

This is I think what might be a common application scenario for WP7 (downloading media for playback later).  Hopefully over time our platform will improve to make some of this better (i.e., progress indication), but for now this should help those get started on this task.  The meat of the solution is in the MediaStreamSource implementation.  If you are working with MP3 format, then the sample code will help you greatly as it’s mostly done!  There are other implementations floating around for WAV and other things as well if you need them.

Hopefully this might nudge Chris along the right path and be a helpful tip to others as well.  If you have any feedback on the implementation or a better way of doing this, please share!  Here’s the solution bits to my sample in full: Mp3StoreandPlayback.zip.

Hope this helps!


This work is licensed under a Creative Commons Attribution By license.


8/17/2010 5:31 AM | # re: Storing and playing media on Windows Phone 7
Many thanks for this Tim. The other problem I have with this at the moment is being able to browse to your content, download it and then play it. I don't seem to be able to download arbitrary files in the built in browser or WebBrowser control. There also doesn't seem to be a way of associating my application with a file extension or MIME type. A post on the other half of your solution would be really welcome!

Thanks,
Jason
8/17/2010 9:31 AM | # re: Storing and playing media on Windows Phone 7
Jason - where do you want to download the files to?

Brad - you should be able to just specify an MP3. As long as the container is correct it should play. AG_E_NETWORK_ERROR could mean a few things...ensure there is no authentication, ensure the same scheme is being used (i.e., the media protocol and the XAP protocol match -- http/http -- cannot be file/http)
8/17/2010 9:53 AM | # re: Storing and playing media on Windows Phone 7
Thanks for this article. I downloaded the project and got following error when building it:
"error CS0518: Predefined type 'System.Object' is not defined or imported".
I am using the WP7 tool Beta release with VS 2010 Ultimate. I found the same error in other occasions that I had to create a new project and copy/replace the project content to work around. If you happened to know why, I would really appreciate to learn the reason. :-)
8/17/2010 10:11 AM | # re: Storing and playing media on Windows Phone 7
Dale -- (from another user): manually add it in as <Reference Include="mscorlib" /> then it works fine. When I say it works fine - I actually got this error "Installation of the application failed. Run time error has occurred. Fix the Capabilities in WMAppManifest.xml file." and from looking at the Capabilities, you have about twice as many as the default new project list so I just copied in my one and I was away.
8/17/2010 10:43 AM | # re: Storing and playing media on Windows Phone 7
Thanks Tim, yes it is working now. And as long as I removed the following capability from the downloaded project, the Run time error is fixed, too.
<Capability Name="ID_CAP_IDENTITY_DEVICE"/>
Cheers,
--Dale
8/17/2010 11:42 AM | # re: Storing and playing media on Windows Phone 7
The same implementation works from a silverlight app I've already written for the pc. I can also hit the link from a browser instance and it will start playing through windows media player.
8/17/2010 11:45 AM | # re: Storing and playing media on Windows Phone 7
Brad -- feel free to ping me directly via email with your solution and I can take a better look.
8/18/2010 9:47 AM | # re: Storing and playing media on Windows Phone 7

>> Jason - where do you want to download the files to?

Tim - Anywhere that my app can get to them, or I could process the stream. For example, if I browse to a pdf link in the built in browser it says it can't download the file as there is no associated application. Is there anyway I can associate my application so that it will download it? Alternatively I could use the WebBrowser control but when I try to navigate to a pdf link there isn't even an error event to tell me that I can't (If there were I could trap it, examine the url and then use your example to download the file).

Any ideas?
Thanks,
Jason


8/19/2010 6:02 AM | # re: Storing and playing media on Windows Phone 7
Tim,
If you load the file from IsolatedStorage you don't need the Mp3MediaStreamSource, just pass and IsolatedStorageFileStream and it works...
8/22/2010 4:31 PM | # re: Storing and playing media on Windows Phone 7
I emailed you my issue. Just making sure you got it.
8/24/2010 4:22 PM | # re: Storing and playing media on Windows Phone 7
@Brad
What IP/host are you using? Are you sure it's reachable from the emulator? I got the same error trying to download from a web app on the same machine as the emulator. I didnt solve the problem though, just put my stuff on a different server.
9/21/2010 7:51 AM | # re: Storing and playing media on Windows Phone 7
Could you please detail where should the <Reference Include="mscorlib" /> line be added to, in order to get rid of the "Predefined type 'System.Object' is not defined or imported" error?
Thanks!
Gravatar
9/28/2010 4:21 AM | # re: Storing and playing media on Windows Phone 7
Hi,

Above given example Mp3MediaStreamSource is not working with Windows phone 7 sdk RTM version.
Gravatar
9/29/2010 6:03 AM | # re: Storing and playing media on Windows Phone 7
Tim,

If Mp3MediaStreamSource is set as a source for MediaElement then MediaElement doesn't play that file and donesn't show any error in Windows phone 7 sdk RTM version. In previsios version it was working but it's not working with Windows phone 7 sdk final release.
10/9/2010 11:55 AM | # re: Storing and playing media on Windows Phone 7
Hi Tim,
Thanks for the wonderful article. I was trying to play some music streams which is capable of playing through standalone Windows Media Player/VLC Player and the Windows Media Player Activex control on the Internet explorer. To my surprise, only the URIs containing a .mp3 are played by the player. But URI of .asx are not playing.

http://www.geethamradio.com:8020/hifi.mp3.m3u - Is palying well
http://www.desi-radio.com/servers/desiradio.asx - Is not at all playing

Any idea how the strange behavior?

Also is there a way to check whether any error occured (streaming error/unsupported format/no internet connection)while I start playing the content using the .Source() and .Play() methods of the mediaelement?

Thx

Shibu
10/9/2010 11:59 AM | # re: Storing and playing media on Windows Phone 7
@ Joyeria
You probably need a pdf viewer application (Like Adob pdf viewer), which is supported by WP7 and installed on your phone
11/4/2010 9:16 AM | # re: Storing and playing media on Windows Phone 7
Any update related to lin's comment about Mp3MediaStreamSource not working with MediaElement in RTM SDK? I haven't been able to produce any sound using Mp3StoreandPlayback or a streaming client based on a tweaked Mp3MediaStreamSource.

I've been wondering if we had made an error, but now I'm wondering if maybe there is a different problem.
11/7/2010 8:09 AM | # re: Storing and playing media on Windows Phone 7
Hariindarr - Is this working for you - meaning, are you getting sound? I've tried this on the emulator and device. It executes, but I get no sound. A puzzle.
11/9/2010 6:23 PM | # re: Storing and playing media on Windows Phone 7
My "no sound" problem has been remedied by providing values for the Timestamp property in the MediaStreamSample object. Apparently sometime between beta and release, it became necessary to specify this value, rather than use the 0 default.
11/17/2010 2:25 AM | # re: Storing and playing media on Windows Phone 7
So I'm running into an odd issue; I wrote a simple Google Voice cilent to listen to my voicemails since there isn't a free one on the app market yet (maybe I can solve that problem!) and I have the mp3's downloading fine; I've confirmed that they're being written to the Iso File store and they play fine in the emulator but on the device they don't seem to play. I have a timer that shows MediaElement "location" and it doesn't move at all on the deviceso at a bit of a loss... I don't think Google is using custom codecs or anything and, again, it plays fine on the emulator.

Thoughts?
11/17/2010 2:54 AM | # re: Storing and playing media on Windows Phone 7
Blah, disregard that, I'm an idiot. I forgot to untether the device... for an hour of debugging... I'll never get that time back! :)
12/16/2010 7:56 AM | # re: Storing and playing media on Windows Phone 7
hi
your downloaded sample does not work,
no error, but i cant hear anything. (volumne is up to 1)..
1/7/2011 3:57 PM | # re: Storing and playing media on Windows Phone 7
Hi,

I did download the project :Mp3StoreandPlayback.zip
It does not run because there is a dll needed mediaparser.dll
Where do I get this dll?

Thank you
Thom

1/7/2011 4:13 PM | # re: Storing and playing media on Windows Phone 7
Hi,

problem with mediaparsers.dll is solved. It was with the project but a security flag prevented from using it.

I can now start the application on the phone. Unfortunately there is no sound to hear.

Thom

Gravatar
1/18/2011 3:37 PM | # re: Storing and playing media on Windows Phone 7
Has anyone succeeded in making this sample work? The sample does run in emulator but there is no sound though I set volume to 1.0.
1/22/2011 7:47 AM | # re: Storing and playing media on Windows Phone 7
sorry but i have a question, what about the stop button what should i do in it, can you please make a video tutorial please :)
1/25/2011 3:43 PM | # re: Storing and playing media on Windows Phone 7
Shibu - Could you get asx or m3u formats to play at all? I don't see anywhere in the comments how this was resolved. I am facing the same issue too, so any help is appreciated!
3/12/2011 5:43 AM | # re: Storing and playing media on Windows Phone 7
Simple, huh? Now of course you 70-680 test cannot see the final result in Writer (not sure why actually but I think 70-640 test it has something to do with the 70-642 test rendering techniques they use in authoring mode (hence the white box in the vid), 70-649 test but you can see the end result!
4/15/2011 9:26 AM | # re: Storing and playing media on Windows Phone 7
How does one play media files to the headphone rather than to the speakers?
11/12/2011 3:28 PM | # re: Storing and playing media on Windows Phone 7
One thing to point out is that even when using "OpenReadAsync" the "DownloadProgressChanged" event is still fired

Just thought id share that :-)
1/3/2012 6:26 PM | # re: Storing and playing media on Windows Phone 7
Excellent Article, any idea how to do this but with a BackgroundAdioAgent?, am trying to do it so, but it throws an exception indicating that the path doesn't exist, even when the path has been validated

am storing the tracks as follows:
string[] fileNames = isoStore.GetFileNames(@"/OfflineMusic/*.mp3");


// Set the local PlayList
foreach (string fileName in fileNames)
{
if (!offlineTitles.Contains(fileName))
{
if (isoStore.FileExists(@"/OfflineMusic/" + fileName))
{
//Track Configuration for local playing
Uri trackLocation = new Uri(@"/OfflineMusic/" + fileName, UriKind.Absolute);
AudioTrack newTrack = new AudioTrack(trackLocation, fileName.Remove(fileName.IndexOf("."), 3), "Artist Name",
"Album Name", null, "none",
EnabledPlayerControls.Pause | EnabledPlayerControls.SkipNext);

if (isoStore.FileExists(newTrack.Source.LocalPath))
{
offLine_PlayList.Add(newTrack);
offlineTitles.Add(fileName);
}
}
}

AND am traying to play so:

using (IsolatedStorageFile isoStore = IsolatedStorageFile.GetUserStoreForApplication())
{
if (isoStore.FileExists(offLine_PlayList[currentTrackNumber].Source.LocalPath))
{

player.Track = offLine_PlayList[currentTrackNumber];
player.Volume = 1;
player.Play();
}
}

i hope there's anybody can help thanks a lot !!
Regards!

Wilfrido.
Gravatar
5/16/2012 5:02 PM | # playing embedded media form webpages
hi

I get a message that content will not play when trying to play embedded media off sites such as bbc.co.uk/sport? also iplayer, this uses flash, how can I get it to work? any help appeciated thanks
6/18/2012 12:11 PM | # re: Storing and playing media on Windows Phone 7
I got it to work the first time itself ... Used isolatedstorage file directly for the media player...working awesome...thanks
If it'ss not asking too much ,
Please mail me the exact steps to include the binaries mentioned here by you... As i would like to resolve the Issue i kept getting for "media"
Thanks in advance

 
Please add 4 and 7 and type the answer here:

DISCLAIMER:

The opinions/content expressed on this blog are provided "ASIS" with no warranties and are my own personal opinions/content (unless otherwise noted) and do not represent my employer's view in any way.