Friday, February 12, 2010

VIDIOC_REQBUFS behaviour

A few days ago, Daniel posted a message on the v4l4j mailing list with an issue I had not seen before: the test-gui application captures video fine the first time, but subsequent attempts fail. The only way to get the capture going again is by restarting the test application. The entire discussion can be found here.

Finding out what went wrong
Looking at the debug log generated by the test application, it is clear that everything works well for the first capture, but when the second is initiated, setting the capture parameters (namely the resolution and pixel format) fails, as indicated by this line in the log:
[v4l2-input.c:247 apply_image_format] CAP: palette 0x56595559 rejected
The requested capture parameters are given to the driver using the VIDIO_S_FMT ioctl, which is precisely what causes the second capture to fail, this ioctl call returns EBUSY (device or resource busy). Now, at this point, I could not explain why this ioctl would work the first time, but not the second one.


Looking at the driver side 
I now need to check what the driver does upon reception of an VIDIO_S_FMT ioctl. I updated my mercurial copy of the v4l-dvb tree and took a look at uvc-v4l2.c . The function handling the VIDIO_S_FMT ioctl is at line 244 (uvc_v4l2_set_format() ). This function returns EBUSY if uvc_queue_allocated() != 0. A quick look at uvc_queue.c reveals that uvc_queue_allocated() returns 0 if no buffers have been allocated, or if all allocated buffers have been released...

There it is, that's the solution to the problem: making sure no buffers were allocated before calling VIDIO_S_FMT. However, I could not remember reading anything about this in the V4L2 API specs when writing v4l4j... So I headed back to the V4L2 specs pages, specifically, the one about VIDIO_S_FMT . I realised that I overlooked this (self-explanatory) line: 
When I/O is already in progress or the resource is not available for other reasons drivers return the EBUSY error code.
How to release buffers

Here again, I could not remember reading anything about this in the V4L2 specs at the time I wrote v4l4j. Buffers are requested using the VIDIOC_REQBUF ioctl. Back in uvc_v4l2.c, it is clear by reading the code handling VIDIOC_REQBUF (at line 867) that this ioctl can be called with a value of 0 for the number of buffers requested. A quick look at the documentation for this ioctl (here) indicates that a value of 0 is perfectly acceptable... Weird, I really dont remember reading this, maybe it was added afterwards. Just to confirmed whether it's something recent, I decided to take a quick look at other drivers to see if they also accept a value of 0 when calling VIDIOC_REQBUF . Interestingly, the pwc driver will silently ignore it and do nothing. The gspca driver will gladly accept a value of 0, and release existing buffers. On the other hand, the bttv and zr364xx drivers both use the V4L-generic function videobuf_reqbufs() (in videobuf-core.c) to do the buffer allocation, which will fail (EINVAL) with a buffer count of 0. It seems that different drivers behave differently when handling this ioctl... I should (and will) discuss that on the V4L mailing list.


Solves Daniel's problem ?
I now needed to confirm whether or not these findings would help solving Daniel's issue. I quickly hacked up a simple capture application (here) which runs a video capture twice. Without releasing the buffers, the capture fails the second time (just like it did with the v4l4j test-gui app). And releasing the buffers does allow capture the second time !!!

Next, implementing the fix in v4l4j
I am still working on this one. However, because it seems some drivers will fail when asked to release buffers, I will probably end up adding code to release allocated buffers when stopping an on-going capture and ignore the result.

2 comments:

  1. This is the one and only video capturing library working for my system. Thank you very much for this great piece of software!

    ReplyDelete
  2. No worries, glad to hear v4l4j works great for you !

    ReplyDelete