www.pudn.com > ReadingPeopleTracker-1.28.rar > BufferedSlaveImageSource.cc
/////////////////////////////////////////////////////////////////////////////// // // // BufferedSlaveImageSource.cc defining a buffered ImageSource class // // // // An ImageSource which waits for external images using pthread_cond_wait. // // The images given are buffered in a FIFO buffer and can be queried. // // // // Additionally, the class can fill up the FIFO buffer from a given // // ImageSource, automatically in get_next() or using fill_buffer(). // // // // Author : Nils T Siebel (nts) // // Created : Wed Jan 23 17:52:33 GMT 2002 // // Revision : 1.0 of Thu Aug 15 13:26:33 BST 2002 // // Copyright : The University of Reading // // // /////////////////////////////////////////////////////////////////////////////// #include "BufferedSlaveImageSource.h" #include#include // for memmove(), memset() #include // for struct tm, mktime() #include "RGB32Image.h" #include "text_output.h" // for cerror etc namespace ReadingPeopleTracker { static const char *BufferedSlaveImageSource_Revision = "@(#) BufferedSlaveImageSource.cc, rev 1.0 of Thu Aug 15 13:26:33 BST 2002, Author Nils T Siebel, Copyright (c) 2002 The University of Reading"; BufferedSlaveImageSource::BufferedSlaveImageSource(unsigned int the_xdim, unsigned int the_ydim, ImageSource *the_master_image_source, // optional unsigned int the_buffer_size) // optional { xdim = the_xdim; ydim = the_ydim; master_image_source = the_master_image_source; buffer_size = the_buffer_size; assert (xdim > 0); // we need valid image dimensions at this point assert (ydim > 0); // we need valid image dimensions at this point assert (buffer_size > 0); // empty buffer makes no sense (I strongly recommend > 1 --- nts) // set up the image buffer: an array of pointers to Image classes. // `current' will always be the first pointer in this array etc image_buffer = new Image*[buffer_size]; // Important: zero the *image_buffer (ie, set all pointers to NULL) memset((void *)image_buffer, 0, buffer_size * sizeof (Image*)); // set up write access mutex and condition variables pthread_mutex_init(&image_buffer_modification, NULL); pthread_cond_init(&new_image_in_buffer, NULL); // initialise other variables buffer_entries = 0; // no image received yet current = NULL; ImageSource::source_type = IST_SLAVE; } BufferedSlaveImageSource::~BufferedSlaveImageSource() { // destroy write access mutex and condition variables pthread_mutex_destroy(&image_buffer_modification); pthread_cond_destroy(&new_image_in_buffer); // de-allocate all images in buffer, and our buffer if (image_buffer != NULL) { delete [] image_buffer; delete image_buffer; } // make sure ImageSource::~ImageSource does not try to de-allocate current current = NULL; } ////////////////////////////////////////////////////////////////////////////// // // // BufferedSlaveImageSource::handle_new_image: add given image to buffer // // // // Call this method to request a new image to be put into the buffer. // // The data will be put into a new Image. If the buffer is full, the // // method will issue an error message and ignore the image. // // // // The image data given has to be RGB32Image compatible, ie 24-bit RGB // // colour, padded to 32-bit with image addressing mode the same as // // defined by Image::image_storage_mode. // // // ////////////////////////////////////////////////////////////////////////////// void BufferedSlaveImageSource::handle_new_image(unsigned char *the_data, TS_TIMESTAMP *the_timestamp) { if (buffer_entries >= buffer_size) // no more room in buffer { cerror << " BufferedSlaveImageSource::handle_new_image(): " << "could not handle new image (buffer full) --- frame dropped. " << endl; return; } assert (the_data != NULL); assert (the_timestamp != NULL); // create new time: milliseconds since the Epoch (01/01/1970) tm *f_time = new tm; f_time->tm_sec = the_timestamp->second; f_time->tm_min = the_timestamp->minute; f_time->tm_hour = the_timestamp->hour; f_time->tm_mday = the_timestamp->day; f_time->tm_mon = the_timestamp->month; f_time->tm_year = the_timestamp->year - 1900; // OK for year 3000 issue, see mktime(3) or ctime(3) if man pages still exist f_time->tm_isdst = 0; // probably not necessarily. time_t t_frame = mktime(f_time); frame_time_t the_frame_time_in_ms = ((frame_time_t) t_frame) // this is in seconds * 1000 + the_timestamp->millisecond; // make milliseconds and add delta Image *new_image = new RGB32Image(xdim, ydim, the_timestamp->frame_count, // ie the frame id the_frame_time_in_ms, the_data); new_image->set_timestamp(the_timestamp); // this requires exclusive write access to image_buffer pointers // (we are competing with get_next() which could be removing an old image from the buffer) pthread_mutex_lock(&image_buffer_modification); // image_buffer[buffer_entries] = new_image; buffer_entries++; // // signal ``new image in buffer'' to other thread in case it waits (in get_next()) pthread_cond_signal(&new_image_in_buffer); // pthread_mutex_unlock(&image_buffer_modification); } /////////////////////////////////////////////////////////////////////////////// // // // BufferedSlaveImageSource::get_next: get image from buffer or wait for it // // // // If no image is available in the buffer and we have a master_image_source // // one image will be taken from it and placed into the buffer as current. // // // // The current Image will be de-allocated by this method. // // // /////////////////////////////////////////////////////////////////////////////// Image *BufferedSlaveImageSource::get_next() { Image *old_current = NULL; if (current != NULL) { // dispose of the current Image. if (master_image_source != NULL) { // the master_image_source, if it exists, needs current during get_next() old_current = current; // remember so that we can de-allocate it later } else { // delete it #ifdef THALES_WRAPPER wrapper_release_buffer(current->get_data()); current->set_data(NULL); // so Image::~Image does not de-allocate it #endif // do not de-allocate current but re-use its memory --- for reasons of speed // this requires exclusive write access to image_buffer pointers // (we are competing with handle_new_image(...) which could be filling up the buffer) pthread_mutex_lock(&image_buffer_modification); // keep_image_memory_for_later(current); // pthread_mutex_unlock(&image_buffer_modification); current = NULL; } // remove the image from the buffer: this is done by moving the pointers one up assert (buffer_entries > 0); // otherwise we should not have a current image // this requires exclusive write access to image_buffer pointers // (we are competing with handle_new_image(...) which could be filling up the buffer) pthread_mutex_lock(&image_buffer_modification); // memmove((void *) &(image_buffer[0]), (void *) &(image_buffer[1]), (buffer_size - 1) * sizeof (Image*)); // Important: mark entry as not used (avoiding two pointers to be the same) image_buffer[buffer_size - 1] = NULL; buffer_entries--; // pthread_mutex_unlock(&image_buffer_modification); } if (buffer_entries < 1) // no images available { // we might have a master_image_source available where we can get the new image ourselves if (master_image_source != NULL) { // get one image (and only one) from master_image_source and place into image_buffer // this requires exclusive write access to image_buffer pthread_mutex_lock(&image_buffer_modification); // if (buffer_entries < 1) // check again after locking the image_buffer write access { cdebug << " BufferedSlaveImageSource::get_next(): buffer underrun"; // // query master_image_source for a new Image // set master_image_source's current Image memory to a new Image. // This is so that it does not overwrite its current Image which // might be buffered here, but might still be unused. // It is not necessary for the first frame where its current == NULL if (master_image_source->get_frame_count() > 0) { // try to get new Image memory for master_image_source Image *new_image_memory = get_new_image_memory(); if (new_image_memory == NULL) // found none { // remember the window handle (which must not be copied by copy()) #ifndef NO_DISPLAY #ifdef USE_GL long int old_glwin = master_image_source->get_current()->get_glwin(); #else #error Tugdual, please put the Qt equivalent or whatever here so that the window is preserved. Thanks. #endif #endif // ifndef NO_DISPLAY // copy current Image master_image_source->set_current (master_image_source->get_current()->copy()); // set the window handle to the original so that we use the same window #ifndef NO_DISPLAY #ifdef USE_GL master_image_source->get_current()->set_glwin(old_glwin); #else #error Tugdual, please put the Qt equivalent or whatever here so that the window is preserved. Thanks. #endif #endif // ifndef NO_DISPLAY } else { // copy all but the data to our new image memory unsigned char *saved_data_ptr = master_image_source->get_current()->get_data(); master_image_source->get_current()->virtual_copy(new_image_memory); new_image_memory->set_data(saved_data_ptr); // give it over to master_image_source master_image_source->set_current(new_image_memory); } } // // get a new image if available Image *new_image = master_image_source->get_next(); // // dispose of the old current Image if we had one if (old_current != NULL) { // do not de-allocate current but re-use its memory for speed reasons keep_image_memory_for_later(old_current); } if (new_image != NULL) { // place new image into image_buffer image_buffer[buffer_entries] = new_image; // (buffer_entries == 0) // buffer_entries++; // signal ``new image in buffer'' just in case someone waits for it pthread_cond_signal(&new_image_in_buffer); // cdebug << ", filled up from master. " << endl; } else { // unlock write access to image_buffer before returning with NULL pthread_mutex_unlock(&image_buffer_modification); // signal no more images (probably end of sequence) assert (buffer_entries == 0); // no-one should have changed it image_buffer[0] = NULL; cdebug << " and no more images from master. " << endl; current = NULL; return current; // return "no current image" } } // pthread_mutex_unlock(&image_buffer_modification); } else { // wait for a new image to arrive (done via handle_new_image(...)) pthread_mutex_lock(&image_buffer_modification); // avoid deadlocks // while (buffer_entries < 1) { pthread_cond_wait(&new_image_in_buffer, &image_buffer_modification); } // pthread_mutex_unlock(&image_buffer_modification); } } // At least one image is available in the image_buffer. // We always use the first Image pointer in the buffer for `current'. current = image_buffer[0]; frame_count++; return current; } /////////////////////////////////////////////////////////////////////////////// // // // BufferedSlaveImageSource::fill_buffer: fill up buffer from master // // // // Call this method to fill up the image_buffer from master_image_source. // // Up to number_of_images_to_add images will be read from the master // // using the ImageSource::get_next() call. The buffer will be filled // // up to buffer_size if possible. No more than that many images will be // // read from the master_image_source. // // // // Returns the number of images read from image_source and buffered. // // // /////////////////////////////////////////////////////////////////////////////// unsigned int BufferedSlaveImageSource::fill_buffer(unsigned int number_of_images_to_add) { unsigned int count; // to count how many images we are adding for (count = 0; (count < number_of_images_to_add); count++) { // check whether buffer is already full if (buffer_entries >= buffer_size) // no more room in buffer break; // 1 - get next image from image_source Image *new_image = master_image_source->get_next(); if (new_image == NULL) break; // just break for now. We will check for end of sequence elsewhere. // 2 - tell master not to use this memory any more, we take over master_image_source->set_current(get_new_image_memory()); // 3 - check image dimensions assert (new_image->get_width() == xdim); assert (new_image->get_height() == ydim); // 4 - add image to image_buffer // this requires exclusive write access to image_buffer pointers // (we are competing with get_next() which could be removing an old image from the buffer) pthread_mutex_lock(&image_buffer_modification); // image_buffer[buffer_entries] = new_image; buffer_entries++; // // signal ``new image in buffer'' to other thread in case it waits (in get_next()) pthread_cond_signal(&new_image_in_buffer); // pthread_mutex_unlock(&image_buffer_modification); } return count; } /////////////////////////////////////////////////////////////////////////////// // // // BufferedSlaveImageSource::keep_image_memory_for_later(Image *&old_image) // // // // Given an Image which is no longer used, the method tries to reserve the // // Image memory for later re-use to avoid slow memory allocation and de- // // allocation. The pointer is simply put into an unused slot in the // // image_buffer (beyond buffer_entries). Call get_new_image_memory() // // to access the memory which has been reserved by this method. // // // // This method assumes that it has exclusive write access to image_buffer // // // // The old_image pointer is set to NULL to avoid possible problems. // // // /////////////////////////////////////////////////////////////////////////////// void BufferedSlaveImageSource::keep_image_memory_for_later (Image *&old_image) { if (old_image == NULL) return; unsigned int buffer_index; // find a place to store the old image for (buffer_index = buffer_size-1; buffer_index >= buffer_entries; buffer_index--) { // check whether entry is empty an can hold our image if (image_buffer[buffer_index] == NULL) { // OK: use the place to store old_image image_buffer[buffer_index] = old_image; // // set old_image to NULL so that we will not access it from two locations old_image = NULL; } if (old_image == NULL) // we have stored it break; } if (old_image != NULL) // we have not found a place to store old_image { // make sure old_image's window is not closed #ifndef NO_DISPLAY #ifdef USE_GL old_image->set_glwin(NULLWIN); #else #error Tugdual, please put the Qt equivalent or whatever here so that the window is not closed by Image::~Image. Thanks. #endif #endif // ifndef NO_DISPLAY delete old_image; old_image = NULL; } } /////////////////////////////////////////////////////////////////////////////// // // // BufferedSlaveImageSource::get_new_image_memory() // // // // Get Image memory reserved earlier by keep_image_memory_for_later() // // // // This will scan the image_buffer for unused Image pointers. If one is // // found it will be returned and the image_buffer entry cleared (set to // // NULL). // // // // This method assumes that it has exclusive write access to image_buffer // // // // Returns the pointer to an unused Image or NULL if none is available. // // // /////////////////////////////////////////////////////////////////////////////// Image *BufferedSlaveImageSource::get_new_image_memory() { unsigned int buffer_index; Image *image = NULL; for (buffer_index = buffer_size - 1; buffer_index > buffer_entries; buffer_index--) { // check whether entry can be re-used if (image_buffer[buffer_index] != NULL) { // OK: use the Image* found image = image_buffer[buffer_index]; image_buffer[buffer_index] = NULL; } if (image != NULL) // we have found one break; } return image; } } // namespace ReadingPeopleTracker