www.pudn.com > reacTIVision-1.3.rar > portVideoSDL.cpp


/*  portVideo, a cross platform camera framework
    Copyright (C) 2006 Martin Kaltenbrunner 

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "portVideoSDL.h"

// the thread function which contantly retrieves the latest frame
int getFrameFromCamera(void *obj) {

		portVideoSDL *engine = (portVideoSDL *)obj;
		
		unsigned char *cameraBuffer = NULL;
		unsigned char *cameraWriteBuffer = NULL;
		
		while(engine->running_) {
			if(!engine->pause_) {
				cameraBuffer = engine->camera_->getFrame();
				if (cameraBuffer!=NULL) {
					cameraWriteBuffer = engine->ringBuffer->getNextBufferToWrite();
					if (cameraWriteBuffer!=NULL) {
						memcpy(cameraWriteBuffer,cameraBuffer,engine->ringBuffer->size());
						engine->framenumber_++;
						engine->ringBuffer->writeFinished();
					}
					SDL_Delay(1);
				} else {
					if (!engine->camera_->stillRunning()) {
						engine->running_=false;
						engine->error_=true;
					} else SDL_Delay(1);
				}
			} else SDL_Delay(5);
		}
		return(0);
}

void portVideoSDL::saveBuffer(unsigned char* buffer,int size) {
	char fileName[32];
	sprintf(fileName,"frame%ld_%dx%d.raw",framenumber_, width_, height_);
	FILE*  imagefile=fopen(fileName, "w");
	fwrite((const char *)buffer, 1,  size, imagefile);
	fclose(imagefile);
}


// the principal program sequence
void portVideoSDL::run() {

	if( !setupCamera() ) {
		if( !setupWindow() ) return;
		showError("No camera found!");
		teardownWindow();
		return;
	}

	if( !setupWindow() ) return;

	allocateBuffers();
	initFrameProcessors();

	bool success = camera_->startCamera();

	if( success ){

		// add the help message from all FrameProcessors
		for (frame = processorList.begin(); frame!=processorList.end(); frame++) {
                        std::list processor_text = (*frame)->getOptions();
			if (processor_text.size()>0) help_text.push_back("\n");
			for(std::list::iterator processor_line = processor_text.begin(); processor_line!=processor_text.end(); processor_line++) {
				help_text.push_back(*processor_line);
			} 
		}
		
		//print the help message
		for(std::list::iterator help_line = help_text.begin(); help_line!=help_text.end(); help_line++) {
			std::cout << *help_line << std::endl;
		} std::cout << std::endl;

		running_=true;
		cameraThread = SDL_CreateThread(getFrameFromCamera,this);
		mainLoop();
		
		SDL_KillThread(cameraThread);
		teardownCamera();
	} else {
		showError("Could not start camera!");
	}	

	teardownWindow();
	freeBuffers();
	
}

void portVideoSDL::showError(const char* error)
{
	SDL_FillRect(window_,0,0);
	SDL_Flip(window_);
	SDL_Rect *image_rect = new SDL_Rect();
	image_rect->x = (width_-camera_rect.w)/2-47;
	image_rect->y = (height_-camera_rect.h)/2-39;
	image_rect->w = camera_rect.w;
	image_rect->h = camera_rect.h;
	SDL_BlitSurface(getCamera(), &camera_rect, window_, image_rect);

	std::string error_message = "Press any key to exit "+app_name_+" ...";
	drawString((width_- SFont_TextWidth(sfont,error))/2,height_/2+60,error);
	drawString((width_- SFont_TextWidth(sfont,error_message.c_str()))/2,height_/2+80,error_message.c_str());
	SDL_Flip(window_);

	error_=true;
	while(error_) {
		process_events();
		//SDL_Delay(10);
	}

}

void portVideoSDL::drawString(int x, int y, const char *str)
{
	if(sfont) SFont_Write(window_, sfont, x,y,str);
}

void portVideoSDL::drawHelp()
{
	int y = font_height;

	for(std::list::iterator help_line = help_text.begin(); help_line!=help_text.end(); help_line++) {
		drawString(font_height,y,help_line->c_str());
		y+=font_height;
	}
}

void portVideoSDL::drawObjects()
{
	char *id = new char[4];
	for(std::list::iterator obj = tobjList.begin(); obj!=tobjList.end(); obj++) {
		if(obj->fiducial_id>-1) sprintf(id,"%d",obj->fiducial_id);
		else sprintf(id,"F");
		drawString(obj->x_pos-4,obj->y_pos-8,id);
	}
	tobjList.clear();
	delete[] id;
}

// does what its name suggests
void portVideoSDL::mainLoop()
{
	unsigned char *cameraReadBuffer = NULL;

	while(running_) {
		
		process_events();

		// do nothing if paused
		if (pause_){
			SDL_Delay(5);
			continue;
		}

		cameraReadBuffer = ringBuffer->getNextBufferToRead();
		// loop until we get access to a frame
		while (cameraReadBuffer==NULL) {
			SDL_Delay(1);
			cameraReadBuffer = ringBuffer->getNextBufferToRead();
			if(!running_) { endLoop(); return; }// escape on quit
		}
		/*
		memcpy(sourceBuffer_,cameraReadBuffer,ringBuffer->size());
		ringBuffer->readFinished();
		*/
		
		// try again if we can get a more recent frame
		/*do {
			memcpy(sourceBuffer_,cameraReadBuffer,ringBuffer->size());
			ringBuffer->readFinished();
			
			cameraReadBuffer = ringBuffer->getNextBufferToRead();
		} while( cameraReadBuffer != NULL );*/
		
		// do the actual image processing job
		for (frame = processorList.begin(); frame!=processorList.end(); frame++)
			(*frame)->process(cameraReadBuffer,destBuffer_);
		
		// update display
		switch( displayMode_ ) {
			case NO_DISPLAY:
				break;
			case SOURCE_DISPLAY: {
				memcpy(sourceBuffer_,cameraReadBuffer,ringBuffer->size());
				SDL_BlitSurface(sourceImage_, NULL, window_, NULL);
				if (help_) drawHelp();
				if (verbose_) drawObjects();
				SDL_Flip(window_);
				break;
			}			
			case DEST_DISPLAY: {
				SDL_BlitSurface(destImage_, NULL, window_, NULL);
				if (help_) drawHelp();
				if (verbose_) drawObjects();
				SDL_Flip(window_);
				break;
			}
		}
		
		ringBuffer->readFinished();
		fpsCount();
	}
	
	endLoop(); 
}

