RSS 2.0
# Monday, December 31, 2007

This is the fifth in a series.

This time, let's concentrate on the manipulations a user will want to do on the media library (retrieving songs, genres, artists, etc.). For this, we'll add another WCF Service to the MediaPlayerRemoteListener project called MediaLibraryService. The service contract for this will looks something like:

[ServiceContract]
public interface IMediaLibraryService
{
    [OperationContract]
    List<Song> GetMediaLibrary();
}

With that, all we need to do is plug in the implementation on the server side and start making use of it on the client side. Of course, this is were things get difficult.

Once again, I find myself at odds with the tools. I've labeled both service contracts with the "ServiceContract" attribute. I've also been very careful to use the "DataContract" attribute for any data items returned from our service (in this case, the Song data contract). However, when adding references from the client via the Add Service Reference option, I am faced with two copies of the Song data contract on the client -- one in the MediaLibrary namespace, and one in the NowPlaying namespace. So, if I get a List<Song> from my MediaLibrary service, and I want to send them to the Play() method of the NowPlaying service, I have to convert the MediaLibrary.Song to a NowPlaying.Song. This leads to a large number of mapper classes (or one cleverly designed generic mapper that uses reflection). I recall similar difficulties with the old .NET 2.0 webservices, and I had hoped this challenge had been overcome in the new tool set. Apparently not.

I supposed it's prudent to talk about the idea of a data contract. Should separate services need to share the exact same data definition as an input or an output? One could argue that if they're so similar, they should be a part of the same service. However, the simple truth is that it happens quite often in the real world. For example I'd like to host one interface for read only access and one interface for read/write access. This allows separate levels of permissions / authentication / protocol for each interface.

