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