Beispiel #1
0
// ######################################################################
void KeyBoard::setBlocking(const bool block)
{
  int flags = fcntl(0, F_GETFL, 0);
  if (flags == -1) PLERROR("Cannot get flags");
  if (block) flags &= (~O_NONBLOCK); else flags |= O_NONBLOCK;
  if (fcntl(0, F_SETFL, flags) == -1) PLERROR("Cannot set flags");
  else blocking = block; // remember setting
}
Beispiel #2
0
// ######################################################################
KeyBoard::KeyBoard()
{
  // switch keyboard to non-canonical mode:
  struct termios new_settings;
  if (tcgetattr(0, &stored_settings) == -1)
    PLERROR("Cannot tcgetattr");
  new_settings = stored_settings;
  new_settings.c_lflag &= (~ICANON);
  new_settings.c_cc[VTIME] = 0;
  new_settings.c_cc[VMIN] = 1;
  if (tcsetattr(0, TCSANOW, &new_settings) == -1)
    PLERROR("Cannot tcsetattr");

  // by default, assume keyboard is blocking:
  blocking = true;
}
Beispiel #3
0
// ######################################################################
KeyBoard:: ~KeyBoard()//Destructor
{
  // restore blocking mode:
  setBlocking(true);

  // restore previous keyboard attributes:
  if (tcsetattr(0, TCSANOW, &stored_settings) == -1)
    PLERROR("Cannot tcsetattr");
}
Beispiel #4
0
// ##############################################################################################################
jevois::Gadget::~Gadget()
{
  JEVOIS_TRACE(1);
  
  streamOff();

  // Tell run() thread to finish up:
  itsRunning.store(false);

  // Will block until the run() thread completes:
  if (itsRunFuture.valid()) try { itsRunFuture.get(); } catch (...) { jevois::warnAndIgnoreException(); }

  if (close(itsFd) == -1) PLERROR("Error closing UVC gadget -- IGNORED");
}
Beispiel #5
0
// ##############################################################################################################
jevois::Camera::~Camera()
{
  JEVOIS_TRACE(1);

  // Turn off streaming if it was on:
  try { streamOff(); } catch (...) { jevois::warnAndIgnoreException(); }
 
  // Block until the run() thread completes:
  itsRunning.store(false);
  if (itsRunFuture.valid()) try { itsRunFuture.get(); } catch (...) { jevois::warnAndIgnoreException(); }

  if (itsBuffers) delete itsBuffers;
  
  if (close(itsFd) == -1) PLERROR("Error closing V4L2 camera");
}
Beispiel #6
0
// ##############################################################################################################
void jevois::Camera::run()
{
  JEVOIS_TRACE(1);
  
  fd_set rfds; // For new images captured
  fd_set efds; // For errors
  struct timeval tv;

  // Switch to running state:
  itsRunning.store(true);

  // We may have to wait until the device is opened:
  while (itsFd == -1) std::this_thread::sleep_for(std::chrono::milliseconds(1));

  LDEBUG("run() thread ready");

  // NOTE: The flow is a little complex here, the goal is to minimize latency between a frame being captured and us
  // dequeueing it from the driver and making it available to get(). To achieve low latency, we thus need to be polling
  // the driver most of the time, and we need to prevent other threads from doing various ioctls while we are polling,
  // as the SUNXI-VFE driver does not like that. Thus, there is high contention on itsMtx which we lock most of the
  // time. For this reason we do a bit of sleeping with itsMtx unlocked at places where we know it will not increase our
  // captured image delivery latency.
  std::vector<size_t> doneidx;
  
  // Wait for event from the gadget kernel driver and process them:
  while (itsRunning.load())
    try
    {
      // Requeue any done buffer. To avoid having to use a double lock on itsOutputMtx (for itsDoneIdx) and itsMtx (for
      // itsBuffers->qbuf()), we just swap itsDoneIdx into a local variable here, and invalidate it, with itsOutputMtx
      // locked, then we will do the qbuf() later, if needed, while itsMtx is locked:
      {
        std::lock_guard<std::mutex> _(itsOutputMtx);
        if (itsDoneIdx.empty() == false) itsDoneIdx.swap(doneidx);
      }

      std::unique_lock<std::timed_mutex> lck(itsMtx);

      // Do the actual qbuf of any done buffer, ignoring any exception:
      for (size_t idx : doneidx) try { itsBuffers->qbuf(idx); } catch (...) { jevois::warnAndIgnoreException(); }
      doneidx.clear();
      
      // SUNXI-VFE does not like to be polled when not streaming; if indeed we are not streaming, unlock and then sleep
      // a bit to avoid too much contention on itsMtx:
      if (itsStreaming.load() == false)
      {
        lck.unlock();
        std::this_thread::sleep_for(std::chrono::milliseconds(5));
        continue;
      }
      
      // Poll the device to wait for any new captured video frame:
      FD_ZERO(&rfds); FD_ZERO(&efds); FD_SET(itsFd, &rfds); FD_SET(itsFd, &efds);
      tv.tv_sec = 0; tv.tv_usec = 5000;

      int ret = select(itsFd + 1, &rfds, nullptr, &efds, &tv);
      if (ret == -1) { PLERROR("Select error"); if (errno == EINTR) continue; else break; }
      else if (ret > 0) // NOTE: ret == 0 would mean timeout
      {
        if (FD_ISSET(itsFd, &efds)) LFATAL("Camera device error");

        if (FD_ISSET(itsFd, &rfds))
        {
          // A new frame has been captured. Dequeue a buffer from the camera driver:
          struct v4l2_buffer buf;
          itsBuffers->dqbuf(buf);

          // Create a RawImage from that buffer:
          jevois::RawImage img;
          img.width = itsFormat.fmt.pix.width;
          img.height = itsFormat.fmt.pix.height;
          img.fmt = itsFormat.fmt.pix.pixelformat;
          img.fps = itsFps;
          img.buf = itsBuffers->get(buf.index);
          img.bufindex = buf.index;

          // Unlock itsMtx:
          lck.unlock();

          // We want to never block waiting for people to consume our grabbed frames here, hence we just overwrite our
          // output image here, it just always contains the latest grabbed image:
          {
            std::lock_guard<std::mutex> _(itsOutputMtx);
            itsOutputImage = img;
          }
          LDEBUG("Captured image " << img.bufindex << " ready for processing");

          // Let anyone trying to get() our image know it's here:
          itsOutputCondVar.notify_all();

          // This is also a good time to sleep a bit since it will take a while for the next frame to arrive, this
          // should allow people who had been trying to get a lock on itsMtx to get it now:
          std::this_thread::sleep_for(std::chrono::milliseconds(5));
        }
      }
    } catch (...) { jevois::warnAndIgnoreException(); }
  
  // Switch out of running state in case we did interrupt the loop here by a break statement:
  itsRunning.store(false);
}
Beispiel #7
0
// ######################################################################
void VisualObject::deleteImageFile() const
{
  if (Raster::fileExists(itsImageFname, RASFMT_PNG))
    if (unlink(itsImageFname.c_str()) == -1)
      PLERROR("Could not delete '%s' -- IGNORING", itsImageFname.c_str());
}
Beispiel #8
0
// ##############################################################################################################
void jevois::Gadget::run()
{
  JEVOIS_TRACE(1);
  
  fd_set wfds; // For UVC video streaming
  fd_set efds; // For UVC events
  struct timeval tv;
  
  // Switch to running state:
  itsRunning.store(true);

  // We may have to wait until the device is opened:
  while (itsFd == -1) std::this_thread::sleep_for(std::chrono::milliseconds(1));

  // Wait for event from the gadget kernel driver and process them:
  while (itsRunning.load())
  {
    // Wait until we either receive an event or we are ready to send the next buffer over:
    FD_ZERO(&wfds); FD_ZERO(&efds); FD_SET(itsFd, &wfds); FD_SET(itsFd, &efds);
    tv.tv_sec = 0; tv.tv_usec = 10000;
    
    int ret = select(itsFd + 1, nullptr, &wfds, &efds, &tv);
    
    if (ret == -1) { PLERROR("Select error"); if (errno == EINTR) continue; else break; }
    else if (ret > 0) // We have some events, handle them right away:
    {
      // Note: we may have more than one event, so here we try processEvents() several times to be sure:
      if (FD_ISSET(itsFd, &efds))
      {
        // First event, we will report error if any:
        try { processEvents(); } catch (...) { jevois::warnAndIgnoreException(); }

        // Let's try to dequeue one more, in most cases it should throw:
        while (true) try { processEvents(); } catch (...) { break; }
      }
        
      if (FD_ISSET(itsFd, &wfds)) try { processVideo(); } catch (...) { jevois::warnAndIgnoreException(); }
    }

    // We timed out

    // Sometimes we miss events in the main loop, likely because more events come while we are unlocked in the USB UDC
    // driver and processing here. So let's try to dequeue one more, in most cases it should throw:
    while (true) try { processEvents(); } catch (...) { break; }

    // While the driver is not busy in select(), queue at most one buffer that is ready to send off:
    try
    {
      JEVOIS_TIMED_LOCK(itsMtx);
      if (itsDoneImgs.size())
      {
        LDEBUG("Queuing image " << itsDoneImgs.front() << " for sending over USB");
        
        // We need to prepare a legit v4l2_buffer, including bytesused:
        struct v4l2_buffer buf = { };
        
        buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
        buf.memory = V4L2_MEMORY_MMAP;
        buf.index = itsDoneImgs.front();
        buf.length = itsBuffers->get(buf.index)->length();

        if (itsFormat.fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG)
          buf.bytesused = itsBuffers->get(buf.index)->bytesUsed();
        else
          buf.bytesused = buf.length;

        buf.field = V4L2_FIELD_NONE;
        buf.flags = 0;
        gettimeofday(&buf.timestamp, nullptr);
        
        // Queue it up so it can be sent to the host:
        itsBuffers->qbuf(buf);
        
        // This one is done:
        itsDoneImgs.pop_front();
      }
    } catch (...) { jevois::warnAndIgnoreException(); std::this_thread::sleep_for(std::chrono::milliseconds(10)); }
  }

  // Switch out of running state in case we did interrupt the loop here by a break statement:
  itsRunning.store(false);
}