Fawkes API  Fawkes Development Version
net.cpp
1 
2 /***************************************************************************
3  * net.cpp - Camera to access images over the network
4  *
5  * Created: Wed Feb 01 12:24:04 2006
6  * Copyright 2005-2008 Tim Niemueller [www.niemueller.de]
7  *
8  ****************************************************************************/
9 
10 /* This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version. A runtime exception applies to
14  * this software (see LICENSE.GPL_WRE file mentioned below for details).
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Library General Public License for more details.
20  *
21  * Read the full text in the LICENSE.GPL_WRE file in the doc directory.
22  */
23 
24 #include <fvcams/net.h>
25 #include <fvcams/cam_exceptions.h>
26 
27 #include <core/exception.h>
28 #include <core/exceptions/software.h>
29 
30 #include <fvutils/net/fuse_client.h>
31 #include <fvutils/net/fuse_message.h>
32 #include <fvutils/net/fuse_image_content.h>
33 #include <fvutils/net/fuse_imagelist_content.h>
34 #include <fvutils/system/camargp.h>
35 #include <fvutils/compression/jpeg_decompressor.h>
36 
37 #include <netinet/in.h>
38 #include <cstdlib>
39 #include <cstring>
40 
41 using namespace fawkes;
42 
43 namespace firevision {
44 #if 0 /* just to make Emacs auto-indent happy */
45 }
46 #endif
47 
48 /** @class NetworkCamera <fvcams/net.h>
49  * Network camera.
50  * Retrieve images via network (FUSE).
51  * @see FuseClient
52  * @author Tim Niemueller
53  */
54 
55 /** Constructor.
56  * Allows to initiate a NetworkCamera w/o specifying an image id. This can be
57  * done later with the set_image_id() method.
58  * @param host host to connect to
59  * @param port port to connect to
60  * @param jpeg if true jpeg images will be transferred and automatically be
61  * decompressed, otherwise raw images are transferred
62  */
63 NetworkCamera::NetworkCamera(const char *host, unsigned short port, bool jpeg)
64 {
65  if ( host == NULL ) {
66  throw NullPointerException("NetworkCamera: host must not be NULL");
67  }
68  __image_id = 0;
69  __host = strdup(host);
70  __port = port;
71  __get_jpeg = jpeg;
72 
73  __connected = false;
74  __opened = false;
75  __local_version = 0;
76  __remote_version = 0;
77  __decompressor = NULL;
78  __decompressed_buffer = NULL;
79  __last_width = 0;
80  __last_height = 0;
81  __fuse_image = NULL;
82  __fuse_message = NULL;
83  __fuse_imageinfo = NULL;
84 
85  __fusec = new FuseClient(__host, __port, this);
86  if ( __get_jpeg ) {
87  __decompressor = new JpegImageDecompressor();
88  }
89 }
90 
91 /** Constructor.
92  * @param host host to connect to
93  * @param port port to connect to
94  * @param image_id image ID of image to retrieve
95  * @param jpeg if true jpeg images will be transferred and automatically be
96  * decompressed, otherwise raw images are transferred
97  */
98 NetworkCamera::NetworkCamera(const char *host, unsigned short port, const char *image_id,
99  bool jpeg)
100 {
101  if ( image_id == NULL ) {
102  throw NullPointerException("NetworkCamera: image_id must not be NULL");
103  }
104  if ( host == NULL ) {
105  throw NullPointerException("NetworkCamera: host must not be NULL");
106  }
107  __image_id = strdup(image_id);
108  __host = strdup(host);
109  __port = port;
110  __get_jpeg = jpeg;
111 
112  __connected = false;
113  __opened = false;
114  __local_version = 0;
115  __remote_version = 0;
116  __decompressor = NULL;
117  __decompressed_buffer = NULL;
118  __last_width = 0;
119  __last_height = 0;
120  __fuse_image = NULL;
121  __fuse_message = NULL;
122  __fuse_imageinfo = NULL;
123 
124  __fusec = new FuseClient(__host, __port, this);
125  if ( __get_jpeg ) {
126  __decompressor = new JpegImageDecompressor();
127  }
128 }
129 
130 
131 /** Constructor.
132  * Initialize with parameters from camera argument parser, supported values are:
133  * - host=HOST, hostname or IP of host to connect to
134  * - port=PORT, port number to connect to
135  * - image=ID, image ID of image to retrieve
136  * - jpeg=<true|false>, if true JPEGs are recieved and decompressed otherwise
137  * raw images will be transferred (raw is the default)
138  * @param cap camera argument parser
139  */
140 NetworkCamera::NetworkCamera(const CameraArgumentParser *cap)
141 {
142  if ( cap->has("image") ) {
143  __image_id = strdup(cap->get("image").c_str());
144  } else {
145  throw NullPointerException("image parameter must be set");
146  }
147  if ( cap->has("host") ) {
148  __host = strdup(cap->get("host").c_str());
149  } else {
150  __host = strdup("localhost");
151  }
152  if ( cap->has("port") ) {
153  int i = atoi(cap->get("port").c_str());
154  if ( (i < 0) || (i >= 0xFFFF) ) {
155  throw IllegalArgumentException("Port must be in the range 0-65535");
156  }
157  __port = (unsigned int)i;
158  } else {
159  __port = 2208;
160  }
161 
162  __get_jpeg = ( cap->has("jpeg") && (cap->get("jpeg") == "true"));
163 
164  __connected = false;
165  __opened = false;
166  __local_version = 0;
167  __remote_version = 0;
168  __decompressor = NULL;
169  __decompressed_buffer = NULL;
170  __last_width = 0;
171  __last_height = 0;
172  __fuse_image = NULL;
173  __fuse_message = NULL;
174  __fuse_imageinfo = NULL;
175 
176  __fusec = new FuseClient(__host, __port, this);
177  if ( __get_jpeg ) {
178  __decompressor = new JpegImageDecompressor();
179  }
180 }
181 
182 
183 /** Destructor. */
184 NetworkCamera::~NetworkCamera()
185 {
186  close();
187  delete __fusec;
188  free(__host);
189  free(__image_id);
190  if ( __decompressed_buffer != NULL) free(__decompressed_buffer);
191  delete __decompressor;
192 }
193 
194 
195 void
196 NetworkCamera::open()
197 {
198  if ( __opened ) return;
199 
200  __fusec->connect();
201  __fusec->start();
202  __fusec->wait_greeting();
203 
204  if ( __image_id) {
206  strncpy(imagedesc->image_id, __image_id, IMAGE_ID_MAX_LENGTH);
207  __fusec->enqueue_and_wait(FUSE_MT_GET_IMAGE_INFO, imagedesc, sizeof(FUSE_imagedesc_message_t));
208 
209  if ( ! __fuse_imageinfo ) {
210  throw Exception("Could not receive image info. Image not available?");
211  }
212  }
213 
214  __opened = true;
215 }
216 
217 
218 void
219 NetworkCamera::start()
220 {
221  __started = true;
222 }
223 
224 void
225 NetworkCamera::stop()
226 {
227  __started = false;
228 }
229 
230 
231 void
232 NetworkCamera::print_info()
233 {
234 }
235 
236 
237 void
238 NetworkCamera::capture()
239 {
240  if (! __connected) {
241  throw CaptureException("Capture failed, not connected");
242  }
243  if ( __fuse_image ) {
244  throw CaptureException("You must dispose the buffer before fetching a new image");
245  }
246  if ( !__image_id ) {
247  throw CaptureException("You must specify an image id");
248  }
249 
251  memset(irm, 0, sizeof(FUSE_imagereq_message_t));
252  strncpy(irm->image_id, __image_id, IMAGE_ID_MAX_LENGTH);
253  irm->format = (__get_jpeg ? FUSE_IF_JPEG : FUSE_IF_RAW);
254  __fusec->enqueue_and_wait(FUSE_MT_GET_IMAGE, irm, sizeof(FUSE_imagereq_message_t));
255 
256  if (! __connected) {
257  throw CaptureException("Capture failed, connection died while waiting for image");
258  }
259  if ( ! __fuse_image ) {
260  throw CaptureException("Fetching the image failed, no image received");
261  }
262 
263  if ( __get_jpeg ) {
264  if ( (__fuse_image->pixel_width() != __last_width) ||
265  (__fuse_image->pixel_height() != __last_height) ) {
266  if (__decompressed_buffer != NULL ) {
267  free(__decompressed_buffer);
268  }
269  size_t buffer_size = colorspace_buffer_size(YUV422_PLANAR, __fuse_image->pixel_width(),
270  __fuse_image->pixel_height());
271  __decompressed_buffer = (unsigned char *)malloc(buffer_size);
272  __decompressor->set_decompressed_buffer(__decompressed_buffer, buffer_size);
273  }
274  __decompressor->set_compressed_buffer(__fuse_image->buffer(), __fuse_image->buffer_size());
275  __decompressor->decompress();
276  }
277 }
278 
279 
280 unsigned char *
281 NetworkCamera::buffer()
282 {
283  if (__get_jpeg) {
284  return __decompressed_buffer;
285  } else {
286  if ( __fuse_image ) {
287  return __fuse_image->buffer();
288  } else {
289  return NULL;
290  }
291  }
292 }
293 
294 unsigned int
295 NetworkCamera::buffer_size()
296 {
297  if ( __get_jpeg ) {
298  return colorspace_buffer_size(YUV422_PLANAR, pixel_width(), pixel_height());
299  } else {
300  if (! __fuse_image) {
301  return 0;
302  } else {
303  return colorspace_buffer_size((colorspace_t)__fuse_image->colorspace(),
304  __fuse_image->pixel_width(),
305  __fuse_image->pixel_height());
306  }
307  }
308 }
309 
310 void
311 NetworkCamera::close()
312 {
313  dispose_buffer();
314  if ( __started ) {
315  stop();
316  }
317  if ( __fuse_imageinfo ) {
318  free(__fuse_imageinfo);
319  __fuse_imageinfo = NULL;
320  }
321  if ( __opened ) {
322  __fusec->disconnect();
323  __fusec->cancel();
324  __fusec->join();
325  __opened = false;
326  }
327 }
328 
329 void
330 NetworkCamera::dispose_buffer()
331 {
332  delete __fuse_image;
333  __fuse_image = NULL;
334  if ( __fuse_message ) {
335  __fuse_message->unref();
336  __fuse_message = NULL;
337  }
338 }
339 
340 unsigned int
341 NetworkCamera::pixel_width()
342 {
343  if ( __fuse_imageinfo ) {
344  return ntohl(__fuse_imageinfo->width);
345  } else {
346  throw NullPointerException("No valid image info received");
347  }
348 }
349 
350 unsigned int
351 NetworkCamera::pixel_height()
352 {
353  if ( __fuse_imageinfo ) {
354  return ntohl(__fuse_imageinfo->height);
355  } else {
356  throw NullPointerException("No valid image info received");
357  }
358 }
359 
360 fawkes::Time *
361 NetworkCamera::capture_time()
362 {
363  if ( __fuse_image ) {
364  return __fuse_image->capture_time();
365  } else {
366  throw NullPointerException("No valid image exists");
367  }
368 }
369 
370 void
371 NetworkCamera::flush()
372 {
373  if (! __connected) return;
374  dispose_buffer();
375 }
376 
377 
378 bool
379 NetworkCamera::ready()
380 {
381  return __connected;
382 }
383 
384 
385 /** Select the image that is opened.
386  * @param image_id the image id
387  */
388 void
389 NetworkCamera::set_image_id(const char *image_id)
390 {
391  __image_id = strdup(image_id);
392 
394  strncpy(imagedesc->image_id, __image_id, IMAGE_ID_MAX_LENGTH);
395  __fusec->enqueue_and_wait(FUSE_MT_GET_IMAGE_INFO, imagedesc, sizeof(FUSE_imagedesc_message_t));
396 
397  if ( ! __fuse_imageinfo ) {
398  throw Exception("Could not received image info. Image not available?");
399  }
400 }
401 
402 
403 void
404 NetworkCamera::set_image_number(unsigned int n)
405 {
406  // ignored, has to go away anyway
407 }
408 
409 
410 colorspace_t
411 NetworkCamera::colorspace()
412 {
413  if ( __get_jpeg ) {
414  return YUV422_PLANAR;
415  } else {
416  if ( __fuse_imageinfo ) {
417  return (colorspace_t)ntohs(__fuse_imageinfo->colorspace);
418  } else {
419  return CS_UNKNOWN;
420  }
421  }
422 }
423 
424 
425 /** List the available images.
426  * @return a vector containing information about the available images
427  */
428 std::vector<FUSE_imageinfo_t>&
429 NetworkCamera::image_list()
430 {
431  __image_list.clear();
432 
433  if (! __connected) {
434  throw CaptureException("Capture failed, not connected");
435  }
436 
437  __fusec->enqueue_and_wait(FUSE_MT_GET_IMAGE_LIST);
438 
439  return __image_list;
440 }
441 
442 
443 void
444 NetworkCamera::fuse_invalid_server_version(uint32_t local_version,
445  uint32_t remote_version) throw()
446 {
447  __local_version = local_version;
448  __remote_version = remote_version;
449 }
450 
451 
452 void
453 NetworkCamera::fuse_connection_established() throw()
454 {
455  __connected = true;
456 }
457 
458 
459 void
460 NetworkCamera::fuse_connection_died() throw()
461 {
462  __connected = false;
463 }
464 
465 
466 void
467 NetworkCamera::fuse_inbound_received(FuseNetworkMessage *m) throw()
468 {
469  switch(m->type()) {
470 
471  case FUSE_MT_IMAGE:
472  try {
473  __fuse_image = m->msgc<FuseImageContent>();
474  if ( __fuse_image ) {
475  __fuse_message = m;
476  __fuse_message->ref();
477  }
478  } catch (Exception &e) {
479  __fuse_image = NULL;
480  __fuse_message = NULL;
481  }
482  break;
483 
484 
485  case FUSE_MT_IMAGE_INFO:
486  try {
487  __fuse_imageinfo = m->msg_copy<FUSE_imageinfo_t>();
488  } catch (Exception &e) {
489  __fuse_imageinfo = NULL;
490  }
491  break;
492 
493  case FUSE_MT_IMAGE_INFO_FAILED:
494  __fuse_imageinfo = NULL;
495  break;
496 
497  case FUSE_MT_GET_IMAGE_FAILED:
498  if ( __fuse_message ) {
499  __fuse_message->unref();
500  }
501  __fuse_message = NULL;
502  __fuse_image = NULL;
503  break;
504 
505  case FUSE_MT_IMAGE_LIST:
506  try {
507  FuseImageListContent* fuse_image_list = m->msgc<FuseImageListContent>();
508  if (fuse_image_list ) {
509  while ( fuse_image_list->has_next() ) {
510  FUSE_imageinfo_t *iip = fuse_image_list->next();
511  FUSE_imageinfo_t ii;
512  strncpy(ii.image_id, iip->image_id, IMAGE_ID_MAX_LENGTH);
513  ii.colorspace = ntohs(iip->colorspace);
514  ii.width = ntohl(iip->width);
515  ii.height = ntohl(iip->height);
516  ii.buffer_size = ntohl(iip->buffer_size);
517  __image_list.push_back(ii);
518  }
519  }
520  }
521  catch (Exception &e) {
522  }
523  break;
524 
525  default:
526  break;
527  }
528 }
529 
530 } // end namespace firevision
char image_id[IMAGE_ID_MAX_LENGTH]
image ID
Definition: fuse.h:156
Decompressor for JPEG images.
Image request message.
Definition: fuse.h:147
Image info message.
Definition: fuse.h:165
Fawkes library namespace.
Capturing a frame failed.
A class for handling time.
Definition: time.h:91
A NULL pointer was supplied where not allowed.
Definition: software.h:34
Image description message.
Definition: fuse.h:155
Camera argument parser.
Definition: camargp.h:38
uint32_t width
width in pixels
Definition: fuse.h:169
uint32_t colorspace
color space
Definition: fuse.h:167
char image_id[IMAGE_ID_MAX_LENGTH]
image ID
Definition: fuse.h:166
bool has(std::string s) const
Check if an parameter was given.
Definition: camargp.cpp:152
FUSE Network Message.
Definition: fuse_message.h:41
Base class for exceptions in Fawkes.
Definition: exception.h:36
bool has_next()
Check if another image info is available.
char image_id[IMAGE_ID_MAX_LENGTH]
image ID
Definition: fuse.h:148
uint32_t format
requested image format, see FUSE_image_format_t
Definition: fuse.h:149
uint32_t height
height in pixels
Definition: fuse.h:170
uint32_t buffer_size
size of following image buffer in bytes
Definition: fuse.h:171
Expected parameter is missing.
Definition: software.h:82
FUSE_imageinfo_t * next()
Get next image info.
std::string get(std::string s) const
Get the value of the given parameter.
Definition: camargp.cpp:164