I have a few big subjects coming up soon, politics, the courts, and law. I'd like to take a deviation and write about an application I've been working on for a while now. First I just called it tivo, then http-dvr, now DvrD. They all suck, but that's really not what matters. Tivo's run Linux, which is an operating system, so the web sever on these machines don't really qualify as embedded. Most PVR software for the PC also come with web server's, though they are all designed to control that specific software, and do little else. In fact the software I run on the PVR I built, GB-PVR, contains a web server... Click the title to read the rest.
I like that kind of remote functionality, and want more of it, more than I can get from other peoples software. But I'm getting a head of myself.
I found through the GB-PVR forums that GB-PVR could wake an XP machine out of hibernation in order to record a program. I was thrilled at the prospect, because the PVR I built was sadly much louder than I anticipated when I built it. It is now set to enter hibernation 20 minutes after the system has been idle (it takes about that long for the program to update the Electronic Program Guide), and is turn off the vast majority of time. I quickly discovered however that the infrared remote control application that came with the video capture card ceased to work when the system came out of hibernation, and had to be restarted. I looked at the IR application and found that starting another instance of it with the right command line parameter would force it to restart, and that gave me the an opportunity to solve the problem myself without being forced to stop using hibernation.
In the past, I've written very small background applications in C for Linux/BSD/Solaris machines that I've had shell accounts on that would run a web server log file analyzer, and then sleep for a given period. It would repeat that process endlessly, insuring I would always have a recent report on activity at hand. It was much easier than relying on a cron job. I set out to do something similar, where a small application would sit in the background that could restart the IR application whenever the system returned from hibernation. The first challenge was as simple as its solution; how do you detect when a system has returned from hibernation? The answer: temporal shift.
If the time has changed, the variable is updated with the current time and it goes back to sleep, only to wake up a quarter second later to check again. Theoretically, as long as the machine is running, the difference between the current time and the variables time should never be more than 1 second. When the system goes into hibernation, the variable will no longer be updating as the program is no longer running. When the system is restored to a full operating state, the delta will be checked and the result should be the length of time (in seconds) that the system was offline.
I decided on a delta of 5 seconds as the criteria for triggering the relaunch of the IR application, and it worked flawlessly. Mission accomplished, right? Nah, that's just the beginning.
I quickly decided that as long as I was going to have an application of my own always running on the system, I might as well make use better use of it, and make it do more of the things I wanted done. At that time, the GB-PVR web server did not contain any statistical information on the hard disk. I wanted to know how much space was free, and how much recording time that space represented. I already had a function from another project that used WINAPI calls to discover the amount of free disk space in bytes, it worked on both 98 and NT. I had the function, now I needed a way to deliver it.
I had a decent understanding of how HTTP transactions worked, and I have experience with network programming. I really only needed to decide what language to use. Java was my first choice for two reasons: it was very fast to code in with Java's existing class library, and I already had a very small and general purpose Java web server written by Ron Hitchens. It was even threaded, which would make it very responsive. Unfortunately, the PVR only has 256MB of memory in it, and GB-PVR, being written in C#, has very bloated memory usage. An EPG update takes up almost 170MB of memory, and I just couldn't sacrifice the 20MB+ that a JVM would use, especially since I would only be querying it occasionally.
I chose to use C instead, and started with an embedded mindset. All the HTML was inside the program, making it very inflexible. If I ever wanted to change the web pages it served up, I had to recompile the application. But it did work, and http-dvr was born.
The code wasn't pretty, but it was small, about 11k compiled. It only used about 250K of active memory and had zero memory leaks due to the exclusive use of preallocated character buffers. I didn't need the speed or flexibility that dynamically allocated pointer buffers gave me, and I certainly didn't need the memory leak head ache. It ran for several months, happily reporting how much space was free on the system, and how many hours left I could record. But I wanted it to be better, I was sick of how the canned status web page looked, and I decided that since I streamed video over the LAN to the PVR so often, I needed a better way to launch VLC than to go over to the permanently attached keyboard and mouse to start it.
The first thing I did was add a hard coded path, /launch_vlc, that when loaded would start the VLC application. That worked fine, but since I was already knee-deep in the code, I decided to take a second stab at something that I had tried before, and gave up on. Serving up real HTML files from disk, and replacing custom variables in the HTML with real-time data.
The last time I tried this, the code just kept getting longer and longer, and not working how I wanted. The original intent was to make it a real web server in the sense that it served up web pages, but it just wasn't happening, and I defaulted to hard coded response. But now, I wanted it even more, and it went much much smoother the second time around. In less than half an hour, I had a short list of variables I could drop inside the HTML, such as $free_space_gb$. I made variables for each piece of data I had under the old system. Free space in GB, MB, as a percent, and hour many hours were left at high, medium, and low recording quality.
I quickly expanded the variable list with a full date, then dropped that in favor of splitting the dates into their basic parts for ultimate flexibility. $date_day_name$ would give you Friday, $date_month_name$ would give you April, while $date_month_number$ would give you 4. Every part of the date and time has its own variable, making it totally unnecessary to do something as complex as a real scripting language.
There are some downsides to the way I designed this application. All the code was mixed together, and I had to separate all the functions into separate files, so its not quite as unified as it used to be (http-dvr was the "http-dvr, unified binary".) The use of static buffers means it's using memory that's not needed, the maximum buffer size is 4k, and there are several static buffers of this size. The upside of that is no memory leaks. There are also zero security features. Anyone can reach it over our LAN, shut it down, start VLC, crash it if there are bugs and even use it to open any file on the machine. There are no recursive directory checks; too much code for too small a threat.
The networking system is also as simplistic as it can get. Once a new connection has been accepted, it reads 4k from it, and assumes the request is a GET. It cannot handle and does not check to see if it is anything else. Once the buffer has been filled, the request is processed and nothing else can happen until its finished. One connection at a time hampers its responsiveness, which sucks, but that's the deal. It's not a real web server.
I'm proud of the little bugger. It's tiny, fairly fast, and 100% stable. It just works.