Important: Please read the Qt Code of Conduct -

::close(fd) won't close v4l2 capture device inside QThread run(..) method

  • Hello,

    I'm writing a app to view a v4l2 capture stream.

    I've written a capture thread widget which inherits from QThread and my main work horse loop is inside the classes run(...) method.

    The basic flow is this:

    1. Construct VWCaptureThread class
      2a) call startCapture(...) which calls start( ) on the VWCaptureThread class to execture run(...) method
      2b) /dev/videoX is opened with ::open(...) inside the run(...) method
      2c) data is captured ok
      3a) signal the capture loop to exit by calling stopCapture()
      3b) run(...) method exits ok
      3c) ::close(fd) is called but has no effect

    The call to ::close(fd) on line 186 of VWCaptureThread.cpp has no effect. I know this because I've been debugging the kernel driver and the device is only closed when my whole app exits, ie. when my top level widget is desctructed.

    Does anyone know how I can get the device closed immediately when I call ::close(fd)? My source is below.

    @void VWCaptureThread::run( )
    unsigned int loop = 0;
    static struct v4lconvert_data v4lconvert_data = 0;
    static struct v4l2_format src_fmt;
    static unsigned char
    dst_buf = 0;

    fd = -1;

    qDebug() << "Look for input[" << m_node << "]";

    dev_name = m_node;
    qDebug() << "Open device: " << dev_name;
    fd = ::open(dev_name.toAscii().constData(), O_RDWR, 0);
    if(fd < 0)
    qDebug() << "Failed to open "" << dev_name << """;

    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.width = 640;
    fmt.fmt.pix.height = 480;
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
    qDebug() << "Query source format...";
    ::ioctl(fd, VIDIOC_S_FMT, &fmt);
    if(fmt.fmt.pix.pixelformat != V4L2_PIX_FMT_RGB24)
    qDebug() << "RGB24 not supported!";
    if(fmt.fmt.pix.width != 640 || fmt.fmt.pix.height != 480)
    qDebug() << "Image delivered at: " << fmt.fmt.pix.width
    << "x" << fmt.fmt.pix.height;

    v4lconvert_data = v4lconvert_create(fd);
    if(v4lconvert_data == 0)
    qDebug() << "v4lconvert_create failed";
    if(v4lconvert_try_format(v4lconvert_data, &fmt, &src_fmt) != 0)
    qDebug() << "v4lconvert_try_format failed";
    src_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;
    ::ioctl(fd, VIDIOC_S_FMT, &src_fmt);
    dst_buf = (unsigned char*)malloc(fmt.fmt.pix.sizeimage);

    req.count = 2;
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory = V4L2_MEMORY_MMAP;

    qDebug() << "Request number and size of buffers...";
    ::ioctl(fd, VIDIOC_REQBUFS, &req);

    pBuffers = (psIntBuffer)calloc(req.count, sizeof(*pBuffers));
    n_buffers = 0;
    for(unsigned int loop=0; loop<req.count; ++loop);
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;
    buf.index = loop;

      qDebug() << "Query buffer[" << loop << "]";
      ::ioctl(fd, VIDIOC_QUERYBUF, &buf);
      pBuffers[loop].length = buf.length;
      qDebug() << "mmap buffer[" << loop << "]";
      pBuffers[loop].pStart = mmap(NULL, buf.length,
            fd, buf.m.offset);
      if(pBuffers[loop].pStart == MAP_FAILED)
         qDebug() << "mmap failed...";

    n_buffers = loop;

    for(unsigned int i=0; i<n_buffers; i++)
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;
    buf.index = i;
    qDebug() << "Initial QBUF[" << i << "]";
    ::ioctl(fd, VIDIOC_QBUF, &buf);
    ::ioctl(fd, VIDIOC_STREAMON, &type);

    unsigned char *asil = 0;
    char header[50];

      sprintf(header, "P6\n%d %d 255\n",
            fmt.fmt.pix.width, fmt.fmt.pix.height);
      devam = true;
            FD_SET(fd, &fds);
            r = ::select(fd+1, &fds, NULL, NULL, NULL);
         } while((r == -1) && (errno == EINTR));
         if(r == -1)
            qDebug() << "select failed";
         buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
         buf.memory = V4L2_MEMORY_MMAP;
         ::ioctl(fd, VIDIOC_DQBUF, &buf);
               &src_fmt, &fmt,
               (unsigned char*)pBuffers[buf.index].pStart, buf.bytesused,
               dst_buf, fmt.fmt.pix.sizeimage) < 0)
            qDebug() << "v4lconvert_convert failed: " << errno;
            if(errno != EAGAIN)
               qDebug() << "Failed to convert v4l data";
         //qDebug() << "Copy frame data";
         asil = (unsigned char*)malloc(fmt.fmt.pix.sizeimage+qstrlen(header));
         memmove(asil, dst_buf, fmt.fmt.pix.sizeimage);
         memmove(asil+qstrlen(header), asil, fmt.fmt.pix.sizeimage);
         memcpy(asil, header, qstrlen(header));
         QImage *qq = new QImage();
         //qDebug() << "Load QImage data";
         if(qq->loadFromData(asil, fmt.fmt.pix.sizeimage+qstrlen(header), "PPM"))
            emit capturedImage(*qq);
         delete qq;
         //qDebug() << "Requeue buffer[" << buf.index << "]";
         ::ioctl(fd, VIDIOC_QBUF, &buf);


    ::ioctl(fd, VIDIOC_STREAMOFF, &type);
    qDebug() << "Unmap buffers...";
    for(unsigned int i=0; i<n_buffers; ++i)
    munmap(pBuffers[i].pStart, pBuffers[i].length);

    qDebug() << "Close device...";

    void VWCaptureThread::startCapture(QString& node)
    m_node = node;


  • You didn't test the return value (and the possible error) for close(), munmap() and ioctl()...

  • hi alexisdm,

    Thanks for the reply, I agree, but because I've also written the kernel driver I know that [in this context] all of the those calls suceeded, apart from close(...) that is. I'll check the return from close, it may be being intercepted before it reaches my kernel drivers close routine.

    If I port my code out from Qt, into a non-UI'd app, then all works ok. It's only when this code is executed from a QThread's run(...) method that it fails. I'm not that familiar with the intricicies of Qt yet, could it be because I've not started the QThread's event loop?

    Any other ideas?


  • I don't think Qt thread are different from regular threads, there seems to be only some thread local stored data to link the the thread and the QThread.

    From what I understand, close() can do nothing if there remains active mmapped buffer, and munmap doesn't sync/flush immediately the buffers, so it may have the same effect as having active mmapped buffers.
    Maybe calling msync before each munmap call, and/or fsync(fd) before close(fd) can solve the problem.

    Off-topic: Since the format is RGB24, you could create the image directly from the buffer without adding a header:
    @// The const is necessary to force detach() to do a deep copy of the data
    QImage qq(const_cast<const unsigned char*>(dst_buf), fmt.fmt.pix.width, fmt.fmt.pix.height, QImage::Format_RGB888);
    emit capturedImage(qq);@

  • Thanks again for the reply, I've moved the ::open(..)/::close(...) calls to a class outside my capture class (so doesn't inherit from Qthread) and now the close call is executing correctly. Don't really understand that but maybe I'll revisit one day!

    Thanks for the QImage buffer tip, I need to pick up as many of these as I can now...

Log in to reply