Building your own Media Center

Since I have IPTV I am really annoyed by not having an option to save programs, like in old video recorder and cable network days. True, one can still save using video recorder, but the problem is that video recorder can't change channels on STB (set top box) and thus you are limited to a single channel. Pretty much useless. The other option is using personal recorder, a feature provided by my IPTV provider. But again, the drawbacks are enormous: there is a monthly fee, you are limited to 6 hrs of total saved content. Even worse limitation is that your content can be stored for maximum of two days (forget vacation, or drive back to home every two days to watch the saved content). But there's another drawback, in fact mother of all drawbacks: only a few channels and not all shows on those channels are allowed to be saved. Total useless crap.

Hence the idea of my own media center. After all I live by using my development skills, why not use them for this one. So, RH Media Center project was born.

The main objectives:

  1. ability to save from IPTV
  2. ability to schedule saving
  3. ability to playback saved content
  4. ability to control RH Media Center by remote

Hardware requirements

  1. A computer (server which is always on preferably) that will be used to save content
  2. A computer to playback the saved content (possibly attached to TV). In my case this is my laptop.
  3. A PocketPC device (remote control)

1. and 2. can be the same computer, whatever is feasible for you.

Implementation

VLC media player ActiveX control is at the core of RH Media Center. VLC media player is a free, open source, cross platform application that plays just everything out there, including SIOL IPTV streams. And luckily for me, they have an ActiveX control, too. In fact I've build my application around this ActiveX control using Windows Forms UI. Here is how it looks:

 image

Video (both IPTV stream and saved content playback) is rendered and saved by VLC. You can also see a bunch of controls on the top and the channel listing on the right. There is also saved content listing in the docking panel next to channel listing.

image

Test saved contents listing

Note that UI is pretty rough at this time as prettiness wasn't one of the objectives. Anyway the objectives 1. and 3. are done now.

Scheduling

Once the core functionality is done (see above) the scheduling is pretty easy. The application should parse command line arguments and start saving given channel for given time. The content file name should be made of given argument (i.e. name of the show) plus date. Here is an example:

RhMediaCenter.exe rec "Channel" ShowName 120

This means that content from "Channel" will be recorded for 120 minutes to a file name

ShowName_hh_mm__dd_MM_YYYY.ps

ps is MPEG-PS extension. You'll note that I didn't specify when should the recording start aka scheduling. This step is done using Task Scheduler - no wonders there, just run that command line at any time you specify and that's it. A bit rough to configure but it works just fine (in future I'll enhance the configuration step).

Remote control

Every decent media center has remote control capabilities. How can you skip those commercials otherwise? I've figured out, that I have a bunch of PocketPCs lying around and collecting dust. At the same time I have a Wi-Fi network at home. Get the idea? Yes, I'll use PocketPC over Wi-Fi to control my media center. The technology of choice is WCF which is partially supported with .NET Compact Framework 3.5. BasicHttpBinding, here we go.

So I've build a simple Windows Mobile 6 application which looks like this:

image

It allows to connect to preferred RH Media Center through providing a proper IP, it can get a list of saved content and it allows you to play any of them. It features also a Pause button and move forward (left group of buttons) or backward (right group of buttons) for a given time span. And after creating a hole in Windows Firewall on computer where RH Media Control runs it just works.

The only problem is how to build WCF service client code for .net compact framework. This feature is provided by Power Toys for .NET Compact Framework 3.5's NetCFSvcUtil utility that does the similar job as Service Metadata Utility (SvcUtil.exe) for .net framework.

Conclusion

I solved the biggest IPTV issue - saving programs and playback of saved content using a remote control. By using .net 3.5/Windows Forms/WCF/Compact framework and VLC ActiveX control it took me only around 10 hours of total time over the weekend (most of the time I used for plumbing , user interface and figuring out VLC ActiveX oddities). If you wonder why I'd used Windows Forms instead of WPF: because I was experimenting at the beginning (and the project is still an experiment) and I have no 3rd party controls for WPF yet - so it was easier with Windows Forms. In future I'll be definitely using WPF.

I have to say that .net/VLC made it so easy to build this pet project - the ease of putting pieces together is amazing.

BTW, Is anybody interested in binaries? (I am not saying I'll provide them nor that I won't provide them :-))

"The target assembly contains no service types. You may need to adjust the Code Access Security policy of this assembly." annoyance

Did you ever encounter this dialog box when dealing with WCF services?

"The target assembly contains no service types.  You may need to adjust the Code Access Security policy of this assembly."

It might happen when you run application at debug time. It is highly annoying and time consuming (it pauses application for more than one minute without any apparent reason). But what does it mean and why does it happen?

When you create a project from a WCF project templates Visual Studio knows that this project is a WCF service and thus it offers two debug time helpers: WCF Service Host and WCF Client. These two guys are intended to help you with running and testing WCF services without writing any code - they just appear at debug time. So far so good. But why the annoying dialog?

