src/qvcameras/qvmplayercamera.cpp

Go to the documentation of this file.
00001 /*
00002  *      Copyright (C) 2007, 2008. 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 
00041 #include <QVWorker>
00042 #include <QVMPlayerCamera>
00043 
00044 /******************* Auxiliary QVCheckOKMPlayerCamera Class *******************/
00045 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)
00046         {
00047         qDebug() << "QVCheckOKMplayerCamera::QVCheckOKMplayerCamera()";
00048         qDebug() << "QVCheckOKMplayerCamera::QVCheckOKMplayerCamera() <- return";
00049         }
00050 
00051 QVCheckOKMplayerCamera::~QVCheckOKMplayerCamera()
00052         {
00053         qDebug() << "QVCheckOKMplayerCamera::~QVCheckOKMplayerCamera()";
00054         quit();
00055         wait();
00056         qDebug() << "QVCheckOKMplayerCamera::~QVCheckOKMplayerCamera: thread finished";
00057         qDebug() << "QVCheckOKMplayerCamera::~QVCheckOKMplayerCamera() <- return";
00058         }
00059 
00060 void QVCheckOKMplayerCamera::run()
00061         {
00062         qDebug() << "QVCheckOKMplayerCamera::run()";
00063         QTimer::singleShot(_max_time_ms_to_wait_for_open, this, SLOT(writeErrorInFifo()));
00064         qDebug() << "QVCheckOKMplayerCamera::run() <- entering event loop";
00065         exec();
00066         qDebug() << "QVCheckOKMplayerCamera::run() <- back from event loop";
00067         qDebug() << "QVCheckOKMplayerCamera::run() <- return";
00068         }
00069 
00070 
00071 void QVCheckOKMplayerCamera::writeErrorInFifo()
00072         {
00073         qDebug() << "QVCheckOKMplayerCamera::writeErrorInFifo()";
00074         _fifo_file.write("MPLAYER ERROR\n"); 
00075         qDebug() << "QVCheckOKMplayerCamera::writeErrorInFifo() -> return";
00076         };
00077 
00078 /********************* Auxiliary QVCameraThread Class *************************/
00079 QVCameraThread::QVCameraThread(QObject *object,char *slot): QThread(), _object(object), _slot(slot)
00080         {
00081         qDebug() << "QVCameraThread::QVCameraThread()";
00082         _object->moveToThread(this);
00083         qDebug() << "QVCameraThread::QVCameraThread(): object moved to thread";
00084         qDebug() << "QVCameraThread::QVCameraThread() <- return";
00085         }
00086 
00087 QVCameraThread::~QVCameraThread()
00088         {
00089         qDebug() << "QVCameraThread::~QVCameraThread()";
00090         disconnect();
00091         quit();
00092         qDebug() << "QVCameraThread::~QVCameraThread(): sent quit to thread";
00093         wait();
00094         qDebug() << "QVCameraThread::~QVCameraThread(): thread finished";
00095         qDebug() << "QVCameraThread::~QVCameraThread() <- return";
00096         }
00097 
00098 void QVCameraThread::run()
00099         {
00100         qDebug() << "QVCameraThread::run()";
00101         QTimer *timer = new QTimer();
00102         timer->start(0);
00103         connect(timer, SIGNAL(timeout()),_object,_slot);
00104         qDebug() << "QVCameraThread::run(): timer created, started and connected";
00105         qDebug() << "QVCameraThread::run(): entering event processing loop";
00106         exec();
00107         qDebug() << "QVCameraThread::run(): back from event processing loop";
00108         delete timer;
00109         qDebug() << "QVCameraThread::run(): timer deleted";
00110         qDebug() << "QVCameraThread::run() <- return";
00111         }
00112 
00113 /********************* Auxiliary QVMPlayerIOProcessor class *******************/
00114 QVMPlayerIOProcessor::QVMPlayerIOProcessor(QProcess * mplayer): QObject(), command_queue(), command_queue_mutex()
00115         {
00116         qDebug() << "QVMPlayerIOProcessor::QVMPlayerIOProcessor()";
00117         rows = cols = 0;
00118         fps = time_length = time_pos = 0;
00119         speed = 1;
00120         this->mplayer = mplayer;
00121         mplayer->waitForReadyRead();
00122         qDebug() << "QVMPlayerIOProcessor::QVMPlayerIOProcessor() <- return";
00123         }
00124 
00125 QVMPlayerIOProcessor::~QVMPlayerIOProcessor()
00126         {
00127         qDebug() << "QVMPlayerIOProcessor::~QVMPlayerIOProcessor()";
00128         qDebug() << "QVMPlayerIOProcessor::~QVMPlayerIOProcessor() <- return";
00129         }
00130 
00131 void QVMPlayerIOProcessor::queueCommandToMPlayer(const QString &com_str, bool ifEmpty)
00132         {
00133         qDebug() << "QVMPlayerIOProcessor::queueCommand("<< com_str <<")";
00134         qDebug() << "QVMPlayerIOProcessor::queueCommand(): command_queue.size() = "
00135                 << command_queue.size();
00136         command_queue_mutex.lock();
00137         if(!ifEmpty || command_queue.size()==0)
00138                 command_queue << com_str + '\n';
00139         command_queue_mutex.unlock();
00140         qDebug() << "QVMPlayerIOProcessor::queueCommand() <- return";
00141         }
00142 
00143 void QVMPlayerIOProcessor::sendCommandToMPlayer()
00144         {
00145         qDebug() << "QVMPlayerIOProcessor::sendCommandToMPlayer()";
00146         command_queue_mutex.lock();
00147         if (command_queue.size() > 0)
00148                 {
00149                 qDebug() << "QVMPlayerIOProcessor::sendCommandToMPlayer(): sending command:" << command_queue.head();
00150                 mplayer->write(qPrintable(command_queue.dequeue()));
00151                 }
00152         command_queue_mutex.unlock();
00153         qDebug() << "QVMPlayerIOProcessor::sendCommandToMPlayer(): wait for bytes written";
00154         // BUG: In Windows this hangs?
00155         #ifdef unix
00156         mplayer->waitForBytesWritten();
00157         #endif
00158         qDebug() << "QVMPlayerIOProcessor::sendCommandToMPlayer() <- return";
00159         }
00160 
00161 int QVMPlayerIOProcessor::interpretMPlayerOutput()
00162         {
00163         qDebug() << "QVMPlayerIOProcessor::interpretMPlayerOutput()";
00164         int length = -1;
00165         char buf[1024];
00166 
00167         length = mplayer->readLine(buf, sizeof(buf));
00168 
00169         if (length == -1)
00170                 qDebug() << "QVMPlayerIOProcessor::interpretMPlayerOutput(): length == -1";
00171         else    {
00172                 QString str(buf);
00173                 QStringList variables = str.simplified().split("=");
00174                 QStringList palabras = str.simplified().split(" ");
00175 
00176                 if(variables[0] == "ANS_speed")
00177                         {
00178                         speed = variables[1].toDouble();
00179                         qDebug() << "QVMPlayerIOProcessor::interpretMPlayerOutput(): updating speed =" << speed;
00180                         }
00181                 else if(variables[0] == "ANS_LENGTH")
00182                         {
00183                         time_length = variables[1].toDouble();
00184                         qDebug() << "QVMPlayerIOProcessor::interpretMPlayerOutput(): updating time_length =" << time_length;
00185                         }
00186                 else if(variables[0] == "ANS_TIME_POSITION")
00187                         {
00188                         time_pos = variables[1].toDouble();
00189                         qDebug() << "QVMPlayerIOProcessor::interpretMPlayerOutput(): updating time_pos =" << time_pos;
00190                         }
00191                 else if (palabras[0] == "VIDEO:")
00192                         {
00193                         // ya tenemos las especificaciones de fps
00194                         for (int i = 0; i < palabras.size(); ++i)
00195                                 if (palabras[i] == "fps")
00196                                         fps = palabras[i-1].toDouble();
00197 
00198                         qDebug() << "QVMPlayerIOProcessor::interpretMPlayerOutput(): updating fps" << fps;
00199                         }
00200                 else if (palabras[0] == "VO:")
00201                         {
00202                         // ya tenemos las especificaciones del video y dimensiones
00203                         QStringList dimensiones = palabras[2].split("x");
00204                         cols = dimensiones[0].toInt();
00205                         rows = dimensiones[1].toInt();
00206                         qDebug() << "QVMPlayerIOProcessor::interpretMPlayerOutput(): updating cols" << cols;
00207                         qDebug() << "QVMPlayerIOProcessor::interpretMPlayerOutput(): updating rows" << rows;
00208                         }
00209                 else
00210                         qDebug() << "QVMPlayerIOProcessor::interpretMPlayerOutput(): uninterpreted mplayer output:" << str;
00211                 }
00212         qDebug() << "QVMPlayerIOProcessor::interpretMPlayerOutput() <- return" << length;
00213         return length;
00214         }
00215 
00216 /******************* Auxiliary QVMPlayerFrameGrabber Class ********************/
00217 QVMPlayerFrameGrabber::QVMPlayerFrameGrabber(QVMPlayerIOProcessor *mplayerIOProcessor, QFile *fifoInput, uInt rows, uInt cols, bool realTime): QObject()
00218         {
00219         qDebug() << "QVMPlayerFrameGrabber::QVMPlayerFrameGrabber(fifoInput," << mplayerIOProcessor->rows << "," << mplayerIOProcessor->cols
00220                 << "," << realTime <<")";
00221         
00222         this->mplayerIOProcessor = mplayerIOProcessor;
00223         this->fifoInput = fifoInput;
00224         this->realTime = realTime;
00225         this->finished = false;
00226         this->frames_read = 0;
00227 
00228         debug_in_memcpy = debug_in_updateSlot = false;
00229 
00230         imgY = QVImage<uChar>(cols, rows, cols);
00231         img_auxY = QVImage<uChar>(cols, rows, cols);
00232         img_auxU = QVImage<uChar>(cols/2, rows/2, cols/2);
00233         img_auxV = QVImage<uChar>(cols/2, rows/2, cols/2);
00234         // ...a YUV420 rows x cols image:
00235         buf_size = rows*cols + rows*cols/4 + rows*cols/4;
00236 
00237         if (realTime)
00238                 {
00239                 qvcamerathread = new QVCameraThread(this,SLOT(updateSlot()));
00240                 qvcamerathread->start(QThread::NormalPriority);
00241                 }
00242 
00243         qDebug() << "QVMPlayerFrameGrabber::QVMPlayerFrameGrabber() <- return";
00244         };
00245 
00246 QVMPlayerFrameGrabber::~QVMPlayerFrameGrabber()
00247         {
00248         qDebug() << "QVMPlayerFrameGrabber::~QVMPlayerFrameGrabber()";
00249         finished = true;
00250         if (realTime) delete qvcamerathread;
00251         qDebug() << "QVMPlayerFrameGrabber::~QVMPlayerFrameGrabber(): thread deleted.";
00252         qDebug() << "QVMPlayerFrameGrabber::~QVMPlayerFrameGrabber() <- return";
00253         }
00254 
00255 void QVMPlayerFrameGrabber::getNewFrame(QVImage<uChar> &imgY, QVImage<uChar> &imgU, QVImage<uChar> &imgV)
00256         {
00257         qDebug() << "QVMPlayerFrameGrabber::getNewFrame(): reading YUV header";
00258         char buf[1024];
00259         fifoInput->readLine(buf, sizeof(buf));
00260         readToBuffer(imgY.getWriteData(), 2*buf_size/3);
00261         readToBuffer(imgU.getWriteData(), buf_size/6);
00262         readToBuffer(imgV.getWriteData(), buf_size/6);
00263         qDebug() << "QVMPlayerFrameGrabber::getNewFrame() <- return";
00264         }
00265 
00266 void QVMPlayerFrameGrabber::updateSlot()
00267         {
00268         qDebug() << "QVMPlayerFrameGrabber::updateSlot()";
00269 
00270         qDebug() << "QVMPlayerFrameGrabber::updateSlot(): sent command to mplayer: get_time_pos";
00271         mplayerIOProcessor->queueCommandToMPlayer("pausing_keep get_time_pos\n",true);
00272 
00273         // Avoid QSocketNotifier annoying message, by deferring call:
00274         emit sendCommandSignal(); // NOT mplayerIOProcessor->sendCommandToMPlayer();
00275 
00276         // New frame read:
00277         qDebug() << "QVMPlayerFrameGrabber::updateSlot(): reading frame";
00278         QVImage<uChar, 1> tempY;
00279         QVImage<uChar, 1> tempU;
00280         QVImage<uChar, 1> tempV;
00281 
00282         // We always read in temporal images, that are later safely copied:
00283         // There is no copy overhead because QVImage uses QSharedData (reference
00284         // counting).
00285         getNewFrame(tempY = img_auxY, tempU = img_auxU, tempV = img_auxV);
00286 
00287         // If in real time, we lock, to avoid collision with the thread that is
00288         // grabbing from the camera:
00289         if(realTime)
00290                 {
00291                 mutex.lock();
00292                 debug_in_updateSlot = true;
00293                 if (debug_in_memcpy)
00294                         {
00295                         std::cerr << "(BUG1) entered frameUpdate while memcpying!";
00296                         exit(0);
00297                         }
00298                 }
00299 
00300         img_auxY = tempY;
00301         img_auxU = tempU;
00302         img_auxV = tempV;
00303 
00304         // Unlock (if in real time):
00305         if(realTime)
00306                 {
00307                 if (debug_in_memcpy) {
00308                         std::cerr << "(BUG2) entered frameUpdate while memcpying!";
00309                         exit(0);
00310                 }
00311                 debug_in_updateSlot = false;
00312                 condition.wakeAll(); // Wake possible waiting grabs.
00313                 mutex.unlock();
00314                 }
00315 
00316         qDebug() << "QVMPlayerFrameGrabber::updateSlot(): finished" << finished;
00317         frames_read++;
00318         qDebug() << "QVMPlayerFrameGrabber::updateSlot(): frames read" << frames_read;
00319 
00320         mplayerIOProcessor->interpretMPlayerOutput();
00321 
00322         qDebug() << "QVMPlayerFrameGrabber::updateSlot() <- return";
00323 
00324         emit newReadFrameGrabber();
00325         }
00326 
00327 void QVMPlayerFrameGrabber::updateFrameBuffer()
00328         {
00329         qDebug() << "QVMPlayerFrameGrabber::updateFrameBuffer()";
00330 
00331         // Here we come from a grab, so we always lock (if in real time, it is 
00332         // obviously needed, but also if not in real time, to make non-real-cameras
00333         // grabbing also thread-safe (won't be used normally, but who knows...
00334         // maybe the programmer wish to read different frames from different
00335         // threads, without discarding any frame as could happen with real time
00336         // cameras).
00337         mutex.lock();
00338         if (debug_in_updateSlot) 
00339                 {
00340                 std::cerr << "(BUG3) entered memcpy while frameUpdating!";
00341                 exit(0);
00342                 }
00343 
00344         if(realTime)
00345                 condition.wait(&mutex); // If in real time, wait for next fresh frame.
00346         debug_in_memcpy = true;
00347 
00348         // If in real time, updateSlot is asynchronously called by a dedicated
00349         // thread, otherwise we have to call it explicitly.
00350         if(not realTime)
00351                 updateSlot();
00352 
00353         imgY = img_auxY;
00354         imgU = img_auxU;
00355         imgV = img_auxV;
00356 
00357 
00358         if (debug_in_updateSlot) 
00359                 {
00360                 std::cerr << "(BUG4) entered memcpy while frameUpdating!";
00361                 exit(0);
00362                 }
00363         debug_in_memcpy = false;
00364         mutex.unlock();
00365 
00366         qDebug() << "QVMPlayerFrameGrabber::updateFrameBuffer() <- return";
00367         }
00368 
00369 void QVMPlayerFrameGrabber::getQVImageYUV(QVImage<uChar> &imgY, QVImage<uChar> &imgU, QVImage<uChar> &imgV) const
00370         {
00371         qDebug() << "QVMPlayerFrameGrabber::getQVImageYUV()";
00372         imgY = this->imgY;
00373         imgU = this->imgU;
00374         imgV = this->imgV;
00375         qDebug() << "QVMPlayerFrameGrabber::getQVImageYUV() <- return";
00376         }
00377 
00378 void QVMPlayerFrameGrabber::getQVImageGray(QVImage<uChar> &imgGray) const
00379         {
00380         qDebug() << "QVMPlayerFrameGrabber::getQVImageYUV()";
00381         imgGray = imgY;
00382         qDebug() << "QVMPlayerFrameGrabber::getQVImageYUV() <- return";
00383         }
00384 
00385 void QVMPlayerFrameGrabber::readToBuffer(uChar *buf_img_aux, uInt buf_size)
00386         {
00387         uInt totbytes = 0;
00388         while (totbytes != buf_size and not finished)
00389                 {
00390                 int nbytes = fifoInput->read((char *)(buf_img_aux+totbytes), buf_size-totbytes);
00391 
00392                 totbytes += nbytes;
00393                 qDebug() << "QVMPlayerFrameGrabber::readNewFrame(): read" << nbytes << "bytes";
00394 
00395                 if(nbytes == 0)
00396                         {
00397                         finished = TRUE;
00398                         qDebug() << "QVMPlayerFrameGrabber::readNewFrame(): read 0 bytes, finishing" << finished;
00399                         break;
00400                         }
00401 
00402                 if(nbytes == -1)
00403                         {
00404                         // BUG: problem in Windows, suddenly pipe can not be read ?!!?
00405                         // errorString() -> "Device did not accept the command."
00406                         finished = TRUE;
00407                         qDebug() << "QVMPlayerFrameGrabber::readNewFrame(): error -" << fifoInput->errorString();
00408                         break;
00409                         }                       
00410                 }
00411         }
00412 
00413 /***************************** QVMPlayerCamera Class **************************/
00414 QVMPlayerCamera::QVMPlayerCamera(QString name):QVCamera(name), open_options(Default)
00415         {
00416         qDebug() << "QVMPlayerCamera::QVMPlayerCamera()";
00417         if (qvApp == NULL)
00418                 {
00419                 QString str = "QVWorker::QVWorker(): the QVWorker cannot be created before the QVApplication instance. Aborting now.";
00420                 std::cerr << qPrintable(str) << std::endl;
00421                 exit(1);
00422                 }
00423 
00424         frames_grabbed = 0;
00425         rgbMode = false;
00426         setStatus(Closed);
00427         live_camera = FALSE;
00428 
00429         // Properties (to allow reading from argc and argv):
00430         addProperty<int>("Rows", inputFlag, 0, "Rows to open the camera");
00431         addProperty<int>("Cols", inputFlag, 0, "Columns to open the camera");
00432         addProperty<bool>("RealTime", inputFlag, FALSE,"If the camera should be opened in real time mode");
00433         addProperty<bool>("Deinterlaced", inputFlag, FALSE,"If the camera should be opened in deinterlaced mode");
00434         addProperty<bool>("NoLoop", inputFlag, FALSE,"If the camera should be opened in no loop mode");
00435         addProperty<bool>("RGBMEncoder", inputFlag, FALSE,"If the camera should be opened in RGB using mencoder");
00436         addProperty<QString>("URL", inputFlag, QString(""),"URL of the video source");
00437         //addProperty<int>("Count",0,"Test param");
00438 
00439         // QVImage's
00440         addProperty< QVImage<uChar,3> >("RGB image", outputFlag, QVImage<uChar,3>(), "Last grabbed RGB image");
00441         addProperty< QVImage<uChar,1> >("Y channel image",outputFlag, QVImage<uChar,1>(), "Last grabbed Y channel image");
00442         addProperty< QVImage<uChar,1> >("U channel image",outputFlag, QVImage<uChar,1>(), "Last grabbed U channel image");
00443         addProperty< QVImage<uChar,1> >("V channel image",outputFlag, QVImage<uChar,1>(), "Last grabbed V channel image");
00444 
00445         qDebug() << "QVMPlayerCamera::QVMPlayerCamera() <- return";
00446         }
00447 
00448 QVMPlayerCamera::~QVMPlayerCamera()
00449         {
00450         qDebug() << "QVMPlayerCamera::~QVMPlayerCamera()";
00451         if (status != Closed) closeCam();
00452         qDebug() << "QVMPlayerCamera::~QVMPlayerCamera() <- deleting image";
00453         setImageBuffer(NULL);
00454         qDebug() << "QVMPlayerCamera::~QVMPlayerCamera() <- return";
00455         }
00456 
00457 void QVMPlayerCamera::initMPlayerArgs(QString urlString, uInt rows, uInt cols)
00458         {
00459         qDebug() << "QVMPlayerCamera::initMPlayerArgs()";
00460         qDebug() << "QVMPlayerCamera::initMPlayerArgs(): url string =" << urlString;
00461 
00462         // Loop option and fixed-vo must go first:
00463         if(not (open_options & NoLoop)) mplayer_args << "-loop" << "0";
00464 
00465         mplayer_args << "-fixed-vo";
00466 
00467         QUrl url(urlString);
00468 
00469         if (url.host() != "")
00470                 path = url.host() + "/";
00471 
00472         path += url.path();
00473 
00474         qDebug() << "QVMPlayerCamera::initMPlayerArgs(): path" << path;
00475 
00476         // Here we infer the type of the video source from the url.
00477         // If it is not specified in the schema of it, certain match rules are
00478         // applied to guess it out.
00479         if (url.scheme() != "")                         // schema specified by the user
00480                 schema = url.scheme();
00481         else if (urlString.startsWith("/dev/video"))    // a v4l device
00482                 schema = "v4l";
00483         else if (urlString.startsWith("/dev/dv"))       // a dv device
00484                 schema = "dv";
00485         else if (urlString.contains("*"))               // a multifile
00486                 schema = "mf";
00487         else if (urlString.startsWith("www."))          // a http file
00488                 schema = "http";
00489         else if (urlString.startsWith("ftp."))          // a ftp file
00490                 schema = "ftp";
00491 
00492         qDebug() << "QVMPlayerCamera::initMPlayerArgs(): scheme" << schema;
00493 
00494         live_camera = TRUE;     // default configuration
00495 
00496         // Different mplayer args for different kind of image sources:
00497         if ((schema == "v4l") or (schema == "v4l2") or (schema == "analog"))
00498                 {
00499                 // Video for Linux cameras:
00500                 QString urlQueryValues = QString("driver=%1:device=%2").arg(schema).arg(path);
00501 
00502                 QList<QPair<QString, QString> > queryItems = url.queryItems();
00503                 for (int i = 0; i < queryItems.size(); ++i)
00504                         urlQueryValues += ":" + queryItems.at(i).first + "=" + queryItems.at(i).second;
00505 
00506                 mplayer_args << "tv://" << "-tv" << urlQueryValues;
00507                 }
00508         else if (schema == "dv")
00509                 // DV cameras (connected through firewire):
00510                 mplayer_args << path << "-demuxer" << "rawdv" << "-cache" << "400";
00511         else if (schema == "iidc")
00512                 // IIDC cameras (connected through firewire):
00513                 // For example, iidc:///dev/video1394/0?from=/dev/video0:to=/dev/video1
00514                 qFatal("Currently this driver does not work (apparently with\n"
00515                         "vloopback writing and then reading from a fifo with mplayer\ndoes not work).\n");
00516         else if (schema == "tv")
00517                 // Analog TV input:
00518                 qFatal("tv URL: Still not implemented\n");
00519         else if (schema == "dvb")
00520                 // Digital TV input:
00521                 qFatal("dvb URL: Still not implemented\n");
00522         else    // Format can be rtsp:// http://, ftp://, dvd://, vcd://, mf:// or file://
00523                 { 
00524                 //  We pass the url it directly to mplayer:
00525                 live_camera = FALSE;
00526                 if (schema != "")
00527                         mplayer_args << QString(schema + "://" + path);
00528                 else
00529                         mplayer_args << path;
00530                 }
00531 
00532         // IMPORTANT!! All -vf filters MUST be together, separated by commas, and in
00533         // the right order. By now, we only use deinterlacing and scale, and in that order:
00534         QString aux;
00535 
00536         // Deinterlace option:
00537         if(open_options & Deinterlaced) aux = "pp=md";
00538 
00539         if(rows != 0 or cols != 0)
00540                 {
00541                 if(aux != QString())
00542                         aux += QString(",scale=%1:%2").arg(cols).arg(rows);
00543                 else
00544                         aux = QString("scale=%1:%2").arg(cols).arg(rows);
00545                 }
00546 
00547         if (aux != QString()) mplayer_args << "-vf" << aux;
00548 
00549         // Real time option:
00550         if(not (open_options & RealTime))
00551                 {
00552                 if(not live_camera)
00553                         mplayer_args << "-benchmark";
00554                 else
00555                         // The user attempted to open a live camera in continuous mode.
00556                         // We issue a warning:
00557                         qWarning("Warning: opening live cameras in continuous mode "
00558                                          "wastes less CPU time, but it is prone to delays in "
00559                                          "the pipe of images if your process is slower than "
00560                                          "the camera.");
00561                 }
00562 
00563         // Additional arguments (slave, quiet, nosound & yuv4mpeg output):
00564         mplayer_args << "-slave" << "-quiet" << "-nosound" << "-vo" << QString("yuv4mpeg:file=%1").arg(namedPipe->getInputFilePath());
00565 
00566         qDebug() << "QVMPlayerCamera::initMPlayerArgs() <- return";
00567         }
00568 
00569 // (Implicit) default open method for a camera (uses introspection to read 
00570 // parameters, allowing initialization from argc and argv):
00571 bool QVMPlayerCamera::openCam()
00572         {
00573         OpenOptions optsProp = Default;
00574 
00575         readInputProperties();
00576 
00577         if (getPropertyValue<bool>("RealTime"))         optsProp |= RealTime;
00578         if (getPropertyValue<bool>("Deinterlaced"))     optsProp |= Deinterlaced;
00579         if (getPropertyValue<bool>("NoLoop"))           optsProp |= NoLoop;
00580         if (rgbMode)                                    optsProp |= RGBMEncoder;
00581 
00582         return openCam(getPropertyValue<QString>("URL"), getPropertyValue<int>("Rows"), getPropertyValue<int>("Cols"), optsProp);
00583         }
00584 
00585 // Explicit open url (and possible options) method:
00586 bool QVMPlayerCamera::openCam(const QString & urlstring,OpenOptions opts)
00587         { return openCam(urlstring,0,0,opts); }
00588 
00589 // Main method to open a camera:
00590 bool QVMPlayerCamera::openCam(const QString & urlstring, uInt rows, uInt cols, OpenOptions opts)
00591         {
00592         qDebug() << "QVMPlayerCamera::openCam(" << urlstring << "," << rows << "," << cols << "," << opts << ")";
00593 
00594         setStatus(Closed);
00595 
00596         open_options = opts;
00597 
00598         // Pipe initialization
00599         namedPipe = new QNamedPipe(QString("mplayer"));
00600         fifo_file.setFileName(namedPipe->getOutputFilePath());
00601 
00602         initMPlayerArgs(urlstring, rows, cols);
00603 
00604         qDebug() << "QVMPlayerCamera::openCam(): MPlayer args ->" << mplayer_args;
00605         mplayer.start(MPLAYER_BINARY_PATH,mplayer_args);
00606         qDebug() << "QVMPlayerCamera::openCam(): after mplayer.start()";
00607         if(not mplayer.waitForStarted(1000))
00608                 qFatal("Mplayer failed to start: Are you sure it is installed and in the correct PATH?");
00609         qDebug() << "QVMPlayerCamera::openCam(): after mplayer.waitForstarted()";
00610         
00611         // We create a guarding thread that will unblock the subsequent
00612         // fifo_file.readLine in the case that mplayer does not start OK (due,
00613         // for example, to a wrong URL, an incorrect file format, or whatever).
00614         // If mplayer starts OK before two seconds (for local videos) or ten
00615         // seconds (for DVD, VCD and remote videos), this thread silently stops and
00616         // is deleted.
00617         QVCheckOKMplayerCamera *check_thread;
00618         if (schema == "http" or schema == "ftp" or schema == "rtsp" or schema == "dvd" or schema == "vcd")
00619                 check_thread = new QVCheckOKMplayerCamera(fifo_file, 10000);
00620         else
00621                 check_thread = new QVCheckOKMplayerCamera(fifo_file, 2000);
00622 
00623         check_thread->moveToThread(check_thread);
00624         check_thread->start();
00625         qDebug() << "QVMPlayerCamera::openCam(): after starting QVCheckOKMplayerCamera thread";
00626 
00627         // Important to open in ReadWrite mode, to avoid infinite waiting if
00628         // mplayer did not start OK, and because perhaps the former guarding
00629         // thread will write in the FIFO.
00630         qDebug() << "QVMPlayerCamera::openCam(): opening fifo_file";
00631         if((fifo_file.open(QIODevice::ReadWrite|QIODevice::Unbuffered)) == -1)
00632                 qFatal("Error opening fifo %s\n", qPrintable(namedPipe->getPipeName()));
00633         qDebug() << "QVMPlayerCamera::openCam(): reading fifo_file";
00634 
00635         // Now we read YUV header:
00636         char buf[1024];
00637         int len;
00638         if ((len = fifo_file.readLine(buf, sizeof(buf))) == -1) return FALSE;
00639         if (QString(buf) == QString("MPLAYER ERROR\n")) { 
00640                 std::cerr << "QVMPlayerCamera::openCam(): Warning: Mplayer could not open the requested video source (" << qPrintable(urlstring) << ") of type " << qPrintable(schema) <<std::endl;
00641                 check_thread->quit();
00642                 check_thread->wait();
00643                 delete check_thread;
00644 
00645                 // Cleaning: mplayer.waitForFinished();
00646                 mplayer.terminate();
00647                 if (not mplayer.waitForFinished(500)) mplayer.kill();   
00648                 fifo_file.close();
00649                 delete namedPipe;
00650 
00651                 qDebug() << "QVMPlayerCamera::openCam(): QVCheckOKMplayerCamera thread deleted after detecting an error; returning FALSE";
00652                 return FALSE;
00653         }
00654 
00655         check_thread->quit();
00656         check_thread->wait();
00657 
00658         delete check_thread;
00659         qDebug() << "QVMPlayerCamera::openCam(): QVCheckOKMplayerCamera thread deleted after correct launch of mplayer";
00660 
00661         qDebug() << "QVMPlayerCamera::openCam(): going to create QVMPlayerIOProcessor...";
00662         mplayerIOProcessor = new QVMPlayerIOProcessor(&mplayer);
00663 
00664         qDebug() << "QVMPlayerCamera::openCam(): waiting for initialization.";
00665         while(getRows() == 0 || getCols() == 0)
00666                 mplayerIOProcessor->interpretMPlayerOutput();
00667 
00668         qDebug() << "QVMPlayerCamera::openCam(): video dims (" << getCols() << "," << getRows() << ")";
00669         qDebug() << "QVMPlayerCamera::openCam(): fps " << getFPS();
00670 
00671         mplayerIOProcessor->queueCommandToMPlayer("get_time_length");
00672 
00673         setImageBuffer(new QVImage<uChar,1>(getRows(), getCols(), getCols()));
00674         mplayerFrameGrabber = new QVMPlayerFrameGrabber(mplayerIOProcessor, &fifo_file, getRows(), getCols(), /*false,*/ open_options & QVMPlayerCamera::RealTime);
00675 
00676         connect(mplayerFrameGrabber,SIGNAL(newReadFrameGrabber()),this,SIGNAL(newRead()));
00677 
00678         // These signals are to defer the call to sendCommandToMplayer, and avoid
00679         // the "QSocketNotifier:: socket notifier cannot be enabled from another
00680         // thread" warning.
00681         connect(mplayerFrameGrabber,SIGNAL(sendCommandSignal()),mplayerIOProcessor,SLOT(sendCommandToMPlayer()));
00682         connect(this,SIGNAL(sendCommandSignal()),mplayerIOProcessor,SLOT(sendCommandToMPlayer()));
00683 
00684         setStatus(Running);
00685 
00686         qDebug() << "QVMPlayerCamera::openCam() <- return";
00687 
00688         emit camOpened();
00689 
00690         //grab();
00691 
00692         return TRUE;
00693         }
00694 
00695 // Close camera method:
00696 void QVMPlayerCamera::closeCam()
00697         {
00698         qDebug() << "QVMPlayerCamera::closeCam()";
00699 
00700         if (status == Closed)
00701                 {
00702                 qDebug() << "QVMPlayerCamera::closeCam(): camera already closed. Returning";
00703                 qDebug() << "QVMPlayerCamera::closeCam() <- return";
00704                 return;
00705                 }
00706 
00707         setStatus(Closed);
00708 
00709         emit camClosed();
00710 
00711         mplayerIOProcessor->queueCommandToMPlayer("quit");      
00712         // Avoid QSocketNotifier annoying message, by deferring call:
00713         emit sendCommandSignal(); // NOT mplayerIOProcessor->sendCommandToMPlayer();
00714 
00715         delete mplayerFrameGrabber;
00716         qDebug() << "QVMPlayerCamera::closeCam(): mplayerFrameGrabber deleted";
00717 
00718         delete mplayerIOProcessor;
00719         qDebug() << "QVMPlayerCamera::closeCam(): mplayerIOProcessor deleted";
00720 
00721         mplayer.terminate();
00722         while (not mplayer.waitForFinished(500)) mplayer.kill();
00723         qDebug() << "QVMPlayerCamera::closeCam(): mplayer terminated";
00724         
00725         fifo_file.close();
00726         qDebug() << "QVMPlayerCamera::closeCam(): closed fifo_file";
00727         
00728         delete namedPipe;
00729         qDebug() << "QVMPlayerCamera::closecam(): deleted namedpipe";
00730 
00731         qDebug() << "QVMPlayerCamera::closeCam() <- return";
00732         }
00733 
00734 bool QVMPlayerCamera::grab()
00735         {
00736         qDebug() << "QVMPlayerCamera::grab()";
00737 
00738         if(!performGrab())
00739                 return false;
00740 
00741         QVImage<uChar> grabbedYImage, grabbedUImage, grabbedVImage;
00742         mplayerFrameGrabber->getQVImageYUV(grabbedYImage, grabbedUImage, grabbedVImage);
00743 
00744         setPropertyValue< QVImage<uChar,1> >("Y channel image", grabbedYImage);
00745         setPropertyValue< QVImage<uChar,1> >("U channel image", grabbedUImage);
00746         setPropertyValue< QVImage<uChar,1> >("V channel image", grabbedVImage);
00747 
00748         if (open_options & QVMPlayerCamera::RGBMEncoder)
00749                 {
00750                 QVImage<uChar,3> grabbedRGBImage(grabbedYImage.getCols(),grabbedYImage.getRows());
00751                 YUV420ToRGB(grabbedYImage, grabbedUImage, grabbedVImage, grabbedRGBImage);
00752                 setPropertyValue< QVImage<uChar,3> >("RGB image", grabbedRGBImage);
00753                 }
00754         /*else  {
00755                 //QVImage<uChar> grabbedYImage, grabbedUImage, grabbedVImage;
00756                 //mplayerFrameGrabber->getQVImageYUV(grabbedYImage, grabbedUImage, grabbedVImage);
00757                 setPropertyValue< QVImage<uChar,1> >("Y channel image", grabbedYImage);
00758                 setPropertyValue< QVImage<uChar,1> >("U channel image", grabbedUImage);
00759                 setPropertyValue< QVImage<uChar,1> >("V channel image", grabbedVImage);
00760                 }*/
00761 
00762         writeOutputProperties();
00763 
00764         qDebug() << "QVMPlayerCamera::grab() <~ return";
00765 
00766         return true;
00767         }
00768 
00769 bool QVMPlayerCamera::grab(QVImage<uChar> &imgY, QVImage<uChar> &imgU, QVImage<uChar> &imgV)
00770         {
00771         qDebug() << "QVMPlayerCamera::grab(imageY, imageU, imageV)";
00772 
00773         bool newImages = performGrab();
00774 
00775         if (newImages)
00776                 mplayerFrameGrabber->getQVImageYUV(imgY, imgU, imgV);
00777 
00778         setPropertyValue< QVImage<uChar,1> >("Y channel image", imgY);
00779         setPropertyValue< QVImage<uChar,1> >("U channel image", imgU);
00780         setPropertyValue< QVImage<uChar,1> >("V channel image", imgV);
00781 
00782         qDebug() << "QVMPlayerCamera::grab() <~ return";
00783 
00784         return newImages;
00785         }
00786 
00787 bool QVMPlayerCamera::grab(QVImage<uChar,3> & imageRGB)
00788         {
00789         qDebug() << "QVMPlayerCamera::grab(imageRGB)";
00790         bool newImages = performGrab();
00791 
00792         QVImage<uChar> grabbedYImage, grabbedUImage, grabbedVImage;
00793 
00794         if (newImages)
00795                 {
00796                 mplayerFrameGrabber->getQVImageYUV(grabbedYImage, grabbedUImage, grabbedVImage);
00797 
00798                 imageRGB = QVImage<uChar,3>(grabbedYImage.getCols(),grabbedYImage.getRows());
00799                 YUV420ToRGB(grabbedYImage, grabbedUImage, grabbedVImage, imageRGB);
00800                 }
00801 
00802         setPropertyValue< QVImage<uChar,3> >("RGB image", imageRGB);
00803         
00804         qDebug() << "QVMPlayerCamera::grab() <~ return";
00805         return newImages;
00806         }
00807 
00808 bool QVMPlayerCamera::grab(QVImage<uChar,1> & imageGray)
00809         {
00810         qDebug() << "QVMPlayerCamera::grab(imageGray)";
00811 
00812         bool newImages = performGrab();
00813 
00814         if (newImages)
00815                 mplayerFrameGrabber->getQVImageGray(imageGray);
00816 
00817         setPropertyValue< QVImage<uChar,1> >("Y channel image", imageGray);
00818 
00819         qDebug() << "QVMPlayerCamera::grab() <~ return";
00820         return newImages;
00821         }
00822 
00823 bool QVMPlayerCamera::performGrab()
00824         {
00825         // First, if there are any pending commands, we send them to mplayer:
00826         qDebug() << "QVMPlayerCamera::performGrab()";
00827 
00828         if (isClosed()) return false;
00829 
00830         qDebug() << "QVMPlayerCamera::performGrab(): status != Closed";
00831 
00832         bool newFrameGrabbed = false;
00833         // This is for when the camera is paused; it will return the same frames
00834         // over and over, but not immediately, but at a maximum fps rate (to avoid
00835         // intense CPU usage, that will slow the rest of the application and the
00836         // GUI.
00837 
00838         switch(status)
00839                 {
00840                 case RunningOneStep:
00841                         qDebug() << "QVMPlayerCamera::performGrab(): status == RunningOneStep";
00842                         setStatus(Paused);
00843                 case Running:
00844                         qDebug() << "QVMPlayerCamera::performGrab(): status == Running";
00845                         mplayerFrameGrabber->updateFrameBuffer();
00846                         frames_grabbed++;
00847                         newFrameGrabbed = true;
00848                         qDebug() << "QVMPlayerCamera::performGrab(): frames grabbed" << getFramesGrabbed();
00849                         break;
00850 
00851                 case Paused:
00852                         qDebug() << "QVMPlayerCamera::performGrab(): status == Paused";
00853                         /*
00854                         mutexJustToSleepMiliseconds.lock();
00855                         qDebug() << "QVMPlayerCamera::performGrab(): pausing" << milisecsToWait
00856                                 << "milisecs (video is "<< getFPS() <<" FPS's)";
00857                         conditionJustToSleepMiliseconds.wait(&mutexJustToSleepMiliseconds,milisecsToWait);
00858                         qDebug() << "QVMPlayerCamera::performGrab(): unlocking";
00859                         mutexJustToSleepMiliseconds.unlock(); */
00860                         break;
00861 
00862                 default:
00863                         break;
00864                 }
00865         qDebug() << "QVMPlayerCamera::performGrab(): checking finished";
00866 
00867         if (mplayerFrameGrabber->finished)
00868                 closeCam();
00869         else
00870                 emit newGrab();
00871 
00872         qDebug() << "QVMPlayerCamera::performGrab() <- return";
00873 
00874         return newFrameGrabbed;
00875         }
00876 
00877 // Pause, unpause, next frame, set speed, seek and close camer slots:
00878 // In all of them, the corresponding command is sent to the shared command queue
00879 // data structure, so access is accordingly protected .
00880 void QVMPlayerCamera::pauseCam()
00881         {
00882         qDebug() << "QVMPlayerCamera::pauseCam()";
00883         if(status == Closed) return;
00884         setStatus(Paused);
00885         qDebug() << "QVMPlayerCamera::pauseCam() <- return";
00886         }
00887 
00888 void QVMPlayerCamera::unpauseCam()
00889         {
00890         qDebug() << "QVMPlayerCamera::unpauseCam()";
00891         if(status == Closed) return;
00892         setStatus(Running);
00893         qDebug() << "QVMPlayerCamera::unpauseCam() <~ return";
00894         }
00895 
00896 void QVMPlayerCamera::nextFrameCam()
00897         {
00898         qDebug() << "QVMPlayerCamera::nextFrameCam()";
00899         if(status == Closed) return;
00900         setStatus(RunningOneStep);
00901         qDebug() << "QVMPlayerCamera::nextFrameCam() <~ return";
00902         }
00903 
00904 void QVMPlayerCamera::setSpeedCam(double d)
00905         {
00906         qDebug() << "QVMPlayerCamera::setSpeedCam()";
00907         if(status == Closed) return;
00908         mplayerIOProcessor->queueCommandToMPlayer(QString("pausing_keep speed_set ") + QString::number(d));
00909         mplayerIOProcessor->queueCommandToMPlayer("get_property speed");
00910         qDebug() << "QVMPlayerCamera::setSpeedCam() <~ return";
00911         }
00912 
00913 void QVMPlayerCamera::seekCam(TSeekType seek,double d)
00914         {
00915         qDebug() << "QVMPlayerCamera::seekCam()";
00916         if(status == Closed) return;
00917         if(status == Paused)
00918                 setStatus(RunningOneStep);
00919         QString command = QString("pausing_keep seek ") + QString::number(d) + " " + QString::number(seek);
00920         mplayerIOProcessor->queueCommandToMPlayer(command);
00921         qDebug() << "QVMPlayerCamera::seekCam() <~ return";
00922         }
00923 
00924 bool QVMPlayerCamera::link(QVWorker* worker, const QString imageName)
00925         {
00926         if(worker->isType<QVImage<uChar,1> >(imageName))
00927                 {
00928                 connect(worker, SIGNAL(startIteration()), this, SLOT(grab()), Qt::DirectConnection);
00929                 return this->linkProperty("Y channel image", worker, imageName, AsynchronousLink);
00930                 }
00931         else if(worker->isType<QVImage<uChar,3> >(imageName))
00932                 {
00933                 rgbMode = true;
00934                 connect(worker, SIGNAL(startIteration()), this, SLOT(grab()), Qt::DirectConnection);
00935                 return this->linkProperty("RGB image", worker, imageName, AsynchronousLink);
00936                 }
00937         else
00938                 {
00939                 std::cerr << "Warning: QVMPlayerCamera::link(): unsupported property type: property " << qPrintable(imageName)
00940                         << " in holder " << qPrintable(worker->getName()) << "is not a QVImage<uChar,1> or QVImage<uChar,3>" << std::endl;
00941                 return FALSE; //Should never get here;
00942                 }
00943         }
00944 
00945 bool QVMPlayerCamera::link(QVWorker* worker, const QString imageNameY, const QString imageNameU, const QString imageNameV)
00946         {
00947         if(!worker->isType< QVImage<uChar,1> >(imageNameY))
00948                 {
00949                 std::cerr << "Warning: QVMPlayerCamera::link(): image " << qPrintable(imageNameY) << "is not of type QVImage<uChar,1> in holder "
00950                         << qPrintable(worker->getName()) << std::endl;
00951                 return FALSE;
00952                 }
00953 
00954         if(!worker->isType< QVImage<uChar,1> >(imageNameU))
00955                 {
00956                 std::cerr << "Warning: QVMPlayerCamera::link(): image " << qPrintable(imageNameU) << "is not of type QVImage<uChar,1> in holder "
00957                         << qPrintable(worker->getName()) << std::endl;
00958                 return FALSE;
00959                 }
00960 
00961         if(!worker->isType< QVImage<uChar,1> >(imageNameV))
00962                 {
00963                 std::cerr << "Warning: QVMPlayerCamera::link(): image " << qPrintable(imageNameV) << "is not of type QVImage<uChar,1> in holder "
00964                         << qPrintable(worker->getName()) << std::endl;
00965                 return FALSE;
00966                 }
00967 
00968         connect(worker, SIGNAL(startIteration()), this, SLOT(grab()), Qt::DirectConnection);
00969         return  this->linkProperty("Y channel image", worker, imageNameY, AsynchronousLink) &&
00970                 this->linkProperty("U channel image", worker, imageNameU, AsynchronousLink) &&
00971                 this->linkProperty("V channel image", worker, imageNameV, AsynchronousLink);
00972         }
00973 
00974 bool QVMPlayerCamera::unlink(QVWorker* worker, const QString imageName)
00975         {
00976                 disconnect(worker, SIGNAL(startIteration()), this, SLOT(grab()));
00977 
00978                 if (rgbMode) {
00979                         return unlinkProperty("RGB image", worker, imageName);
00980                 }
00981                 else {
00982                         return unlinkProperty("Y channel image", worker, imageName);
00983                 }
00984         }
00985