00001 /*!@file AppPsycho/psycho-movie2.C Psychophysics interactive display of movies */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2001 by the // 00005 // University of Southern California (USC) and the iLab at USC. // 00006 // See http://iLab.usc.edu for information about this project. // 00007 // //////////////////////////////////////////////////////////////////// // 00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00010 // in Visual Environments, and Applications'' by Christof Koch and // 00011 // Laurent Itti, California Institute of Technology, 2001 (patent // 00012 // pending; application number 09/912,225 filed July 23, 2001; see // 00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00014 // //////////////////////////////////////////////////////////////////// // 00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00016 // // 00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00018 // redistribute it and/or modify it under the terms of the GNU General // 00019 // Public License as published by the Free Software Foundation; either // 00020 // version 2 of the License, or (at your option) any later version. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00025 // PURPOSE. See the GNU General Public License for more details. // 00026 // // 00027 // You should have received a copy of the GNU General Public License // 00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00030 // Boston, MA 02111-1307 USA. // 00031 // //////////////////////////////////////////////////////////////////// // 00032 // 00033 // Primary maintainer for this file: Laurent Itti <itti@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/AppPsycho/psycho-movie3test.C $ 00035 // $Id: psycho-movie3test.C 10794 2009-02-08 06:21:09Z itti $ 00036 // 00037 00038 #include "Component/ModelManager.H" 00039 #include "Component/ModelOptionDef.H" 00040 #include "Audio/AudioWavFile.H" 00041 #include "Devices/AudioGrabber.H" 00042 #include "Devices/AudioMixer.H" 00043 #include "Devices/DeviceOpts.H" 00044 #include "Image/Image.H" 00045 #include "Media/MPEGStream.H" 00046 #include "Media/MediaOpts.H" 00047 #include "Psycho/PsychoDisplay.H" 00048 #include "Psycho/EyeTrackerConfigurator.H" 00049 #include "Psycho/EyeTracker.H" 00050 #include "Psycho/PsychoOpts.H" 00051 #include "Component/EventLog.H" 00052 #include "Component/ComponentOpts.H" 00053 #include "GUI/SDLdisplay.H" 00054 #include "Util/MathFunctions.H" 00055 #include "Util/Types.H" 00056 #include "Video/VideoFrame.H" 00057 #include "Neuro/NeuroOpts.H" 00058 00059 #include <vector> 00060 #include <pthread.h> 00061 00062 #define CACHELEN 150 00063 00064 volatile bool recordaudio = false; 00065 volatile bool keepgoing = true; 00066 volatile int recnb = 0; 00067 00068 static const ModelOptionDef OPT_AudioAfter = 00069 { MODOPT_FLAG, "AudioAfter", &MOC_DISPLAY, OPTEXP_SAVE, 00070 "Record audio after the movie presentation", 00071 "audio-after", '\0', "", "false" }; 00072 00073 // ###################################################################### 00074 static void *audiorecorder(void *agbv) 00075 { 00076 bool recording = false; 00077 std::vector<AudioBuffer<byte> > rec; 00078 AudioGrabber *agb = (AudioGrabber *)agbv; 00079 00080 while(keepgoing) 00081 { 00082 // start recording? 00083 if (recording == false && recordaudio == true) 00084 { rec.clear(); recording = true; } 00085 00086 // stop recording? 00087 if (recording == true && recordaudio == false) 00088 { 00089 // save our current records 00090 char fname[100]; sprintf(fname, "audio%04d.wav", recnb); 00091 writeAudioWavFile(fname, rec); 00092 00093 // ready for next recording: 00094 recnb ++; recording = false; 00095 } 00096 00097 // we grab the audio data all the time, so that it does not 00098 // start piling up into the internal buffers of the audio 00099 // grabber; but we will save it only if we are in recording 00100 // mode. Note: the call to grab() is blocking and hence sets 00101 // the pace of our loop here. With 256 samples at 11.025kHz 00102 // that's about 23ms: 00103 AudioBuffer<byte> data; 00104 agb->grab(data); 00105 if (data.nsamples() != 256U) 00106 LERROR("Recorded len %u is not 256!", data.nsamples()); 00107 00108 // continue recording? 00109 if (recording == true && recordaudio == true && data.nsamples() == 256U) 00110 // store in our queue of records: 00111 rec.push_back(data); 00112 } 00113 00114 // terminate thread: 00115 pthread_exit(0); 00116 return NULL; 00117 } 00118 00119 // // ###################################################################### 00120 // static bool cacheFrame(nub::soft_ref<InputMPEGStream>& mp, 00121 // std::deque<VideoFrame>& cache) 00122 // { 00123 // const VideoFrame frame = mp->readVideoFrame(); 00124 // if (!frame.initialized()) return false; // end of stream 00125 00126 // cache.push_front(frame); 00127 // return true; 00128 // } 00129 00130 // ###################################################################### 00131 static int submain(const int argc, char** argv) 00132 { 00133 MYLOGVERB = LOG_INFO; // suppress debug messages 00134 00135 // Instantiate a ModelManager: 00136 ModelManager manager("Psycho Movie"); 00137 00138 OModelParam<bool> audioAfter(&OPT_AudioAfter, &manager); 00139 00140 // Instantiate our various ModelComponents: 00141 // nub::soft_ref<InputMPEGStream> mp 00142 // (new InputMPEGStream(manager, "Input MPEG Stream", "InputMPEGStream")); 00143 // manager.addSubComponent(mp); 00144 00145 // nub::soft_ref<PsychoDisplay> d(new PsychoDisplay(manager)); 00146 // manager.addSubComponent(d); 00147 00148 nub::soft_ref<AudioMixer> mix(new AudioMixer(manager)); 00149 manager.addSubComponent(mix); 00150 00151 nub::soft_ref<AudioGrabber> agb(new AudioGrabber(manager)); 00152 manager.addSubComponent(agb); 00153 00154 // nub::soft_ref<EyeTrackerConfigurator> 00155 // etc(new EyeTrackerConfigurator(manager)); 00156 // manager.addSubComponent(etc); 00157 00158 nub::soft_ref<EventLog> el(new EventLog(manager)); 00159 manager.addSubComponent(el); 00160 00161 // set a few defaults: 00162 // manager.setOptionValString(&OPT_InputMPEGStreamPreload, "true"); 00163 // manager.setOptionValString(&OPT_EventLogFileName, "psychodata.psy"); 00164 // manager.setOptionValString(&OPT_EyeTrackerType, "ISCAN"); 00165 manager.setOptionValString(&OPT_AudioMixerLineIn, "false"); 00166 manager.setOptionValString(&OPT_AudioMixerCdIn, "false"); 00167 manager.setOptionValString(&OPT_AudioMixerMicIn, "true"); 00168 manager.setOptionValString(&OPT_AudioGrabberBits, "8"); 00169 manager.setOptionValString(&OPT_AudioGrabberFreq, "11025"); 00170 manager.setOptionValString(&OPT_AudioGrabberBufSamples, "256"); 00171 manager.setOptionValString(&OPT_AudioGrabberStereo, "false"); 00172 00173 // Parse command-line: 00174 // if (manager.parseCommandLine(argc, argv, 00175 // "<movie1.mpg> ... <movieN.mpg>", 1, -1)==false) 00176 // return(1); 00177 00178 // if(manager.numExtraArgs() > 0) 00179 // { 00180 // infilename = manager.getExtraArgAs<std::string>(0); 00181 // } 00182 00183 // hook our various babies up and do post-command-line configs: 00184 // nub::soft_ref<EyeTracker> et = etc->getET(); 00185 // d->setEyeTracker(et); 00186 // d->setEventLog(el); 00187 // et->setEventLog(el); 00188 00189 // let's get all our ModelComponent instances started: 00190 manager.start(); 00191 00192 // let's display a static low-level ISCAN calibration grid: 00193 // d->clearScreen(); 00194 // d->displayISCANcalib(); 00195 // d->waitForKey(); 00196 00197 // // setup array of movie indices: 00198 // uint nbmovies = manager.numExtraArgs(); int index[nbmovies]; 00199 // for (uint i = 0; i < nbmovies; i ++) index[i] = i; 00200 // LINFO("Randomizing movies..."); randShuffle(index,nbmovies); 00201 00202 // get the audio going: 00203 pthread_t runner; 00204 pthread_create(&runner, NULL, &audiorecorder, (void *)(agb.get())); 00205 char txt[100]; 00206 00207 // main loop: 00208 std::deque<VideoFrame> cache; 00209 00210 int movieCount = 1; 00211 00212 for (uint i = 0; i < nbmovies; i ++) 00213 { 00214 // // let's do an eye-tracker calibration once in a while: 00215 // if (i % 10 == 0) 00216 // { 00217 // d->displayText("<SPACE> for eye-tracker calibration"); 00218 // d->waitForKey(); 00219 // // d->displayEyeTrackerCalibration(3, 3); 00220 // d->clearScreen(); 00221 // if (i == 0) d->displayText("<SPACE> to start with the movies"); 00222 // else d->displayText("<SPACE> to continue with the movies"); 00223 // d->waitForKey(); 00224 // } 00225 00226 // // cache initial movie frames: 00227 // d->clearScreen(); 00228 // if (cache.size()) LFATAL("ooops, cache not empty?"); 00229 // bool streaming = true; 00230 // LINFO("Buffering '%s'...", manager.getExtraArg(index[i]).c_str()); 00231 // // NOTE: we now specify --preload-mpeg=true with a 00232 // // setOptionValString() a few lines up from here 00233 // mp->setFileName(manager.getExtraArg(index[i])); 00234 // for (uint j = 0; j < CACHELEN; j ++) 00235 // { 00236 // streaming = cacheFrame(mp, cache); 00237 // if (streaming == false) break; // all movie frames got cached! 00238 // } 00239 // LINFO("'%s' ready.", manager.getExtraArg(index[i]).c_str()); 00240 00241 // // give a chance to other processes (useful on single-CPU machines): 00242 // sleep(1); system("sync"); 00243 00244 // // display fixation to indicate that we are ready: 00245 // d->displayFixation(); 00246 00247 // ready to go whenever the user is ready: 00248 // d->waitForKey(); int frame = 0; 00249 // d->waitNextRequestedVsync(false, true); 00250 // d->pushEvent(std::string("===== Playing movie: ") + 00251 // manager.getExtraArg(index[i]) + " ====="); 00252 00253 // // start the eye tracker: 00254 // et->track(true); 00255 00256 // // blink the fixation: 00257 // d->displayFixationBlink(); 00258 00259 // // create an overlay: 00260 // d->createVideoOverlay(VIDFMT_YUV420P); // mpeg stream returns YUV420P 00261 00262 // start audio recording, unless we were flagged to record later: 00263 if (audioAfter.getVal() == false) 00264 { 00265 sprintf(txt, "Start audio recording: audio%04d.wav", recnb); 00266 d->pushEvent(txt); 00267 recordaudio = true; 00268 } 00269 00270 // play the movie: 00271 // while(cache.size()) 00272 // { 00273 // // let's first cache one more frame: 00274 // if (streaming) streaming = cacheFrame(mp, cache); 00275 00276 // // get next frame to display and put it into our overlay: 00277 // VideoFrame vidframe = cache.back(); 00278 // d->displayVideoOverlay(vidframe, frame, 00279 // SDLdisplay::NEXT_VSYNC); 00280 // cache.pop_back(); 00281 00282 // ++frame; 00283 // } 00284 00285 // destroy the overlay. Somehow, mixing overlay displays and 00286 // normal displays does not work. With a single overlay created 00287 // before this loop and never destroyed, the first movie plays 00288 // ok but the other ones don't show up: 00289 // d->destroyYUVoverlay(); 00290 // d->clearScreen(); // sometimes 2 clearScreen() are necessary 00291 00292 00293 00294 // // wait for a while to allow people to finish their current sentence: 00295 // sleep(3); 00296 00297 if(movieCount % 5 == 0) 00298 { 00299 // stop the eye tracker: 00300 et->track(false); 00301 d->displayText("Please describe what you saw in the last five videos."); 00302 sleep(5); 00303 00304 // do we want to record some audio after the movie is done? 00305 if (audioAfter.getVal()) 00306 { 00307 sprintf(txt, "Start audio recording after 5 movies: audio%04d.wav", recnb); 00308 d->pushEvent(txt); 00309 recordaudio = true; 00310 d->clearScreen(); 00311 d->displayFixation(); 00312 00313 usleep(25000000); // 25 secs of recording 00314 00315 // stop audio recording: 00316 sprintf(txt, "Stop audio recording after 5 movies: audio%04d.wav", recnb); 00317 d->pushEvent(txt); 00318 recordaudio = false; 00319 00320 d->clearScreen(); 00321 } 00322 } 00323 00324 movieCount++; 00325 00326 00327 // // stop the eye tracker: 00328 // et->track(false); 00329 00330 // stop audio recording: 00331 sprintf(txt, "Stop audio recording: audio%04d.wav", recnb); 00332 d->pushEvent(txt); 00333 recordaudio = false; 00334 } 00335 00336 // stop audio recording: 00337 sprintf(txt, "Stop audio recording: audio%04d.wav", recnb); 00338 d->pushEvent(txt); 00339 recordaudio = false; 00340 00341 d->clearScreen(); 00342 d->displayText("Experiment complete. Thank you!"); 00343 d->waitForKey(); 00344 00345 // kill the audio recording thread: 00346 keepgoing = false; 00347 usleep(500000); 00348 00349 // stop all our ModelComponents 00350 manager.stop(); 00351 00352 // all done! 00353 return 0; 00354 } 00355 00356 // ###################################################################### 00357 extern "C" int main(const int argc, char** argv) 00358 { 00359 // simple wrapper around submain() to catch exceptions (because we 00360 // want to allow PsychoDisplay to shut down cleanly; otherwise if we 00361 // abort while SDL is in fullscreen mode, the X server won't return 00362 // to its original resolution) 00363 try 00364 { 00365 return submain(argc, argv); 00366 } 00367 catch (...) 00368 { 00369 REPORT_CURRENT_EXCEPTION; 00370 } 00371 00372 return 1; 00373 } 00374 00375 // ###################################################################### 00376 /* So things look consistent in everyone's emacs... */ 00377 /* Local Variables: */ 00378 /* indent-tabs-mode: nil */ 00379 /* End: */