One solution is to revert to command line tools with the new /shareTypes parameter to wsdl.exe (via http://www.theserverside.net/tt/blogs/showblog.tss?id=WSStrikesBackP6). But, this makes the already tedious task of updating our service reference all the more tedious. Every time I change my contract (which has happened several times so far), I have to run the hosted version of my service library, escape to the command line and run a batch file refreshing the service reference in our client application. Of course, now that I write it down, it's not that much more difficult than changing the port numbers in my app.config and running the built-in refresh command.

Here's another option. Given our current implementation, we may be able to change the interfaces to remove any need to share data contracts. Does the Play method really need the entire song, or just some form of unique identifier to allow the media player to add it to the play list? It seems like we may be able to get by with a single GetMediaInfo method returning all of the Song information, and simply passing around some form of unique identifier for all of the rest of the methods. So we end up with interfaces like this:

[ServiceContract(Namespace = "http://www.cavinconsulting.com/MediaPlayerRemote/")]
public interface INowPlayingService
{
    [OperationContract]
    PlayState GetPlayState();
    [OperationContract]
    string GetCurrentSong();
    [OperationContract]
    List<string> GetCurrentPlaylist();
    [OperationContract]
    void VolumeUp(int amount);
    [OperationContract]
    void VolumeDown(int amount);
    [OperationContract]
    void SetVolume(int volume);
    [OperationContract]
    void MoveNext();
    [OperationContract]
    void MovePrevious();
    [OperationContract]
    void MoveToSong(string song);
    [OperationContract]
    void Play();
    [OperationContract]
    void Pause();
    [OperationContract]
    void Stop();
    [OperationContract]
    void SetRandom(bool random);
    [OperationContract]
    void SetRepeat(bool repeat);
    [OperationContract]
    void SetMute(bool mute);
    [OperationContract]
    void PlayPlaylist(List<string> playlist, bool appendToCurrentPlaylist);
}
[ServiceContract(Namespace="http://www.cavinconsulting.com/MediaPlayerRemote/")]
public interface IMediaLibraryService
{
    [OperationContract]
    List<string> GetMediaLibrary();
    [OperationContract]
    Song GetMediaInfo(string song);
}

This has the advantage of sending around significantly less data than our previous version. It has the downside of passing primary keys around as strings (I think WMP uses a 128 bit GUID). This isn't exactly what I had in mind for a solution, but it seems to be the direction the tools are pushing me. I'm still not sure this makes sense from a practical standpoint, but we'll keep on following this path for a while longer.

The source for this post is available here. There is a really bad implementation of a ListBox that supports being a DragDrop source as well as destination, and a horrible implementation of displaying a media library in a TreeView (BTW, Drag and Drop on playlists only works for songs in this example).

Monday, December 31, 2007 3:02:51 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
Windows Media Player
# Friday, December 21, 2007

This is the fourth in a series.

With our "design" out of the way, we can start digging in to the actual implementation. Our first draft will be somewhat exploratory in nature. Given my limited knowledge of the Windows Media Player plug-in architecture, I have decided to start by hosting the WMP ActiveX control in a standalone WinForms application, and have this WinForms application host a WCF service. So, here goes:

We'll start off by designing the communication contract through which our client controls and queries our media player. For now, we'll concentrate on controlling the media player. For this, we'll use a WCF contract called INowPlayingService:

[ServiceContract]
public interface INowPlayingService
{
    [OperationContract]
    PlayState GetPlayState();
    [OperationContract]
    Song GetCurrentSong();
    [OperationContract]
    List<Song> GetCurrentPlaylist();
    [OperationContract]
    void VolumeUp(int amount);
    [OperationContract]
    void VolumeDown(int amount);
    [OperationContract]
    void SetVolume(int volume);
    [OperationContract]
    void MoveNext();
    [OperationContract]
    void MovePrevious();
    [OperationContract]
    void Play();
    [OperationContract]
    void Pause();
    [OperationContract]
    void Stop();
    [OperationContract]
    void SetRandom(bool random);
    [OperationContract]
    void SetRepeat(bool repeat);
    [OperationContract]
    void SetMute(bool mute);
    [OperationContract]
    List<Song> AddToCurrentPlaylist(List<Song> playlist);
    [OperationContract]
    void PlayInPlaylist(Song song);
    [OperationContract]
    void ReplaceCurrentPlaylist(List<Song> playlist);
}

That looks like a pretty comprehensive list of operations we would want to perform on a media player. So, let's get down to the business of actually implementing this in Visual Studio 2008. I start a new solution and add three projects, a WinForms host application, a WinForms Client application, and WCF Service Library. I add a project reference from the host application to the service library. Then, I notice I can add a service reference from the client application to the service library. Right click on the project, click add service reference, click discover->services in this solution, and choose the service of interest. Nice! Or, so I thought.

Here's where the first problem arises. Remember, I'm using this as an exercise to get my head around the new technologies. The WCF service library project type does a nice job of housing the service components as well as publishing the appropriate meta data for things like adding service references. However, when I go to debug this application, VS automatically spins up the WcfSvcHost process to host our service library. But, remember, I want to host the WCF endpoint within my host WinForms application. No matter what projects you tell VS to start up (or not start up) on debug, it ALWAYS runs the WcfSvcHost application and starts hosting my service library.

It turns out, I'm not the first person to have this problem: Visual Studio 2008, WCF Service Libraries, and CTRL-F5. It appears that the solution is to "remove the project type GUID" that tells VS that it's a WCF Service Library. What? So, in order to host a WCF service library in my own application, I have to disable all of the helpful aspects of VS regarding WCF endpoints (like, say, updating my service reference as the contract changes over time). Ridiculous.

Here's how I finally resolved it. Leave it as a WCF service library, but change the port values in the app.conf sections so my host uses a different port than the WCF service library. That way, in the course of normal debugging, the client points to the ports opened by my host. When I need to update the reference, I change the port to the VS host port, update the reference, and then change it back. It's a nasty bit of kludgery, but it works for my needs. It seems like I must be making a habit of doing things Microsoft doesn't expect. First, I expected a standard windows application to be simple in WPF, now this.

Anyway, source code (so far) is available here. You'll want to change the URL property of the media player control in the host application. You'll also want to set the startup projects so that the host and the client are both started. It's a pretty basic remote control with standard navigation utilities. Next, we'll flesh out some of the media library functionality so we can change what gets played.

Friday, December 21, 2007 2:56:46 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
Windows Media Player
# Friday, December 14, 2007

This is the third in a series.

Design

As with any good development effort, we begin by designing the solution, complete with an assessment of the pros and cons of each option. Of course, as with any good development effort, we begin with every intention of designing the solution, but we usually skip right past this to writing the code since designs are never any fun. Design: There is a server and there are 0 or more clients that want to control what the server spits out the sound card.

Right. With the design done, let's talk about implementation options:

As I see it, there are two options for implementing the functionality as exhaustively enumerated in Part 0 of our series: 1) A standalone application in which we host and automate a Windows Media Player control, 2) A plug-in to the existing standalone Windows Media Player in which we automate the instance of Windows Media Player in which we are hosted.