The dialog in question means that you have a project, created using one of the WCF project templates, with an interface marked with ServiceContract attribute and in the same project you don't have a class that implements this interface (perhaps you implemented that interface in another project). So, the WCF Service Host can't find a suitable class to host the service and it complains through that dreaded dialog box. Note that WCF Service Host is pretty dumb and it is incapable of searching through other projects in same solution. OK, the solution is to stop running WCF Service Host or even better, instruct it which class implements the interface in question. Well, AFAIK the later is impossible while the former can be done through project file modification using notepad. Here is how:

Delete this line from your project file:

<ProjectTypeGuids>{3D9AD99F-2412-4246-B90B-4EAA41C64699};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>

And you won't see WCF Service Host or dreaded dialog anymore.

The question is, why didn't [MS] think of these scenarios before?

BTW, if you just want to stop WCF Client from runing then delete this command line argument: /client:"WcfTestClient.exe", created by WCF project template.

VS2008 SP1?

I just read an interesting post about debugging enhancement for Visual Studio 2008 SP1. Right, the post isn't interesting just because of new features but also because it actually mentions the existence of SP1. Looks like we might see SP1 earlier then expected :-)

A part of .NET Framework Source Code is available for debugging purposes

Great news. [MS] finally released a part of .NET Framework Source Code for debugging purposes. A move that didn't even seem possible a year or so ago. Anyway, these are the assemblies available for inspection. If I understand properly more will follow.

  • Mscorlib.DLL
  • System.DLL
  • System.Data.DLL
  • System.Drawing.DLL
  • System.Web.DLL
  • System.Web.Extensions.DLL
  • System.Windows.Forms.DLL
  • System.XML.DLL
  • WPF (UIAutomation*.dll, System.Windows.DLL, System.Printing.DLL, System.Speech.DLL, WindowsBase.DLL, WindowsFormsIntegration.DLL, Presentation*.dll, some others)
  • Microsoft.VisualBasic.DLL

Read more about how to enable symbol/source code loading in Shawn Burke's post and don't forget to install this Visual Studio 2008 QFE (otherwise you'll be bugged with license agreement windows all the time).

Read also license agreement clarification (from Scott Guthrie's post) about possible concerns:

The .NET Framework source is being released under a read-only reference license. When we announced that we were releasing the source back in October, some people had concerns about the potential impact of their viewing the source. To help clarify and address these concerns, we made a small change to the license to specifically call out that the license does not apply to users developing software for a non-Windows platform that has “the same or substantially the same features or functionality” as the .NET Framework. If the software you are developing is for Windows platforms, you can look at the code, even if that software has "the same or substantially the same features or functionality" as the .NET Framework.

Righthand Dataset Debugger Visualizer goes 2008 and looses expiration restriction at the same time

As a (late) new year gift I've removed annoying expiration restriction from my dataset debugger visualizer. Find the 0.9.12 update here.

I've also rebuilt it for Visual Studio 2008 version which will be the only one supported from now on. Find the 0.9.12 version for 2008 here.

Read more about Righthand Dataset Debugger Visualizer.

A contribution to the Webheads CoP

Saša is doing some online community work for TESOL EVO BaW08 event. A part of her work is to create a nicely formatted weekly digest of forum threads (hosted on Yahoo forums). Nice digest is then published on BaW08 Wiki. Since the work is tedious I've created an application, named Webhead Digest Parser, for her and any other member that will do the same work. Using this application cuts 90% of the copy&paste&format work. Instead the user can focus on organizing the messages and other entities. Once the messages, groups and authors are organized the user can Export the output to clipboard and paste it into their Wiki site. You can read more about in Saša's blog post.

Here is an application screenshot:

shoot

The application is built on .net 3.5 framework and even uses some LINQ stuff.

Nicer way to check for a flag presence

Imagine you have this enum definition and a variable of same type:

[Flags] public enum Tubo { One, Two } ... Tubo tubo = Tubo.One;

Now, how do you check if a variable of type Tubo contains a flag, i.e. Tubo.One? Simple, like this:

bool b = (tubo & Tubo.One) == Tubo.One;

But do you really like this notation? I mean it is a lot of typing and the expression is not clear at first sight. That's why I checked out if extension methods might help. So I created this experimental method:

public static bool Contains<T>(this T list, T flag) where T : struct { return (Convert.ToInt32(list) & Convert.ToInt32(flag)) == Convert.ToInt32(flag); }

And the new test would look like this:

bool b = tubo.Contains(Tubo.One);

Isn't this notation much more readable and easy to type? Sure. But there are two drawbacks in here:

  1. If a non-numeric structure is used the compiler won't catch the error (the restriction of generic type T is struct) - at the runtime you'll get casting error when the structure can't be converted to Int32.
  2. Performance. There is a monstrous performance hit, like 500x slower. There are many operations involved - mostly because of Convert.ToInt32 method usage. (extension method call is probably inlined). There is no other way since, as far as Contains method knows, the arguments are struct types, and struct types can't be used like numbers just like that.IOW this code won't work:
  3. public static bool Contains<T>(this T list, T flag) where T : struct { return (int)list & (int)flag == (int)flag; }

    But if the code above worked one would get a decent performance, not much slower than doing a simple bitwise and operation.
Unfortunately there is no better solution AFAIK. It would certainly help if we could restrict generic type T to enum (where T: enum).