PARP Research Group University of Murcia, Spain


src/qvio/qvmplayerreader.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 <iostream>
00033 
00034 #include <QDebug>
00035 #include <QStringList>
00036 #include <QRegExp>
00037 
00038 #include <qvipp.h>
00039 #include <qvio.h>
00040 
00041 #include <QVMPlayerReader>
00042 
00043 #include <QTimer>
00044 
00045 /******************* Auxiliary QVCheckOKMPlayer class *******************/
00046 
00047 QVCheckOKMPlayer::QVCheckOKMPlayer(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)
00048         {
00049         qDebug() << "QVCheckOKMPlayer::QVCheckOKMPlayer() <- starting thread";
00050         moveToThread(this);
00051         start();
00052         }
00053 
00054 void QVCheckOKMPlayer::run()
00055         {
00056         QTimer::singleShot(_max_time_ms_to_wait_for_open, this, SLOT(writeErrorInFifo()));
00057         qDebug() << "QVCheckOKMPlayer::run() <- entering event loop";
00058         exec();
00059         qDebug() << "QVCheckOKMPlayer::run() <- back from event loop";
00060         }
00061 
00062 void QVCheckOKMPlayer::writeErrorInFifo()
00063         {
00064         qDebug() << "QVCheckOKMPlayerCamera::writeErrorInFifo()";
00065         _fifo_file.write("MPLAYER ERROR\n"); 
00066         qDebug() << "QVCheckOKMPlayerCamera::writeErrorInFifo() -> return";
00067         };
00068 
00069 /***************************** QVMPlayerReader class **************************/
00070 
00071 void QVMPlayerReader::initMPlayerArgs(QString urlString, unsigned int suggested_cols, unsigned int suggested_rows)
00072         {
00073         qDebug() << "QVMPlayerReader::initMPlayerArgs(" << urlString << "," << suggested_cols << "," << suggested_rows << ")";
00074 
00075         mplayer_args = QStringList();
00076 
00077         // Loop option and fixed-vo must go first:
00078         if(not (open_options & NoLoop)) mplayer_args << "-loop" << "0";
00079 
00080         mplayer_args << "-fixed-vo";
00081 
00082         QUrl url(urlString);
00083 
00084         path = QString();
00085 
00086         if (url.host() != "")
00087                 path = url.host() + "/";
00088 
00089         path += url.path();
00090 
00091         // Here we infer the type of the video source from the url.
00092         // If it is not specified in the schema of it, certain match rules are
00093         // applied to guess it out.
00094         if (url.scheme() != "")                         // schema specified by the user
00095                 schema = url.scheme();
00096         else if (urlString.startsWith("/dev/video"))    // a v4l device
00097                 schema = "v4l";
00098         else if (urlString.startsWith("/dev/dv"))       // a dv device
00099                 schema = "dv";
00100         else if (urlString.contains("*"))               // a multifile
00101                 schema = "mf";
00102         else if (urlString.startsWith("www."))          // a http file
00103                 schema = "http";
00104         else if (urlString.startsWith("ftp."))          // a ftp file
00105                 schema = "ftp";
00106         else
00107                 schema = QString();
00108 
00109         live_camera = TRUE;     // Default configuration; later we set it to FALSE if it applies.
00110 
00111         // Different mplayer args for different kind of image sources:
00112         if ((schema == "v4l") or (schema == "v4l2") or (schema == "analog"))
00113                 {
00114                 // Video for Linux cameras:
00115                 QString urlQueryValues = QString("driver=%1:device=%2").arg(schema).arg(path);
00116 
00117                 QList<QPair<QString, QString> > queryItems = url.queryItems();
00118                 for (int i = 0; i < queryItems.size(); ++i)
00119                         urlQueryValues += ":" + queryItems.at(i).first + "=" + queryItems.at(i).second;
00120 
00121                 mplayer_args << "tv://" << "-tv" << urlQueryValues;
00122                 }
00123         else if (schema == "dv")
00124                 // DV cameras (connected through firewire):
00125                 mplayer_args << path << "-demuxer" << "rawdv" << "-cache" << "400";
00126         else if (schema == "iidc")
00127                 // IIDC cameras (connected through firewire):
00128                 // For example, iidc:///dev/video1394/0?from=/dev/video0:to=/dev/video1
00129                 qFatal("Currently this driver does not work (apparently with\n"
00130                         "vloopback writing and then reading from a fifo with mplayer\ndoes not work).\n");
00131         else if (schema == "tv")
00132                 // Analog TV input:
00133                 qFatal("tv URL: Still not implemented\n");
00134         else if (schema == "dvb")
00135                 // Digital TV input:
00136                 qFatal("dvb URL: Still not implemented\n");
00137         else    // Format can be rtsp:// http://, ftp://, dvd://, vcd://, mf:// or file://
00138                 { 
00139                 //  We pass the url it directly to mplayer:
00140                 live_camera = FALSE;
00141                 if (schema != "")
00142                         mplayer_args << QString(schema + "://" + path);
00143                 else
00144                         mplayer_args << path;
00145                 }
00146 
00147         // IMPORTANT!! All -vf filters MUST be together, separated by commas, and in
00148         // the right order. By now, we only use deinterlacing and scale, and in that order:
00149         QString aux;
00150 
00151         // Deinterlace option:
00152         if(open_options & Deinterlaced) aux = "pp=md";
00153 
00154         // Scaling:
00155         if(suggested_cols != 0 and suggested_rows != 0)
00156                 {
00157                 if(aux != QString())
00158                         aux += QString(",scale=%1:%2").arg(suggested_cols).arg(suggested_rows);
00159                 else
00160                         aux = QString("scale=%1:%2").arg(suggested_cols).arg(suggested_rows);
00161                 } 
00162         if (aux != QString()) mplayer_args << "-vf" << aux;
00163 
00164         // Real time option:
00165         if(not (open_options & RealTime))
00166                 {
00167                 if(not live_camera)
00168                         mplayer_args << "-benchmark";
00169                 }
00170 
00171         // Additional arguments (slave, quiet, nosound & yuv4mpeg output):
00172         mplayer_args << "-slave" << "-quiet" << "-nosound" << "-vo" << QString("yuv4mpeg:file=%1").arg(namedPipe->getInputFilePath());
00173 
00174         qDebug() << "QVMPlayerReader::initMPlayerArgs(): MPlayer args = " << mplayer_args;
00175         qDebug() << "QVMPlayerReader::initMPlayerArgs() <- return";
00176         }
00177 
00178 int QVMPlayerReader::interpretMPlayerOutput()
00179         {
00180         int length = -1;
00181         char buf[1024];
00182 
00183         length = mplayer->readLine(buf, sizeof(buf));
00184 
00185         if (length == -1)
00186                 qDebug() << "QVMPlayerReader::interpretMPlayerOutput(): length == -1";
00187         else    {
00188                 QString str(buf);
00189                 QStringList variables = str.simplified().split("=");
00190                 QStringList palabras = str.simplified().split(" ");
00191 
00192                 if(variables[0] == "ANS_LENGTH")
00193                         {
00194                         time_length = variables[1].toDouble();
00195                         qDebug() << "QVMPlayerReader::interpretMPlayerOutput(): updating time_length =" << time_length;
00196                         }
00197                 else if(variables[0] == "ANS_TIME_POSITION")
00198                         {
00199                         time_pos = variables[1].toDouble();
00200                         qDebug() << "QVMPlayerReader::interpretMPlayerOutput(): updating time_pos =" << time_pos;
00201                         }
00202                 else
00203                         qDebug() << "QVMPlayerReader::interpretMPlayerOutput(): uninterpreted mplayer output:" << str;
00204                 }
00205         qDebug() << "QVMPlayerReader::interpretMPlayerOutput() <- return " << length;
00206         return length;
00207         }
00208 
00209 bool QVMPlayerReader::performGrab()
00210         {
00211         qDebug() << "QVMPlayerReader::performGrab()";
00212 
00213         if (not camera_opened)
00214                 {
00215                 qDebug() << "QVMPlayerReader::performGrab() returns FALSE (camera is closed)";
00216                 return false;
00217                 }
00218 
00219         qDebug() << "QVMPlayerReader::performGrab(): sending command get_time_pos to mplayer";
00220         mplayer->write("pausing_keep get_time_pos\n"); // pausing_keep in fact not needed...
00221 
00222         // New frame read:
00223         qDebug() << "QVMPlayerReader::performGrab: reading YUV frame: readYUV4MPEG2Frame()";
00224         if (!readYUV4MPEG2Frame(fifoInput, imgY, imgU, imgV))
00225                 {
00226                 qDebug() << "QVMPlayerReader::performGrab: No more frames left, closing camera";
00227                 end_of_video = TRUE;
00228                 closeCam();
00229                 qDebug() << "QVMPlayerReader::performGrab: No more frames left, camera closed, returning false";
00230                 return FALSE;
00231                 }
00232 
00233         frames_grabbed++;
00234         qDebug() << "QVMPlayerReader::performGrab: new frame read (" << frames_grabbed << ")";
00235 
00236         // Interpret mplayer output while it is writing something:
00237         qDebug() << "QVMPlayerReader::performGrab: now interpreting mplayer output";
00238         while(interpretMPlayerOutput() > 0);
00239 
00240         qDebug() << "QVMPlayerReader::performGrab: emitting newGrab() signal";
00241         emit newGrab();
00242 
00243         qDebug() << "QVMPlayerReader::performGrab() <- returning TRUE";
00244         return TRUE;
00245         }
00246 
00247 static inline int iRoundUp(int a, int b) {
00248   return (a % b == 0) ? a : b*(a / b + 1) ;
00249 }
00250 
00251 bool QVMPlayerReader::openCam(const QString & urlstring, OpenOptions opts, unsigned int suggested_cols, unsigned int suggested_rows)
00252         {
00253         qDebug() << "QVMPlayerReader::openCam(" << qPrintable(urlstring) << "," << static_cast<int>(opts) << ","
00254                 << suggested_cols << "," << suggested_rows << "," << opts << ")";
00255 
00256         if (camera_opened)
00257                 {
00258                 qDebug() << "QVMPlayerReader::openCam() <- closing previously opened camera";
00259                 closeCam();
00260                 qDebug() << "QVMPlayerReader::openCam() <- previously opened camera closed";
00261                 }
00262 
00263         open_options = opts;
00264 
00265         // Pipe initialization
00266         namedPipe = new QNamedPipe(QString("mplayer"));
00267         qDebug() << "QVMPlayerReader::openCam(): Named pipe created" << namedPipe->getOutputFilePath();
00268 
00269         // Launching MPlayer:
00270         mplayer = new QProcess;
00271         initMPlayerArgs(urlstring, suggested_cols, suggested_rows);
00272         mplayer->start(MPLAYER_BINARY_PATH, mplayer_args);
00273         qDebug() << "QVMPlayerReader::openCam(): after mplayer->start()";
00274         if(not mplayer->waitForStarted(1000))
00275                 qFatal("Mplayer failed to start in a second: Are you sure it is installed and in the correct PATH?");
00276         qDebug() << "QVMPlayerReader::openCam(): after mplayer->waitForstarted()";
00277         
00278         // Important to open fifo now in ReadWrite mode, to avoid infinite waiting if mplayer did
00279         // not start OK, and because perhaps the former guarding thread will write in the FIFO:
00280         qDebug() << "QVMPlayerReader::openCam(): opening fifo " << qPrintable(namedPipe->getOutputFilePath());
00281         fifoInput.setFileName(namedPipe->getOutputFilePath());
00282         if(fifoInput.open(QIODevice::ReadWrite|QIODevice::Unbuffered) == -1)
00283                 qFatal("Error opening fifo %s\n", qPrintable(namedPipe->getOutputFilePath()));
00284         qDebug() << "QVMPlayerReader::openCam(): fifo opened";
00285 
00286         // We create a guarding thread that will unblock the subsequent readYUV4MPEG2Header in the
00287         // case that mplayer does not start OK (due, for example, to a wrong URL, an incorrect
00288         // file format, or whatever). If mplayer starts OK before two seconds (for local videos) or
00289         // ten seconds (for DVD, VCD and remote videos), this thread silently stops and is deleted.
00290         const uInt waitMilisecs = (     schema == "http" or schema == "ftp" or
00291                                         schema == "rtsp" or schema == "dvd" or
00292                                         schema == "vcd" )? 10000:2000;
00293         QVCheckOKMPlayer check_thread(fifoInput, waitMilisecs);
00294 
00295         qDebug()<< "QVMPlayerReader::openCam(): going to read YUV header";
00296         // Now we read the YUV header:
00297         if (!readYUV4MPEG2Header(fifoInput, cols, rows, fps))
00298                 {
00299                 qWarning() << "QVMPlayerReader::openCam(): Warning: Mplayer could not open the requested video source ("
00300                            << qPrintable(urlstring) << ") of type " << qPrintable(schema);
00301                 qDebug() << "QVMPlayerReader::openCam(): Terminating and killing mplayer";
00302                 mplayer->terminate();
00303                 if (not mplayer->waitForFinished(500)) mplayer->kill(); 
00304                 qDebug() << "QVMPlayerReader::openCam(): Deleting pipe and mplayer";
00305                 delete namedPipe;
00306                 delete mplayer;
00307                 qDebug() << "QVMPlayerReader::openCam(): closing fifo";
00308                 fifoInput.close();
00309                 // Finish guarding thread.
00310                 while(not check_thread.isFinished())
00311                 {
00312                         qDebug() << "QVMPlayerReader::openCam(): quitting guarding thread (incorrect mplayer launch)";
00313                         check_thread.quit();
00314                         qDebug() << "QVMPlayerReader::openCam(): CheckOKMPlayerCamera thread quitted after correct launch of mplayer";
00315                         check_thread.wait(100);
00316                 }               
00317                 qDebug() << "QVMPlayerReader::openCam(): guarding thread finished after incorrect launch of mplayer";
00318                 qDebug() << "QVMPlayerReader::openCam(): Returning FALSE";
00319                 cols = 0;
00320                 rows = 0;
00321                 fps = 0;
00322                 frames_grabbed = 0;
00323                 camera_opened = FALSE;
00324                 return FALSE;
00325                 }
00326         qDebug()<< "QVMPlayerReader::openCam(): back from read YUV header: cols = " << cols << " rows = " << rows << ", fps = " << fps;
00327 
00328         // Finish guarding thread.
00329         while(not check_thread.isFinished())
00330         {
00331                 qDebug() << "QVMPlayerReader::openCam(): quitting guarding thread  (correct mplayer launch)";
00332                 check_thread.quit();
00333                 qDebug() << "QVMPlayerReader::openCam(): CheckOKMPlayerCamera thread quitted after correct launch of mplayer";
00334                 check_thread.wait(100);
00335         }               
00336         qDebug() << "QVMPlayerReader::openCam(): CheckOKMPlayerCamera thread finished after correct launch of mplayer";
00337 
00338         // Now we close the fifo and open it again in ReadOnly mode (to avoid readYUV4MPEGFrame to block if we reach
00339         // the end of the mplayer video). There is also another "dirty trick" here: we must open an aux fifo (which we
00340         // close just a little bit later) in order not to leave at any moment the FIFO without readers (which could
00341         // provoke a race condition, because the mplayer process could decide to write in the FIFO just when there
00342         // are not any FIFO open for reading on it):
00343         QFile fifoAux;
00344         fifoAux.setFileName(namedPipe->getOutputFilePath());
00345         fifoAux.open(QIODevice::ReadOnly);
00346         fifoInput.close();
00347         // Here we open the "good" FIFO again, but now in ReadOnly mode:
00348         if(fifoInput.open(QIODevice::ReadOnly|QIODevice::Unbuffered) == -1)
00349                 qFatal("Error opening fifo %s\n", qPrintable(namedPipe->getOutputFilePath()));
00350         fifoAux.close();
00351 
00352         qDebug() << "QVMPlayerReader::openCam(): Header correctly read: cols = "
00353                  << cols << ", rows = " << rows << ", fps = " << fps;
00354 
00355         // If we get here, the header was correctly read, proceed to init MPlayer IO processing.
00356         frames_grabbed = 0;
00357         camera_opened = TRUE;
00358 
00359         // Create adequately sized images (observe 8 byte step alignment by rows):
00360         imgY = QVImage<uChar>(cols, rows, iRoundUp(cols,8));
00361         imgU = QVImage<uChar>(cols/2, rows/2, iRoundUp(cols/2,8));
00362         imgV = QVImage<uChar>(cols/2, rows/2, iRoundUp(cols/2,8));
00363 
00364         // THIS DOES NOT WORK (mplayer does not write anything until later... so we have to read from it in performGrab()):
00365         // while(mplayerIO->time_length == 0)
00366         //      mplayerIO->interpretMPlayerOutput();
00367 
00368         qDebug() << "QVMPlayerReader::openCam() <- sending get_time_length command to mplayer";
00369         mplayer->write("get_time_length\n");
00370         mplayer->waitForReadyRead();
00371 
00372         qDebug() << "QVMPlayerReader::openCam() <- emitting camOpened signal";
00373         emit camOpened();
00374 
00375         qDebug() << "QVMPlayerReader::openCam() <- return";
00376         return TRUE;
00377         }
00378 
00379 void QVMPlayerReader::closeCam()
00380         {
00381         qDebug() << "QVMPlayerReader::closeCam()";
00382 
00383         if (not camera_opened)
00384                 {
00385                 qDebug() << "QVMPlayerReader::closeCam(): camera already closed. Returning";
00386                 return;
00387                 }
00388 
00389         qDebug() << "QVMPlayerReader::closeCam(): closing fifo";
00390         fifoInput.close();
00391 
00392         if(not end_of_video) 
00393                 {
00394                 qDebug() << "QVMPlayerReader::closeCam(): going to send quit command to mplayer";
00395                 mplayer->write("quit\n");
00396                 }
00397         qDebug() << "QVMPlayerReader::closeCam(): going to terminate mplayer";
00398         mplayer->terminate();
00399         qDebug() << "QVMPlayerReader::closeCam(): going to kill mplayer";
00400         mplayer->kill();
00401         qDebug() << "QVMPlayerReader::closeCam(): going to wait for mplayer to finish";
00402         mplayer->waitForFinished();
00403         qDebug() << "QVMPlayerReader::closeCam(): mplayer finished";
00404 
00405         qDebug() << "QVMPlayerReader::closecam(): deleting namedpipe";          
00406         delete namedPipe;
00407 
00408         qDebug() << "QVMPlayerReader::closecam(): deleting QProcess mplayer";
00409         delete mplayer;
00410 
00411         // We reset all the members of the class:
00412         open_options = Default;
00413         path = QString();
00414         schema = QString();
00415         camera_opened = FALSE;
00416         frames_grabbed = 0;
00417         live_camera = FALSE;
00418         imgY = QVImage<uChar>();
00419         imgU = QVImage<uChar>();
00420         imgV = QVImage<uChar>();
00421         cols = 0;
00422         rows = 0;
00423         fps = 0;
00424         time_length = 0;
00425         time_pos = 0;
00426         end_of_video = FALSE;
00427 
00428         qDebug() << "QVMPlayerReader::closeCam() <- emitting camClosed signal";
00429         emit camClosed();
00430 
00431         qDebug() << "QVMPlayerReader::closeCam() <- return";
00432         }
00433 
00434 bool QVMPlayerReader::grab(QVImage<uChar,1> & imageGray)
00435         {
00436         qDebug() << "QVMPlayerReader::grab(imageGray)";
00437         if (performGrab())
00438                 {
00439                 imageGray = imgY;
00440                 return TRUE;
00441                 }
00442         else
00443                 return FALSE;
00444         }
00445 
00446 bool QVMPlayerReader::grab(QVImage<uChar,3> & imageRGB)
00447         {
00448         qDebug() << "QVMPlayerReader::grab(imageRGB)";
00449         if (performGrab())
00450                 {
00451                 imageRGB = QVImage<uChar,3>(imgY.getCols(),imgY.getRows());
00452                 YUV420ToRGB(imgY, imgU, imgV, imageRGB);
00453                 return TRUE;
00454                 }
00455         else
00456                 return FALSE;
00457         }
00458 
00459 bool QVMPlayerReader::grab(QVImage<uChar> &imageY, QVImage<uChar> &imageU, QVImage<uChar> &imageV)
00460         {
00461         qDebug() << "QVMPlayerReader::grab(imageY, imageU, imageV)";
00462         if (performGrab())
00463                 {
00464                 imageY = imgY;
00465                 imageU = imgU;
00466                 imageV = imgV;
00467                 return TRUE;
00468                 }
00469         else
00470                 return FALSE;
00471         }
00472 
00473 void QVMPlayerReader::seekCam(TSeekType seek,double d)
00474         {
00475         qDebug() << "QVMPlayerReader::seekCam(" << static_cast<int>(seek) << "," << d << ")";
00476         if(not camera_opened) return;
00477         QString command = QString("pausing_keep seek ") + QString::number(d) + " " + QString::number(seek) + "\n";
00478         mplayer->write(qPrintable(command)); // pausing keep in fact not needed...
00479         qDebug() << "QVMPlayerReader::seekCam() <~ return";
00480         }



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