VideoTools
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
gvcapture_v4l.cpp
1 //
2 // Copyright (C) 2017 Graeme Walker
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // gvcapture_v4l.cpp
19 //
20 
21 #include "gdef.h"
22 #include "gvcapturebuffer.h"
23 #include "gvcapture.h"
24 #include "gvcapture_v4l.h"
25 #include "gprocess.h"
26 #include "gcleanup.h"
27 #include "groot.h"
28 #include "gstr.h"
29 #include "glog.h"
30 #include "gassert.h"
31 #include <algorithm>
32 #include <stdexcept>
33 #include <sstream>
34 #include <iomanip>
35 #include <vector>
36 #include <limits>
37 #include <utility>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <sys/ioctl.h> // ioctl()
41 #include <sys/stat.h>
42 #include <sys/mman.h> // PROT_READ
43 #include <linux/videodev2.h>
44 #include <unistd.h> // close()
45 
46 #if GCONFIG_HAVE_LIBV4L
47 #include <libv4l2.h>
48 namespace { bool have_libv4l() { return true ; } }
49 #else
50 namespace
51 {
52  bool have_libv4l() { return false ; }
53  int v4l2_open( const char * p , int n , mode_t m ) { return ::open( p , n , m ) ; }
54  void v4l2_close( int fd ) { ::close( fd ) ; }
55  void * v4l2_mmap( void * p , size_t n , int prot , int flags , int fd , off_t o ) { return ::mmap( p , n , prot , flags , fd , o ) ; }
56  int v4l2_munmap( void * p , size_t n ) { return ::munmap( p , n ) ; }
57  int v4l2_read( int fd , void * p , size_t n ) { return ::read( fd , p , n ) ; }
58  int v4l2_ioctl( int fd , int request , void * arg ) { return ::ioctl( fd , request , arg ) ; }
59 }
60 #endif
61 
62 namespace
63 {
64  struct ioctl_name { unsigned int n ; const char * p ; } ioctl_names[] = {
65  { VIDIOC_CROPCAP , "VIDIOC_CROPCAP" } ,
66  { VIDIOC_DQBUF , "VIDIOC_DQBUF" } ,
67  { VIDIOC_G_SELECTION , "VIDIOC_G_SELECTION" } ,
68  { VIDIOC_QBUF , "VIDIOC_QBUF" } ,
69  { VIDIOC_QUERYBUF , "VIDIOC_QUERYBUF" } ,
70  { VIDIOC_QUERYCAP , "VIDIOC_QUERYCAP" } ,
71  { VIDIOC_REQBUFS , "VIDIOC_REQBUFS" } ,
72  { VIDIOC_S_CROP , "VIDIOC_S_CROP" } ,
73  { VIDIOC_S_FMT , "VIDIOC_S_FMT" } ,
74  { VIDIOC_TRY_FMT , "VIDIOC_TRY_FMT" } ,
75  { VIDIOC_S_INPUT , "VIDIOC_S_INPUT" } ,
76  { VIDIOC_S_SELECTION , "VIDIOC_S_SELECTION" } ,
77  { VIDIOC_STREAMOFF , "VIDIOC_STREAMOFF" } ,
78  { VIDIOC_STREAMON , "VIDIOC_STREAMON" } ,
79  { VIDIOC_ENUM_FMT , "VIDIOC_ENUM_FMT" } ,
80  { 0 , nullptr }
81  } ;
82 
83  std::string fourcc( g_uint32_t pf )
84  {
85  std::string result ;
86  result.append( 1U , char((pf>>0)&0xff) ) ;
87  result.append( 1U , char((pf>>8)&0xff) ) ;
88  result.append( 1U , char((pf>>16)&0xff) ) ;
89  result.append( 1U , char((pf>>24)&0xff) ) ;
90  return G::Str::trimmed(G::Str::printable(result),G::Str::ws()) ;
91  }
92 }
93 
94 namespace Gv
95 {
96  class CaptureImp ;
97 }
98 
99 /// \class Gv::CaptureImp
100 /// A private implementation class used by Gv::CaptureV4l.
101 ///
103 {
104 public:
105  typedef std::pair<int,int> pair_t ;
106  static bool set_format( v4l2_format * , int fd , unsigned int dx , unsigned int dy , g_uint32_t pf ,
107  g_uint32_t f , const char * pfn , const char * fn ) ;
108  static bool set_format( v4l2_format * , int fd , unsigned int dx , unsigned int dy , g_uint32_t pf ,
109  const char * pfn ) ;
110  static pair_t xioctl( int fd , int request , void * arg , bool v ) ;
111  static bool xioctl_ok( int fd , int request , void * arg , bool v = false ) ;
112  static void xioctl_warn( int fd , int request , void * arg , bool v = false ) ;
113  static void xioctl_check( int fd , int request , void * arg , bool v = false ) ;
114  static bool xioctl_eagain( int fd , int request , void * arg , bool v = false ) ;
115  static void xioctl_einval( int fd , int request , void * arg , const std::string & more , bool v = false ) ;
116  static const char * xioctl_name ( int request ) ;
117  static void throw_errno( const char * , int e ) ;
118 
119  static bool m_use_libv4l ;
120  static int xioctl_imp( int fd , int request , void * arg ) ;
121  static int open( const char * , int , mode_t ) ;
122  static void close( int ) ;
123  static void * mmap( void * , size_t , int , int , int fd , off_t ) ;
124  static int read( int , void * , size_t ) ;
125 
126 private:
127  struct Cleanup
128  {
129  typedef std::vector<int> List ;
130  static List m_list_lib ;
131  static List m_list_raw ;
132  static void fn( G::SignalSafe , const char * ) ;
133  static void add( int , bool ) ;
134  static void remove( int fd ) ;
135  } ;
136 } ;
137 
138 /// \class Gv::CaptureRequeuer
139 /// A RAII class to do ioctl(VIDIOC_QBUF) on a v4l buffer.
140 ///
142 {
143 public:
144  typedef struct v4l2_buffer buffer_type ;
145  int m_fd ;
146  buffer_type & m_buffer ;
147  CaptureRequeuer( int fd , buffer_type & buffer ) ;
148  ~CaptureRequeuer() ;
149 } ;
150 
151 // ==
152 
153 Gv::CaptureV4l::CaptureV4l( const std::string & dev_name , const std::string & dev_config ) :
154  m_fd(-1) ,
155  m_io(IO_METHOD_MMAP) ,
156  m_dx(0) ,
157  m_dy(0) ,
158  m_active(false)
159 {
160  // libv4l is not built in by default, but if it is built in it is enabled by default,
161  // but it can still be disabled at run time by setting this config item
162  CaptureImp::m_use_libv4l = have_libv4l() ;
163  if( G::Str::splitMatch(dev_config,"nolibv4l",";") )
164  CaptureImp::m_use_libv4l = false ;
165 
166  static bool first = true ;
167  if( first )
168  {
169  first = false ;
170  G_LOG( "Gv::CaptureV4l::ctor: libv4l is " << (have_libv4l()?"":"not ") << "built-in"
171  << ((have_libv4l()&&!CaptureImp::m_use_libv4l)?" but disabled by 'nolibv4l' device option":"") ) ;
172  }
173 
174  m_fd = open_device( dev_name ) ;
175  m_io = check_device( m_fd , dev_config ) ;
176  m_buffer_scale = init_device( dev_config ) ;
177  create_buffers( m_buffer_scale.m_buffersize , dev_config ) ;
178  add_buffers() ;
179  start() ;
180  set_simple( dev_config ) ;
181  G_ASSERT( m_dx > 0 && m_dy > 0 ) ;
182 }
183 
185 {
186  m_buffers.clear() ; // stop libv4l whingeing
187  CaptureImp::close( m_fd ) ;
188 }
189 
191 {
192  return m_fd ;
193 }
194 
195 unsigned int Gv::CaptureV4l::dx() const
196 {
197  return m_dx ;
198 }
199 
200 unsigned int Gv::CaptureV4l::dy() const
201 {
202  return m_dy ;
203 }
204 
206 {
207  return m_active ;
208 }
209 
210 std::string Gv::CaptureV4l::info() const
211 {
212  std::ostringstream ss ;
213  ss
214  << "io=" << (m_simple?"simple-":"") << (m_io==IO_METHOD_READ?"read":(m_io==IO_METHOD_MMAP?"mmap":"userptr")) << " "
215  << "dx=" << m_dx << " dy=" << m_dy << " "
216  << "format=[" << m_format.name() << "] "
217  << "fourcc=[" << fourcc(m_format.id()) << "]" ;
218  return ss.str() ;
219 }
220 
221 void Gv::CaptureV4l::set_simple( const std::string & dev_config )
222 {
223  m_simple =
224  m_io == IO_METHOD_READ &&
225  m_format.is_simple() &&
226  m_buffer_scale.m_buffersize == size_t(m_dx)*size_t(m_dy)*size_t(3U) &&
227  dev_config.find("nosimple") == std::string::npos ;
228 }
229 
231 {
232  return m_simple ;
233 }
234 
235 bool Gv::CaptureV4l::read( unsigned char * p , size_t n )
236 {
237  G_ASSERT( m_io == IO_METHOD_READ ) ;
238  if( m_io != IO_METHOD_READ ) return false ;
239  return read_frame_read_simple( p , n ) ;
240 }
241 
243 {
244  if( m_io == IO_METHOD_READ )
245  {
246  if( !read_frame_read(callback) )
247  return false ;
248  }
249  else if( m_io == IO_METHOD_MMAP )
250  {
251  if( !read_frame_mmap(callback) )
252  return false ;
253  }
254  else
255  {
256  if( !read_frame_userptr(callback) )
257  return false ;
258  }
259  return true ;
260 }
261 
262 bool Gv::CaptureV4l::read_frame_read_simple( unsigned char * p , size_t n )
263 {
264  G_ASSERT( p != nullptr && n > 0U ) ;
265  ssize_t rc = CaptureImp::read( m_fd , p , n ) ;
266  if( rc < 0 )
267  {
268  int e = errno ;
269  if( e == EAGAIN ) return false ;
270  CaptureImp::throw_errno( "read" , e ) ;
271  }
272  return true ;
273 }
274 
275 bool Gv::CaptureV4l::read_frame_read( CaptureCallback & callback )
276 {
277  G_ASSERT( m_buffers.size() <= 1U ) ;
278  G_ASSERT( m_buffer_scale.m_buffersize > 0U ) ;
279  if( m_buffers.empty() )
280  m_buffers.push_back( shared_ptr<CaptureBuffer>(new CaptureBuffer(m_buffer_scale.m_buffersize)) ) ;
281 
282  ssize_t rc = CaptureImp::read( m_fd , m_buffers[0]->begin() , m_buffers[0]->size() ) ;
283  if( rc < 0 )
284  {
285  int e = errno ;
286  if( e == EAGAIN ) return false ;
287  CaptureImp::throw_errno( "read" , e ) ;
288  }
289  Gv::CaptureBuffer & buffer = *m_buffers[0] ;
290  buffer.setFormat( m_format , m_buffer_scale ) ;
291  callback( buffer ) ;
292  return true ;
293 }
294 
295 bool Gv::CaptureV4l::read_frame_mmap( CaptureCallback & callback )
296 {
297  // dequeue a buffer
298  static struct v4l2_buffer zero_buf ;
299  struct v4l2_buffer buf = zero_buf ;
300  buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
301  buf.memory = V4L2_MEMORY_MMAP ;
302  if( CaptureImp::xioctl_eagain( m_fd , VIDIOC_DQBUF , &buf ) )
303  return false ;
304 
305  // requeue it on return
306  CaptureRequeuer requeuer( m_fd , buf ) ;
307 
308  G_ASSERT( m_buffers.size() >= 1U ) ;
309  G_ASSERT( buf.index < m_buffers.size() ) ;
310  Gv::CaptureBuffer & buffer = *m_buffers[buf.index] ;
311  buffer.setFormat( m_format , m_buffer_scale ) ;
312  callback( buffer ) ;
313  return true ;
314 }
315 
316 bool Gv::CaptureV4l::read_frame_userptr( CaptureCallback & callback )
317 {
318  // dequeue a buffer
319  static struct v4l2_buffer zero_buf ;
320  static struct v4l2_buffer buf = zero_buf ;
321  buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
322  buf.memory = V4L2_MEMORY_USERPTR ;
323  if( CaptureImp::xioctl_eagain( m_fd , VIDIOC_DQBUF, &buf ) )
324  return false ;
325 
326  // requeue it on return
327  CaptureRequeuer requeuer( m_fd , buf ) ;
328 
329  // find it in our list
330  G_ASSERT( m_buffers.size() >= 1U ) ;
331  unsigned int i = 0 ;
332  for( ; i < m_buffers.size() ; ++i )
333  {
334  if( buf.m.userptr == reinterpret_cast<unsigned long>(m_buffers[i]->begin()) &&
335  buf.length == m_buffers[i]->size() )
336  break ;
337  }
338  if( i >= m_buffers.size() )
339  throw Capture::Error( "buffer not found" ) ;
340 
341  Gv::CaptureBuffer & buffer = *m_buffers[i] ;
342  buffer.setFormat( m_format , m_buffer_scale ) ;
343  callback( buffer ) ;
344  return true ;
345 }
346 
348 {
349  if( m_active )
350  {
351  if( m_io == IO_METHOD_READ )
352  stop_capturing_read() ;
353  else if( m_io == IO_METHOD_MMAP )
354  stop_capturing_mmap( m_fd ) ;
355  else
356  stop_capturing_userptr( m_fd ) ;
357  m_active = false ;
358  }
359 }
360 
361 void Gv::CaptureV4l::stop_capturing_read()
362 {
363  // no-op
364 }
365 
366 void Gv::CaptureV4l::stop_capturing_mmap( int fd )
367 {
368  enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
369  CaptureImp::xioctl_warn( fd , VIDIOC_STREAMOFF , &type ) ;
370 }
371 
372 void Gv::CaptureV4l::stop_capturing_userptr( int fd )
373 {
374  enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
375  CaptureImp::xioctl_warn( fd , VIDIOC_STREAMOFF , &type ) ;
376 }
377 
378 void Gv::CaptureV4l::add_buffers()
379 {
380  if( m_io == IO_METHOD_READ )
381  add_buffers_read() ;
382  else if( m_io == IO_METHOD_MMAP )
383  add_buffers_mmap( m_buffers , m_fd ) ;
384  else
385  add_buffers_userptr( m_buffers , m_fd ) ;
386 }
387 
388 void Gv::CaptureV4l::add_buffers_read()
389 {
390  // no-op
391 }
392 
393 void Gv::CaptureV4l::add_buffers_mmap( CaptureBuffers & buffers , int fd )
394 {
395  G_ASSERT( buffers.size() > 0U ) ;
396  for( unsigned int i = 0 ; i < buffers.size() ; ++i )
397  {
398  static struct v4l2_buffer zero_buf ;
399  struct v4l2_buffer buf = zero_buf ;
400  buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
401  buf.memory= V4L2_MEMORY_MMAP ;
402  buf.index= i ;
403 
404  CaptureImp::xioctl_check( fd , VIDIOC_QBUF , &buf ) ;
405  }
406 }
407 
408 void Gv::CaptureV4l::add_buffers_userptr( CaptureBuffers & buffers , int fd )
409 {
410  G_ASSERT( buffers.size() > 0U ) ;
411  for( unsigned int i = 0 ; i < buffers.size() ; ++i )
412  {
413  static struct v4l2_buffer zero_buf ;
414  struct v4l2_buffer buf = zero_buf ;
415  buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
416  buf.memory= V4L2_MEMORY_USERPTR ;
417  buf.index = i ;
418  buf.m.userptr = reinterpret_cast<unsigned long>( buffers[i]->begin() ) ;
419  buf.length = buffers[i]->size() ;
420 
421  CaptureImp::xioctl_check( fd , VIDIOC_QBUF , &buf ) ;
422  }
423 }
424 
426 {
427  if( !m_active )
428  {
429  if( m_io == IO_METHOD_READ )
430  start_capturing_read() ;
431  else if( m_io == IO_METHOD_MMAP )
432  start_capturing_mmap( m_fd ) ;
433  else
434  start_capturing_userptr( m_fd ) ;
435  m_active = true ;
436  }
437 }
438 
439 void Gv::CaptureV4l::start_capturing_read()
440 {
441  // no-op
442 }
443 
444 void Gv::CaptureV4l::start_capturing_mmap( int fd )
445 {
446  enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
447  CaptureImp::xioctl_check( fd , VIDIOC_STREAMON , &type ) ;
448 }
449 
450 void Gv::CaptureV4l::start_capturing_userptr( int fd )
451 {
452  enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
453  CaptureImp::xioctl_check( fd , VIDIOC_STREAMON , &type ) ;
454 }
455 
456 void Gv::CaptureV4l::create_buffers( size_type buffersize , const std::string & dev_config )
457 {
458  unsigned int nbuffers = std::min( G::Str::toUInt( G::Str::head(G::Str::tail(dev_config,"buffers="),";",false) , "0" ) , 30U ) ;
459 
460  if( m_io == IO_METHOD_READ )
461  create_buffers_read( m_buffers , buffersize , nbuffers ) ;
462  else if( m_io == IO_METHOD_MMAP )
463  create_buffers_mmap( m_buffers , m_fd , buffersize , nbuffers == 0U ? 2U : nbuffers ) ;
464  else
465  create_buffers_userptr( m_buffers , m_fd , buffersize , nbuffers == 0U ? 4U : nbuffers ) ;
466 }
467 
468 void Gv::CaptureV4l::create_buffers_read( CaptureBuffers & buffers , size_type buffer_size , unsigned int nbuffers )
469 {
470  G_LOG( "Gv::CaptureV4l::create_buffers_read: read buffers: request=" << nbuffers << " count=1 size=" << buffer_size ) ;
471  // don't do buffers.push_back(...) here -- do it lazily in case we can do simple() reads
472 }
473 
474 void Gv::CaptureV4l::create_buffers_mmap( CaptureBuffers & buffers , int fd , size_t buffersize , unsigned int nbuffers )
475 {
476  // have the device create the mmap buffers -- how many buffers is negotiated
477  static struct v4l2_requestbuffers zero_req ;
478  struct v4l2_requestbuffers req = zero_req ;
479  req.count = nbuffers ; // negotiated up or down
480  req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
481  req.memory = V4L2_MEMORY_MMAP ;
482  CaptureImp::xioctl_einval( fd , VIDIOC_REQBUFS , &req , "device does not support memory mapped streaming" ) ;
483  if( req.count < 1 )
484  throw Capture::Error( "insufficient buffer memory" ) ;
485 
486  // map the buffers
487  for( unsigned int i = 0 ; i < req.count ; ++i )
488  {
489  // query the length and offset
490  static struct v4l2_buffer zero_buf ;
491  struct v4l2_buffer buf = zero_buf ;
492  buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
493  buf.memory = V4L2_MEMORY_MMAP ;
494  buf.index = i ;
495  CaptureImp::xioctl_check( fd , VIDIOC_QUERYBUF , &buf ) ;
496 
497  if( i == 0 )
498  G_LOG( "Gv::CaptureV4l::create_buffers_mmap: mmap buffers: request=" << nbuffers << " count=" << req.count << " size=" << buf.length ) ;
499 
500  if( buf.length < buffersize )
501  throw Capture::Error( "buffer size mis-match between VIDIOC_QUERYBUF and VIDIOC_S_FMT" ) ;
502 
503  void * p = CaptureImp::mmap( nullptr , buf.length , PROT_READ | PROT_WRITE , MAP_SHARED , fd , buf.m.offset ) ;
504  if( p == MAP_FAILED )
505  {
506  int e = errno ;
507  CaptureImp::throw_errno( "mmap" , e ) ; // others are unmapped via ~CaptureBuffer()
508  }
509 
510  buffers.push_back( shared_ptr<CaptureBuffer>( new CaptureBuffer(buf.length,p,::v4l2_munmap) ) ) ;
511  }
512  G_ASSERT( !buffers.empty() ) ;
513 }
514 
515 void Gv::CaptureV4l::create_buffers_userptr( CaptureBuffers & buffers , int fd , size_type buffersize_in , unsigned int nbuffers )
516 {
517  const int page_size = getpagesize() ; // unistd.h
518  if( page_size <= 0 )
519  throw Capture::Error( "assertion failure: invalid pagesize" ) ;
520 
521  unsigned int page = static_cast<unsigned int>(page_size) ;
522  size_t buffersize = (buffersize_in + page - 1U) & ~(page - 1U) ; // round up
523  G_ASSERT( buffersize >= buffersize_in ) ;
524 
525  static struct v4l2_requestbuffers zero_req ;
526  struct v4l2_requestbuffers req = zero_req ;
527  req.count = nbuffers ;
528  req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
529  req.memory = V4L2_MEMORY_USERPTR ;
530  CaptureImp::xioctl_einval( fd , VIDIOC_REQBUFS, &req , "device does not support user pointer i/o" ) ;
531 
532  if( req.count < 1 )
533  throw Capture::Error( "invalid negotiated buffer count" ) ;
534 
535  G_LOG( "Gv::CaptureV4l::create_buffers_userptr: userptr buffers: request=" << nbuffers << " count=" << req.count << " size=" << buffersize ) ;
536 
537  for( unsigned int i = 0U ; i < req.count ; ++i )
538  {
539  buffers.push_back( shared_ptr<CaptureBuffer>( new CaptureBuffer(page_size,buffersize) ) ) ;
540  }
541  G_ASSERT( !buffers.empty() ) ;
542 }
543 
544 int Gv::CaptureV4l::open_device( const std::string & dev_name )
545 {
546  struct stat st ;
547  int rc = stat( dev_name.c_str() , &st ) ;
548  if( rc < 0 )
549  {
550  std::string help( G::is_linux() ? " or do \"modprobe vivi\" as root" : "" ) ;
551  throw Capture::NoDevice( "[" + dev_name + "]: try device \"test\"" + help ) ;
552  }
553 
554  if( !S_ISCHR(st.st_mode) )
555  throw Capture::Error( "not a device: " + dev_name ) ;
556 
557  int fd = CaptureImp::open( dev_name.c_str() , O_RDWR | O_NONBLOCK , 0 ) ;
558  if( fd < 0 )
559  throw Capture::Error( "cannot open [" + dev_name + "]: " + G::Process::strerror(errno) ) ;
560 
561  ::fcntl( fd , F_SETFD , ::fcntl(fd,F_GETFD) | FD_CLOEXEC ) ;
562  return fd ;
563 }
564 
565 Gv::CaptureV4l::io_method Gv::CaptureV4l::check_device( int fd , const std::string & dev_config )
566 {
567  const bool config_mmap = G::Str::splitMatch(dev_config,"mmap",";") ;
568  const bool config_read = G::Str::splitMatch(dev_config,"read",";") ;
569  const bool config_userptr = G::Str::splitMatch(dev_config,"userptr",";") ;
570 
571  struct v4l2_capability cap ;
572  CaptureImp::xioctl_einval( fd , VIDIOC_QUERYCAP, &cap , "device is not a V4L2 device" ) ;
573  if( !(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) )
574  throw Capture::Error( "device is not a video capture device" ) ;
575 
576  const bool can_read = !! (cap.capabilities & V4L2_CAP_READWRITE) ;
577  const bool can_stream = !! (cap.capabilities & V4L2_CAP_STREAMING) ;
578  G_LOG( "Gv::CaptureV4l::check_device: device capabilities: can-read=" << can_read << " can-stream=" << can_stream ) ;
579 
580  const char * error = nullptr ;
581  if( !can_read && !can_stream )
582  error = "device does not support any i/o method" ;
583 
584  if( config_read && !can_read )
585  error = "device does not support read i/o" ;
586 
587  if( ( config_userptr || config_mmap ) && !can_stream )
588  error = "device does not support streaming i/o" ;
589 
590  // normally prefer mmap so that the device driver can fill the buffer in
591  // the kernel without doing a copy to userspace, and we convert the
592  // (normally) YUV mmap buffer to an RGB image in CaptureBuffer::copyTo() --
593  // however, when using libv4l it is better to use a read() of the libv4l RGB
594  // image straight into our RGB image buffer
595 
596  io_method method =
597  CaptureImp::m_use_libv4l ?
598  ( can_read ? IO_METHOD_READ : IO_METHOD_MMAP ) :
599  ( can_stream ? IO_METHOD_MMAP : IO_METHOD_READ ) ;
600 
601  if( config_read ) method = IO_METHOD_READ ;
602  if( config_mmap ) method = IO_METHOD_MMAP ;
603  if( config_userptr ) method = IO_METHOD_USERPTR ;
604 
605  G_LOG( "Gv::CaptureV4l::check_device: device io-method: "
606  << "require-mmap=" << config_mmap << " "
607  << "require-read=" << config_read << " "
608  << "require-userptr=" << config_userptr << " "
609  << "can-read=" << can_read << " "
610  << "can-stream=" << can_stream << ": "
611  << (error?error:(method==IO_METHOD_READ?"read":(method==IO_METHOD_MMAP?"mmap":"userptr")))
612  ) ;
613 
614  if( error != nullptr )
615  throw Capture::Error( error ) ;
616 
617  return method ;
618 }
619 
620 namespace
621 {
622  int format_type_gross( const Gv::CaptureBufferFormat & f )
623  {
624  return f.is_grey() ? 2 : 0 ; // grey is bad
625  }
626  int format_type_fine( const Gv::CaptureBufferFormat & f )
627  {
628  if( f.is_rgb() ) return 0 ; // rgb is better
629  if( f.is_yuv() ) return 1 ;
630  return 2 ;
631  }
632  int format_depth( const Gv::CaptureBufferFormat & f )
633  {
634  unsigned int d = std::max( f.component(0).depth() , std::max(f.component(1).depth(),f.component(2).depth()) ) ;
635  return d == 8 ? -1000 : -d ; // eight is best, otherwise bigger is better
636  }
637  int format_compression( const Gv::CaptureBufferFormat & f )
638  {
639  return f.is_yuv() ? (f.component(1).xshift() * f.component(1).yshift()) : 0 ;
640  }
641  bool format_less( const Gv::CaptureBufferFormat & a , const Gv::CaptureBufferFormat & b )
642  {
643  if( format_type_gross(a) != format_type_gross(b) ) return format_type_gross(a) < format_type_gross(b) ;
644  if( format_depth(a) != format_depth(b) ) return format_depth(a) < format_depth(b) ;
645  if( format_type_fine(a) != format_type_fine(b) ) return format_type_fine(a) < format_type_fine(b) ;
646  if( format_compression(a) != format_compression(b) ) return format_compression(a) < format_compression(b) ;
647  return false ;
648  }
649 }
650 
651 Gv::CaptureBufferScale Gv::CaptureV4l::init_device( const std::string & dev_config )
652 {
653  // select the first input
654  {
655  int index = 0 ;
656  CaptureImp::xioctl_warn( m_fd , VIDIOC_S_INPUT , &index , true ) ;
657  }
658 
659  // cropping parameters persist across close/open (see 1.13.3) so start by
660  // resetting the cropping using S_SELECTION or S_CROP
661  //
662  unsigned int dx = 0 ;
663  unsigned int dy = 0 ;
664  {
665  bool done = false ;
666  {
667  static struct v4l2_cropcap zero_cropcap ;
668  struct v4l2_cropcap cropcap = zero_cropcap ;
669  cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
670  struct v4l2_crop crop ;
671  if( CaptureImp::xioctl_ok( m_fd , VIDIOC_CROPCAP , &cropcap , true ) )
672  {
673  dx = cropcap.defrect.width ;
674  dy = cropcap.defrect.height ;
675 
676  crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
677  crop.c = cropcap.defrect; // reset to default
678  done = CaptureImp::xioctl_ok( m_fd , VIDIOC_S_CROP , &crop , true ) ;
679  }
680  }
681  if( !done ) // try using the newer selection API -- see 1.14.4 and 1.14.5
682  {
683  static struct v4l2_selection zero_selection ;
684  struct v4l2_selection selection = zero_selection ;
685  selection.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
686  selection.target = V4L2_SEL_TGT_CROP_DEFAULT ;
687  if( CaptureImp::xioctl_ok( m_fd , VIDIOC_G_SELECTION , &selection , true ) )
688  {
689  dx = selection.r.width ;
690  dy = selection.r.height ;
691 
692  selection.target = V4L2_SEL_TGT_CROP ;
693  done = CaptureImp::xioctl_ok( m_fd , VIDIOC_S_SELECTION , &selection , true ) ;
694  }
695  }
696  }
697 
698  // show a list of the device's supported formats (not a well-supported xioctl)
699  {
700  const int sanity = 1000 ;
701  for( int i = 0 ; i < sanity ; i++ )
702  {
703  static struct v4l2_fmtdesc zero_fmtdesc ;
704  struct v4l2_fmtdesc fmtdesc = zero_fmtdesc ;
705  fmtdesc.index = i ;
706 
707  if( ! CaptureImp::xioctl_ok( m_fd , VIDIOC_ENUM_FMT , &fmtdesc , true ) )
708  break ;
709 
710  if( fmtdesc.type != V4L2_BUF_TYPE_VIDEO_CAPTURE )
711  continue ;
712 
713  const char * description = reinterpret_cast<char *>(fmtdesc.description) ;
714  fmtdesc.description[sizeof(fmtdesc.description)-1] = '\0' ;
715 
716  G_LOG( "Gv::CaptureV4l::init_device: device format " << i << ": "
717  << "pixelformat=" << fmtdesc.pixelformat << " "
718  << "fourcc=[" << fourcc(fmtdesc.pixelformat) << "] "
719  << "description=[" << G::Str::printable(description) << "] "
720  << "flags=" << fmtdesc.flags ) ;
721  }
722  }
723 
724  // prepare a list of our supported formats -- device drivers are not supposed to do
725  // conversions in kernel space, so we have to make an effort to support a reasonable
726  // number in case we do not have libv4l built in -- the format descriptions are
727  // interpreted by Gv::CaptureBuffer in-line methods
728  //
729  std::vector<CaptureBufferFormat> format ;
730  {
731  typedef CaptureBufferFormat Format ;
732  typedef CaptureBufferFormat::Type Type ;
733  typedef CaptureBufferComponent Component ;
734  const CaptureBufferComponent::Type c_byte = CaptureBufferComponent::c_byte ;
735  const CaptureBufferComponent::Type c_word_le = CaptureBufferComponent::c_word_le ;
736  const CaptureBufferComponent::Type c_word_be = CaptureBufferComponent::c_word_be ;
737  const size_t XY = Gv::CaptureBufferComponent::XY ;
738  const size_t XY2 = Gv::CaptureBufferComponent::XY2 ;
739  const size_t XY4 = Gv::CaptureBufferComponent::XY4 ;
740  const size_t XY8 = Gv::CaptureBufferComponent::XY8 ;
741 
742  static const Format ff[] = {
743  Format( V4L2_PIX_FMT_RGB332 , "rgb332" , Format::rgb ,
744  // offset, x_shift, y_shift, step, depth, shift, wordsize
745  Component( 0 , 0 , 0 , 1 , 3 , 5 , c_byte ) ,
746  Component( 0 , 0 , 0 , 1 , 3 , 2 , c_byte ) ,
747  Component( 0 , 0 , 0 , 1 , 2 , 0 , c_byte ) ) ,
748  Format( V4L2_PIX_FMT_RGB565X , "rgb565x" , Format::rgb ,
749  Component( 0 , 0 , 0 , 2 , 5 , 11 , c_word_be ) ,
750  Component( 0 , 0 , 0 , 2 , 6 , 5 , c_word_be ) ,
751  Component( 0 , 0 , 0 , 2 , 5 , 0 , c_word_be ) ) ,
752  Format( V4L2_PIX_FMT_RGB24 , "rgb24" , Format::rgb ,
753  Component( 0 , 0 , 0 , 3 , 8 , 0 , c_byte ) ,
754  Component( 1 , 0 , 0 , 3 , 8 , 0 , c_byte ) ,
755  Component( 2 , 0 , 0 , 3 , 8 , 0 , c_byte ) ) ,
756  Format( V4L2_PIX_FMT_BGR24 , "bgr24" , Format::rgb ,
757  Component( 2 , 0 , 0 , 3 , 8 , 0 , c_byte ) ,
758  Component( 1 , 0 , 0 , 3 , 8 , 0 , c_byte ) ,
759  Component( 0 , 0 , 0 , 3 , 8 , 0 , c_byte ) ) ,
760  Format( V4L2_PIX_FMT_RGB444 , "rgb444" , Format::rgb ,
761  Component( 0 , 0 , 0 , 2 , 4 , 8 , c_word_le ) ,
762  Component( 0 , 0 , 0 , 2 , 4 , 4 , c_word_le ) ,
763  Component( 0 , 0 , 0 , 2 , 4 , 0 , c_word_le ) ) ,
764  Format( V4L2_PIX_FMT_RGB555 , "rgb555" , Format::rgb ,
765  Component( 0 , 0 , 0 , 2 , 5 , 10 , c_word_le ) ,
766  Component( 0 , 0 , 0 , 2 , 5 , 5 , c_word_le ) ,
767  Component( 0 , 0 , 0 , 2 , 5 , 0 , c_word_le ) ) ,
768  Format( V4L2_PIX_FMT_RGB555X , "rgb555x" , Format::rgb ,
769  Component( 0 , 0 , 0 , 2 , 5 , 10 , c_word_be ) ,
770  Component( 0 , 0 , 0 , 2 , 5 , 5 , c_word_be ) ,
771  Component( 0 , 0 , 0 , 2 , 5 , 0 , c_word_be ) ) ,
772  Format( V4L2_PIX_FMT_BGR32 , "bgr32" , Format::rgb ,
773  Component( 2 , 0 , 0 , 4 , 8 , 0 , c_byte ) ,
774  Component( 1 , 0 , 0 , 4 , 8 , 0 , c_byte ) ,
775  Component( 0 , 0 , 0 , 4 , 8 , 0 , c_byte ) ) ,
776  Format( V4L2_PIX_FMT_RGB32 , "rgb32" , Format::rgb ,
777  Component( 1 , 0 , 0 , 4 , 8 , 0 , c_byte ) ,
778  Component( 2 , 0 , 0 , 4 , 8 , 0 , c_byte ) ,
779  Component( 3 , 0 , 0 , 4 , 8 , 0 , c_byte ) ) ,
780  //
781  Format( V4L2_PIX_FMT_GREY , "grey" , Format::grey ,
782  Component( 0 , 0 , 0 , 1 , 8 , 0 , c_byte ) ,
783  Component( 0 , 0 , 0 , 1 , 0 , 0 , c_byte ) ,
784  Component( 0 , 0 , 0 , 1 , 0 , 0 , c_byte ) ) ,
785  Format( V4L2_PIX_FMT_Y10 , "y10" , Format::grey ,
786  Component( 0 , 0 , 0 , 1 , 10 , 0 , c_word_le ) ,
787  Component( 0 , 0 , 0 , 1 , 0 , 0 , c_word_le ) ,
788  Component( 0 , 0 , 0 , 1 , 0 , 0 , c_word_le ) ) ,
789  Format( V4L2_PIX_FMT_Y12 , "y12" , Format::grey ,
790  Component( 0 , 0 , 0 , 1 , 12 , 0 , c_word_le ) ,
791  Component( 0 , 0 , 0 , 1 , 0 , 0 , c_word_le ) ,
792  Component( 0 , 0 , 0 , 1 , 0 , 0 , c_word_le ) ) ,
793  Format( V4L2_PIX_FMT_Y16 , "y16" , Format::grey ,
794  Component( 0 , 0 , 0 , 1 , 16 , 0 , c_word_le ) ,
795  Component( 0 , 0 , 0 , 1 , 0 , 0 , c_word_le ) ,
796  Component( 0 , 0 , 0 , 1 , 0 , 0 , c_word_le ) ) ,
797  //
798  Format( V4L2_PIX_FMT_YUYV , "yuyv" , Format::yuv ,
799  Component( 0 , 0 , 0 , 2 , 8 , 0 , c_byte ) ,
800  Component( 1 , 1 , 0 , 4 , 8 , 0 , c_byte ) ,
801  Component( 3 , 1 , 0 , 4 , 8 , 0 , c_byte ) ) ,
802  Format( V4L2_PIX_FMT_YVYU , "yvyu" , Format::yuv ,
803  Component( 0 , 0 , 0 , 2 , 8 , 0 , c_byte ) ,
804  Component( 3 , 1 , 0 , 4 , 8 , 0 , c_byte ) ,
805  Component( 1 , 1 , 0 , 4 , 8 , 0 , c_byte ) ) ,
806  Format( V4L2_PIX_FMT_UYVY , "uyvy" , Format::yuv ,
807  Component( 1 , 0 , 0 , 2 , 8 , 0 , c_byte ) ,
808  Component( 0 , 1 , 0 , 4 , 8 , 0 , c_byte ) ,
809  Component( 2 , 1 , 0 , 4 , 8 , 0 , c_byte ) ) ,
810  Format( V4L2_PIX_FMT_VYUY , "vyuy" , Format::yuv ,
811  Component( 1 , 0 , 0 , 2 , 8 , 0 , c_byte ) ,
812  Component( 2 , 1 , 0 , 4 , 8 , 0 , c_byte ) ,
813  Component( 0 , 1 , 0 , 4 , 8 , 0 , c_byte ) ) ,
814  Format( V4L2_PIX_FMT_YVU420 , "yvu420" , Format::yuv , // YUV 4:2:0
815  Component( 0 , 0 , 0 , 1 , 8 , 0 , c_byte ) ,
816  Component( XY+XY4 , 1 , 1 , 1 , 8 , 0 , c_byte , 1 ) ,
817  Component( XY , 1 , 1 , 1 , 8 , 0 , c_byte , 1 ) ) ,
818  Format( V4L2_PIX_FMT_YUV420 , "yuv420" , Format::yuv ,
819  Component( 0 , 0 , 0 , 1 , 8 , 0 , c_byte ) ,
820  Component( XY , 1 , 1 , 1 , 8 , 0 , c_byte , 1 ) ,
821  Component( XY+XY4 , 1 , 1 , 1 , 8 , 0 , c_byte , 1 ) ) ,
822  Format( V4L2_PIX_FMT_YVU410 , "yvu410" , Format::yuv , // YUV 4:1:0
823  Component( 0 , 0 , 0 , 1 , 8 , 0 , c_byte ) ,
824  Component( XY+XY8 , 2 , 2 , 1 , 8 , 0 , c_byte , 2 ) ,
825  Component( XY , 2 , 2 , 1 , 8 , 0 , c_byte , 2 ) ) ,
826  Format( V4L2_PIX_FMT_YUV410 , "yuv410" , Format::yuv ,
827  Component( 0 , 0 , 0 , 1 , 8 , 0 , c_byte ) ,
828  Component( XY , 2 , 2 , 1 , 8 , 0 , c_byte , 2 ) ,
829  Component( XY+XY8 , 2 , 2 , 1 , 8 , 0 , c_byte , 2 ) ) ,
830  Format( V4L2_PIX_FMT_YUV422P , "yuv422p" , Format::yuv , // YUV 4:2:2
831  Component( 0 , 0 , 0 , 1 , 8 , 0 , c_byte ) ,
832  Component( XY , 1 , 0 , 1 , 8 , 0 , c_byte , 1 ) ,
833  Component( XY+XY2 , 1 , 0 , 1 , 8 , 0 , c_byte , 1 ) ) ,
834  Format( V4L2_PIX_FMT_YUV411P , "yuv411p" , Format::yuv , // YUV 4:1:1
835  Component( 0 , 0 , 0 , 1 , 8 , 0 , c_byte ) ,
836  Component( XY , 2 , 0 , 1 , 8 , 0 , c_byte , 2 ) ,
837  Component( XY+XY4 , 2 , 0 , 1 , 8 , 0 , c_byte , 2 ) ) ,
838  Format( V4L2_PIX_FMT_NV12 , "nv12" , Format::yuv , // YUV 4:2:0
839  Component( 0 , 0 , 0 , 1 , 8 , 0 , c_byte ) ,
840  Component( XY , 1 , 1 , 2 , 8 , 0 , c_byte , 0 ) ,
841  Component( XY+1 , 1 , 1 , 2 , 8 , 0 , c_byte , 0 ) ) ,
842  Format( V4L2_PIX_FMT_NV21 , "nv21" , Format::yuv ,
843  Component( 0 , 0 , 0 , 1 , 8 , 0 , c_byte ) ,
844  Component( XY+1 , 1 , 1 , 2 , 8 , 0 , c_byte , 0 ) ,
845  Component( XY , 1 , 1 , 2 , 8 , 0 , c_byte , 0 ) ) ,
846  Format( V4L2_PIX_FMT_NV16 , "nv16" , Format::yuv , // YUV 4:2:2
847  Component( 0 , 0 , 0 , 1 , 8 , 0 , c_byte ) ,
848  Component( XY , 1 , 0 , 2 , 8 , 0 , c_byte , 0 ) ,
849  Component( XY+1 , 1 , 0 , 2 , 8 , 0 , c_byte , 0 ) ) ,
850  Format( V4L2_PIX_FMT_NV61 , "nv61" , Format::yuv ,
851  Component( 0 , 0 , 0 , 1 , 8 , 0 , c_byte ) ,
852  Component( XY+1 , 1 , 0 , 2 , 8 , 0 , c_byte , 0 ) ,
853  Component( XY , 1 , 0 , 2 , 8 , 0 , c_byte , 0 ) ) ,
854  Format( V4L2_PIX_FMT_NV24 , "nv24" , Format::yuv , // YUV 4:4:4
855  Component( 0 , 0 , 0 , 1 , 8 , 0 , c_byte ) ,
856  Component( XY , 0 , 0 , 2 , 8 , 0 , c_byte , -1 ) ,
857  Component( XY+1 , 0 , 0 , 2 , 8 , 0 , c_byte , -1 ) ) ,
858  Format( V4L2_PIX_FMT_NV42 , "nv42" , Format::yuv ,
859  Component( 0 , 0 , 0 , 1 , 8 , 0 , c_byte ) ,
860  Component( XY+1 , 0 , 0 , 2 , 8 , 0 , c_byte , -1 ) ,
861  Component( XY , 0 , 0 , 2 , 8 , 0 , c_byte , -1 ) )
862  } ;
863 
864  // build the vector of supported formats
865  size_t n = sizeof(ff)/sizeof(ff[0]) ;
866  format.reserve( n ) ;
867  for( unsigned int f = 0 ; f < n ; f++ )
868  format.push_back( ff[f] ) ;
869 
870  // set a priority order
871  std::stable_sort( format.begin() , format.end() , format_less ) ;
872 
873  // log the formats in priority order
874  for( unsigned int f = 0 ; f < format.size() ; f++ )
875  {
876  G_LOG( "Gv::CaptureV4l::init_device: supported format: "
877  << "index=" << f << " "
878  << "name=[" << format[f].name() << "] "
879  << "pixelformat=" << format[f].id() << " "
880  << "fourcc=[" << fourcc(format[f].id()) << "] "
881  << "type=" << int(format[f].type()) ) ;
882  }
883  }
884 
885  // parse out a preferred format from the device config
886  unsigned int first_format = G::Str::toUInt( G::Str::head(G::Str::tail(dev_config,"fmt="),";",false) , "0" ) ;
887 
888  // negotiate a format -- go through our list in perferred order and
889  // do 'try' and 'set' ioctls until something works
890  //
891  CaptureBufferScale scale ;
892  for( unsigned int f = 0 ; f < format.size() ; f++ )
893  {
894  int i = (f+first_format) % format.size() ;
895  v4l2_format fmt ;
896  if( CaptureImp::set_format( &fmt , m_fd , dx , dy , format[i].id() , format[i].name() ) )
897  {
898  G_LOG( "Gv::CaptureV4l::init_device: format accepted by device: "
899  << "index=" << i << " "
900  << "name=[" << format[i].name() << "] "
901  << "pixelformat=" << format[i].id() << " "
902  << "fourcc=[" << fourcc(format[i].id()) << "] "
903  << "sizeimage=" << fmt.fmt.pix.sizeimage << " "
904  << "bytesperline=" << fmt.fmt.pix.bytesperline << " "
905  << "(" << fmt.fmt.pix.width << "x" << fmt.fmt.pix.height << ")"
906  ) ;
907 
908  m_dx = fmt.fmt.pix.width ;
909  m_dy = fmt.fmt.pix.height ;
910  m_format = format[i] ;
911  scale = CaptureBufferScale( fmt.fmt.pix.sizeimage , fmt.fmt.pix.bytesperline , fmt.fmt.pix.width , fmt.fmt.pix.height ) ;
912  break ;
913  }
914  else
915  {
916  G_LOG( "Gv::CaptureV4l::init_device: format rejected by device: "
917  << "index=" << i << " "
918  << "name=[" << format[i].name() << "] "
919  << "pixelformat=" << format[i].id() << " "
920  << "fourcc=[" << fourcc(format[i].id()) << "]: rejected" ) ;
921  }
922  }
923  if( scale.m_buffersize == 0U )
924  throw Capture::Error( "failed to negotiate a pixel format" ) ;
925  return scale ; // buffersize, linesize, dx, dy
926 }
927 
928 bool Gv::CaptureImp::set_format( v4l2_format * out_p , int fd , unsigned int dx , unsigned int dy ,
929  g_uint32_t pixelformat , const char * name )
930 {
931  return
932  set_format( out_p , fd , dx , dy , pixelformat , V4L2_FIELD_NONE , name , "none" ) ||
933  set_format( out_p , fd , dx , dy , pixelformat , V4L2_FIELD_INTERLACED , name , "interlaced" ) ||
934  set_format( out_p , fd , dx , dy , pixelformat , V4L2_FIELD_SEQ_TB , name , "top-bottom" ) ||
935  set_format( out_p , fd , dx , dy , pixelformat , V4L2_FIELD_SEQ_BT , name , "bottom-top" ) ||
936  set_format( out_p , fd , dx , dy , pixelformat , V4L2_FIELD_INTERLACED_TB , name , "interlaced-top-bottom" ) ||
937  set_format( out_p , fd , dx , dy , pixelformat , V4L2_FIELD_INTERLACED_BT , name , "interlaced-bottom-top" ) ||
938  false ;
939 }
940 
941 bool Gv::CaptureImp::set_format( v4l2_format * out_p , int fd , unsigned int dx , unsigned int dy ,
942  g_uint32_t pixelformat , g_uint32_t field , const char * format_name , const char * field_name )
943 {
944  static struct v4l2_format zero_fmt ;
945  struct v4l2_format fmt = zero_fmt ;
946  fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
947  fmt.fmt.pix.width = dx ? dx : 640 ;
948  fmt.fmt.pix.height = dy ? dy : 480 ;
949  fmt.fmt.pix.pixelformat = pixelformat ;
950  fmt.fmt.pix.field = field ;
951 
952  bool try_ok = CaptureImp::xioctl_ok( fd , VIDIOC_TRY_FMT , &fmt , true ) ;
953  bool ok =
954  fmt.fmt.pix.width > 8 && fmt.fmt.pix.height > 8 &&
955  fmt.fmt.pix.pixelformat == pixelformat && fmt.fmt.pix.field == field ;
956 
957  if( try_ok && !ok )
958  {
959  G_DEBUG( "Gv::CaptureImp::set_format: ioctl try_fmt: " << try_ok << ": "
960  << pixelformat << "->" << fmt.fmt.pix.pixelformat << " "
961  << field << "->" << fmt.fmt.pix.field ) ;
962  }
963  else
964  {
965  CaptureImp::xioctl_check( fd , VIDIOC_S_FMT , &fmt , true ) ;
966  ok =
967  fmt.fmt.pix.width > 8 && fmt.fmt.pix.height > 8 &&
968  fmt.fmt.pix.pixelformat == pixelformat && fmt.fmt.pix.field == field ;
969  }
970 
971  G_DEBUG( "Gv::CaptureV4l::init_device: format negiotiation: "
972  << "[" << format_name << "," << field_name << "]: " << (ok?"accepted":"rejected") ) ;
973 
974  if( ok )
975  {
976  *out_p = fmt ;
977  return true ;
978  }
979  else
980  {
981  return false ;
982  }
983 }
984 
985 // ==
986 
987 std::pair<int,int> Gv::CaptureImp::xioctl( int fd , int request , void * arg , bool v )
988 {
989  int rc ;
990  do
991  {
992  rc = xioctl_imp( fd , request , arg ) ;
993  } while( rc == -1 && errno == EINTR ) ;
994  int e = rc >= 0 ? 0 : errno ;
995 
996  if( v )
997  {
998  G_DEBUG( "Gv::CaptureImp::xioctl: "
999  << "fd=" << fd << " "
1000  << "request=" << xioctl_name(request) << "(" << static_cast<unsigned int>(request) << ") "
1001  << "rc=" << rc << " "
1002  << "errno=" << e ) ;
1003  }
1004  return std::make_pair( rc , e ) ;
1005 }
1006 
1007 bool Gv::CaptureImp::xioctl_ok( int fd , int request , void * arg , bool v )
1008 {
1009  pair_t p = xioctl( fd , request , arg , v ) ;
1010  return p.first >= 0 ;
1011 }
1012 
1013 void Gv::CaptureImp::xioctl_warn( int fd , int request , void * arg , bool v )
1014 {
1015  pair_t p = xioctl( fd , request , arg , v ) ;
1016  if( p.first < 0 )
1017  G_WARNING( "CaptureImp::xioctl: ioctl " << xioctl_name(request) << ": " << G::Process::strerror(p.second) ) ;
1018 }
1019 
1020 void Gv::CaptureImp::xioctl_check( int fd , int request , void * arg , bool v )
1021 {
1022  pair_t p = xioctl( fd , request , arg , v ) ;
1023  if( p.first < 0 )
1024  throw_errno( xioctl_name(request) , p.second ) ;
1025 }
1026 
1027 bool Gv::CaptureImp::xioctl_eagain( int fd , int request , void * arg , bool v )
1028 {
1029  pair_t p = xioctl( fd , request , arg , v ) ;
1030  if( p.first == -1 )
1031  {
1032  if( p.second == EAGAIN ) return true ;
1033  throw_errno( xioctl_name(request) , p.second ) ;
1034  }
1035  return false ;
1036 }
1037 
1038 void Gv::CaptureImp::xioctl_einval( int fd , int request , void * arg , const std::string & more , bool v )
1039 {
1040  pair_t p = xioctl( fd , request , arg , v ) ;
1041  if( p.first == -1 )
1042  {
1043  if( p.second == EINVAL )
1044  throw Capture::Error( more ) ;
1045  throw_errno( xioctl_name(request) , p.second ) ;
1046  }
1047 }
1048 
1049 const char * Gv::CaptureImp::xioctl_name( int request )
1050 {
1051  for( const ioctl_name * np = ioctl_names ; np->p ; np++ )
1052  {
1053  if( static_cast<int>(np->n) == request )
1054  return np->p ;
1055  }
1056  return "xioctl" ;
1057 }
1058 
1059 void Gv::CaptureImp::throw_errno( const char * op , int e )
1060 {
1061  std::ostringstream ss ;
1062  ss << op << " error " << e << ", " << G::Process::strerror(e) ;
1063  if( e == ENOSPC )
1064  ss << " (not enough spare usb bandwidth)" ;
1065  if( e == ENODEV )
1066  throw Capture::NoDevice( ss.str() ) ;
1067  else
1068  throw Capture::Error( ss.str() ) ;
1069 }
1070 
1071 bool Gv::CaptureImp::m_use_libv4l = true ;
1072 
1073 int Gv::CaptureImp::xioctl_imp( int fd , int request , void * arg )
1074 {
1075  return m_use_libv4l ? v4l2_ioctl( fd , request , arg ) : ::ioctl( fd , request , arg ) ;
1076 }
1077 
1078 int Gv::CaptureImp::open( const char * p , int n , mode_t mode )
1079 {
1080  int fd = -1 ;
1081  {
1082  G::Root claim_root ;
1083  fd = m_use_libv4l ? v4l2_open( p , n , mode ) : ::open( p , n , mode ) ;
1084  }
1085  Cleanup::add( fd , m_use_libv4l ) ;
1086  return fd ;
1087 }
1088 
1089 void Gv::CaptureImp::close( int fd )
1090 {
1091  if( m_use_libv4l ) v4l2_close( fd ) ; else ::close( fd ) ;
1092  Cleanup::remove( fd ) ;
1093 }
1094 
1095 void * Gv::CaptureImp::mmap( void * p , size_t n , int prot , int flags , int fd , off_t offset )
1096 {
1097  G::Root claim_root ;
1098  return m_use_libv4l ? v4l2_mmap( p , n , prot , flags , fd , offset ) : ::mmap( p , n , prot , flags , fd , offset ) ;
1099 }
1100 
1101 int Gv::CaptureImp::read( int fd , void * buffer , size_t n )
1102 {
1103  return m_use_libv4l ? v4l2_read( fd , buffer , n ) : ::read( fd , buffer , n ) ;
1104 }
1105 
1106 // ==
1107 
1108 Gv::CaptureRequeuer::CaptureRequeuer( int fd , buffer_type & buffer ) :
1109  m_fd(fd),
1110  m_buffer(buffer)
1111 {
1112 }
1113 
1114 Gv::CaptureRequeuer::~CaptureRequeuer()
1115 {
1116  CaptureImp::xioctl_warn( m_fd , VIDIOC_QBUF , &m_buffer ) ;
1117 }
1118 
1119 // ==
1120 
1121 // work-round for linux usb bug "not enough bandwidth for altsetting" ...
1122 
1123 Gv::CaptureImp::Cleanup::List Gv::CaptureImp::Cleanup::m_list_lib ;
1124 Gv::CaptureImp::Cleanup::List Gv::CaptureImp::Cleanup::m_list_raw ;
1125 
1126 void Gv::CaptureImp::Cleanup::fn( G::SignalSafe , const char * )
1127 {
1128  for( List::iterator p = m_list_lib.begin() ; p != m_list_lib.end() ; ++p )
1129  v4l2_close( *p ) ;
1130  for( List::iterator p = m_list_raw.begin() ; p != m_list_raw.end() ; ++p )
1131  ::close( *p ) ;
1132 }
1133 
1134 void Gv::CaptureImp::Cleanup::add( int fd , bool lib )
1135 {
1136  static bool first = true ;
1137  if( first )
1138  G::Cleanup::add( CaptureImp::Cleanup::fn , nullptr ) ;
1139  first = false ;
1140 
1141  remove( fd ) ;
1142  List & list = lib ? m_list_lib : m_list_raw ;
1143  list.push_back( fd ) ;
1144 }
1145 
1146 void Gv::CaptureImp::Cleanup::remove( int fd )
1147 {
1148  m_list_lib.erase( std::remove(m_list_lib.begin(),m_list_lib.end(),fd) , m_list_lib.end() ) ;
1149  m_list_raw.erase( std::remove(m_list_raw.begin(),m_list_raw.end(),fd) , m_list_raw.end() ) ;
1150 }
1151 
triple< unsigned char > rgb(triple< unsigned char > yuv) g__noexcept
A top-level function that calculates rgb from yuv with default implementation options.
static std::string printable(const std::string &in, char escape= '\\')
Returns a printable represention of the given input string.
Definition: gstr.cpp:663
An empty structure that is used to indicate a signal-safe, reentrant implementation.
Definition: gsignalsafe.h:36
unsigned short xshift() const
Returns the x_shift.
bool is_grey() const
Returns true if grey colour components.
unsigned short yshift() const
Returns the y_shift.
A RAII class to do ioctl(VIDIOC_QBUF) on a v4l buffer.
virtual unsigned int dy() const override
Override from Gv::Capture.
virtual bool simple() const override
Override from Gv::Capture.
A class which acquires the process's special privileges on construction and releases them on destruct...
Definition: groot.h:49
virtual unsigned int dx() const override
Override from Gv::Capture.
~CaptureV4l()
Destructor.
A descriptor for a v4l pixel format.
virtual bool active() const override
Override from Gv::Capture.
static std::string tail(const std::string &in, std::string::size_type pos, const std::string &default_=std::string())
Returns the last part of the string after the given position.
Definition: gstr.cpp:1051
bool is_rgb() const
Returns true if rgb colour components.
CaptureV4l(const std::string &dev_name, const std::string &dev_config)
Constructor.
void setFormat(const CaptureBufferFormat &, const CaptureBufferScale &)
Used by the Gv::Capture class to imbue the buffer with a particular format description and scale...
const CaptureBufferComponent & component(int c) const
Returns the c'th colour component descriptor.
static unsigned int toUInt(const std::string &s)
Converts string 's' to an unsigned int.
Definition: gstr.cpp:450
A video-capture buffer class to hold image data, with overloaded constructors for the various V4l i/o...
static std::string head(const std::string &in, std::string::size_type pos, const std::string &default_=std::string())
Returns the first part of the string up to just before the given position.
Definition: gstr.cpp:1037
static bool splitMatch(const std::string &in, const std::string &s, const std::string &ws=Str::ws())
Returns true if any of the split parts of 'in' are equal to 's'.
Definition: gstr.cpp:927
A structure holding capture buffer dimensions.
bool is_yuv() const
Returns true if yuv colour components.
static std::string trimmed(const std::string &s, const std::string &ws)
Returns a trim()med version of s.
Definition: gstr.cpp:213
virtual bool read(unsigned char *, size_t) override
Override from Gv::Capture.
A callback interface for the Gv::Capture class.
Definition: gvcapture.h:99
triple< unsigned char > yuv(triple< unsigned char > rgb) g__noexcept
A top-level function that calculates yuv from rgb with default implementation options.
unsigned short depth() const
Returns the depth.
A private implementation class used by Gv::CaptureV4l.
virtual std::string info() const override
Override from Gv::Capture.
static std::string ws()
A convenience function returning standard whitespace characters.
Definition: gstr.cpp:1027
static void add(void(*fn)(SignalSafe, const char *), const char *arg)
Adds the given handler to the list of handlers that are to be called when the process terminates abno...
virtual int fd() const override
Override from Gv::Capture.
virtual void stop() override
Override from Gv::Capture.
virtual void start() override
Override from Gv::Capture.