Silverlight, WCF REST and Streaming My Personal Music Repository from a Standalone EXE
Silverlight, WCF REST and Streaming My Personal Music Repository from a Standalone EXE
This is way cool. From time to time, I tend to screw around with stuff I enjoy that really has little productive value. A year ago this month, I recall wanting to continue writing about sharing your personal music without using IIS and hosting the music directly from a WCF standalone executable. Well it turns out this is a whole lot simpler than I originally thought.
Disclaimer: This probably isn’t best practice, and it’s ripe for enhancements. My hope is that it gets your imagination fired up and motivates you to continue where I left off.
Challenges:
- How the heck do I stream an MP3 file to a media element via WCF (without having to download the whole file before playing it)?
- Cross-site scripting policies are blocking my download requests. How can I serve these up via my self hosted WCF REST service?
- Silverlight3 doesn’t support WCF REST yet. How do I deserialize the WCF REST contract messages?
Changes Since Last Release
My original design was very simple. I hosted the Silverlight files and used WCF with an IIS server at home. The player pulled a list of files and locations. The player’s media element source was set directly to a music file hosted in an IIS virtual directory. All that works great until you are on a machine without a web server. Ultimately, I wanted to figure out a way for the user to host their music repository from their desktop with a simple executable and port forwarding. Which means it would have to serve up all the resources necessary to run the Silverlight player. It would also have to serve up the WCF services and actual music files themselves.
Since the last post a year ago, I’ve made quite a few little tweaks to the player like: regular expression search, random songs, random play order, and a volume control. I also integrated a compact SQLite database for searching so the server-side doesn’t have to do a file system search for each request.
WCF REST
So first, how to we host the resources and music files? This is where WCF REST comes into play. WCF REST allows us to host a service or respond to an HTTP GET request very easily. And since we can host any WCF service in an executable, it makes for a pretty easy standalone option. The only real caveats here are security and firewall access, which in this demo are non-existent. You will also need to enable port-forwarding in your firewall for whichever port you decide to host the WCF service app. For this sample, I’m using TCP port 8000.
I found in this article by Peter Vucetin on how to serve up any request with a custom MIME type. I took what he had and tweaked it a little so I could serve up content like the silverlight package, html files and a javascript resources.
Playing Media
Okay, so the first problem with playing media is… how do we stream audio files from a WCF service to a media element in Silverlight? Well, it’s as simple as setting the MediaElement source to a method endpoint in the WCF service, which returns a FileStream to the audio file.
For example, our service endpoint lives at: http://localhost:8000/Music for the MusicStreamerService. Our function endpoint is Files(string filename) using the route: /Files/{filename}. So our source for the media element becomes http://localhost:8000/Music/Files/{filename}. On the server side, I simply set the content type and returned a direct FileStream to the file. This allows the player to behave like a normal web server and stream the file, which allows the Media Element to immediately start reading the data as it comes down. I also tweaked the request path a little bit for the filename parameter, which contains full relative path. If using the WCF service to host the file, then I enable a base64 encode option that encodes the entire relative path so as not to confuse the WCF request routing. If I choose to serve it up with a normal web servr, then I can disable the base64 encode and use UrlEncode instead so it treats the request as a normal file system request against a virtual directory.
Hosting Policy Files
Cross site scripting… That’s our next problem. Say your website with silverlight resources are hosted on one server and your music file server is hosted on another. If you host a silverlight app and access a web url across domains, then you’re going to need to serve up the cross site policy files. I wrote a simple file serve function that kicks out a local file when you request it. However I setup a separate service contract and endpoint since the policy files have to reside at the root of the host.
Deserializing DataContracts Manually
Silverlight3 doesn’t like consuming WCF REST services yet. So for the functionality of searching and listing the files available, I’m manually retrieving the XML response over asynchronous HTTP calls and then use System.Runtime.Serialization.DataContractSerializer to do handle the data. On the client side, I copied data contract proxy classes from my service so their serialized definitions match.
Configure the Projects
There are a few settings to be aware of. First, the Silverlight initializes with the location of the WCF endpoints. This is why we have a separate html file for the console app and the web app in the sample. The url is specified using the InitParams parameter on the html object. The standalone sample uses console.html whose initParams is set to: baseuri=http://localhost:8000/Music shown here.
<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="814px" height="580px">
<param name="source" value="ClientBin/MusicPlayerCustom.xap"/>
<param name="onError" value="onSilverlightError" />
<param name="background" value="white" />
<param name="minRuntimeVersion" value="3.0.40624.0" />
<param name="autoUpgrade" value="true" />
<param name="initParams" value="baseuri=http://localhost:8000/Music" />
<a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=3.0.40624.0" style="text-decoration:none">
<img src="http://go.microsoft.com/fwlink/?LinkId=108181" alt="Get Microsoft Silverlight" style="border-style:none"/>
</a>
</object>
So the html file tells Silverlight how to get to the WCF service, now the app.config
tells the server side how to get to the music files using these settings. The server
side returns a list of files with the configured web root so the Silverlight client
knows where to make a request to for the actual file. Note that this is also
where you change the base64 encode flag (false for virtual directory, true for WCF).
<MusicService.Properties.Settings>
<setting name="LocalRootPath" serializeAs="String">
<value>C:\music</value>
</setting>
<setting name="WebRootUrl" serializeAs="String">
<value>http://localhost:8000/Music/Files/</value>
</setting>
<setting name="Base64EncodedFilePath" serializeAs="String">
<value>True</value>
</setting>
</MusicService.Properties.Settings>
Next, for the database to work properly, I added the provider configuration for
System.Data.SQLite. This is mostly just an FYI that it’s there and that I clear
all existing ones during runtime. (In case you already have it installed to your
system). This will allow SubSonic (the data framework I chose for this project)
to load the provider for SQLite when the server performs searches.
<system.data>
<DbProviderFactories>
<add name="SQLite Data Provider"
invariant="System.Data.SQLite"
description=".Net Framework Data Provider for SQLite"
type="System.Data.SQLite.SQLiteFactory, System.Data.SQLite, Version=1.0.65.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139"/>
</DbProviderFactories>
</system.data>
Run the Standalone
To make the standalone version run, follow these steps:
- Get the files here
(Released Open Source via Creative Commons. See below for more details) - Open the app.config in the MusicHost project. Check the LocalRootPath
setting and set it to the root of your music folder (for example: C:\Music or \\Server\Share. - Also set the WebRootUrl setting to the public location where your music files are
available. For example: http://myhost.com/Music or for the standalone:http://localhost:8000/Music/Files/
- Run the MusicHost application.
- Browse to http://localhost:8000/Music/console.html
- For the first run, wait about 20 seconds for the indexer to index your music repository.
If you click Random or search for files with no results, it may still be empty. - Select an item in the list to start playing it.
Hosting with IIS
I left the functionality in there to host music files from a web server directly.
To see it, edit your Web.Config in the Web project and set WebRootUrl to
the IIS virtual directory of your music files. (For example: http://some.web.server/Music/).
Set Base64EncodedFilePath to False so it doesn’t encode the file request.
Then run the Web project and browse to Default.html. During the first run, click
Reindex and wait for the confirmation dialog, which make take awhile depending on
the size and location of your local repository.
Enjoy!
MusicCenter
by Nathan Bridgewater is licensed
under a Creative
Commons Attribution-Noncommercial 3.0 Unported License.


11 Comments
Hi @Pitchai
Well you have a couple options. If you choose to use a full web server to host your music, then you will have to expose it somehow using either a virtual directory to the folder on your D drive or by moving it to a subdirectory in inetpub/wwwroot (default). In IIS, you can require authentication for specific folders or virtual directories that will tighten up your music. I would probably create a virtual directory to a specific folder on the D drive and require authentication to it; then drop your music files under that. You’ll add the virtual directory’s web url to your configuration in web.config so the player knows where to find it.
The other option is to host the files using the standalone executable here. This is insecure because there is no authentication mechanism built-in. However, they do have to be base64 encoded in order to get the right file. Security by obscurity I guess.
Let me know if you have any problems.
Hi,
i have a mp3 file in the D drive of our server. How can I stream and play this file without moving this file into inetpup.
Please help me
@Peter
Never mind… i figured it out… I was able to isolate all the code i didn’t need and got the demo down to bare essentials. But i have a question, when i create a new WCF Service (.svc file) within the web project hosting the silverlight control, i can not get your code to work… i need to explictely remove the .cs from behind the .svc file and put in a separate assembly… Why is this necessary and can it be avoided to do this?
@Nathan
Thanks for the quick reply… i tried to download the first sample, but it seems it’s not longer available for download?
@Peter
Sorry about that.
Well that is exactly what this is doing. With a Silverlight MediaElement, I set its source to a static MP3 file on a webserver and let the media element handle streaming the file from the server directly. The only thing I’m using WCF for is managing the play list and performing searches.
I’m not sure it’s possible without considerable effort to replace the streaming portion itself with WCF.
I think to answer your question specifically relating to playing music files from a virtual directory share in IIS, take a look at the uxPlayList_SelectionChanged method. In there, I do a little crazy stuff to void url problems with special characters, but you’ll find I set the MediaElement.Source = url to the mp3 file.
Take a look at the first version of this article. It was a simpler version of what I did here.
Hi
I am looking for a way to stream files hosted on IIS using WCF, but i’m having trouble understanding your code. I’m very new to silverlight and there is so much going in the app… that i can’t make heads nor tails of it.
Would it be possible to provide a basic example of a service that reads mp3 file from a iis folder?
Just a mediaplayer element that streams and play a hardcoded .mp3 song using WCF would do.
I would really appreciate it.
Peter
@ScoobyDon
Excellent! Glad to hear it helped! Thanks!
Awesome. I had created a Silverlight app to stream my own music over IIS. Works great. Then a friend wanted to do the same thing but I thought it would be a pain to setup IIS for him and started looking into hosting a WCF service within an app to serve the requests. This article was a big help in getting the old thinker well oiled and running. Thanks.
Just a few quick updates…
The source is pre-built for any platform, but only seems to work with 32bit without pre-installing all the SQLite tools. For 64 bit, grab the 64bit System.Data.SQLite assembly from their website and copy it to your bin folder.
http://sqlite.phxsoftware.com/
This potentially can wire up to any database with just a connection string switch. Be sure to specify the providerName. Some providers may be a little buggy with SubSonic.
And to set this up on your server with a secure file share, use the web application and configure it to share the music folder with a virtual directory. Restrict access to the virtual directory through NTFS and IIS. When you run the application, searching, listing will work fine, but playing a file will prompt a login dialog.
Recent Posts
Tags