A while ago I bought two Logitech Orbit/Sphere webcams for my amateur computer vision experiments. They are USB 1.1 pan/tilt cameras capable of sending video output up to 640x480 resolution at 15 fps frame rate. There is no problem with using them on Linux thanks to the authors of PWC driver.

I wanted to experiment with depth calculation from series of stereo images containing moving objects. Therefore I needed perfectly synchronized cameras. First I checked how much asynchronously my cameras are working. I did some experiments and found random time difference up to about 1 frame, which was quite a while. Then I decided to take a look at the PWC driver to see how does it work and how can I make my cameras synchronized better.
In Video for Linux Two API documentation I found that "When the VIDIOCMCAPTURE ioctl returns the frame is not captured yet, the driver just instructed the hardware to start the capture", so I thought it would be easy to synchronize them by issuing VIDIOCMCAPTURE on both cameras simultaneously and retrieving frames by using VIDIOCSYNC later. However it looks like it's not the case with PWC driver - and probably it's generally not the case with USB cameras. In fact the driver is not sending any commands to camera during VIDIOCMCAPTURE. Camera is sending a stream of frames isochronously and they are put to the driver internal queue where they are waiting to be retrieved by an user.
It was obvious that there's no way to make them synchronized from software perspective, so I turned to hardware instead. Logitech Orbit/Sphere is based on SAA8116 general purpose webcam chip made by Philips. Fortunately there is some official documentation freely available on Philips web page. I was quite surprised when I found that there is a 80C51 microcontroller embedded inside the chip which is among other things controlling the USB interface. So in fact the camera is a little computer specialized in image processing, it has its own internal clock, RAM and ROM.
I was desperate enough to use any possible way of synchronizing my cameras. I dug into the specs even deeper and found in SAA8116 data sheet that it's possible to provide an external vertical pulse to camera after setting bit 3 in register PIN_CONFIG_1 (address 0x7F). In fact pin V number 96 of SAA8116 chip can serve both as an input and as an output depending on the PIN_CONFIG_1 register value. So I just had to found a way to modify this register.
After hours of googling I found a Windows WcRmac program capable of reading and writing webcam registers. It is used by amateur astronomers to switch SAA8116-based webcams to RAW mode. I realized that it's possible to change register values in camera EEPROM by using some special USB control commands. It looks like all control registers are mapped to upper 256 bytes of the EEPROM. After some USB traffic sniffing I discovered the following control messages:
request type: USB_TYPE_VENDOR + USB_RECIP_DEVICE + USB_DIR_OUT request: 0x0000005 value: 0x0003000 index: 0x0000003 data: [0x6d] size: 1
request type: USB_TYPE_VENDOR + USB_RECIP_DEVICE + USB_DIR_IN request: 0x000000a value: two bytes of EEPROM memory address in reverse order index: 0x00000ff size: 1
request type: USB_TYPE_VENDOR + USB_RECIP_DEVICE + USB_DIR_OUT request: 0x0000009 value: two bytes of EEPROM memory address in reverse order index: 0x00000ff data: [memory byte value] size: 1
request type: USB_TYPE_VENDOR + USB_RECIP_DEVICE + USB_DIR_OUT request: 0x0000005 value: 0x0003000 index: 0x0000003 data: [0x00] size: 1
memcpy(buf, "\x6d", 1); // don't ask me why it's 0x6d ;) ret = usb_control_msg(devh, USB_TYPE_VENDOR + USB_RECIP_DEVICE, 5, 0x3000,3, buf, 1, 1000);
Each read command stores 1 byte in buf:
usb_control_msg(devh, USB_TYPE_VENDOR + USB_RECIP_DEVICE + USB_END POINT_IN, 0xa, address, 0xff, buf, 1, 1000);
Each write command writes 1 byte from buf (0x60 in this example):
memcpy(buf, "\x60", 0x0000001); usb_control_msg(devh, USB_TYPE_VENDOR + USB_RECIP_DEVICE, 0x9, address, 0xff, buf, 1, 1000);
memcpy(buf, "\x00", 0x0000001); ret = usb_control_msg(devh, USB_TYPE_VENDOR + USB_RECIP_DEVICE, 5, 0x3000, 3, buf, 1, 1000);
I added support for these commands in pwc driver and created a small utility to write and read values from camera EEPROM memory. Correct operation of this utility was confirmed by using it to switch my webcam to RAW mode and back. After all this research and development I was finally able to change value of SAA8116 0x7F control register to check my webcam synchronization idea.

I tried to modify PIN_CONFIG_1 register by using my utility to send write commands to address 0x17Fh. However setting SAA8116 to external vertical pulse mode had no visible effect on the camera. Was it all for nothing?
Then I decided to make a final test. I tried connecting two webcams - first webcam's SAA8116 V pin serving as output to second webcam's SAA8116 V pin serving as input. I opened both cameras and soldered a wire from one webcam to another. It was quite challenging because pins of the chip are separated by only 0.3mm of free space. Somehow I managed to do it and put my cameras back together. There was some effect! Unluckily resulting image was slowly moving horizontally and changing colours. I checked different settings of PIN_CONFIG_1 bits 2 and 3, all were not better. Probably the necessary vertical synchronization input signal is somehow different from an output signal on this pin. There was nothing left to do, so I disconnected the wire. Both cameras survived this amateur neurosurgical operation without any damage.
|
Camera in parts |
PCB top side |
PCB bottom side |
After doing some more research on this, I'm quite sure that my idea of using external source of synchronization was fundamentally flawed and couldn't possibly work.
External vertical synchronization signal in SAA8116 is intended for CMOS sensors working in master mode. It means that sensor itself provides synchronization signals like pixel clock, vertical pulse etc. It's shown clearly on Figure 13 in SAA8116 datasheet. When using CCD sensors, an external vertical clock driver circuit is required - CXD1267AN in this case - and the whole synchronization process looks even more complicated, without any hope for easy external synchronization.