void portVideoSDL::endLoop() {
	// finish all FrameProcessors
	for (frame = processorList.begin(); frame!=processorList.end(); frame++)
		(*frame)->finish();

	if(error_) showError("Camera disconnected!");
}

void portVideoSDL::fpsCount() {

	frames_++;

	time_t currentTime;
	time(¤tTime);
	long diffTime = (long)( currentTime - lastTime_ );
	
	if (diffTime >= 2) {
		current_fps = (int)( frames_ / diffTime );
		char caption[24] = "";
		sprintf(caption,"%s - %d FPS",app_name_.c_str(),current_fps);
		if ((fpson_) && (!calibrate_)) SDL_WM_SetCaption(caption, NULL );
		frames_ = 0;
		
		lastTime_ = (long)currentTime;
    }
}

bool portVideoSDL::setupWindow() {

	if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
		printf("SDL could not be initialized: %s\n", SDL_GetError());
		return false;
	}
	
	window_ = SDL_SetVideoMode(width_, height_, 32, SDL_HWSURFACE);
	if ( window_ == NULL ) {
		printf("Could not open window: %s\n", SDL_GetError());
		SDL_Quit();
		return false;
	}

	#ifndef __APPLE__
	SDL_WM_SetIcon(getIcon(), getMask());
	#endif

	sfont = SFont_InitDefaultFont();
	font_height = SFont_TextHeight(sfont);
	SDL_EnableKeyRepeat(200, 10);
	SDL_WM_SetCaption(app_name_.c_str(), NULL);

	return true;
}

void portVideoSDL::teardownWindow()
{
	SFont_FreeFont(sfont);
	SDL_Quit();
}

