Hi Jacob, I'm very sorry for the (very) late review. I barely had time to work on the UVC driver for the past 4 weeks. Things will hopefully settle down a bit soon. Please don't believe your contribution isn't appreciated.
On Saturday 31 October 2009 00:07:51 Jacob Fehr wrote: > Hey Laurent, > > Attached is a changeset that will sync the computer's clock to the camera > clock. Thank you very much. I've copied the patch inline for the purpose of commenting. > The final result is that uvc_buffer.buf.timestamp is adjusted to compensate > for the latency that happens on the camera and the latency that happens in > the USB stack. It seems to work quite nicely. With the patch, I get very > even timestamp intervals. In fact it's so accurate that I see a 9 > microsecond/frame drift between my camera clock and my computer clock. Very nice. Do you have a GPS clock to check which one is correct ;-) > There is still +-105 microseconds of latency that could be tweaked out. But > for now this is should be accurate enough for even the most demanding > application... such as watching TV, DVDs, etc. :-) I suspect the remaining > latency is coming from the interrupt handler and USB stack. Getting that out > would require that I get down into the HCD code. > > Have a look and let me know what you think. > diff -r 214c94aa62aa -r 4617ec8de827 > linux/drivers/media/video/uvc/uvc_driver.c > --- a/linux/drivers/media/video/uvc/uvc_driver.c Sat Oct 10 01:55:23 > 2009 +0200 > +++ b/linux/drivers/media/video/uvc/uvc_driver.c Fri Oct 30 16:24:31 > 2009 -0600 > @@ -198,24 +198,23 @@ > kfree(an); > } > > -/* Convert a fraction to a frame interval in 100ns multiples. The idea here > is > - * to compute numerator / denominator * 10000000 using 32 bit fixed point > - * arithmetic only. > +/* Calculate a crossproduct. The idea here is to compute > + * numerator / denominator * multiplier using 32 bit fixed point arithmetic > + * only. > */ > -uint32_t uvc_fraction_to_interval(uint32_t numerator, uint32_t denominator) > +uint32_t uvc_cross_product(uint32_t numerator, uint32_t denominator, > + uint32_t multiplier) Interesting reuse of the function. Are you sure this is called a cross product ? Both Wikipedia and my algebra classes fail to give me a definition for cross product that could apply here. > { > - uint32_t multiplier; > > /* Saturate the result if the operation would overflow. */ > if (denominator == 0 || > - numerator/denominator >= ((uint32_t)-1)/10000000) > + numerator/denominator >= ((uint32_t)-1)/multiplier) > return (uint32_t)-1; > > /* Divide both the denominator and the multiplier by two until > * numerator * multiplier doesn't overflow. If anyone knows a better > * algorithm please let me know. > */ > - multiplier = 10000000; > while (numerator > ((uint32_t)-1)/multiplier) { > multiplier /= 2; > denominator /= 2; [snip] > diff -r 214c94aa62aa -r 4617ec8de827 linux/drivers/media/video/uvc/uvc_video.c > --- a/linux/drivers/media/video/uvc/uvc_video.c Sat Oct 10 01:55:23 > 2009 +0200 > +++ b/linux/drivers/media/video/uvc/uvc_video.c Fri Oct 30 16:24:31 > 2009 -0600 > @@ -373,6 +373,8 @@ > struct uvc_buffer *buf, const __u8 *data, int len) > { > __u8 fid; > + __u32 pts = 0, stc = 0, pts_stc_delta, device_latency = 0; > + int i = 0, idx = 2; > > /* Sanity checks: > * - packet must be at least 2 bytes long > @@ -417,7 +419,44 @@ > return -ENODATA; > } > > - /* TODO: Handle PTS and SCR. */ > + do_gettimeofday(&buf->buf.timestamp); > + > + if((data[1] & UVC_STREAM_PTS) && > + (data[1] & UVC_STREAM_SCR && > + stream->dev->clock_frequency)) { Please make sure that your patch conforms with Linux kernel coding style (Documentation/CodingStyle). You can verify patches using the scripts/checkpatch.pl before submitting them. > + > + for(i = 0; i < 4; i++) { > + pts += data[idx+i] << (8*i); > + } > + > + idx += 4; > + > + for(i = 0; i < 4; i++) { > + stc += data[idx+i] << (8*i); > + } You should use the le32_to_cpup macros instead of reinventing them: pts = le32_to_cpup(&data[2]); stc = le32_to_cpup(&data[6]); > + > + if(stc < pts) { > + // Camera clock rolled over > + pts_stc_delta = UINT_MAX-pts+stc; > + } > + else { > + pts_stc_delta = stc-pts; > + } You don't need to handle the overflow manually, pts_stc_delta = stc - pts; will produce the right value thanks to the integer overflow in all cases. > + device_latency = uvc_cross_product( > + pts_stc_delta, stream->dev->clock_frequency, > + 1000000); You should use stream->ctrl.dwClockFrequency here. The VC Interface Header Descriptor dwClockFrequency, stored in stream->dev->clock_frequency, has been deprecated in UVC 1.1. > + if(device_latency && > + buf->buf.timestamp.tv_usec < device_latency) { > + > + // borrow a second > + buf->buf.timestamp.tv_sec--; > + buf->buf.timestamp.tv_usec += 1000000; > + } > + > + buf->buf.timestamp.tv_usec -= device_latency; You're trying to compute device latency by subtracting the PTS field from the STC field, and then subtracting that latency, converted to microseconds using the nominal device clock frequency, from the system time acquired when the first URB of a video frame completes. I'm afraid this is not correct. First of all, the nominal device clock frequency isn't accurate. Drift and jitter make the frequency vary over time. Even if it didn't, the device clock isn't synchronized to the host clock, so you can't use the device clock frequency to convert a time stamp difference to a host time delta. The whole purpose of the PTS and SCR fields is to account for the device clock inaccuracies by offering a mean to match the device and host clock rates over time in an adaptive way. The PTS field gives the device time at which the raw frame capture begins. This is the time that must be copied to the buffer's timestamp after being converted to host time. As the device and host clocks are not synchronized and do not slave to a common clock, conversion from device time to host time requires additional information. This is the purpose of the SCR field. The SCR field gives two timestamps sampled from two clocks at the same time. The 32 low-order bits are sampled from the device clock (System Time Clock or STC), and the next 10 bits are sampled from the USB bus clock (SOF counter). As the SOF counter resolution is only 1ms in full-speed USB, an error of up to 1ms can be present in the SCR field. This error can be mitigated using statistical analysis on consecutive SCR samples. Using that information, the relationship between the device clock and the USB bus clock can be computed, and the PTS field can be converted to USB bus clock units. Now, on the host side, a similar process can be done by sampling the USB SOF counter and the system time at regular intervals, and the relationship between the USB bus clock and the system clock can be established. The PTS field converted to USB bus clock units can further be converted to host clock units. This is what the PTS/SCR support patch should do. With such an algorithm, the USB latency calculation in uvc_video_decode_isoc() becomes unnecessary. I'm afraid the version you submitted doesn't perform those operations. I'm sorry to have to refuse the patch in its current state. -- Best regards, Laurent Pinchart _______________________________________________ Linux-uvc-devel mailing list Linux-uvc-devel@lists.berlios.de https://lists.berlios.de/mailman/listinfo/linux-uvc-devel