JeVois  1.16
JeVois Smart Embedded Machine Vision Toolkit
Share this page:
VideoBuffers.C
Go to the documentation of this file.
1 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2 //
3 // JeVois Smart Embedded Machine Vision Toolkit - Copyright (C) 2016 by Laurent Itti, the University of Southern
4 // California (USC), and iLab at USC. See http://iLab.usc.edu and http://jevois.org for information about this project.
5 //
6 // This file is part of the JeVois Smart Embedded Machine Vision Toolkit. This program is free software; you can
7 // redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software
8 // Foundation, version 2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
9 // without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
10 // License for more details. You should have received a copy of the GNU General Public License along with this program;
11 // if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
12 //
13 // Contact information: Laurent Itti - 3641 Watt Way, HNB-07A - Los Angeles, CA 90089-2520 - USA.
14 // Tel: +1 213 740 3527 - itti@pollux.usc.edu - http://iLab.usc.edu - http://jevois.org
15 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
16 /*! \file */
17 
19 #include <jevois/Util/Utils.H>
20 #include <jevois/Debug/Log.H>
21 #include <sys/ioctl.h>
22 #include <thread>
23 
24 #define FDLDEBUG(msg) LDEBUG('[' << itsFd << ':' << itsName << "] " << msg)
25 #define FDLINFO(msg) LINFO('[' << itsFd << ':' << itsName << "] " << msg)
26 #define FDLERROR(msg) LERROR('[' << itsFd << ':' << itsName << "] " << msg)
27 #define FDLFATAL(msg) LFATAL('[' << itsFd << ':' << itsName << "] " << msg)
28 
29 // ####################################################################################################
30 jevois::VideoBuffers::VideoBuffers(char const * name, int const fd, v4l2_buf_type const type, size_t const num) :
31  itsFd(fd), itsName(name), itsType(type), itsNqueued(0)
32 {
33  // First, request the buffers:
34  struct v4l2_requestbuffers req = { };
35  req.count = num;
36  req.type = type;
37  req.memory = V4L2_MEMORY_MMAP;
38  XIOCTL(fd, VIDIOC_REQBUFS, &req);
39  FDLDEBUG("Reqbufs for " << num << " buffers returned " << req.count << " buffers");
40 
41  // Allocate VideoBuf and MMAP the buffers:
42  for (unsigned int i = 0; i < req.count; ++i)
43  {
44  struct v4l2_buffer buf = { };
45  struct v4l2_plane plane = { };
46  buf.type = type;
47  buf.memory = V4L2_MEMORY_MMAP;
48  buf.index = i;
49  // Note: guvcview adds: buf.flags=V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC
50  if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
51  {
52  // In multi-plane mode, allocate a struct for the planes:
53  buf.length = 1; // FIXME only 1 plane supported
54  buf.m.planes = &plane;
55 
56  try { XIOCTL(fd, VIDIOC_QUERYBUF, &buf); } catch (...) { FDLFATAL("Failed to request buffers"); }
57  if (buf.length != 1) LFATAL("Only one V4L2 plane is supported for now, i.e., only packed video formats.");
58  }
59  else
60  {
61  // Standard single-plane mode:
62  try { XIOCTL(fd, VIDIOC_QUERYBUF, &buf); } catch (...) { FDLFATAL("Failed to request buffers"); }
63  }
64 
65  int dmafd = -1;
66 
67 #ifdef JEVOIS_PRO
68  // Try to get a dma_buf fd, if kernel driver supports it:
69  struct v4l2_exportbuffer ex_buf = { };
70  ex_buf.type = type;
71  ex_buf.index = i;
72  ex_buf.plane = 0; // FIXME only 1 plane supported
73  ex_buf.flags = 0;
74  ex_buf.fd = -1;
75 
76  try { XIOCTL(fd, VIDIOC_EXPBUF, &ex_buf); } catch (...) { FDLDEBUG("Could not get dma_buf fd for buffer " << i); }
77  dmafd = ex_buf.fd;
78 #endif
79 
80  if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
81  itsBuffers.push_back(std::make_shared<jevois::VideoBuf>(fd, buf.m.planes[0].length,
82  buf.m.planes[0].m.mem_offset, dmafd));
83  else
84  itsBuffers.push_back(std::make_shared<jevois::VideoBuf>(fd, buf.length, buf.m.offset, dmafd));
85 
86  FDLDEBUG("Added mmap'd buffer " << i << " of size " << buf.length);
87  }
88 }
89 
90 // ####################################################################################################
92 {
93  if (itsNqueued) FDLDEBUG(itsNqueued << " buffers are still queued...");
94 
95  // Free and unmap all our buffers:
96  for (auto & b : itsBuffers)
97  {
98  if (b.use_count() > 1) FDLDEBUG("Ref count non zero when attempting to free VideoBuf");
99 
100  b.reset(); // VideoBuf destructor will do the memory unmapping
101  }
102 
103  itsBuffers.clear();
104 
105  // Then free all buffers at the device level:
106  struct v4l2_requestbuffers req = { };
107  req.count = 0; // request 0 buffers to free all the previously requested ones
108  req.type = itsType;
109  req.memory = V4L2_MEMORY_MMAP;
110  try { XIOCTL_QUIET(itsFd, VIDIOC_REQBUFS, &req); }
111  catch (...) { FDLDEBUG("Error trying to free V4L2 buffers -- IGNORED"); }
112 }
113 
114 // ####################################################################################################
116 {
117  return itsBuffers.size();
118 }
119 
120 // ####################################################################################################
122 {
123  return itsNqueued;
124 }
125 
126 // ####################################################################################################
127 std::shared_ptr<jevois::VideoBuf> jevois::VideoBuffers::get(size_t const index) const
128 {
129  if (index >= itsBuffers.size()) FDLFATAL("Index " << index << " out of range [0.." << itsBuffers.size() << ']');
130 
131  return itsBuffers[index];
132 }
133 
134 // ####################################################################################################
135 void jevois::VideoBuffers::qbuf(size_t const index)
136 {
137  if (itsType == V4L2_BUF_TYPE_VIDEO_OUTPUT) FDLFATAL("Cannot enqueue output buffers by index");
138  if (itsNqueued == itsBuffers.size())
139  throw std::runtime_error('[' + std::to_string(itsFd) + ':' + itsName + "] All buffers have already been queued");
140 
141  struct v4l2_buffer buf = { };
142  buf.type = itsType;
143  buf.memory = V4L2_MEMORY_MMAP;
144  buf.index = index;
145 
146  if (itsType == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
147  {
148  struct v4l2_plane plane = { };
149  buf.length = 1; // FIXME only one plane supported.
150  buf.m.planes = &plane;
151  }
152 
153  XIOCTL_QUIET(itsFd, VIDIOC_QBUF, &buf);
154 
155  ++itsNqueued;
156 }
157 
158 // ####################################################################################################
159 void jevois::VideoBuffers::qbuf(struct v4l2_buffer & buf)
160 {
161  if (itsNqueued == itsBuffers.size())
162  throw std::runtime_error('[' + std::to_string(itsFd) + ':' + itsName + "] All buffers have already been queued");
163 
164  XIOCTL_QUIET(itsFd, VIDIOC_QBUF, &buf);
165 
166  ++itsNqueued;
167 }
168 
169 // ####################################################################################################
171 {
172  for (unsigned int i = 0; i < itsBuffers.size(); ++i) qbuf(i);
173 }
174 
175 // ####################################################################################################
176 void jevois::VideoBuffers::qbufallbutone(size_t const index)
177 {
178  for (unsigned int i = 0; i < itsBuffers.size(); ++i)
179  if (i != index) try { qbuf(i); } catch (...) { }
180 }
181 
182 // ####################################################################################################
183 void jevois::VideoBuffers::dqbuf(struct v4l2_buffer & buf)
184 {
185  if (itsNqueued == 0) FDLFATAL("No buffer is currently queued");
186 
187  memset(&buf, 0, sizeof(struct v4l2_buffer));
188  buf.type = itsType;
189  buf.memory = V4L2_MEMORY_MMAP;
190 
191  if (itsType == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
192  {
193  struct v4l2_plane plane = { };
194  buf.length = 1; // FIXME only one plane supported.
195  buf.m.planes = &plane;
196 
197  XIOCTL_QUIET_ONCE(itsFd, VIDIOC_DQBUF, &buf);
198  }
199  else
200  {
201  XIOCTL_QUIET_ONCE(itsFd, VIDIOC_DQBUF, &buf);
202  }
203 
204  --itsNqueued;
205 }
206 
207 // ####################################################################################################
209 {
210  struct v4l2_buffer buf;
211  int retry = 100;
212 
213  // Loop until all are dequeued; we may need to sleep a bit if some buffers are still in use by the hardware:
214  while (itsNqueued && retry-- >= 0)
215  {
216  try { dqbuf(buf); } catch (...) { std::this_thread::sleep_for(std::chrono::milliseconds(5)); }
217  }
218 }
FDLFATAL
#define FDLFATAL(msg)
Definition: VideoBuffers.C:27
jevois::VideoBuffers::size
size_t size() const
Get the number of buffers allocated.
Definition: VideoBuffers.C:115
jevois::VideoBuffers::get
std::shared_ptr< VideoBuf > get(size_t const index) const
Get one buffer, by index [0 .. size()[.
Definition: VideoBuffers.C:127
jevois::VideoBuffers::~VideoBuffers
~VideoBuffers()
Free the MMAP'd memory area.
Definition: VideoBuffers.C:91
jevois::VideoBuffers::qbufall
void qbufall()
Queue all buffers, typically used when starting streaming on capture devices.
Definition: VideoBuffers.C:170
jevois::VideoBuffers::nqueued
size_t nqueued() const
Get the number of buffers queued, this is always in [0 .. size()[.
Definition: VideoBuffers.C:121
jevois::VideoBuffers::qbuf
void qbuf(size_t const index)
Queue one buffer to V4L2, by index [0 .. size()[.
Definition: VideoBuffers.C:135
Log.H
jevois::VideoBuffers::dqbuf
void dqbuf(struct v4l2_buffer &buf)
Dequeue the next captured/displayed buffer, blocks until one is available.
Definition: VideoBuffers.C:183
FDLDEBUG
#define FDLDEBUG(msg)
Definition: VideoBuffers.C:24
jevois::VideoBuffers::VideoBuffers
VideoBuffers(char const *name, int const fd, v4l2_buf_type type, size_t const num=4)
Construct and allocate MMAP'd video buffers.
Definition: VideoBuffers.C:30
LFATAL
#define LFATAL(msg)
Convenience macro for users to print out console or syslog messages, FATAL level.
Definition: Log.H:217
VideoBuffers.H
jevois::VideoBuffers::dqbufall
void dqbufall()
Dequeue all buffers, typically used when stopping a stream, not that this may take some time.
Definition: VideoBuffers.C:208
jevois::to_string
std::string to_string(T const &val)
Convert from type to string.
Utils.H
jevois::VideoBuffers::qbufallbutone
void qbufallbutone(size_t const index)
Queue all buffers that are not already queued except one specified.
Definition: VideoBuffers.C:176