PARP Research Group University of Murcia, Spain


src/qvio/qvmplayerproxy.cpp

00001 /*
00002  *      Copyright (C) 2007, 2008, 2009. PARP Research Group.
00003  *      <http://perception.inf.um.es>
00004  *      University of Murcia, Spain.
00005  *
00006  *      This file is part of the QVision library.
00007  *
00008  *      QVision is free software: you can redistribute it and/or modify
00009  *      it under the terms of the GNU Lesser General Public License as
00010  *      published by the Free Software Foundation, version 3 of the License.
00011  *
00012  *      QVision is distributed in the hope that it will be useful,
00013  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *      GNU Lesser General Public License for more details.
00016  *
00017  *      You should have received a copy of the GNU Lesser General Public
00018  *      License along with QVision. If not, see <http://www.gnu.org/licenses/>.
00019  */
00020 
00024 
00025 #include <sys/types.h>
00026 #include <sys/stat.h>
00027 #include <unistd.h>
00028 #include <stdlib.h>
00029 #include <fcntl.h>
00030 #include <stdio.h>
00031 
00032 #include <QApplication>
00033 #include <QDebug>
00034 #include <QObject>
00035 #include <QStringList>
00036 #include <QRegExp>
00037 #include <QMutex>
00038 
00039 #include <qvipp.h>
00040 #include <qvio.h>
00041 
00042 #include <QVWorker>
00043 #include <QVMPlayerProxy>
00044 
00045 /******************* Auxiliary QVCheckOKMPlayerCamera Class *******************/
00046 QVCheckOKMplayerCamera::QVCheckOKMplayerCamera(QFile & fifo_file,int max_time_ms_to_wait_for_open) : QThread(), _fifo_file(fifo_file), _max_time_ms_to_wait_for_open(max_time_ms_to_wait_for_open)
00047         {
00048         qDebug() << "QVCheckOKMplayerCamera::QVCheckOKMplayerCamera()";
00049         moveToThread(this);
00050         start();
00051         qDebug() << "QVCheckOKMplayerCamera::QVCheckOKMplayerCamera() <- return";
00052         }
00053 
00054 void QVCheckOKMplayerCamera::run()
00055         {
00056         qDebug() << "QVCheckOKMplayerCamera::run()";
00057         QTimer::singleShot(_max_time_ms_to_wait_for_open, this, SLOT(writeErrorInFifo()));
00058         qDebug() << "QVCheckOKMplayerCamera::run() <- entering event loop";
00059         exec();
00060         qDebug() << "QVCheckOKMplayerCamera::run() <- back from event loop";
00061         qDebug() << "QVCheckOKMplayerCamera::run() <- return";
00062         }
00063 
00064 
00065 void QVCheckOKMplayerCamera::writeErrorInFifo()
00066         {
00067         qDebug() << "QVCheckOKMplayerCamera::writeErrorInFifo()";
00068         _fifo_file.write("MPLAYER ERROR\n"); 
00069         qDebug() << "QVCheckOKMplayerCamera::writeErrorInFifo() -> return";
00070         };
00071 
00072 /********************* Auxiliary QVCameraThread Class *************************/
00073 QVCameraThread::QVCameraThread(QObject *object,char *slot): QThread(), _object(object), _slot(slot)
00074         {
00075         qDebug() << "QVCameraThread::QVCameraThread()";
00076         _object->moveToThread(this);
00077         qDebug() << "QVCameraThread::QVCameraThread(): object moved to thread";
00078         qDebug() << "QVCameraThread::QVCameraThread() <- return";
00079         }
00080 
00081 QVCameraThread::~QVCameraThread()
00082         {
00083         qDebug() << "QVCameraThread::~QVCameraThread()";
00084         disconnect();
00085         quit();
00086         qDebug() << "QVCameraThread::~QVCameraThread(): sent quit to thread";
00087         wait();
00088         qDebug() << "QVCameraThread::~QVCameraThread(): thread finished";
00089         qDebug() << "QVCameraThread::~QVCameraThread() <- return";
00090         }
00091 
00092 void QVCameraThread::run()
00093         {
00094         qDebug() << "QVCameraThread::run()";
00095         QTimer *timer = new QTimer();
00096         timer->start(0);
00097         connect(timer, SIGNAL(timeout()),_object,_slot);
00098         qDebug() << "QVCameraThread::run(): timer created, started and connected";
00099         qDebug() << "QVCameraThread::run(): entering event processing loop";
00100         exec();
00101         qDebug() << "QVCameraThread::run(): back from event processing loop";
00102         delete timer;
00103         qDebug() << "QVCameraThread::run(): timer deleted";
00104         qDebug() << "QVCameraThread::run() <- return";
00105         }
00106 
00107 /********************* Auxiliary QVMPlayerIOProcessor class *******************/
00108 QVMPlayerIOProcessor::QVMPlayerIOProcessor(QProcess * mplayer): QObject(), command_queue(), command_queue_mutex()
00109         {
00110         qDebug() << "QVMPlayerIOProcessor::QVMPlayerIOProcessor()";
00111         rows = cols = 0;
00112         fps = time_length = time_pos = 0;
00113         speed = 1;
00114         this->mplayer = mplayer;
00115 
00116         queueCommandToMPlayer("get_time_length");
00117 
00118         qDebug() << "QVMPlayerIOProcessor::QVMPlayerIOProcessor() <- return";
00119         }
00120 
00121 QVMPlayerIOProcessor::~QVMPlayerIOProcessor()
00122         {
00123         qDebug() << "QVMPlayerIOProcessor::~QVMPlayerIOProcessor()";
00124         qDebug() << "QVMPlayerIOProcessor::~QVMPlayerIOProcessor() <- return";
00125         }
00126 
00127 void QVMPlayerIOProcessor::queueCommandToMPlayer(const QString &com_str, bool ifEmpty)
00128         {
00129         qDebug() << "QVMPlayerIOProcessor::queueCommand("<< com_str <<")";
00130         qDebug() << "QVMPlayerIOProcessor::queueCommand(): command_queue.size() = "
00131                 << command_queue.size();
00132         command_queue_mutex.lock();
00133         if(!ifEmpty || command_queue.size()==0)
00134                 command_queue << com_str + '\n';
00135         command_queue_mutex.unlock();
00136         qDebug() << "QVMPlayerIOProcessor::queueCommand() <- return";
00137         }
00138 
00139 void QVMPlayerIOProcessor::sendCommandToMPlayer()
00140         {
00141         qDebug() << "QVMPlayerIOProcessor::sendCommandToMPlayer()";
00142         command_queue_mutex.lock();
00143         if (command_queue.size() > 0)
00144                 {
00145                 qDebug() << "QVMPlayerIOProcessor::sendCommandToMPlayer(): sending command:" << command_queue.head();
00146                 mplayer->write(qPrintable(command_queue.dequeue()));
00147                 }
00148         command_queue_mutex.unlock();
00149         qDebug() << "QVMPlayerIOProcessor::sendCommandToMPlayer(): wait for bytes written";
00150         // BUG: In Windows this hangs?
00151         #ifdef unix
00152         mplayer->waitForBytesWritten();
00153         #endif
00154         qDebug() << "QVMPlayerIOProcessor::sendCommandToMPlayer() <- return";
00155         }
00156 
00157 int QVMPlayerIOProcessor::interpretMPlayerOutput()
00158         {
00159         qDebug() << "QVMPlayerIOProcessor::interpretMPlayerOutput()";
00160         int length = -1;
00161         char buf[1024];
00162 
00163         length = mplayer->readLine(buf, sizeof(buf));
00164 
00165         if (length == -1)
00166                 qDebug() << "QVMPlayerIOProcessor::interpretMPlayerOutput(): length == -1";
00167         else    {
00168                 QString str(buf);
00169                 QStringList variables = str.simplified().split("=");
00170                 QStringList palabras = str.simplified().split(" ");
00171 
00172                 if(variables[0] == "ANS_speed")
00173                         {
00174                         speed = variables[1].toDouble();
00175                         qDebug() << "QVMPlayerIOProcessor::interpretMPlayerOutput(): updating speed =" << speed;
00176                         }
00177                 else if(variables[0] == "ANS_LENGTH")
00178                         {
00179                         time_length = variables[1].toDouble();
00180                         qDebug() << "QVMPlayerIOProcessor::interpretMPlayerOutput(): updating time_length =" << time_length;
00181                         }
00182                 else if(variables[0] == "ANS_TIME_POSITION")
00183                         {
00184                         time_pos = variables[1].toDouble();
00185                         qDebug() << "QVMPlayerIOProcessor::interpretMPlayerOutput(): updating time_pos =" << time_pos;
00186                         }
00187                 else if (palabras[0] == "VIDEO:")
00188                         {
00189                         // We have FPS specification
00190                         for (int i = 0; i < palabras.size(); ++i)
00191                                 if (palabras[i] == "fps")
00192                                         fps = palabras[i-1].toDouble();
00193 
00194                         qDebug() << "QVMPlayerIOProcessor::interpretMPlayerOutput(): updating fps" << fps;
00195                         }
00196                 else if (palabras[0] == "VO:")
00197                         {
00198                         // We have video specifications and image size.
00199                         QStringList dimensiones = palabras[2].split("x");
00200                         cols = dimensiones[0].toInt();
00201                         rows = dimensiones[1].toInt();
00202                         qDebug() << "QVMPlayerIOProcessor::interpretMPlayerOutput(): updating cols" << cols;
00203                         qDebug() << "QVMPlayerIOProcessor::interpretMPlayerOutput(): updating rows" << rows;
00204                         }
00205                 else
00206                         qDebug() << "QVMPlayerIOProcessor::interpretMPlayerOutput(): uninterpreted mplayer output:" << str;
00207                 }
00208         qDebug() << "QVMPlayerIOProcessor::interpretMPlayerOutput() <- return" << length;
00209         return length;
00210         }
00211 
00212 /******************* Auxiliary QVMPlayerFrameGrabber Class ********************/
00213 QVMPlayerFrameGrabber::QVMPlayerFrameGrabber(QVMPlayerIOProcessor *mplayerIOProcessor, QString pipeName,
00214         bool realTime, const uInt waitMilisecs): QObject()
00215         {
00216         // We create a guarding thread that will unblock the subsequent fifo_file.readLine in the
00217         // case that mplayer does not start OK (due, for example, to a wrong URL, an incorrect
00218         // file format, or whatever). If mplayer starts OK before two seconds (for local videos) or
00219         // ten seconds (for DVD, VCD and remote videos), this thread silently stops and is deleted.
00220         QVCheckOKMplayerCamera check_thread(fifoInput, waitMilisecs);
00221 
00222         // We open the pipeline file with the designated name:
00223         fifoInput.setFileName(pipeName);
00224         if(fifoInput.open(QIODevice::ReadWrite|QIODevice::Unbuffered) == -1)
00225                 qFatal("Error opening fifo %s\n", qPrintable(pipeName));
00226 
00227         // Now we read YUV header:
00228         int cols = 0, rows = 0, fps = 0;
00229         finished = !readYUV4MPEG2Header(fifoInput, cols, rows, fps);
00230 
00231         check_thread.quit();
00232         check_thread.wait();
00233 
00234         // Case the header is incorrect, stop.
00235         if (finished)
00236                 return;
00237 
00238         // If the header was correctly read, proceed to init MPlayer IO processing.
00239         qDebug() << "QVMPlayerFrameGrabber::QVMPlayerFrameGrabber(fifoInput,"
00240                         << mplayerIOProcessor->rows << "x" << mplayerIOProcessor->cols << "," << realTime <<")";
00241 
00242         //std::cout << "xxx QVMPlayerFrameGrabber::QVMPlayerFrameGrabber(fifoInput,"
00243         //              << mplayerIOProcessor->rows << "x" << mplayerIOProcessor->cols << "," << realTime <<")" << std::endl;
00244         
00245         this->mplayerIOProcessor = mplayerIOProcessor;
00246         this->realTime = realTime;
00247         this->frames_read = 0;
00248 
00249         //debug_in_memcpy = debug_in_updateSlot = false;
00250 
00252         imgY = QVImage<uChar>(cols, rows, cols);
00253 
00254         img_auxY = QVImage<uChar>(cols, rows, cols);
00255         img_auxU = QVImage<uChar>(cols/2, rows/2, cols/2);
00256         img_auxV = QVImage<uChar>(cols/2, rows/2, cols/2);
00257 
00258         if (realTime)
00259                 {
00260                 qvcamerathread = new QVCameraThread(this,SLOT(updateSlot()));
00261                 qvcamerathread->start(QThread::NormalPriority);
00262                 }
00263 
00264         connect(this, SIGNAL(sendCommandSignal()), mplayerIOProcessor, SLOT(sendCommandToMPlayer()));
00265         qDebug() << "QVMPlayerFrameGrabber::QVMPlayerFrameGrabber() <- return";
00266         };
00267 
00268 QVMPlayerFrameGrabber::~QVMPlayerFrameGrabber()
00269         {
00270         qDebug() << "QVMPlayerFrameGrabber::~QVMPlayerFrameGrabber()";
00271         fifoInput.close();
00272 
00273         finished = true;
00274         if (realTime) delete qvcamerathread;
00275         qDebug() << "QVMPlayerFrameGrabber::~QVMPlayerFrameGrabber(): thread deleted.";
00276         qDebug() << "QVMPlayerFrameGrabber::~QVMPlayerFrameGrabber() <- return";
00277         }
00278 
00279 void QVMPlayerFrameGrabber::updateSlot()
00280         {
00281         qDebug() << "QVMPlayerFrameGrabber::updateSlot()";
00282 
00283         qDebug() << "QVMPlayerFrameGrabber::updateSlot(): sent command to mplayer: get_time_pos";
00284         mplayerIOProcessor->queueCommandToMPlayer("pausing_keep get_time_pos\n",true);
00285 
00286         // Avoid QSocketNotifier annoying message, by deferring call:
00287         emit sendCommandSignal(); // NOT mplayerIOProcessor->sendCommandToMPlayer();
00288 
00289         // New frame read:
00290         qDebug() << "QVMPlayerFrameGrabber::updateSlot(): reading frame";
00291         QVImage<uChar, 1> tempY;
00292         QVImage<uChar, 1> tempU;
00293         QVImage<uChar, 1> tempV;
00294 
00295         // We always read in temporal images, that are later safely copied:
00296         // There is no copy overhead because QVImage uses QSharedData (reference
00297         // counting).
00298         tempY = img_auxY;
00299         tempU = img_auxU;
00300         tempV = img_auxV;
00301 
00302         qDebug() << "QVMPlayerFrameGrabber::readYUV4MPEG2Frame(): reading YUV header";
00303         finished = !readYUV4MPEG2Frame(fifoInput, tempY, tempU, tempV);
00304 
00305         // If in real time, we lock, to avoid collision with the thread that is
00306         // grabbing from the camera:
00307         if(realTime)
00308                 mutex.lock();
00309 
00310         img_auxY = tempY;
00311         img_auxU = tempU;
00312         img_auxV = tempV;
00313 
00314         // Unlock (if in real time):
00315         if(realTime)
00316                 {
00317                 condition.wakeAll(); // Wake possible waiting grabs.
00318                 mutex.unlock();
00319                 }
00320 
00321         qDebug() << "QVMPlayerFrameGrabber::updateSlot(): finished" << finished;
00322         frames_read++;
00323         qDebug() << "QVMPlayerFrameGrabber::updateSlot(): frames read" << frames_read;
00324 
00325         mplayerIOProcessor->interpretMPlayerOutput();
00326 
00327         qDebug() << "QVMPlayerFrameGrabber::updateSlot() <- return";
00328 
00329         emit newReadFrameGrabber();
00330         }
00331 
00332 void QVMPlayerFrameGrabber::updateFrameBuffer()
00333         {
00334         qDebug() << "QVMPlayerFrameGrabber::updateFrameBuffer()";
00335 
00336         // Here we come from a grab, so we always lock (if in real time, it is 
00337         // obviously needed, but also if not in real time, to make non-real-cameras
00338         // grabbing also thread-safe (won't be used normally, but who knows...
00339         // maybe the programmer wish to read different frames from different
00340         // threads, without discarding any frame as could happen with real time
00341         // cameras).
00342         mutex.lock();
00343 
00344         if(realTime)
00345                 condition.wait(&mutex); // If in real time, wait for next fresh frame.
00346 
00347         // If in real time, updateSlot is asynchronously called by a dedicated
00348         // thread, otherwise we have to call it explicitly.
00349         if(not realTime)
00350                 updateSlot();
00351 
00352         imgY = img_auxY;
00353         imgU = img_auxU;
00354         imgV = img_auxV;
00355 
00356         mutex.unlock();
00357 
00358         qDebug() << "QVMPlayerFrameGrabber::updateFrameBuffer() <- return";
00359         }
00360 
00361 void QVMPlayerFrameGrabber::getQVImageYUV(QVImage<uChar> &imgY, QVImage<uChar> &imgU, QVImage<uChar> &imgV) const
00362         {
00363         qDebug() << "QVMPlayerFrameGrabber::getQVImageYUV()";
00364         imgY = this->imgY;
00365         imgU = this->imgU;
00366         imgV = this->imgV;
00367         qDebug() << "QVMPlayerFrameGrabber::getQVImageYUV() <- return";
00368         }
00369 
00370 void QVMPlayerFrameGrabber::getQVImageGray(QVImage<uChar> &imgGray) const
00371         {
00372         qDebug() << "QVMPlayerFrameGrabber::getQVImageGray()";
00373         imgGray = imgY;
00374         qDebug() << "QVMPlayerFrameGrabber::getQVImageGray() <- return";
00375         }
00376 
00377 /***************************** QVMPlayerProxy Class **************************/
00378 QVMPlayerProxy::QVMPlayerProxy(QString /* name */): status(QVCamera::Closed), open_options(Default)
00379         {
00380         qDebug() << "QVMPlayerProxy::QVMPlayerProxy()";
00381 
00382         frames_grabbed = 0;
00383         live_camera = FALSE;
00384 
00385         qDebug() << "QVMPlayerProxy::QVMPlayerProxy() <- return";
00386         }
00387 
00388 QVMPlayerProxy::~QVMPlayerProxy()
00389         {
00390         qDebug() << "QVMPlayerProxy::~QVMPlayerProxy()";
00391         if (status != QVCamera::Closed) closeCam();
00392         qDebug() << "QVMPlayerProxy::~QVMPlayerProxy() <- deleting image";
00393         qDebug() << "QVMPlayerProxy::~QVMPlayerProxy() <- return";
00394         }
00395 
00396 void QVMPlayerProxy::initMPlayerArgs(QString urlString, uInt rows, uInt cols)
00397         {
00398         qDebug() << "QVMPlayerProxy::initMPlayerArgs()";
00399         qDebug() << "QVMPlayerProxy::initMPlayerArgs(): url string =" << urlString;
00400 
00401         // Loop option and fixed-vo must go first:
00402         if(not (open_options & NoLoop)) mplayer_args << "-loop" << "0";
00403 
00404         mplayer_args << "-fixed-vo";
00405 
00406         QUrl url(urlString);
00407 
00408         if (url.host() != "")
00409                 path = url.host() + "/";
00410 
00411         path += url.path();
00412 
00413         qDebug() << "QVMPlayerProxy::initMPlayerArgs(): path" << path;
00414 
00415         // Here we infer the type of the video source from the url.
00416         // If it is not specified in the schema of it, certain match rules are
00417         // applied to guess it out.
00418         if (url.scheme() != "")                         // schema specified by the user
00419                 schema = url.scheme();
00420         else if (urlString.startsWith("/dev/video"))    // a v4l device
00421                 schema = "v4l";
00422         else if (urlString.startsWith("/dev/dv"))       // a dv device
00423                 schema = "dv";
00424         else if (urlString.contains("*"))               // a multifile
00425                 schema = "mf";
00426         else if (urlString.startsWith("www."))          // a http file
00427                 schema = "http";
00428         else if (urlString.startsWith("ftp."))          // a ftp file
00429                 schema = "ftp";
00430 
00431         qDebug() << "QVMPlayerProxy::initMPlayerArgs(): scheme" << schema;
00432 
00433         live_camera = TRUE;     // default configuration
00434 
00435         // Different mplayer args for different kind of image sources:
00436         if ((schema == "v4l") or (schema == "v4l2") or (schema == "analog"))
00437                 {
00438                 // Video for Linux cameras:
00439                 QString urlQueryValues = QString("driver=%1:device=%2").arg(schema).arg(path);
00440 
00441                 QList<QPair<QString, QString> > queryItems = url.queryItems();
00442                 for (int i = 0; i < queryItems.size(); ++i)
00443                         urlQueryValues += ":" + queryItems.at(i).first + "=" + queryItems.at(i).second;
00444 
00445                 mplayer_args << "tv://" << "-tv" << urlQueryValues;
00446                 }
00447         else if (schema == "dv")
00448                 // DV cameras (connected through firewire):
00449                 mplayer_args << path << "-demuxer" << "rawdv" << "-cache" << "400";
00450         else if (schema == "iidc")
00451                 // IIDC cameras (connected through firewire):
00452                 // For example, iidc:///dev/video1394/0?from=/dev/video0:to=/dev/video1
00453                 qFatal("Currently this driver does not work (apparently with\n"
00454                         "vloopback writing and then reading from a fifo with mplayer\ndoes not work).\n");
00455         else if (schema == "tv")
00456                 // Analog TV input:
00457                 qFatal("tv URL: Still not implemented\n");
00458         else if (schema == "dvb")
00459                 // Digital TV input:
00460                 qFatal("dvb URL: Still not implemented\n");
00461         else    // Format can be rtsp:// http://, ftp://, dvd://, vcd://, mf:// or file://
00462                 { 
00463                 //  We pass the url it directly to mplayer:
00464                 live_camera = FALSE;
00465                 if (schema != "")
00466                         mplayer_args << QString(schema + "://" + path);
00467                 else
00468                         mplayer_args << path;
00469                 }
00470 
00471         // IMPORTANT!! All -vf filters MUST be together, separated by commas, and in
00472         // the right order. By now, we only use deinterlacing and scale, and in that order:
00473         QString aux;
00474 
00475         // Deinterlace option:
00476         if(open_options & Deinterlaced) aux = "pp=md";
00477 
00478         if(rows != 0 or cols != 0)
00479                 {
00480                 if(aux != QString())
00481                         aux += QString(",scale=%1:%2").arg(cols).arg(rows);
00482                 else
00483                         aux = QString("scale=%1:%2").arg(cols).arg(rows);
00484                 }
00485 
00486         if (aux != QString()) mplayer_args << "-vf" << aux;
00487 
00488         // Real time option:
00489         if(not (open_options & RealTime))
00490                 {
00491                 if(not live_camera)
00492                         mplayer_args << "-benchmark";
00493                 else
00494                         // The user attempted to open a live camera in continuous mode.
00495                         // We issue a warning:
00496                         qWarning("Warning: opening live cameras in continuous mode "
00497                                          "wastes less CPU time, but it is prone to delays in "
00498                                          "the pipe of images if your process is slower than "
00499                                          "the camera.");
00500                 }
00501 
00502         // Additional arguments (slave, quiet, nosound & yuv4mpeg output):
00503         mplayer_args << "-slave" << "-quiet" << "-nosound" << "-vo" << QString("yuv4mpeg:file=%1").arg(namedPipe->getInputFilePath());
00504 
00505         qDebug() << "QVMPlayerProxy::initMPlayerArgs() <- return";
00506         }
00507 
00508 // Main method to open a camera:
00509 bool QVMPlayerProxy::openCam(const QString & urlstring, OpenOptions opts, uInt suggestedRows, uInt suggestedCols)
00510         {
00511         //std::cout << "xxx QVMPlayerProxy::openCam(" << qPrintable(urlstring) << ","
00512         //      << suggestedCols << "x" << suggestedRows << "," << opts << ")" << std::endl;
00513 
00514         qDebug() << "QVMPlayerProxy::openCam(" << urlstring << ","
00515                 << suggestedCols << "x" << suggestedRows << "," << opts << ")";
00516 
00517         setStatus(QVCamera::Closed);
00518 
00519         open_options = opts;
00520 
00521         // Pipe initialization
00522         namedPipe = new QNamedPipe(QString("mplayer"));
00523 
00524         initMPlayerArgs(urlstring, suggestedRows, suggestedCols);
00525 
00526         //std::cout << "xxx QVMPlayerProxy::openCam(): MPlayer command: " << MPLAYER_BINARY_PATH;
00527         //foreach(QString arg, mplayer_args)
00528         //      std::cout << " " << qPrintable(arg);
00529         //std::cout << std::endl;
00530 
00531         qDebug() << "QVMPlayerProxy::openCam(): MPlayer args ->" << mplayer_args;
00532         mplayer.start(MPLAYER_BINARY_PATH, mplayer_args);
00533         qDebug() << "QVMPlayerProxy::openCam(): after mplayer.start()";
00534         if(not mplayer.waitForStarted(1000))
00535                 qFatal("Mplayer failed to start: Are you sure it is installed and in the correct PATH?");
00536         qDebug() << "QVMPlayerProxy::openCam(): after mplayer.waitForstarted()";
00537         
00538         qDebug() << "QVMPlayerProxy::openCam(): creating QVMPlayerIOProcessor...";
00539         mplayerIOProcessor = new QVMPlayerIOProcessor(&mplayer);
00540         // -----------------------------------------
00541 
00542         const uInt waitMilisecs = (     schema == "http" or schema == "ftp" or
00543                                         schema == "rtsp" or schema == "dvd" or
00544                                         schema == "vcd" )? 10000:2000;
00545 
00546         // Important to open in ReadWrite mode, to avoid infinite waiting if mplayer did
00547         // not start OK, and because perhaps the former guarding thread will write in the FIFO.
00548         qDebug() << "QVMPlayerProxy::openCam(): opening fifo_file";
00549 
00550         mplayerFrameGrabber = new QVMPlayerFrameGrabber(mplayerIOProcessor, namedPipe->getOutputFilePath(),
00551                                                         open_options & QVMPlayerProxy::RealTime, waitMilisecs);
00552 
00553         if (mplayerFrameGrabber->isFinished())
00554                 { 
00555                 std::cerr << "QVMPlayerProxy::openCam(): Warning: Mplayer could not open the requested video source ("
00556                         << qPrintable(urlstring) << ") of type " << qPrintable(schema) <<std::endl;
00557 
00558                 mplayer.terminate();
00559                 if (not mplayer.waitForFinished(500)) mplayer.kill();   
00560                 delete namedPipe;
00561 
00562                 qDebug() << "QVMPlayerProxy::openCam(): QVCheckOKMplayerCamera thread deleted after detecting an error."
00563                         << " Returning FALSE";
00564                 return FALSE;
00565                 }
00566 
00567         qDebug() << "QVMPlayerProxy::openCam(): QVCheckOKMplayerCamera thread deleted after correct launch of mplayer";
00568 
00569 
00570         // -----------------------------------------
00571         qDebug() << "QVMPlayerProxy::openCam(): initializing MPlayer.";
00572 
00573         // These signals are to defer the call to sendCommandToMplayer, and avoid
00574         // the "QSocketNotifier:: socket notifier cannot be enabled from another
00575         // thread" warning.
00576         connect(mplayerFrameGrabber,SIGNAL(newReadFrameGrabber()),this,SIGNAL(newRead()));
00577         connect(this,SIGNAL(sendCommandSignal()),mplayerIOProcessor,SLOT(sendCommandToMPlayer()));
00578 
00579         mplayer.waitForReadyRead();
00580 
00581         setStatus(QVCamera::Running);
00582 
00583         qDebug() << "QVMPlayerProxy::openCam() <- return";
00584 
00585         emit camOpened();
00586 
00587         return TRUE;
00588         }
00589 
00590 // Close camera method:
00591 void QVMPlayerProxy::closeCam()
00592         {
00593         qDebug() << "QVMPlayerProxy::closeCam()";
00594 
00595         if (status == QVCamera::Closed)
00596                 {
00597                 qDebug() << "QVMPlayerProxy::closeCam(): camera already closed. Returning";
00598                 qDebug() << "QVMPlayerProxy::closeCam() <- return";
00599                 return;
00600                 }
00601 
00602         setStatus(QVCamera::Closed);
00603 
00604         emit camClosed();
00605 
00606         mplayerIOProcessor->queueCommandToMPlayer("quit");      
00607         // Avoid QSocketNotifier annoying message, by deferring call:
00608         emit sendCommandSignal();
00609 
00610         delete mplayerFrameGrabber;
00611         qDebug() << "QVMPlayerProxy::closeCam(): mplayerFrameGrabber deleted";
00612 
00613         delete mplayerIOProcessor;
00614         qDebug() << "QVMPlayerProxy::closeCam(): mplayerIOProcessor deleted";
00615 
00616         mplayer.terminate();
00617         while (not mplayer.waitForFinished(500)) mplayer.kill();
00618         qDebug() << "QVMPlayerProxy::closeCam(): mplayer terminated";
00619         
00620         delete namedPipe;
00621         qDebug() << "QVMPlayerProxy::closecam(): deleted namedpipe";
00622 
00623         qDebug() << "QVMPlayerProxy::closeCam() <- return";
00624         }
00625 
00626 bool QVMPlayerProxy::grab(QVImage<uChar> &imgY, QVImage<uChar> &imgU, QVImage<uChar> &imgV)
00627         {
00628         qDebug() << "QVMPlayerProxy::grab(imageY, imageU, imageV)";
00629 
00630         bool newImages = performGrab();
00631 
00632         if (newImages)
00633                 mplayerFrameGrabber->getQVImageYUV(imgY, imgU, imgV);
00634 
00635         qDebug() << "QVMPlayerProxy::grab() <~ return";
00636 
00637         return newImages;
00638         }
00639 
00640 bool QVMPlayerProxy::grab(QVImage<uChar,3> & imageRGB)
00641         {
00642         qDebug() << "QVMPlayerProxy::grab(imageRGB)";
00643         bool newImages = performGrab();
00644 
00645         QVImage<uChar> grabbedYImage, grabbedUImage, grabbedVImage;
00646 
00647         if (newImages)
00648                 {
00649                 mplayerFrameGrabber->getQVImageYUV(grabbedYImage, grabbedUImage, grabbedVImage);
00650 
00651                 imageRGB = QVImage<uChar,3>(grabbedYImage.getCols(),grabbedYImage.getRows());
00652                 YUV420ToRGB(grabbedYImage, grabbedUImage, grabbedVImage, imageRGB);
00653                 }
00654 
00655         qDebug() << "QVMPlayerProxy::grab() <~ return";
00656         return newImages;
00657         }
00658 
00659 bool QVMPlayerProxy::grab(QVImage<uChar,1> & imageGray)
00660         {
00661         qDebug() << "QVMPlayerProxy::grab(imageGray)";
00662 
00663         bool newImages = performGrab();
00664 
00665         if (newImages)
00666                 mplayerFrameGrabber->getQVImageGray(imageGray);
00667 
00668         qDebug() << "QVMPlayerProxy::grab() <~ return";
00669         return newImages;
00670         }
00671 
00672 bool QVMPlayerProxy::performGrab()
00673         {
00674         // First, if there are any pending commands, we send them to mplayer:
00675         qDebug() << "QVMPlayerProxy::performGrab()";
00676 
00677         if (isClosed()) return false;
00678 
00679         qDebug() << "QVMPlayerProxy::performGrab(): status != Closed";
00680 
00681         bool newFrameGrabbed = false;
00682         // This is for when the camera is paused; it will return the same frames
00683         // over and over, but not immediately, but at a maximum fps rate (to avoid
00684         // intense CPU usage, that will slow the rest of the application and the
00685         // GUI.
00686 
00687         switch(status)
00688                 {
00689                 case QVCamera::RunningOneStep:
00690                         qDebug() << "QVMPlayerProxy::performGrab(): status == RunningOneStep";
00691                         setStatus(QVCamera::Paused);
00692                 case QVCamera::Running:
00693                         qDebug() << "QVMPlayerProxy::performGrab(): status == Running";
00694                         mplayerFrameGrabber->updateFrameBuffer();
00695                         frames_grabbed++;
00696                         newFrameGrabbed = true;
00697                         qDebug() << "QVMPlayerProxy::performGrab(): frames grabbed" << getFramesGrabbed();
00698                         break;
00699 
00700                 case QVCamera::Paused:
00701                         qDebug() << "QVMPlayerProxy::performGrab(): status == Paused";
00702                         /*
00703                         mutexJustToSleepMiliseconds.lock();
00704                         qDebug() << "QVMPlayerProxy::performGrab(): pausing" << milisecsToWait
00705                                 << "milisecs (video is "<< getFPS() <<" FPS's)";
00706                         conditionJustToSleepMiliseconds.wait(&mutexJustToSleepMiliseconds,milisecsToWait);
00707                         qDebug() << "QVMPlayerProxy::performGrab(): unlocking";
00708                         mutexJustToSleepMiliseconds.unlock(); */
00709                         break;
00710 
00711                 default:
00712                         break;
00713                 }
00714         qDebug() << "QVMPlayerProxy::performGrab(): checking finished";
00715 
00716         if (mplayerFrameGrabber->isFinished())
00717                 closeCam();
00718         else
00719                 emit newGrab();
00720 
00721         qDebug() << "QVMPlayerProxy::performGrab() <- return";
00722 
00723         return newFrameGrabbed;
00724         }
00725 
00726 // Pause, unpause, next frame, set speed, seek and close camer slots:
00727 // In all of them, the corresponding command is sent to the shared command queue
00728 // data structure, so access is accordingly protected .
00729 void QVMPlayerProxy::pauseCamSlot()
00730         {
00731         qDebug() << "QVMPlayerProxy::pauseCam()";
00732         if(status == QVCamera::Closed) return;
00733         setStatus(QVCamera::Paused);
00734         qDebug() << "QVMPlayerProxy::pauseCam() <- return";
00735         }
00736 
00737 void QVMPlayerProxy::unpauseCamSlot()
00738         {
00739         qDebug() << "QVMPlayerProxy::unpauseCam()";
00740         if(status == QVCamera::Closed) return;
00741         setStatus(QVCamera::Running);
00742         qDebug() << "QVMPlayerProxy::unpauseCam() <~ return";
00743         }
00744 
00745 void QVMPlayerProxy::nextFrameCamSlot()
00746         {
00747         qDebug() << "QVMPlayerProxy::nextFrameCam()";
00748         if(status == QVCamera::Closed) return;
00749         setStatus(QVCamera::RunningOneStep);
00750         qDebug() << "QVMPlayerProxy::nextFrameCam() <~ return";
00751         }
00752 
00753 void QVMPlayerProxy::setSpeedCamSlot(double d)
00754         {
00755         qDebug() << "QVMPlayerProxy::setSpeedCam()";
00756         if(status == QVCamera::Closed) return;
00757         mplayerIOProcessor->queueCommandToMPlayer(QString("pausing_keep speed_set ") + QString::number(d));
00758         mplayerIOProcessor->queueCommandToMPlayer("get_property speed");
00759         qDebug() << "QVMPlayerProxy::setSpeedCam() <~ return";
00760         }
00761 
00762 void QVMPlayerProxy::seekCamSlot(QVCamera::TSeekType seek,double d)
00763         {
00764         qDebug() << "QVMPlayerProxy::seekCam()";
00765         if(status == QVCamera::Closed) return;
00766         if(status == QVCamera::Paused)
00767                 setStatus(QVCamera::RunningOneStep);
00768         QString command = QString("pausing_keep seek ") + QString::number(d) + " " + QString::number(seek);
00769         mplayerIOProcessor->queueCommandToMPlayer(command);
00770         qDebug() << "QVMPlayerProxy::seekCam() <~ return";
00771         }



QVision framework. PARP research group, copyright 2007, 2008.