A man will die, but not his ideas.

Friday, January 05, 2007

My Little JMF Adventure

About ten days ago, I was assigned the task of implementing an application that required the ability to capture, play, and stream audio/video. Yes, it is the task that I've mentioned in previous posts which made me re-install Windows and caused a lot of GRUB problems. Anyway, since I'm pretty much busy this month, I decided I'd go the easy way, try what .NET has to offer, and use Visual Studio.NET 2005. Little did I know that I was probably making a bad decision.

After a few days of frustration and sinking in the pond of DirectShow(.NET), COM, COM Interop, C, C++, wrapper classes, delegates, and the likes, it was obvious that there had to be a better way. So, being the Java fan that I am, I decided to give the Java Media Framework (JMF) a try. I recall hearing about JMF back in 2002 when some elder students used it in their graduation project but I never really got the chance to look into it. And since I was sick and wanted to relax for a few days, it was a good chance to learn something new and test drive NetBeans 5.5 which I had already downloaded and installed on both Linux and Windows.

I installed JMF 2.1.1e (also on both Linux and Windows) and started Googling around and reading the JMF 2.0 API guide. I was really fascinated by the architecture and it had almost everything I needed. I'm really surprised why Sun has not made any attempts to further improve the framework and add more functionalities to it since 2004 or so. JMF even comes with a ready made application (with source code) called JMStudio that you could use as a player/streamer or to test JMF capabilities. In addition, I found some excellent sample code available on Sun's Web site. So, I launched NetBeans on Windows and started coding away. I first wanted to see whether I could capture the video stream from my Webcam since JMStudio was able to do so. I coded a simple application that performs the task. But when I launched the app, nothing appeared on the JPanel. I looked around for errors and searched online for answers but with no luck.

Finally, I noticed in some example an invocation to the pack() method of the JFrame. So, after everything else failed, I tried doing the same. Magically, after launching the application again, the captured video stream appeared on the JPanel. I don't know the reason yet, but I will check what the magical pack() method does. OK, that looks promising. Now for the real challenge. Let's try it on Linux. Ubuntu detects my Philips Webcam with no problems. So, first I wanted to double check using JMStudio. However, attempting to launch JMStudio failed with a NullPointerException. Now what?!

Looking around at Sun's forums, I found many posts recommending to make sure that the CLASSPATH and other related system variables are properly set. Some even went to say that Java 5.0 doesn't work with JMF. I discovered that this wasn't ture, since I was using the 5.0 version of the SDK. The solution to this exception is to set the AWT Toolkit to the Motif toolkit via the awt.toolkit JRE property when launching the application (java -Dawt.toolkit=sun.awt.motif.MToolkit JMStudio). JMStudio was up and running, and able to capture live video stream from my Webcam. Now to test my application.

Hmmm, there seems to be a problem (an obvious one I might say). In my application I created a MediaLocator using the "vfw://0" URI. But what should the URI be in Linux. After all, "vfw" stands for "Video For Windows". Amazingly, I discovered there was something called "v4l" (video4linux) on Linux. Refer to Capture Requirements sections in the JMF 2.1.1 - SW/HW Requirements. Some links to resources related to video4linux can be found here. The URI used for the MediaLocator will basically the same except that the "w" should become an "l". This even shows up if we launch the JMFRegistery application which comes with JMF alongside the JMStudio. Now, references to objects belonging to the com.sun.media.protocol.vfw package should be replaced to similar objects in the com.sun.media.protocol.v4l package. So far, so good. I got it working on Linux without any hassle.

Did I say having the camera working on Linux was a real challenge?! I was definitely wrong (although it took a while to figure out). The real challenge is using RTP to stream the captured video and receiving it on another side. This basically took most of my time to get right. What really saved the day was the excellent JMF 2.1.1 Solutions found at the Sun Developer Network (SDN). For your information, JMF also supports RTSP. So, ironically, in the end, it seems that the solution I was trying to avoid in order to save time turned out to be the most effective. I managed to finish the task in just 3 days and had a wonderful (sometimes difficult) experience with JMF.

Update:
According to the JavaDoc of the Window class, here is what pack() does:

"Causes this Window to be sized to fit the preferred size and layouts of its subcomponents. If the window and/or its owner are not yet displayable, both are made displayable before calculating the preferred size. The Window will be validated after the preferredSize is calculated."

After reading this, I'm not quite sure calling pack() is the right solution to the problem. But, at least it is working.

And here they are folks...



The Camera Server


The Monitoring (Client) Application


Notes:
When attempting streaming, especially on Windows XP systems, avoid obtaining the local IP of the machine using the getLocalHost() method of the InetAddress class because there might be many interfaces connected to the PC and the return value of the method will most likely be unpredictable. And even if there was only one network interface connected, Microsoft has a very stupid virtual connection entitled "Microsoft TV/Video Connection" that appears suddenly on Windows XP machines when a Web-cam or a TV/video card gets connected or starts functioning. And by the way, don't bother disabling it, simply because it won't allow you. The only solution Microsoft has to offer regarding this is to "restart the computer"! As it is explained in their knowledge base, "This behavior is by design"!!! I don't know why I suddenly recall Stephanie Tanner from Full House saying: "How ruuuuuude!!"

Labels: , ,


Posted by A :: 6:53 PM :: 4 Comments:

Post / Read Comments

-------------------------------------


Creative Commons License
Unless otherwise expressly stated, all original material of whatever nature created by the blog's author and included in the "A man will die, but not his ideas." weblog and any related pages, including the weblog's archives, is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 2.5 License.

* Java and the Java Coffee Cup Logo are trademarks or registered trademarks of Sun Microsystems,Inc. in the U.S. and other countries.