void portVideoSDL::process_events()
{
    SDL_Event event;
    while( SDL_PollEvent( &event ) ) {

        switch( event.type ) {
		case SDL_KEYDOWN:
			error_ = false;
			//printf("%d\n",event.key.keysym.sym);
			if( event.key.keysym.sym == SDLK_n ){
				displayMode_ = NO_DISPLAY;
				// turn the display black
				SDL_FillRect(window_,0,0);
				SDL_Flip(window_);
			} else if( event.key.keysym.sym == SDLK_s ){
				displayMode_ = SOURCE_DISPLAY;
			} else if( event.key.keysym.sym == SDLK_t ){
				displayMode_ = DEST_DISPLAY;
			} else if( event.key.keysym.sym == SDLK_o ){
				camera_->showSettingsDialog(); 
			} else if( event.key.keysym.sym == SDLK_v ){
				if (verbose_) {
					verbose_=false;
				} else {
					help_ = false;
					verbose_=true;
				}
			} else if( event.key.keysym.sym == SDLK_f ){
				if (fpson_) {
					fpson_=false;
					SDL_WM_SetCaption(app_name_.c_str(), NULL );
				}
				else {
					// reset the fps counter
					fpson_=true;
					char caption[24] = "";
					sprintf(caption,"%s - %d FPS",app_name_.c_str(),current_fps);
					SDL_WM_SetCaption( caption, NULL );
				} 
			} else if( event.key.keysym.sym == SDLK_p ) {
				if (pause_) {
					pause_=false;
					char caption[24] = "";
					if (fpson_) sprintf(caption,"%s - %d FPS",app_name_.c_str(),current_fps);
					else sprintf(caption,"%s",app_name_.c_str());
					SDL_WM_SetCaption( caption, NULL );
					
				} else {
					pause_=true;
					std::string caption = app_name_ + " - paused";
					SDL_WM_SetCaption( caption.c_str(), NULL );
					// turn the display black
					SDL_FillRect(window_,0,0);
					SDL_Flip(window_);
				}				
			} else if( event.key.keysym.sym == SDLK_c ) {
				if (calibrate_) {
					calibrate_=false;
					char caption[24] = "";
					if (fpson_) sprintf(caption,"%s - %d FPS",app_name_.c_str(),current_fps);
					else sprintf(caption,"%s",app_name_.c_str());
					SDL_WM_SetCaption( caption, NULL );
					
				} else {
					calibrate_=true;
					std::string caption = app_name_ + " - calibration";
					SDL_WM_SetCaption( caption.c_str(), NULL );
				}				
			} else if( event.key.keysym.sym == SDLK_b ){
				char fileName[32];
				sprintf(fileName,"frame%li.bmp",framenumber_);
				SDL_SaveBMP(sourceImage_, fileName);
			} else if( event.key.keysym.sym == SDLK_r ){
				saveBuffer(sourceBuffer_, width_ * height_ * bytesPerSourcePixel_);
			} else if( event.key.keysym.sym == SDLK_h ){
				help_=!help_;
				verbose_=false;
			} else if( event.key.keysym.sym == SDLK_ESCAPE ){
				running_=false;
			}

			for (frame = processorList.begin(); frame!=processorList.end(); frame++)
				(*frame)->toggleFlag(event.key.keysym.sym);

			break;
		case SDL_QUIT:
			running_ = false;
			error_ = false;
			break;
        }
    }
}

bool portVideoSDL::setupCamera() {

	camera_ = cameraTool::findCamera();	
	if (camera_ == NULL) return false;
	
	bool colour = false;
	if (sourceDepth_==24) colour = true;
	bool success = camera_->initCamera(width_, height_, colour);
	
	if(success) {
		width_ = camera_->getWidth();
		height_ = camera_->getHeight();
		fps_ = camera_->getFps();
					
		printf("camera: %s\n",camera_->getName());
		printf("format: %dx%d, %dfps\n\n",width_,height_,fps_);
		return true;
	} else {
		printf("could not initialize camera\n");
		camera_->closeCamera();
		delete camera_;
		return false;
	}
}

void portVideoSDL::teardownCamera()
{
	camera_->stopCamera();
	camera_->closeCamera();
	delete camera_;
}

