00001 /*!@file Psycho/MPlayerWrapper.C Wrapper class for playing videos in MPlayer */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005 // 00005 // by the 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: John Shen <shenjohn at usc dot edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Psycho/MPlayerWrapper.C $ 00035 // $Id: MPlayerWrapper.C 13712 2010-07-28 21:00:40Z itti $ 00036 // 00037 00038 #ifndef PSYCHO_MPLAYERWRAPPER_C_DEFINED 00039 #define PSYCHO_MPLAYERWRAPPER_C_DEFINED 00040 00041 #include "Psycho/MPlayerWrapper.H" 00042 #include "Component/ComponentOpts.H" 00043 #include "Component/ModelManager.H" 00044 #include "Util/Pause.H" 00045 #include "Util/csignals.H" 00046 #include "Util/Types.H" 00047 #include "GUI/GUIOpts.H" 00048 #include "Util/StringConversions.H" 00049 #include "Util/StringUtil.H" 00050 #include "rutz/pipe.h" 00051 #include "nub/ref.h" 00052 #include "Component/ModelOptionDef.H" 00053 #include <stdlib.h> 00054 #include "Psycho/PsychoDisplay.H" 00055 #include <iostream> 00056 #include <sstream> 00057 #include <string> 00058 #include <stdio.h> 00059 #include <vector> 00060 #include <unistd.h> 00061 00062 const ModelOptionCateg MOC_MPLAYER = { 00063 MOC_SORTPRI_2, "MPlayer-Related Options" }; 00064 00065 const ModelOptionDef OPT_MPlayerPath = 00066 { MODOPT_ARG_STRING, "MPlayerPath", &MOC_MPLAYER, OPTEXP_CORE, 00067 "Path for the mplayer application.", 00068 "mplayer-path", '\0', "<std::string>", "/usr/bin/mplayer" }; 00069 00070 const ModelOptionDef OPT_MPlayerSound = 00071 { MODOPT_FLAG, "MPlayerSound", &MOC_MPLAYER, OPTEXP_CORE, 00072 "Play sound.", "sound", '\0', "", "true" }; 00073 00074 const ModelOptionDef OPT_MPlayerAudioDriver = 00075 { MODOPT_ARG_STRING, "MPlayerAudioDriver", &MOC_MPLAYER, OPTEXP_CORE, 00076 "Audio driver to send to mplayer. If sound does not work try alsa/oss. Other valid options are esd/pulse/jack/nas/sdl/mpegpes/v4l2/pcm. Setting to null is equivalent to --nosound.", "ao", '\0', "<std::string>", "alsa" }; 00077 00078 const ModelOptionDef OPT_MPlayerCacheSize = 00079 { MODOPT_ARG(uint), "MPlayerCacheSize", &MOC_MPLAYER, OPTEXP_CORE, 00080 "Amount of memory for precaching the video, in kB.", 00081 "cachesize", '\0', "<uint>", "16384"}; 00082 00083 const ModelOptionDef OPT_MPlayerCachePercent = 00084 { MODOPT_ARG(double), "MPlayerCachePercent", &MOC_MPLAYER, OPTEXP_CORE, 00085 "Playback begins when the cache is filled to this percent of the total.", 00086 "cachepercent", '\0', "<0-99>", "50" }; 00087 00088 00089 MPlayerWrapper::MPlayerWrapper(OptionManager &mgr, 00090 const std::string& descrName, 00091 const std::string& tagName) : 00092 ModelComponent(mgr, descrName, tagName), 00093 itsEventLog(), 00094 itsPlayingVideo(false), 00095 itsIsFullScreen(&OPT_SDLdisplayFullscreen, this), 00096 itsMPlayerPath(&OPT_MPlayerPath, this), 00097 itsIsSound(&OPT_MPlayerSound, this), 00098 itsAudioDriver(&OPT_MPlayerAudioDriver, this), 00099 itsCacheSize(&OPT_MPlayerCacheSize, this), 00100 itsCachePercent(&OPT_MPlayerCachePercent, this) 00101 { 00102 } 00103 00104 // ###################################################################### 00105 MPlayerWrapper::~MPlayerWrapper() 00106 {} 00107 00108 // ###################################################################### 00109 void MPlayerWrapper::setSourceVideo(std::string fname) 00110 { 00111 itsFileName = fname; 00112 } 00113 00114 // ###################################################################### 00115 void MPlayerWrapper::pushEvent(const std::string& msg, const bool& useLinfo) 00116 { 00117 if (useLinfo) 00118 LINFO("%s", msg.c_str()); 00119 if (itsEventLog.isValid()) 00120 itsEventLog->pushEvent(msg); 00121 } 00122 00123 // ###################################################################### 00124 00125 //display a movie in 00126 int MPlayerWrapper::run(const int argc, const char** argv, 00127 const char* extraArgsDescription, 00128 const int minExtraArgs, const int maxExtraArgs) 00129 { 00130 try 00131 { 00132 return this->tryRun(argc, argv, extraArgsDescription, 00133 minExtraArgs, maxExtraArgs); 00134 } 00135 catch (...) 00136 { 00137 REPORT_CURRENT_EXCEPTION; 00138 } 00139 00140 return 1; 00141 } 00142 00143 00144 // ###################################################################### 00145 00146 //display a movie in mplayer - meant to call through run() 00147 int MPlayerWrapper::tryRun(const int argc, const char** argv, 00148 const char* extraArgsDescription, 00149 const int minExtraArgs, const int maxExtraArgs) 00150 { 00151 volatile int signum = 0; 00152 catchsignals(&signum); 00153 00154 try 00155 { 00156 //playing in mplayer 00157 pushEvent(std::string("===== Playing movie: ") + 00158 itsFileName + " ====="); 00159 00160 std::string mp_out; 00161 rutz::exec_pipe mphandle("r", itsMPlayerPath.getVal().c_str(), "-fs", itsFileName.c_str(), (const char*) 0); 00162 00163 bool movie_begun = false; 00164 while(std::getline(mphandle.stream(), mp_out, '\r')) //Read the output from mplayer word by word 00165 { 00166 //when the video starts, the status line reads as 00167 //A: # V: # A-V: # ct: # #/ # ??% ??% ??,?% 0 0 00168 00169 movie_begun |= (mp_out.find("A:")!=std::string::npos); 00170 00171 //send messages through parser 00172 if(movie_begun) 00173 parseStatusLine(mp_out); 00174 } 00175 } 00176 catch (...) 00177 { 00178 fprintf(stderr,"Mplayer initializer failed.\n"); 00179 } 00180 //itsManager->stop(); 00181 00182 return 0; 00183 } 00184 00185 // ###################################################################### 00186 00187 //run a movie while closing an SDL display 00188 //the movie should display seamlessly after the SDL stops 00189 //in reality, mplayer will rush the first few frames regardless. 00190 //also, the timing is inconsistent 00191 00192 int MPlayerWrapper::runfromSDL(nub::soft_ref<PsychoDisplay> D) 00193 { 00194 try 00195 { 00196 //play movie in mplayer 00197 //create a pipe which sends mplayer output to a stream 00198 //NB: mplayer process inherits SDLpriority 00199 nub::ref<rutz::exec_pipe> mphandle = createMPlayerPipe(); 00200 std::string mp_out; 00201 00202 ModelComponent * displayManager(D->getParent()); //for some reason a nub_ref crashes the program 00203 00204 bool status_displayed = false, movie_begun = false, display_closed=false; 00205 00206 //this is the last line before screen displays, e.g. 00207 //VO: [vdpau] 1280x720 => 1920x1080 Planar YV12 [fs] 00208 const std::string firstline = "VO: ["; 00209 while(std::getline(mphandle->stream(), mp_out, '\r')) 00210 //Read the output from mplayer line by line 00211 { 00212 status_displayed = (mp_out.find("A:")!=std::string::npos); 00213 movie_begun |= (mp_out.find(firstline)!=std::string::npos); 00214 00215 //if the status line begins stop the SDL display 00216 if(!display_closed && movie_begun) 00217 { 00218 //there may be a rescaling: need to wait before closing SDL display 00219 pushEvent("===== Movie starting ====="); 00220 //const uint delay = 250000; //in microseconds 00221 //usleep(delay); 00222 00223 //remove D from its manager or close it independently 00224 while(displayManager->hasSubComponent(D) && displayManager != NULL) 00225 { 00226 LINFO("removing subcomponent from %s", D->getParent()->tagName().c_str()); 00227 displayManager->removeSubComponent(*D); 00228 displayManager = D->getParent(); 00229 } 00230 pushEvent("===== Closing PsychoDisplay: " + 00231 D->descriptiveName() + " ====="); 00232 D->stop(); 00233 display_closed = true; 00234 } 00235 00236 //loop that runs while movie plays 00237 //send messages through parser 00238 if(status_displayed && movie_begun) 00239 { 00240 //itsEventLog->pushEvent(mp_out.c_str()); 00241 parseStatusLine(mp_out); 00242 itsPlayingVideo = true; 00243 } 00244 } 00245 //after video restart the PsychoDisplay 00246 //displayManager->addSubComponent(D); 00247 itsPlayingVideo = false; 00248 D->start(); 00249 D->clearScreen(); 00250 } 00251 catch (...) 00252 { 00253 LFATAL("MPlayer initializer failed."); 00254 } 00255 00256 return 0; 00257 } 00258 00259 nub::ref<rutz::exec_pipe> MPlayerWrapper::createMPlayerPipe() 00260 { 00261 const uint maxargs = 20; 00262 char** arglist = new(char* [maxargs]); 00263 for (uint i = 0; i < maxargs; i++) 00264 arglist[i] = new char(50); 00265 00266 int numargs = 0; 00267 //explicit casts b/c the pipe only takes char* 00268 00269 arglist[numargs++] = (char*) (itsMPlayerPath.getVal()).c_str(); 00270 arglist[numargs++] = (char*) itsFileName.c_str(); 00271 00272 //these arguments allow for full screening 00273 if(itsIsFullScreen.getVal()) 00274 { 00275 arglist[numargs++] = (char*) "-fs"; 00276 arglist[numargs++] = (char*) "-xineramascreen"; 00277 arglist[numargs++] = (char*) "0"; 00278 } 00279 00280 if(itsIsSound.getVal()) // sets correct audio output driver 00281 { 00282 arglist[numargs++] = (char*) "-ao"; 00283 arglist[numargs++] = (char*) (itsAudioDriver.getVal()).c_str(); 00284 } 00285 else //no sound 00286 { 00287 arglist[numargs++] = (char*) "-nosound"; 00288 } 00289 00290 //this argument *SHOULD* get rid of tearing artifacts 00291 //arglist[numargs++] = (char*) "-vsync"; 00292 00293 //trying to get better initial A-V sync 00294 if(0) 00295 { 00296 arglist[numargs++] = (char*) "-autosync"; 00297 arglist[numargs++] = (char*) "30"; 00298 } 00299 if(0) //allowing dropped frames 00300 { 00301 //this allows framedropping for slower displays 00302 arglist[numargs++] = (char*) "-framedrop"; 00303 } 00304 00305 //&OPT_InputMPEGStreamPreload is the OModelParam<bool> for this 00306 if(itsCacheSize.getVal() > 0) //caching 00307 { 00308 //this sets the cache size 00309 arglist[numargs++] = (char*) "-cache"; 00310 std::string cacheStr = toStr<int>(itsCacheSize.getVal()); 00311 arglist[numargs++] = (char*) cacheStr.c_str(); 00312 00313 //this precaches a certain % of the video 00314 arglist[numargs++] = (char*) "-cache-min"; 00315 std::string percentStr = toStr<double>(itsCachePercent.getVal()); 00316 arglist[numargs++] = (char*) percentStr.c_str(); 00317 00318 } 00319 00320 arglist[numargs] = (char*) 0; //null-terminate 00321 for(int i = 0; arglist[i]!=NULL; i++) 00322 LINFO("arg %d: \"%s\"", i, arglist[i]); 00323 return nub::ref<rutz::exec_pipe>(new rutz::exec_pipe("r", arglist)); 00324 } 00325 00326 void MPlayerWrapper::parseStatusLine(const std::string stat_line) 00327 { 00328 //parse status line with sscanf... 00329 //A: # V: # A-V: # ct: # #/ # ??% ??% ??.?% 0 0 00330 unsigned int num_frames = 1, num_dropped = 0; 00331 static unsigned int num_prev_dropped = 0; 00332 double t_video = 0; 00333 00334 if(sscanf(stat_line.c_str(),"A: %*f V: %lf A-V: %*f ct: %*f %u/%*u %*s %*s %*s %u %*u", 00335 &t_video, &num_frames, &num_dropped)==EOF) 00336 pushEvent("Status line error"); 00337 else //if status line is correctly read 00338 { 00339 //for the start of each video, restart dropped frame count 00340 if(num_frames == 1) num_prev_dropped = 0; 00341 00342 //see if the number of dropped frames so far increased 00343 if(num_dropped > num_prev_dropped) 00344 { 00345 pushEvent("MPlayerWrapper - " + toStr<int>(num_dropped - num_prev_dropped) + " frames dropped"); 00346 num_prev_dropped = num_dropped; 00347 } 00348 //account for drop frames, start frame index from 0 00349 //NB: sometimes counter will report back to frame 0 - we prevent this from happening here: 00350 if(!(num_frames==1 && itsPlayingVideo)) 00351 pushEvent("MPlayerWrapper - frame " + toStr<int>(num_frames + num_dropped-1) 00352 + " at time " + toStr<double>(t_video)); 00353 } 00354 00355 } 00356 00357 #endif 00358 // PSYCHO_MPLAYERWRAPPER_C_DEFINED