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