Aiptek PocketDV 3300 for Linux : history

This page gives a quick history of my work on the Aiptek PocketDV 3300 support for Linux.
I guess this may help you if you want to work on the project, or if you want to program your own driver for your camera.
Anyway this is not a "howto" but just my own ugly experience ;-)

Step 1 : spca50x

After a quick search on Google, I found that many products from Aiptek were supported by the spca50x module.
So I downloaded it, built it, and tested it.
Unfortunately it did not work for my camera. I tried to "hack" it a bit changing the USB vendor/prod ids but I did not manage to use it (has suggested here : http://spca50x.sourceforge.net/spca50x.php?page=faq#3).
I then contacted the maintainer of the module (Michel Xhaard) who also tried to patch his code to match my camera without success.

Step 2 : usbsnoop

I then returned under Windows to use a USB sniffing tool.
With Snoopy I captured the USB trafic between the PC and camera while using netmeeting (has suggested here : http://spca50x.sourceforge.net/spca50x.php?page=faq#4).
I then sent this sniff and the .ini files of the windows driver to Michel Xhaard, who gave me very useful informations : Wow ! How did you see that ? Well... Thank you :-)
Comparing a sniff and the video captured in MJPEG format while sniffing confirms this : the same sequences are in both file (except that bytes are swapped : little indian VS big indian).

Step 3 : perl decoder

Since I never coded with USB, I started with some perl script to try to get the JPEG frames.
If I can't do this, I won't do anything anyway...
So instead of programming the USB interface, the perl script is working on the sniff files from step 2.
What I want to do is to build a usable JPEG file using the binary values in the sniff.
I get some informations about the JPEG header here : http://www.obrador.com/essentialjpeg/headerinfo.htm
Some headers are easy to build but some others can't be guessed (Huffman table and quantization tables).
Looking at others JPEG images and reading the previous page, I can see the "quantization tables" are 2 x 64 bytes... and the first 128 bytes of each frame sniffed look different (incremental values like in other JPEG files, with many repetitions).
It's easy to see it's different since it is not compressed (not "random" values like the compressed data).
So now I have my quantization tables !
But the Huffman table is still missing... I guess it must be static since it is not sent with each frame like the quantization tables... So I have to find it in the windows driver...
I spent a *long* time looking for this ;-)
Here again, looking at other JPEG files, we can see the huffman table is often made of incremental values... So "often" you'll find alphabet portions in it like "cdefghijstuvwxyz".
I used the "grep" tool on all the Windows driver files and I found the magical sequence in files called "CoachTw.ds", "CoachVc.sys" and "jpegcode.dll".
So now I have all the needed header parts...
After some (long) time mixing all this together and switching back to "good indian", I get my first image !!!

Step 4 : usblib

Well so I have to write my own driver. That's great ! But where do I have to start ?
The easiest way to program USB stuff under Linux is to use the libusb since it is in the "user land" (not kernel).
This was quite a fast and fun part, since it worked :-)
In the sniff, I have all the initialisation values the PC sends to the camera (don't know yet what it means but who cares ;-))
I write "dv3300grab" who is first capable to grab JPEG images and then MJPEG video files using the "avilib" library (found in the spca50x project again).
Comparing a "160x120" sniff and a "320x240" sniff, I found which byte in the init values was responsible for the resolution of the pictures.
Hacking this value, we can use a "640x480" grab resolution which I *think* is not available in the Windows driver ?
Setting another byte to "1" in the initialisation sequence sets the resolution to a strange "128x128" size... I don't know yet what it is for... And I don't know either what others values are for (maybe brightness/contrast/etc).

Step 5 : kernel module

The grabber allows you to capture videos and pictures, but in order to use the camera in other programs (xawtv, gnomemeeting, mplayer/mencoder), you need a Video4Linux compatible driver.
I'm working on this part at the moment.
I write my driver heavily based on "usb-skeleton.c", "spca50x.c" and "vicam.c".
Everything is ready *but* I need to decode the JPEG frames in order to pass them to the Video4Linux interface.
The small JPEG decoder used in spca50x and based on the "bootsplash" project does not seem to work for the PocketDV 3300 JPEG frames...
The libjpeg can decode the frames in user land... but no way to include it in the kernel ;-)
I don't have enought time to learn how the JPEG decompression works, so I just read some doc and hack a bit the values in the decoder... (ugly way to process I know ;-)).
Changing some values to match my JPEG header (4 instead of 6 in decode_mcus, and HV factor is 2 and not 4), I manage to decode the image in black and white, and I release the version 0.1 of the driver.
When I get some more time I get back in the code, I hack again and I finally manage to get color ! (long time spent on this decoder for few changes).

Step 6 : integration into kernel

For some years, the driver continued to improve : bug fixes, new cameras, etc. But it was still using Video 4 Linux version 1 and a embeded JPEG decoder.
When V4L1 was marked as "deprecated" in the Linux Kernel, I figured out it was the good time to switch to V4L2 :)
The driver became really smaller and cleaner since the JPEG decoder was no longer useful. The only downside is that it lost compatibility with all V4L1 applications...
Someone asked me why I did not merge my driver in the kernel mainline... Well my driver was very ugly with its JPEG decoder, but now with V4L2 it is really nicer.
So I joined the LinuxTV project and started working on the integration of my driver in the kernel.
I spoke about this on my blog here, here, here, here and here.
So zr364xx is now part of the kernel mainline since version 2.6.22 :)