Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. ::close(fd) won't close v4l2 capture device inside QThread run(..) method
Forum Updated to NodeBB v4.3 + New Features

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

Scheduled Pinned Locked Moved General and Desktop
5 Posts 2 Posters 4.8k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • D Offline
    D Offline
    danny8106
    wrote on last edited by
    #1

    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.

    VWCaptureThread.cpp
    @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 << """;
    quit();
    return;
    }

    CLEAR(fmt);
    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!";
    return;
    }
    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);

    CLEAR(req);
    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);
    {
    CLEAR(buf);
    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,
            PROT_READ | PROT_WRITE, MAP_SHARED,
            fd, buf.m.offset);
    
      if(pBuffers[loop].pStart == MAP_FAILED)
      {
         qDebug() << "mmap failed...";
         return;
      }
    

    }
    n_buffers = loop;

    for(unsigned int i=0; i<n_buffers; i++)
    {
    CLEAR(buf);
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;
    buf.index = i;
    qDebug() << "Initial QBUF[" << i << "]";
    ::ioctl(fd, VIDIOC_QBUF, &buf);
    }
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ::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;
      while(devam)
      {
         mutex.lock();
         do
         {
            FD_ZERO(&fds);
            FD_SET(fd, &fds);
    
            r = ::select(fd+1, &fds, NULL, NULL, NULL);
         } while((r == -1) && (errno == EINTR));
         if(r == -1)
         {
            qDebug() << "select failed";
            return;
         }
    
         CLEAR(buf);
         buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
         buf.memory = V4L2_MEMORY_MMAP;
         ::ioctl(fd, VIDIOC_DQBUF, &buf);
    
         if(v4lconvert_convert(v4lconvert_data,
               &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);
         }
         free(asil);
         delete qq;
    
         //qDebug() << "Requeue buffer[" << buf.index << "]";
         ::ioctl(fd, VIDIOC_QBUF, &buf);
         mutex.unlock();
      }
    

    }

    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ::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...";
    ::close(fd);
    }

    void VWCaptureThread::startCapture(QString& node)
    {
    m_node = node;
    this->start();
    }

    @

    1 Reply Last reply
    0
    • A Offline
      A Offline
      alexisdm
      wrote on last edited by
      #2

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

      1 Reply Last reply
      0
      • D Offline
        D Offline
        danny8106
        wrote on last edited by
        #3

        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?

        Thanks.

        1 Reply Last reply
        0
        • A Offline
          A Offline
          alexisdm
          wrote on last edited by
          #4

          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);
          qq.detach();
          emit capturedImage(qq);@

          1 Reply Last reply
          0
          • D Offline
            D Offline
            danny8106
            wrote on last edited by
            #5

            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...

            1 Reply Last reply
            0

            • Login

            • Login or register to search.
            • First post
              Last post
            0
            • Categories
            • Recent
            • Tags
            • Popular
            • Users
            • Groups
            • Search
            • Get Qt Extensions
            • Unsolved