void portVideoSDL::allocateBuffers()
{
	bytesPerSourcePixel_ = sourceDepth_/8;	
	bytesPerDestPixel_ = destDepth_/8;
	sourceBuffer_ = new unsigned char[width_*height_*bytesPerSourcePixel_];
	destBuffer_ = new unsigned char[width_*height_*bytesPerDestPixel_];
	cameraBuffer_ = NULL;
		
	sourceImage_ = SDL_CreateRGBSurfaceFrom(sourceBuffer_, width_, height_, sourceDepth_, width_*bytesPerSourcePixel_, 0 , 0, 0, 0);
	if (sourceDepth_==8)
		SDL_SetPalette(sourceImage_, SDL_LOGPAL|SDL_PHYSPAL, palette_, 0, 256 );

	destImage_ = SDL_CreateRGBSurfaceFrom(destBuffer_, width_, height_, destDepth_, width_*bytesPerDestPixel_, 0 , 0, 0, 0);
	if (destDepth_==8)
		SDL_SetPalette(destImage_, SDL_LOGPAL|SDL_PHYSPAL, palette_, 0, 256 );
		
	SDL_DisplayFormat(sourceImage_);
	SDL_DisplayFormat(destImage_);
	
	ringBuffer = new RingBuffer(width_*height_*bytesPerSourcePixel_);
}

void portVideoSDL::freeBuffers()
{
	SDL_FreeSurface(sourceImage_);
	SDL_FreeSurface(destImage_);
	delete [] sourceBuffer_;
	delete [] destBuffer_;
	
	delete ringBuffer;
}

void portVideoSDL::addFrameProcessor(FrameProcessor *fp) {

	processorList.push_back(fp);
	fp->addMessageListener(this);
}


void portVideoSDL::removeFrameProcessor(FrameProcessor *fp) {
	frame = std::find( processorList.begin(), processorList.end(), fp );
	if( frame != processorList.end() ) {
		processorList.erase( frame );
		fp->removeMessageListener(this);
	}
}

void portVideoSDL::setObject(int fiducial_id, int x_pos, int y_pos, float angle)
{
	if ((!verbose_) || (displayMode_ == NO_DISPLAY)) return;

	tobj nobj;
	nobj.fiducial_id = fiducial_id;
	nobj.x_pos  = x_pos;
	nobj.y_pos  = y_pos;
	nobj.angle  = angle;
	tobjList.push_back(nobj);
}

void portVideoSDL::setMessage(std::string message)
{
	if (verbose_) {
		std::cout << message << std::endl;
		std::cout.flush();
	}
}

void portVideoSDL::setDisplayMode(DisplayMode mode) {
	displayMode_ = mode;
}

void portVideoSDL::initFrameProcessors() {
	for (frame = processorList.begin(); frame!=processorList.end(); ) {
		bool success = (*frame)->init(width_ , height_, bytesPerSourcePixel_, bytesPerDestPixel_);
		if(!success) {	
			processorList.erase( frame );
			printf("removed frame processor\n");
		} else frame++;
	}
}

unsigned int portVideoSDL::current_fps = 0;

portVideoSDL::portVideoSDL(char* name, bool srcColour, bool destColour)
	: error_( false )
	, pause_( false )
	, calibrate_( false )
	, help_( false )
	, framenumber_( 0 )
	, frames_( 0 )
	, fpson_( true )
	, width_( WIDTH )
	, height_( HEIGHT )
	, displayMode_( DEST_DISPLAY )
{
	time_t start_time;
	time(&start_time);
	lastTime_ = (long)start_time;

	app_name_ = std::string(name);
	sourceDepth_ = (srcColour?24:8);
	destDepth_   = (destColour?24:8);

	sourceBuffer_ = NULL;
	destBuffer_ = NULL;

	for(int i=0;i<256;i++){
		palette_[i].r=i;
		palette_[i].g=i;
		palette_[i].b=i;
	}
	
	help_text.push_back("display:");
 	help_text.push_back("   n - no image");
	help_text.push_back("   s - source image");
	help_text.push_back("   t - target image");	
	help_text.push_back("   f - fps in title");
	help_text.push_back("   v - verbose output");
	help_text.push_back("   h - this help text");
	help_text.push_back("");
	help_text.push_back("commands:");
	help_text.push_back("   b - save BMP frame");
	help_text.push_back("   r - save RAW frame");
	help_text.push_back("   o - camera options");
	help_text.push_back("   p - pause processing");
	help_text.push_back("   ESC - quit " + app_name_);
}