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