net.cpp

00001 
00002 /***************************************************************************
00003  *  net.cpp - Camera to access images over the network
00004  *
00005  *  Created: Wed Feb 01 12:24:04 2006
00006  *  Copyright  2005-2008  Tim Niemueller [www.niemueller.de]
00007  *
00008  ****************************************************************************/
00009 
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version. A runtime exception applies to
00014  *  this software (see LICENSE.GPL_WRE file mentioned below for details).
00015  *
00016  *  This program is distributed in the hope that it will be useful,
00017  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00018  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019  *  GNU Library General Public License for more details.
00020  *
00021  *  Read the full text in the LICENSE.GPL_WRE file in the doc directory.
00022  */
00023 
00024 #include <cams/net.h>
00025 #include <cams/cam_exceptions.h>
00026 
00027 #include <core/exception.h>
00028 #include <core/exceptions/software.h>
00029 
00030 #include <fvutils/net/fuse_client.h>
00031 #include <fvutils/net/fuse_message.h>
00032 #include <fvutils/net/fuse_image_content.h>
00033 #include <fvutils/net/fuse_imagelist_content.h>
00034 #include <fvutils/system/camargp.h>
00035 #include <fvutils/compression/jpeg_decompressor.h>
00036 
00037 #include <netinet/in.h>
00038 #include <cstdlib>
00039 #include <cstring>
00040 
00041 using namespace fawkes;
00042 
00043 namespace firevision {
00044 #if 0 /* just to make Emacs auto-indent happy */
00045 }
00046 #endif
00047 
00048 /** @class NetworkCamera <cams/net.h>
00049  * Network camera.
00050  * Retrieve images via network (FUSE).
00051  * @see FuseClient
00052  * @author Tim Niemueller
00053  */
00054 
00055 /** Constructor.
00056  * Allows to initiate a NetworkCamera w/o specifying an image id. This can be
00057  * done later with the set_image_id() method.
00058  * @param host host to connect to
00059  * @param port port to connect to
00060  * @param jpeg if true jpeg images will be transferred and automatically be
00061  * decompressed, otherwise raw images are transferred
00062  */
00063 NetworkCamera::NetworkCamera(const char *host, unsigned short port, bool jpeg)
00064 {
00065   if ( host == NULL ) {
00066     throw NullPointerException("NetworkCamera: host must not be NULL");
00067   }
00068   __image_id = 0;
00069   __host = strdup(host);
00070   __port = port;
00071   __get_jpeg = jpeg;
00072 
00073   __connected       = false;
00074   __opened          = false;
00075   __local_version   = 0;
00076   __remote_version  = 0;
00077   __decompressor    = NULL;
00078   __decompressed_buffer = NULL;
00079   __last_width = 0;
00080   __last_height = 0;
00081   __fuse_image = NULL;
00082   __fuse_message = NULL;
00083   __fuse_imageinfo = NULL;
00084 
00085   __fusec = new FuseClient(__host, __port, this);
00086   if ( __get_jpeg ) {
00087     __decompressor = new JpegImageDecompressor();
00088   }
00089 }
00090 
00091 /** Constructor.
00092  * @param host host to connect to
00093  * @param port port to connect to
00094  * @param image_id image ID of image to retrieve
00095  * @param jpeg if true jpeg images will be transferred and automatically be
00096  * decompressed, otherwise raw images are transferred
00097  */
00098 NetworkCamera::NetworkCamera(const char *host, unsigned short port, const char *image_id,
00099                              bool jpeg)
00100 {
00101   if ( image_id == NULL ) {
00102     throw NullPointerException("NetworkCamera: image_id must not be NULL");
00103   }
00104   if ( host == NULL ) {
00105     throw NullPointerException("NetworkCamera: host must not be NULL");
00106   }
00107   __image_id = strdup(image_id);
00108   __host = strdup(host);
00109   __port = port;
00110   __get_jpeg = jpeg;
00111 
00112   __connected       = false;
00113   __opened          = false;
00114   __local_version   = 0;
00115   __remote_version  = 0;
00116   __decompressor    = NULL;
00117   __decompressed_buffer = NULL;
00118   __last_width = 0;
00119   __last_height = 0;
00120   __fuse_image = NULL;
00121   __fuse_message = NULL;
00122   __fuse_imageinfo = NULL;
00123 
00124   __fusec = new FuseClient(__host, __port, this);
00125   if ( __get_jpeg ) {
00126     __decompressor = new JpegImageDecompressor();
00127   }
00128 }
00129 
00130 
00131 /** Constructor.
00132  * Initialize with parameters from camera argument parser, supported values are:
00133  * - host=HOST, hostname or IP of host to connect to
00134  * - port=PORT, port number to connect to
00135  * - image=ID, image ID of image to retrieve
00136  * - jpeg=<true|false>, if true JPEGs are recieved and decompressed otherwise
00137  *   raw images will be transferred (raw is the default)
00138  * @param cap camera argument parser
00139  */
00140 NetworkCamera::NetworkCamera(const CameraArgumentParser *cap)
00141 {
00142   if ( cap->has("image") ) {
00143     __image_id = strdup(cap->get("image").c_str());
00144   } else {
00145     throw NullPointerException("image parameter must be set");
00146   }
00147   if ( cap->has("host") ) {
00148     __host = strdup(cap->get("host").c_str());
00149   } else {
00150     __host = strdup("localhost");
00151   }
00152   if ( cap->has("port") ) {
00153     int i = atoi(cap->get("port").c_str());
00154     if ( (i < 0) || (i >= 0xFFFF) ) {
00155       throw IllegalArgumentException("Port must be in the range 0-65535");
00156     }
00157     __port = (unsigned int)i;
00158   } else {
00159     __port = 2208;
00160   }
00161 
00162   __get_jpeg = ( cap->has("jpeg") && (cap->get("jpeg") == "true"));
00163 
00164   __connected       = false;
00165   __opened          = false;
00166   __local_version   = 0;
00167   __remote_version  = 0;
00168   __decompressor    = NULL;
00169   __decompressed_buffer = NULL;
00170   __last_width = 0;
00171   __last_height = 0;
00172   __fuse_image = NULL;
00173   __fuse_message = NULL;
00174   __fuse_imageinfo = NULL;
00175 
00176   __fusec = new FuseClient(__host, __port, this);
00177   if ( __get_jpeg ) {
00178     __decompressor = new JpegImageDecompressor();
00179   }
00180 }
00181 
00182 
00183 /** Destructor. */
00184 NetworkCamera::~NetworkCamera()
00185 {
00186   close();
00187   delete __fusec;
00188   free(__host);
00189   free(__image_id);
00190   if ( __decompressed_buffer != NULL) free(__decompressed_buffer);
00191   delete __decompressor;
00192 }
00193 
00194 
00195 void
00196 NetworkCamera::open()
00197 {
00198   if ( __opened )  return;
00199 
00200   __fusec->connect();
00201   __fusec->start();
00202   __fusec->wait_greeting();
00203 
00204   if ( __image_id) {
00205     FUSE_imagedesc_message_t *imagedesc = (FUSE_imagedesc_message_t *)calloc(1, sizeof(FUSE_imagedesc_message_t));
00206     strncpy(imagedesc->image_id, __image_id, IMAGE_ID_MAX_LENGTH);
00207     __fusec->enqueue_and_wait(FUSE_MT_GET_IMAGE_INFO, imagedesc, sizeof(FUSE_imagedesc_message_t));
00208 
00209     if ( ! __fuse_imageinfo ) {
00210       throw Exception("Could not receive image info. Image not available?");
00211     }
00212   }
00213 
00214   __opened = true;
00215 }
00216 
00217 
00218 void
00219 NetworkCamera::start()
00220 {
00221   __started = true;
00222 }
00223 
00224 void
00225 NetworkCamera::stop()
00226 {
00227   __started = false;
00228 }
00229 
00230 
00231 void
00232 NetworkCamera::print_info()
00233 {
00234 }
00235 
00236 
00237 void
00238 NetworkCamera::capture()
00239 {
00240   if (! __connected) {
00241     throw CaptureException("Capture failed, not connected");
00242   }
00243   if ( __fuse_image ) {
00244     throw CaptureException("You must dispose the buffer before fetching a new image");
00245   }
00246   if ( !__image_id ) {
00247     throw CaptureException("You must specify an image id");
00248   }
00249 
00250   FUSE_imagereq_message_t *irm = (FUSE_imagereq_message_t *)malloc(sizeof(FUSE_imagereq_message_t));
00251   memset(irm, 0, sizeof(FUSE_imagereq_message_t));
00252   strncpy(irm->image_id, __image_id, IMAGE_ID_MAX_LENGTH);
00253   irm->format = (__get_jpeg ? FUSE_IF_JPEG : FUSE_IF_RAW);
00254   __fusec->enqueue_and_wait(FUSE_MT_GET_IMAGE, irm, sizeof(FUSE_imagereq_message_t));
00255 
00256   if (! __connected) {
00257     throw CaptureException("Capture failed, connection died while waiting for image");
00258   }
00259   if ( ! __fuse_image ) {
00260     throw CaptureException("Fetching the image failed, no image received");
00261   }
00262 
00263   if ( __get_jpeg ) {
00264     if ( (__fuse_image->pixel_width() != __last_width) ||
00265          (__fuse_image->pixel_height() != __last_height) ) {
00266       if (__decompressed_buffer != NULL ) {
00267         free(__decompressed_buffer);
00268       }
00269       size_t buffer_size = colorspace_buffer_size(YUV422_PLANAR, __fuse_image->pixel_width(),
00270                                                   __fuse_image->pixel_height());
00271       __decompressed_buffer = (unsigned char *)malloc(buffer_size);
00272       __decompressor->set_decompressed_buffer(__decompressed_buffer, buffer_size);
00273     }
00274     __decompressor->set_compressed_buffer(__fuse_image->buffer(), __fuse_image->buffer_size());
00275     __decompressor->decompress();
00276   }
00277 }
00278 
00279 
00280 unsigned char *
00281 NetworkCamera::buffer()
00282 {
00283   if (__get_jpeg) {
00284     return __decompressed_buffer;
00285   } else {
00286     if ( __fuse_image ) {
00287       return __fuse_image->buffer();
00288     } else {
00289       return NULL;
00290     }
00291   }
00292 }
00293 
00294 unsigned int
00295 NetworkCamera::buffer_size()
00296 {
00297   if ( __get_jpeg ) {
00298     return colorspace_buffer_size(YUV422_PLANAR, pixel_width(), pixel_height());
00299   } else {
00300     if (! __fuse_image) {
00301       return 0;
00302     } else {
00303       return colorspace_buffer_size((colorspace_t)__fuse_image->colorspace(),
00304                                     __fuse_image->pixel_width(),
00305                                     __fuse_image->pixel_height());
00306     }
00307   }
00308 }
00309 
00310 void
00311 NetworkCamera::close()
00312 {
00313   dispose_buffer();
00314   if ( __started ) {
00315     stop();
00316   }
00317   if ( __fuse_imageinfo ) {
00318     free(__fuse_imageinfo);
00319     __fuse_imageinfo = NULL;
00320   }
00321   if ( __opened ) {
00322     __fusec->disconnect();
00323     __fusec->cancel();
00324     __fusec->join();
00325     __opened = false;
00326   }
00327 }
00328 
00329 void
00330 NetworkCamera::dispose_buffer()
00331 {
00332   delete __fuse_image;
00333   __fuse_image = NULL;
00334   if ( __fuse_message ) {
00335     __fuse_message->unref();
00336     __fuse_message = NULL;
00337   }
00338 }
00339 
00340 unsigned int
00341 NetworkCamera::pixel_width()
00342 {
00343   if ( __fuse_imageinfo ) {
00344     return ntohl(__fuse_imageinfo->width);
00345   } else {
00346     throw NullPointerException("No valid image info received");
00347   }
00348 }
00349 
00350 unsigned int
00351 NetworkCamera::pixel_height()
00352 {
00353   if ( __fuse_imageinfo ) {
00354     return ntohl(__fuse_imageinfo->height);
00355   } else {
00356     throw NullPointerException("No valid image info received");
00357   }
00358 }
00359 
00360 fawkes::Time *
00361 NetworkCamera::capture_time()
00362 {
00363   if ( __fuse_image ) {
00364     return __fuse_image->capture_time();
00365   } else {
00366     throw NullPointerException("No valid image exists");
00367   }  
00368 }
00369 
00370 void
00371 NetworkCamera::flush()
00372 {
00373   if (! __connected)  return;
00374   dispose_buffer();
00375 }
00376 
00377 
00378 bool
00379 NetworkCamera::ready()
00380 {
00381   return __connected;
00382 }
00383 
00384 
00385 /** Select the image that is opened.
00386  * @param image_id the image id
00387  */
00388 void
00389 NetworkCamera::set_image_id(const char *image_id)
00390 {
00391   __image_id = strdup(image_id);
00392 
00393   FUSE_imagedesc_message_t *imagedesc = (FUSE_imagedesc_message_t *)calloc(1, sizeof(FUSE_imagedesc_message_t));
00394   strncpy(imagedesc->image_id, __image_id, IMAGE_ID_MAX_LENGTH);
00395   __fusec->enqueue_and_wait(FUSE_MT_GET_IMAGE_INFO, imagedesc, sizeof(FUSE_imagedesc_message_t));
00396 
00397   if ( ! __fuse_imageinfo ) {
00398     throw Exception("Could not received image info. Image not available?");
00399   }
00400 }
00401 
00402 
00403 void
00404 NetworkCamera::set_image_number(unsigned int n)
00405 {
00406   // ignored, has to go away anyway
00407 }
00408 
00409 
00410 colorspace_t
00411 NetworkCamera::colorspace()
00412 {
00413   if ( __get_jpeg ) {
00414     return YUV422_PLANAR;
00415   } else {
00416     if ( __fuse_imageinfo ) {
00417       return (colorspace_t)ntohs(__fuse_imageinfo->colorspace);
00418     } else {
00419       return CS_UNKNOWN;
00420     }
00421   }
00422 }
00423 
00424 
00425 /** List the available images.
00426  * @return a vector containing information about the available images
00427  */
00428 std::vector<FUSE_imageinfo_t>&
00429 NetworkCamera::image_list()
00430 {
00431   __image_list.clear();
00432 
00433   if (! __connected) {
00434     throw CaptureException("Capture failed, not connected");
00435   }
00436 
00437   __fusec->enqueue_and_wait(FUSE_MT_GET_IMAGE_LIST);
00438 
00439   return __image_list;
00440 }
00441 
00442 
00443 void
00444 NetworkCamera::fuse_invalid_server_version(uint32_t local_version,
00445                                            uint32_t remote_version) throw()
00446 {
00447   __local_version  = local_version;
00448   __remote_version = remote_version;
00449 }
00450 
00451 
00452 void
00453 NetworkCamera::fuse_connection_established() throw()
00454 {
00455   __connected = true;
00456 }
00457 
00458 
00459 void
00460 NetworkCamera::fuse_connection_died() throw()
00461 {
00462   __connected = false;
00463 }
00464 
00465 
00466 void
00467 NetworkCamera::fuse_inbound_received(FuseNetworkMessage *m) throw()
00468 {
00469   switch(m->type()) {
00470 
00471   case FUSE_MT_IMAGE:
00472     try {
00473       __fuse_image = m->msgc<FuseImageContent>();
00474       if ( __fuse_image ) {
00475         __fuse_message = m;
00476         __fuse_message->ref();
00477       }
00478     } catch (Exception &e) {
00479       __fuse_image = NULL;
00480       __fuse_message = NULL;
00481     }
00482     break;
00483 
00484 
00485   case FUSE_MT_IMAGE_INFO:
00486     try {
00487       __fuse_imageinfo = m->msg_copy<FUSE_imageinfo_t>();
00488     } catch (Exception &e) {
00489       __fuse_imageinfo = NULL;
00490     }
00491     break;
00492 
00493   case FUSE_MT_IMAGE_INFO_FAILED:
00494     __fuse_imageinfo = NULL;
00495     break;
00496 
00497   case FUSE_MT_GET_IMAGE_FAILED:
00498     if ( __fuse_message ) {
00499       __fuse_message->unref();
00500     }
00501     __fuse_message = NULL;
00502     __fuse_image = NULL;
00503     break;
00504 
00505   case FUSE_MT_IMAGE_LIST:
00506     try {
00507       FuseImageListContent* fuse_image_list = m->msgc<FuseImageListContent>();
00508       if (fuse_image_list ) {
00509         while ( fuse_image_list->has_next() ) {
00510           FUSE_imageinfo_t *iip = fuse_image_list->next();
00511           FUSE_imageinfo_t ii;
00512           strncpy(ii.image_id, iip->image_id, IMAGE_ID_MAX_LENGTH);
00513           ii.colorspace = ntohs(iip->colorspace);
00514           ii.width = ntohl(iip->width);
00515           ii.height = ntohl(iip->height);
00516           ii.buffer_size = ntohl(iip->buffer_size);
00517           __image_list.push_back(ii);
00518         }
00519       }
00520     }
00521     catch (Exception &e) {
00522     }
00523     break;
00524 
00525   default:
00526       break;
00527   }
00528 }
00529 
00530 } // end namespace firevision