VideoTools
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
gvcamera.cpp
Go to the documentation of this file.
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 // gvcamera.cpp
19 //
20 
21 #include "gdef.h"
22 #include "gvcamera.h"
23 #include "gvcapturefactory.h"
24 #include "gstr.h"
25 #include "grimagetype.h"
26 #include "grcolourspace.h"
27 
29  const std::string & dev_name , const std::string & dev_config ,
30  bool one_shot , bool lazy_open , unsigned int open_timeout ,
31  const std::string & caption , const Gv::Timezone & tz ,
32  const std::string & image_input_source_name ) :
33  ImageInputSource(converter,image_input_source_name.empty()?dev_name:image_input_source_name) ,
34  m_dev_name(dev_name) ,
35  m_dev_config(dev_config) ,
36  m_one_shot(one_shot) ,
37  m_lazy_open(lazy_open) ,
38  m_open_timeout(std::max(1U,open_timeout)) ,
39  m_caption(caption) ,
40  m_tz(tz) ,
41  m_dx(0) ,
42  m_dy(0) ,
43  m_open_timer(*this,&Gv::Camera::onOpenTimeout,*this) ,
44  m_read_timer(*this,&Gv::Camera::onReadTimeout,*this) ,
45  m_send_timer(*this,&Gv::Camera::onSendTimeout,*this)
46 {
47  m_sleep_ms = G::Str::toUInt( G::Str::splitMatchTail(dev_config,"sleep=",";") , "0" ) ;
48  G_DEBUG( "Gv::Camera::Camera: device [" << dev_name << "] sleep=" << m_sleep_ms << "ms" ) ;
49  m_capture.reset( create(dev_name,dev_config,m_lazy_open) ) ;
50  if( m_capture.get() == nullptr )
51  m_open_timer.startTimer( m_open_timeout ) ;
52  else
53  onOpen() ;
54 }
55 
57 {
58  if( m_capture.get() != nullptr )
60 }
61 
62 bool Gv::Camera::isOpen() const
63 {
64  return fd() != -1 ;
65 }
66 
67 Gv::Capture * Gv::Camera::create( const std::string & dev_name , const std::string & dev_config , bool lazy_open )
68 {
69  try
70  {
71  return Gv::CaptureFactory::create( dev_name , dev_config , m_tz ) ;
72  }
73  catch( Gv::Capture::NoDevice & )
74  {
75  if( lazy_open ) return nullptr ;
76  throw ;
77  }
78 }
79 
80 void Gv::Camera::onOpenTimeout()
81 {
82  m_capture.reset( create(m_dev_name,m_dev_config,true) ) ;
83  if( m_capture.get() != nullptr )
84  onOpen() ;
85  else
86  m_open_timer.startTimer( m_open_timeout ) ;
87 }
88 
89 void Gv::Camera::onOpen()
90 {
91  m_dx = static_cast<int>(m_capture->dx()) ;
92  m_dy = static_cast<int>(m_capture->dy()) ;
93  m_image_type = Gr::ImageType::raw( m_dx , m_dy , 3 ) ;
94 
95  G_LOG( "Gv::Camera::onOpen: camera device=[" << m_dev_name << "] " << m_capture.get()->info() ) ;
97 }
98 
99 int Gv::Camera::fd() const
100 {
101  return m_capture.get() ? m_capture->fd() : -1 ;
102 }
103 
104 void Gv::Camera::onReadTimeout()
105 {
106  G_DEBUG( "Gv::Camera::readEvent: delayed read" ) ;
108  doRead() ;
109 }
110 
111 void Gv::Camera::onException( std::exception & e )
112 {
113  // exception thrown from readEvent() (ie. Gv::Capture or operator()())
114  // out to the event loop and delivered back
115  G_ERROR( "Gv::Camera::onException: exception: " << e.what() ) ;
116  throw ; // rethrow to the event loop to terminate
117 }
118 
119 void Gv::Camera::readEvent()
120 {
121  if( m_sleep_ms )
122  {
124  m_read_timer.startTimer( 0 , m_sleep_ms * 1000UL ) ;
125  }
126  else
127  {
128  doRead() ;
129  }
130 }
131 
132 void Gv::Camera::doRead()
133 {
134  if( !doReadImp() ) // if no device
135  {
137  blank() ;
138  m_capture.reset() ;
139  m_open_timer.startTimer( m_open_timeout ) ;
140  m_send_timer.startTimer( 0 ) ;
141  }
142 }
143 
144 void Gv::Camera::blank()
145 {
146  G_ASSERT( m_capture.get() != nullptr ) ;
147  Gr::ImageBuffer * image_buffer_p = Gr::Image::blank( m_image , m_image_type , m_capture->simple() ) ;
148  Gr::ImageData image_data( *image_buffer_p , m_image_type.dx() , m_image_type.dy() , m_image_type.channels() ) ;
149  int dy = image_data.dy() ;
150  int dx = image_data.dx() ;
151  for( int y = 0 ; y < dy ; y++ )
152  {
153  for( int x = 0 ; x < dx ; x++ )
154  {
155  image_data.rgb( x , y , image_data.r(x,y)/8U , image_data.g(x,y)/8U , image_data.b(x,y)/8U ) ;
156  }
157  }
158  Gv::Overlay overlay( image_data ) ;
159  overlay.write( (dx-8)/2 , (dy-8)/2 , "X" , m_tz ) ; // could do better
160 }
161 
162 namespace
163 {
164  struct Fill : public Gv::CaptureCallback
165  {
166  Fill( Gr::ImageData & image_data ) :
167  m_image_data(image_data)
168  {
169  }
170  virtual void operator()( const Gv::CaptureBuffer & cb ) override
171  {
172  cb.copyTo( m_image_data ) ;
173  }
174  Gr::ImageData & m_image_data ;
175  } ;
176 }
177 
178 bool Gv::Camera::doReadImp()
179 {
180  try
181  {
182  bool read_ok = false ;
183  Gr::ImageBuffer * image_buffer_p = Gr::Image::blank( m_image , m_image_type , m_capture->simple() ) ;
184  Gr::ImageData image_data( *image_buffer_p , m_image_type.dx() , m_image_type.dy() , m_image_type.channels() ) ;
185  if( m_capture->simple() ) // RGB24 etc, typically libv4l
186  {
187  // read() straight into the contiguous image buffer
188  char * p = Gr::imagebuffer::row_ptr( Gr::imagebuffer::row_begin(*image_buffer_p) ) ;
189  size_t n = Gr::imagebuffer::size_of( *image_buffer_p ) ;
190  read_ok = m_capture->read( reinterpret_cast<unsigned char*>(p) , n ) ;
191  }
192  else
193  {
194  // apply the capture buffer to the Fill object via operator()()
195  Fill copy_to_image( image_data ) ;
196  read_ok = m_capture->read( copy_to_image ) ;
197  }
198  if( read_ok )
199  {
200  if( !m_caption.empty() )
201  {
202  // add a timestamp overlay
203  Gv::Overlay overlay( image_data ) ;
204  overlay.write( 10 , 10 , m_caption , m_tz ) ;
205  }
206  m_send_timer.startTimer( 0 ) ; // send asynchronously (for historical reasons)
207  }
208  else
209  {
210  G_DEBUG( "Gv::Camera::doRead: failed to acquire an image from the capture device" ) ; // eg. EAGAIN
211  }
212  return true ;
213  }
214  catch( Gv::Capture::NoDevice & )
215  {
216  G_LOG( "Gv::Camera::doRead: capture device has disappeared" ) ;
217  if( m_one_shot )
218  throw ;
219  return false ;
220  }
221 }
222 
223 void Gv::Camera::onSendTimeout()
224 {
225  if( !m_image.empty() )
226  sendImageInput( m_image ) ; // Gv::ImageInputSource
227 }
228 
229 void Gv::Camera::resend( ImageInputHandler & )
230 {
231  m_send_timer.startTimer( 0 ) ;
232 }
233 
234 /// \file gvcamera.cpp
virtual ~Camera()
Destructor.
Definition: gvcamera.cpp:56
A high-level webcam interface that hooks into the GNet event loop and acts as an Gv::ImageInputSource...
Definition: gvcamera.h:50
A holder for image data, having eight bits per sample and one or three channels.
Definition: grimagedata.h:46
A representation of a timezone.
Definition: gvtimezone.h:35
A class that can add a text overlay to an image.
Definition: gvoverlay.h:44
Vectors ImageBuffer
An ImageBuffer is used to hold raw image data, typically in more than one chunk.
Definition: grimagebuffer.h:47
virtual void dropRead(Descriptor fd)=0
Removes the given event source descriptor from the list of read sources.
bool isOpen() const
Returns true if the webcam device is open.
Definition: gvcamera.cpp:62
A class that encapsulates a network file descriptor and hides knowledge of its o/s-spefific error val...
Definition: gdescriptor.h:37
static Capture * create(const std::string &dev_name, const std::string &config, Gv::Timezone=Gv::Timezone())
Factory method.
static ImageType raw(int dx, int dy, int channels)
Factory function for a raw image type.
A base class for distributing incoming images to multiple client objects, supporting some simple imag...
Definition: gvimageinput.h:116
static unsigned int toUInt(const std::string &s)
Converts string 's' to an unsigned int.
Definition: gstr.cpp:450
virtual void addRead(Descriptor fd, EventHandler &handler)=0
Adds the given event source descriptor and associated handler to the read list.
A video-capture buffer class to hold image data, with overloaded constructors for the various V4l i/o...
static ImageBuffer * blank(Image &, ImageType raw_type, bool contiguous=false)
Factory function for a not-really-blank raw image that is temporarily writable via the returned image...
Definition: grimage.cpp:54
A callback interface for the Gv::Capture class.
Definition: gvcapture.h:99
static std::string splitMatchTail(const std::string &in, const std::string &s, const std::string &ws=Str::ws())
Splits the input string into parts and looks for a part that starts with 's' and returns the trailing...
Definition: gstr.cpp:934
void copyTo(Gr::ImageData &) const
Copies the image to a correctly-sized image data buffer.
A video capture abstract interface, exposing a file descriptor and a method to handle read events on ...
Definition: gvcapture.h:40
An image format converter that can convert to and from the raw and jpeg formats (only), with scaling and monochrome options.
int dy() const
Returns the height.
Definition: grimagedata.h:335
Camera(Gr::ImageConverter &, const std::string &dev_name, const std::string &dev_config, bool one_shot, bool lazy_open, unsigned int lazy_open_timeout, const std::string &caption, const Gv::Timezone &caption_tz=Gv::Timezone(), const std::string &image_input_source_name=std::string())
Constructor.
Definition: gvcamera.cpp:28
static EventLoop & instance()
Returns a reference to an instance of the class, if any.
Definition: geventloop.cpp:43