VideoTools
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
gvimageinput.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 // gvimageinput.cpp
19 //
20 
21 #include "gdef.h"
22 #include "gvimageinput.h"
23 #include "grjpeg.h"
24 #include "gassert.h"
25 #include <algorithm>
26 
27 namespace Gv
28 {
29  bool operator==( ImageInputConversion a , ImageInputConversion b )
30  {
31  return a.type == b.type && a.scale == b.scale && a.monochrome == b.monochrome ;
32  }
33  bool operator==( const ImageInputTask & a , const ImageInputTask & b )
34  {
35  return a.m_conversion == b.m_conversion ;
36  }
37 }
38 
39 // ==
40 
41 Gv::ImageInputSource::ImageInputSource( Gr::ImageConverter & converter , const std::string & name ) :
42  m_name(name) ,
43  m_converter(converter)
44 {
45 }
46 
48 {
49  G_ASSERT( handlers() == 0U ) ;
50 }
51 
52 std::string Gv::ImageInputSource::name() const
53 {
54  return m_name ;
55 }
56 
58 {
59  size_t n = 0U ;
60  for( TaskList::const_iterator task_p = m_tasks.begin() ; task_p != m_tasks.end() ; ++task_p )
61  {
62  if( (*task_p).m_handler != nullptr )
63  n++ ;
64  }
65  return n ;
66 }
67 
69 {
70  for( TaskList::iterator task_p = m_tasks.begin() ; task_p != m_tasks.end() ; ++task_p )
71  {
72  if( (*task_p).m_handler == &handler )
73  return ;
74  }
75 
76  m_tasks.push_back( ImageInputTask() ) ;
77  m_tasks.back().m_handler = &handler ;
78 }
79 
81 {
82  for( TaskList::iterator task_p = m_tasks.begin() ; task_p != m_tasks.end() ; ++task_p )
83  {
84  if( (*task_p).m_handler == &handler )
85  (*task_p).m_handler = nullptr ;
86  }
87 }
88 
89 void Gv::ImageInputSource::sendNonImageInput( Gr::Image non_image_in , const std::string & type_str ,
90  ImageInputHandler * one_handler_p )
91 {
92  for( TaskList::iterator task_p = m_tasks.begin() ; task_p != m_tasks.end() ; ++task_p )
93  {
94  if( (*task_p).m_handler != nullptr && (one_handler_p==nullptr || (*task_p).m_handler==one_handler_p) )
95  (*task_p).m_handler->onNonImageInput( *this , non_image_in , type_str ) ;
96  }
97  collectGarbage() ;
98 }
99 
101 {
102  G_DEBUG( "Gv::ImageInputSource::sendImageInput: type=[" << image_in.type() << "](" << image_in.type() << ")" ) ;
103 
104  // update the conversion types
105  for( TaskList::iterator task_p = m_tasks.begin() ; task_p != m_tasks.end() ; ++task_p )
106  {
107  if( (*task_p).m_handler != nullptr && (one_handler_p==nullptr || (*task_p).m_handler==one_handler_p) )
108  {
109  (*task_p).m_conversion = (*task_p).m_handler->imageInputConversion(*this) ;
110  }
111  }
112 
113  // do the conversions
114  bool all_ok = true ;
115  for( TaskList::iterator task_p = m_tasks.begin() ; task_p != m_tasks.end() ; ++task_p )
116  {
117  if( one_handler_p != nullptr && (*task_p).m_handler == one_handler_p )
118  {
119  if( ! (*task_p).run(m_converter,image_in) )
120  all_ok = false ;
121  }
122  else if( (*task_p).m_handler != nullptr )
123  {
124  TaskList::iterator previous = std::find( m_tasks.begin() , task_p , *task_p ) ;
125  if( previous != task_p )
126  (*task_p).m_image = (*previous).m_image ; // we've already done it, so just take a copy
127  else if( ! (*task_p).run(m_converter,image_in) )
128  all_ok = false ;
129  }
130  }
131 
132  // call the handlers
133  for( TaskList::iterator task_p = m_tasks.begin() ; task_p != m_tasks.end() ; ++task_p )
134  {
135  if( (*task_p).m_handler != nullptr && (one_handler_p==nullptr || (*task_p).m_handler==one_handler_p) )
136  {
137  if( (*task_p).m_image.empty() ) // ie. no conversion -- use the input image
138  (*task_p).m_handler->onImageInput( *this , image_in ) ;
139  else if( (*task_p).m_image.valid() )
140  (*task_p).m_handler->onImageInput( *this , (*task_p).m_image ) ;
141  }
142  }
143 
144  collectGarbage() ;
145  return all_ok ;
146 }
147 
148 void Gv::ImageInputSource::collectGarbage()
149 {
150  for( TaskList::iterator task_p = m_tasks.begin() ; task_p != m_tasks.end() ; )
151  {
152  if( (*task_p).m_handler == nullptr )
153  task_p = m_tasks.erase( task_p ) ;
154  else
155  ++task_p ;
156  }
157 }
158 
159 // ==
160 
161 Gv::ImageInput::ImageInput( Gr::ImageConverter & converter , const std::string & channel_name , bool lazy_open ) :
162  Gv::ImageInputSource(converter,channel_name) , // source name
163  m_channel(channel_name,lazy_open) , // channel subscription
164  m_resend_timer(*this,&ImageInput::onResendTimeout,*this) ,
165  m_resend_to(nullptr)
166 {
167 }
168 
170 {
171 }
172 
174 {
175  G_ASSERT( m_channel.fd() == -1 ) ;
176  G_ASSERT( m_image.empty() ) ;
177  return m_channel.open() ;
178 }
179 
180 std::string Gv::ImageInput::info() const
181 {
182  return m_channel.info() ;
183 }
184 
186 {
187  return m_channel.fd() ;
188 }
189 
191 {
192  return receive( false ) ;
193 }
194 
195 bool Gv::ImageInput::receive( bool peek )
196 {
197  if( peek )
198  {
199  if( ! Gr::Image::peek(m_channel,m_image,m_type_str) )
200  return false ; // publisher has gone away
201  }
202  else
203  {
204  if( ! Gr::Image::receive(m_channel,m_image,m_type_str) )
205  return false ; // publisher has gone away
206  }
207 
208  if( m_image.valid() )
209  sendImageInput( m_image ) ;
210  else if( !m_image.empty() )
211  sendNonImageInput( m_image , m_type_str ) ;
212 
213  return true ;
214 }
215 
217 {
218  if( m_channel.fd() != -1 )
219  {
220  m_channel.close() ;
221  m_image.clear() ;
222  }
223  G_ASSERT( m_channel.fd() == -1 ) ;
224 }
225 
226 void Gv::ImageInput::resend( ImageInputHandler & handler )
227 {
228  m_resend_timer.startTimer( 0U ) ;
229  m_resend_to = &handler ;
230 }
231 
232 void Gv::ImageInput::onResendTimeout()
233 {
234  if( m_channel.fd() != -1 && m_resend_to != nullptr &&
235  Gr::Image::peek(m_channel,m_image,m_type_str) )
236  {
237  ImageInputHandler * one_handler = m_resend_to ;
238  m_resend_to = nullptr ;
239 
240  if( m_image.valid() )
241  sendImageInput( m_image , one_handler ) ;
242  else if( !m_image.empty() )
243  sendNonImageInput( m_image , m_type_str , one_handler ) ;
244  }
245 }
246 
247 void Gv::ImageInput::onException( std::exception & )
248 {
249  throw ;
250 }
251 
252 // ==
253 
255 {
256 }
257 
259 {
260 }
261 
262 // ==
263 
265  type(none) ,
266  scale(1) ,
267  monochrome(false)
268 {
269 }
270 
271 Gv::ImageInputConversion::ImageInputConversion( ImageInputConversion::Type type_ , int scale_ , bool monochrome_ ) :
272  type(type_) ,
273  scale(scale_) ,
274  monochrome(monochrome_)
275 {
276 }
277 
278 // ==
279 
281  m_handler(nullptr)
282 {
283 }
284 
286 {
287  const bool to_raw = m_conversion.type == ImageInputConversion::to_raw ;
288  const bool to_jpeg = m_conversion.type == ImageInputConversion::to_jpeg ;
289  const bool keep_type = m_conversion.type == ImageInputConversion::none ;
290  const bool is_jpeg = image_in.type().isJpeg() ;
291  const bool is_raw = image_in.type().isRaw() ;
292  const int channels = image_in.type().channels() ;
293 
294  G_DEBUG( "Gv::ImageInputTask::run: conversion=[t=" << m_conversion.type << ",m=" << m_conversion.monochrome << ",s=" << m_conversion.scale << "]" ) ;
295  G_DEBUG( "Gv::ImageInputTask::run: input=[" << image_in.type() << "]" ) ;
296 
297  if( ( keep_type || (to_jpeg && is_jpeg) || (to_raw && is_raw) ) &&
298  m_conversion.scale == 1 && (m_conversion.monochrome?1:3) == channels )
299  {
300  G_DEBUG( "Gv::ImageInputTask::run: no-op" ) ;
301  m_image.clear() ; // no conversion
302  return true ;
303  }
304  else if( to_jpeg || (keep_type && is_jpeg) )
305  {
306  G_DEBUG( "Gv::ImageInputTask::run: to-jpeg: s=" << m_conversion.scale << " m=" << m_conversion.monochrome ) ;
307  return converter.toJpeg( image_in , m_image , m_conversion.scale , m_conversion.monochrome ) ;
308  }
309  else if( to_raw || (keep_type && is_raw) )
310  {
311  G_DEBUG( "Gv::ImageInputTask::run: to-raw: s=" << m_conversion.scale << " m=" << m_conversion.monochrome ) ;
312  return converter.toRaw( image_in , m_image , m_conversion.scale , m_conversion.monochrome ) ;
313  }
314  else
315  {
316  G_DEBUG( "Gv::ImageInputTask::run: unsupported conversion" ) ;
317  return false ;
318  }
319 }
320 
321 /// \file gvimageinput.cpp
void sendNonImageInput(Gr::Image non_image, const std::string &type_str, ImageInputHandler *one_handler_p=nullptr)
Sends non-image data to registered handlers (or one).
bool isRaw() const
Returns true if a raw image type.
ImageInputTask()
Default constructor.
void close()
Closes the input channel, releasing resources.
void addImageInputHandler(ImageInputHandler &)
Adds a handler for image callbacks.
bool isJpeg() const
Returns true if a jpeg image type.
virtual ~ImageInputSource()
Destructor.
int channels() const
Returns the number of channels.
Definition: grimagetype.h:197
void removeImageInputHandler(ImageInputHandler &)
Removes a handler for image callbacks.
virtual ~ImageInputHandler()
Destructor.
static bool receive(G::PublisherSubscription &channel, Image &, std::string &type_str)
Reads a publication channel into an image.
Definition: grimage.cpp:155
std::string info() const
Returns the channel info.
bool open()
Tries to re-open the input channel some time after handleReadEvent() has returned false...
A class for reading images from a publication channel and distributing them to multiple client object...
Definition: gvimageinput.h:168
A callback interface for Gv::ImageInputSource.
Definition: gvimageinput.h:84
bool toRaw(Image image_in, Image &image_out, int scale=1, bool monochrome_out=false)
Converts the image to raw format. Returns a false on error.
ImageInputSource(Gr::ImageConverter &, const std::string &source_name=std::string())
Constructor.
~ImageInput()
Destructor.
A class holding shared read-only image data (Gr::ImageBuffer) and its associated image type (Gr::Imag...
Definition: grimage.h:41
A base class for distributing incoming images to multiple client objects, supporting some simple imag...
Definition: gvimageinput.h:116
virtual void onNonImageInput(ImageInputSource &, Gr::Image, const std::string &type_str)
Called by Gv::ImageInputSource to deliver a new non-image.
bool sendImageInput(Gr::Image, ImageInputHandler *one_handler_p=nullptr)
Sends a new image to all registered handlers, or optionally to just one of them.
ImageType type() const
Returns the image type.
Definition: grimage.cpp:85
int fd() const
Returns the publication channel file descriptor, or -1 if close()d.
A private implementation class used by Gv::ImageInputSource.
Definition: gvimageinput.h:67
ImageInputConversion()
Default constructor for a 'none' conversion, with scale one.
bool toJpeg(Image image_in, Image &image_out, int scale=1, bool monochrome_out=false)
Converts the image to jpeg format. Returns false on error.
bool handleReadEvent()
Called to process a read event on the file descriptor.
bool run(Gr::ImageConverter &, Gr::Image)
Runs the conversion. Returns false on error.
size_t handlers() const
Returns the number of registered handlers.
An image format converter that can convert to and from the raw and jpeg formats (only), with scaling and monochrome options.
ImageInput(Gr::ImageConverter &, const std::string &channel_name, bool lazy_open)
Constructor.
std::string name() const
Returns the source name, as passed to the constructor.
static bool peek(G::PublisherSubscription &channel, Image &, std::string &type_str)
A variant on receive() that does a channel peek().
Definition: grimage.cpp:160