So, which of these approaches is better? I have no idea. So, we'll design the approach in such a way that the end user (client machine) doesn't care. How? SOA, WCF, and other TLA's. I figure I can hide my media playing component/service/application behind a WCF connection point. Then, if hosting the ActiveX control fails miserably or creating the WMP plug-in doesn't play nicely with .NET, then I'll always be able to call some arcane WIN32 function to dump directly to the sound card or convert to Linux, convert all of my WMA's to raw .au files and just cat [file.au] > /dev/audio.

Of course, that's an exaggeration. I'm sure one of the two approaches will work fine. I just don't know which. So, hiding the solution behind a WCF connection point should give me the flexibility to try both approaches without rewriting the clients.

At this point, I find that there are a couple of interesting questions to which I do not know the answer. This, of course, will not keep me from plunging headlong into the implementation. They're just questions.

  • If I host an instance of the WMP ActiveX control, does it still do all of the clever background work of sniffing media information from the Internet, listening for changes in my media library, and downloading album art or is it just a numb playback machine using the existing media library?
  • What are the multi-threading characteristics of the WMP library? Will I have to perform the read/write locks, or will WMP take care of that for me?
Friday, December 14, 2007 2:54:53 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
Windows Media Player
# Tuesday, December 11, 2007

Build or Buy decision

This is the second in a series. The overview is here.

State of the industry:

So far, I can find a bunch of solutions to stream media to a client. That is, given a server containing a bunch of media, Spit it out to a client machine (PC, cell phone, television, etc.) so you can consume it wherever you are. This doesn't cut it for our purposes. Instead, we want the media to play at the source, but be able to change the behavior of the media player at the host from a client somewhere.

Items of interest:

So far, that's all I have. So, if I want a remote control to work on a laptop, pocket pc, smartphone, (and maybe a website), it looks like I'm out of luck in the commercial market. So, it looks like the build option is the one for me.

Of course, that's what I expected to choose. Otherwise, this would have been a really short series.

Tuesday, December 11, 2007 2:51:22 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
Windows Media Player
# Monday, December 10, 2007

This is the beginning in a series of unknown length regarding controlling the Windows Media Player from remote machines. So far, I'm at the requirements phase. Next will come industry research (buy or build), but I'm pretty sure I'm going to build something anyway (even if there are perfectly usable buy options -- I'm just stubborn that way).

Here are the requirements in no particular format or order:

  • I have a Server 2003 machine running with its audio output hooked up to the whole house audio system (okay, it's just two speakers in a different room, but there's a volume control in there, so I can call it what I want). The server houses the media collection and runs a uPnP server (TwonkyMedia) for the rest of the machines in the house to stream.
  • I can play audio through the server if I remote terminal to the server, run media player, and choose some songs. That, of course, completely fails the Wife Acceptance Factor (WAF).
  • What I would like is a service or application running on the server to be ready to play at all times, and have the rest of the machines (including handheld and smartphone) be able to dynamically change the playlist, start, stop, fast forward (most important) and rewind.
  • I'd like this to run as part of Media Player. I'm not sure why, since SOA dictates that we're just exposing a service, not a particular technology. I think it's mainly because I'm used to maintaining the media library in WMP and I don't want to change.
  • An added bonus is if there were a sidebar gadget for my wife's Vista machine (yes, I'm still on XP until some of the performance issues are worked out), and a 10' UI for the media center PC in the family room.
  • Since the uPnP server streams media to all of the important places (laptop and media center), there's no reason to mess around with streaming the actual audio to the remote control client, though the now-playing art would be nice.

That's it. In those few, short paragraphs, I have managed to cut out a lot of work for myself. I'm looking for a media player with remote controls that run on smartphone, pda, xp, and as a sidebar gadget. I'd like it to run on server 2003 every time it starts up, be multi-remote control robust, and extremely fault tolerant.

Now, to the buy or build comparisons...

Monday, December 10, 2007 7:48:37 AM (Pacific Standard Time, UTC-08:00)  #    Comments [0] -
Windows Media Player
Navigation
Categories
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2012
Cavin Consulting
Sign In
Statistics
Total Posts: 28
This Year: 0
This Month: 0
This Week: 0
Comments: 4
All Content © 2012, Cavin Consulting
DasBlog theme 'Business' created by Christoph De Baene (delarou)