Advertisement
Guest User

Untitled

a guest
Feb 29th, 2012
107
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 15.94 KB | None | 0 0
  1. #include <iostream>
  2. #include <cstdlib>
  3. #include <q3network.h>
  4. #include <qdatetime.h>
  5. #include <qpainter.h>
  6. #include <qdir.h>
  7. #include <qtimer.h>
  8. #include <q3process.h>
  9. #include <qstringlist.h>
  10. #include <q3dict.h>
  11. #include <q3vbox.h>
  12. #include <qlayout.h>
  13. #include <qregexp.h>
  14. #include <q3url.h>
  15. #include "recorder.h"
  16.  
  17. using namespace std;
  18.  
  19. RecorderManager::RecorderManager(QObject */*parent*/, StreamStorage *storage)
  20. {
  21.    streamStorage = storage;
  22.    recList.setAutoDelete(false);
  23.    kill = false;
  24.  
  25.     connect ( streamStorage, SIGNAL(storageEvent(int, int, bool)), this, SLOT(slotStorageEvent(int, int, bool)) );
  26.     connect( streamStorage, SIGNAL(recordInserted(ChangedRecord*)), this, SLOT(slotRecordInserted(ChangedRecord*)) );
  27.     connect( streamStorage, SIGNAL(recordUpdated(ChangedRecord*)), this, SLOT(slotRecordUpdated(ChangedRecord*)) );
  28.     connect( streamStorage, SIGNAL(recordRemoved(ChangedRecord*)), this, SLOT(slotRecordRemoved(ChangedRecord*)) );
  29.  
  30.     QTimer *timer = new QTimer( this );
  31.     connect( timer, SIGNAL(timeout()), this, SLOT(timerEvent()) );
  32.     timer->start(10000, false);
  33. }
  34.  
  35. RecorderManager::~RecorderManager()
  36. {
  37.   kill = true;
  38.   recList.setAutoDelete(true);
  39.   recList.clear();
  40. }
  41.  
  42. void RecorderManager::timerEvent()
  43. {
  44.     Recorder *recorder;
  45.     QString errorMessage;
  46.  
  47.     Q3DictIterator<Recorder> i(recList);
  48.  
  49.     for(; i.current(); ++i)
  50.     {
  51.       recorder = i.current();
  52.       if (recorder)
  53.       {
  54.         if ( !recorder->checkSchedule(errorMessage) )
  55.           emit scheduleEvent(recorder->recName, errorMessage, false);
  56.       }
  57.     }
  58. }
  59.  
  60. void RecorderManager::slotStorageEvent(int ident, int eventType, bool error)
  61. {
  62.    if (ident == 105 || error) return;
  63.  
  64.    if (recList.count() != 0) // no storage event should occur during recording
  65.      cerr << "TARGET warning: storage manipulation during recording" << endl;
  66.  
  67.    if (eventType == MStorage::loaded)
  68.    {
  69.       if (recList.count() != 0) // no storage event should occur during recording
  70.         stopAllRecordings();
  71.  
  72.       streamStorage->resetRecordList();
  73.  
  74.       ValueList values(5);
  75.       while ( streamStorage->getNextRecord(values) )
  76.       {
  77.         if ( values.count() == 5 && values[0] == "recordings" )
  78.           handleNewRecord(values, false); // do not allow overwrite
  79.       }
  80.    }
  81.  
  82. }
  83.  
  84. void RecorderManager::handleNewRecord(ValueList &values, bool allowOverwrite)
  85. {
  86.    QString errorMsg;
  87.    bool result = scheduleRecording( values[1], values[2], values[3], values[4], errorMsg, allowOverwrite );
  88.  
  89.    emit scheduleEvent(values[1], errorMsg, result);
  90. }
  91.  
  92. bool RecorderManager::scheduleRecording(QString name, QString url, QString descr, QString /*handler*/, QString& errorMessage, bool allowOverwrite)
  93. {
  94.     QDateTime startDt, stopDt;
  95.     bool overwriting = false;
  96.  
  97.     // swap naming
  98.     QString fileName = url;
  99.     QString recName  = name;
  100.     url = descr;
  101.  
  102.     errorMessage = "";
  103.  
  104.     // get scheduling info
  105.     if ( !getUTime(recName, startDt, stopDt) )
  106.     {
  107.        errorMessage = "no schedule info";
  108.        return false;
  109.     }
  110.  
  111.     // ignore old recordings (empty message)
  112.     if (QDateTime::currentDateTime() > stopDt) return false;
  113.  
  114.     // abort if record file exists and contains data
  115.     QFile file(fileName);
  116.     if (file.exists() && file.size() > 0)  // existing recording
  117.     {
  118.       if (!allowOverwrite)                 // do not overwrite
  119.       {
  120.         errorMessage = "record file exists";
  121.         return false;
  122.       }
  123.         else overwriting = true;
  124.     }
  125.  
  126.     // claim file, abort on fail
  127.     if ( !file.exists() )
  128.     {
  129.       bool isOpen = file.open(QIODevice::WriteOnly);
  130.       if (isOpen)
  131.         file.close();
  132.       else
  133.       {
  134.         errorMessage = "file access problem";
  135.         return false;
  136.       }
  137.     }
  138.    
  139.     assignRecorder(recName, url, fileName, startDt, stopDt);
  140.  
  141.     errorMessage = "scheduled";
  142.     if (overwriting)
  143.       errorMessage += " (to overwrite!)";
  144.  
  145.     return true;
  146. }
  147.  
  148.  
  149. void RecorderManager::slotRecordInserted(ChangedRecord* rec)
  150. {
  151.    if (rec->ident == 105 || rec->error) return;
  152.    if (rec->values[0] == "recordings")
  153.      handleNewRecord(rec->values, false); // do not allow overwrite
  154. }
  155.  
  156. void RecorderManager::slotRecordUpdated(ChangedRecord* rec)
  157. {
  158.   QDateTime startDt, stopDt;
  159.   QString errorMsg, recName;
  160.  
  161.   if (rec->ident == 105 || rec->error) return;
  162.  
  163.   if ( rec->values[0] == "recordings" &&
  164.        !getUTime(rec->values[1], startDt, stopDt) )
  165.   {
  166.      emit scheduleEvent(rec->values[1], "no schedule info", false);
  167.      return;
  168.   }
  169.  
  170.   // no recording OR recording with parsed startDt, stopDt from here
  171.  
  172.   Recorder *recorder = recList.find(rec->oldValues[1]);
  173.  
  174.   if (recorder) // update values
  175.   {
  176.       recList.remove(recorder->recName);
  177.       recorder->startDt  = startDt;
  178.       recorder->stopDt   = stopDt;
  179.       recorder->recName  = rec->values[1];
  180.       recorder->fileName = rec->values[2];
  181.       recorder->url      = rec->values[3];
  182.       recorder->handler  = rec->values[4];
  183.       recList.insert(recorder->recName, recorder);
  184.       emit scheduleEvent(rec->values[1], "rescheduled", true);
  185.   }
  186.     else
  187.       if (rec->values[0] == "recordings" &&
  188.           QDateTime::currentDateTime() < stopDt )  // try schedule if pending recording
  189.         handleNewRecord(rec->values, true); // allow overwrite
  190.  
  191. }
  192.  
  193. void RecorderManager::slotRecordRemoved(ChangedRecord* rec)
  194. {
  195.   if (rec->ident == 105 || rec->error) return;
  196.  
  197.   // delete file if: folder=recording
  198.   if ( rec->oldValues[0] == "recordings" )
  199.   {
  200.      stopRecording(rec->values[1]);
  201.      QFile(rec->oldValues[2]).remove();
  202.   }
  203. }
  204.  
  205. // returns empty string on fail
  206. bool createRecordFile(QString& fileName, QString prefix, uint &index)
  207. {
  208.    fileName = prefix + "_" + QString::number(index);
  209.    QFile file(fileName);
  210.  
  211.    while ( file.exists() )
  212.    {
  213.      index++;
  214.      fileName = prefix + "_" + QString::number(index);
  215.      file.setName(fileName);
  216.    }
  217.    bool isOpen = file.open(QIODevice::WriteOnly);
  218.  
  219.    if (isOpen)
  220.    {
  221.      file.close();
  222.      return true;
  223.    }
  224.      
  225.    return false;
  226. }
  227.  
  228. bool deleteRecordFile(QString fileName)
  229. {
  230.     return QFile(fileName).remove();
  231. }
  232.  
  233. bool RecorderManager::createStreamItem(QString name, QString url, QString descr, QString handler)
  234. {
  235.     QString errorMsg;
  236.     ValueList values(5);
  237.     values[s_folder] = "recordings";
  238.     values[s_name]   = name;
  239.     values[s_url]    = url;
  240.     values[s_descr]  = descr;
  241.     values[s_handler]  = handler;
  242.  
  243.     return streamStorage->insertRecord(105, values, errorMsg);
  244. }
  245.  
  246. bool RecorderManager::deleteStreamItem(QString name, QString url, QString descr, QString handler)
  247. {
  248.     QString errorMsg;
  249.     ValueList values(5);
  250.     values[s_folder] = "recordings";
  251.     values[s_name]   = name;
  252.     values[s_url]    = url;
  253.     values[s_descr]  = descr;
  254.     values[s_handler]  = handler;
  255.  
  256.     return streamStorage->removeRecord(105, values, errorMsg);
  257. }
  258.  
  259.  
  260. bool RecorderManager::getUTime(QString name, QDateTime& start, QDateTime& stop)
  261. {
  262.     QRegExp expr;
  263.     int i;
  264.     bool test;
  265.     int year, month, day, sHour, sMin, eHour, eMin;
  266.  
  267.     expr.setPattern("^REC.*(\\d{4})[/-]?(\\d{2})[/-]?(\\d{2}).*(\\d{2}):?(\\d{2}).*(\\d{2}):?(\\d{2})");
  268.     i = expr.search( name, 0 );
  269.     if ( i > -1 )
  270.     {
  271.        year  = expr.cap(1).toInt(&test);
  272.        month = expr.cap(2).toInt(&test);
  273.        day   = expr.cap(3).toInt(&test);
  274.        sHour = expr.cap(4).toInt(&test);
  275.        sMin  = expr.cap(5).toInt(&test);
  276.        eHour = expr.cap(6).toInt(&test);
  277.        eMin  = expr.cap(7).toInt(&test);
  278.  
  279.        //cout << name << endl;
  280.        //cout << " " << year << month << day << " " << sHour << sMin << " " << eHour << eMin << endl;
  281.  
  282.        start = QDateTime( QDate(year, month, day), QTime(sHour, sMin) );
  283.        stop  = QDateTime( QDate(year, month, day), QTime(eHour, eMin) );
  284.  
  285.        if ( stop < start ) stop = stop.addDays(1);
  286.  
  287.        return true;
  288.     }
  289.       else
  290.        return false;
  291. }
  292.  
  293. Recorder* RecorderManager::assignRecorder(QString recName, QString url, QString fileName, QDateTime startDt, QDateTime stopDt)
  294. {
  295.       Recorder *recorder = new Recorder(this, recName, url, fileName, startDt, stopDt);
  296.  
  297.       connect ( recorder, SIGNAL( recordingStopped(Recorder*) ), this, SLOT( slotRecorderStopped(Recorder*) ) );
  298.       connect ( recorder, SIGNAL( recordingStarted(Recorder*) ), this, SLOT( slotRecorderStarted(Recorder*) ) );
  299.  
  300.       recList.insert( recName, recorder );
  301.  
  302.       return recorder;
  303. }
  304.  
  305. // returns recordName (name of stream item entry in storage)
  306. // returns empty string on error
  307. QString RecorderManager::recordNow(QString url, QString name, uint seconds, QString& errorMessage)
  308. {
  309.     errorMessage = "";
  310.  
  311.     QDateTime startDt = QDateTime::currentDateTime();
  312.     QDateTime stopDt  = startDt.addSecs(seconds);
  313.     QString date      = startDt.toString("yyyyMMdd");
  314.     QString sTime     = startDt.toString("hhmm");
  315.     QString eTime     = stopDt.toString("hhmm");
  316.  
  317.     QString path     = QString(getenv("HOME")) + "/."SUBPATH"/recordings/";
  318.     QString prefix   = path + "REC_" + date + "_" + sTime + "_" + eTime;
  319.     QString fileName = "";
  320.     QString recName  = "";
  321.     QString handler  = "";
  322.  
  323.     uint index = 0;
  324.  
  325.     QDir dir(path);
  326.     if (!dir.exists()) dir.mkdir(path);
  327.  
  328.     bool ready = false;
  329.     while (!ready)
  330.     {
  331.       if ( createRecordFile(fileName, prefix, index) )
  332.       {
  333.          recName = "REC" + QString::number(index) + " " + date + " " + sTime + " " + eTime + " " + name;
  334.          ready   = createStreamItem(recName, fileName, url, handler);
  335.          if (!ready) QFile(fileName).remove();
  336.  
  337.          if (index > 20)
  338.          {
  339.             errorMessage = "more than 20 REC files with prefix " + prefix +
  340.                            " OR stream repository problem";
  341.             fileName = "";
  342.             ready = true;
  343.          }
  344.       }
  345.          else
  346.       {
  347.          errorMessage = "cannot create file " + fileName;
  348.          fileName  = "";
  349.          ready = true;
  350.       }
  351.     }
  352.  
  353.     if (fileName != "")
  354.     {
  355.       Recorder *recorder = assignRecorder(recName, url, fileName, startDt, stopDt);
  356.  
  357.       if ( !recorder->startRecording(errorMessage) )
  358.       {
  359.         recList.remove(recName);
  360.         QFile(fileName).remove();
  361.         deleteStreamItem(recName, url, "", "");
  362.         delete recorder;
  363.         recName = "";
  364.       }
  365.     }
  366.       else recName = "";
  367.  
  368.     return recName;
  369. }
  370.  
  371. ItemStatus RecorderManager::getItemStatus(QString recName)
  372. {
  373.    Recorder *recorder = recList.find(recName);
  374.    if (recorder)
  375.      if (recorder->isRecording)
  376.        return recording;
  377.      else
  378.        return scheduled;
  379.    else
  380.      return recorded;
  381. }
  382.  
  383. void RecorderManager::stopRecording(QString recName)
  384. {
  385.    Recorder *recorder = recList.find(recName);
  386.  
  387.    if (recorder)
  388.    {
  389.       if ( !recList.remove(recName) )
  390.         cerr << TARGET": recorder instance not found in list";
  391.       recorder->stopRecording();
  392.    }
  393. }
  394.  
  395. void RecorderManager::stopAllRecordings()
  396. {
  397.     Recorder *recorder;
  398.     Q3DictIterator<Recorder> i(recList);
  399.  
  400.     for(; i.current(); ++i)
  401.     {
  402.       recorder = i.current();
  403.       if (recorder)
  404.       {
  405.         recorder->stopRecording();
  406.       }
  407.     }
  408.  
  409.     recList.clear();
  410. }
  411.  
  412. void RecorderManager::slotRecorderStarted(Recorder* recorder)
  413. {
  414.    emit recordingStarted(recorder->recName);
  415.    emit recorderActive(true);
  416.  
  417. }
  418.  
  419. void RecorderManager::slotRecorderStopped(Recorder* recorder)
  420. {
  421.    // delete empty file on fail
  422.    QString fileName = recorder->fileName;
  423.    QFile file(fileName);
  424.    if ( file.size() == 0 && file.remove() )
  425.    {
  426.        deleteStreamItem( recorder->recName, fileName, "", "" );
  427.        emit scheduleEvent(recorder->recName, "Recording removed because it was empty", false);
  428.    }
  429.  
  430.    recList.remove( recorder->recName );  // if not stopped by stopRecording call
  431.  
  432.  
  433.    emit recordingStopped( recorder->recName, recorder->getStopReason() );
  434.  
  435.    if (!kill) recorder->deleteLater();
  436.  
  437.    // check recordings and report state
  438.    Recorder *p_rec;
  439.    bool oneActive = false;
  440.  
  441.    Q3DictIterator<Recorder> i(recList);
  442.    for(; i.current(); ++i)
  443.    {
  444.       p_rec = i.current();
  445.       if (p_rec && p_rec->isRecording)
  446.         oneActive = true;
  447.    }
  448.    if (!oneActive) emit recorderActive(false);
  449. }
  450.  
  451. //-----------------------------------------------------------------
  452.  
  453. Recorder::Recorder(QObject *parent, QString recName, QString url, QString fileName,
  454.                    QDateTime startDt, QDateTime stopDt)
  455. {
  456.     myParent = parent;
  457.  
  458.     this->recName  = recName;
  459.     this->url      = url;
  460.     this->fileName = fileName;
  461.     this->startDt  = startDt;
  462.     this->stopDt   = stopDt;
  463.  
  464.     isRecording = false;
  465.  
  466.     reason = process;
  467.     sawPlayerOutput = false;
  468.  
  469.     proc = NULL;
  470. }
  471.  
  472. Recorder::~Recorder()
  473. {
  474.     if (proc)
  475.     {
  476.       if ( proc->isRunning() ) proc->tryTerminate();
  477.       //QTimer::singleShot( 1000, proc, SLOT( kill() ) );
  478.     }
  479. }
  480.  
  481. bool Recorder::checkSchedule(QString& errorMsg)
  482. {
  483.     QDateTime now = QDateTime::currentDateTime();
  484.  
  485.     if ( now >= startDt && now < stopDt && !isRecording )
  486.       return startRecording(errorMsg);
  487.  
  488.     if ( isRecording && now >= stopDt )
  489.       stopRecording();
  490.  
  491.     return true;
  492. }
  493.  
  494.  
  495. bool Recorder::startRecording(QString& errorMsg)
  496. {
  497.     if (proc != NULL)
  498.     {
  499.        errorMsg = "Already recording. Should not happen (bug).";
  500.        return false;
  501.     }
  502.  
  503.     this->url = url;
  504.  
  505.     Q3Url qurl = Q3Url(url);
  506.     if ( !qurl.isValid() || qurl.protocol() == "file" || qurl.isLocalFile() )
  507.     {
  508.       errorMsg = "invalid URL: " + url;
  509.       return false;
  510.     }
  511.  
  512.     startStream();
  513.  
  514.     return true;
  515. }
  516.  
  517. void Recorder::stopRecording()
  518. {
  519.    stopStream();
  520. }
  521.  
  522.  
  523. void Recorder::parsePlayerOutput(const QString /*msg*/)
  524. {
  525.    sawPlayerOutput = true;
  526.    // assume player is responsible for unwanted abort
  527. }
  528.  
  529.  
  530. void Recorder::startStream()
  531. {
  532.     if ( proc ) return;
  533.  
  534.     proc = new Q3Process( this );
  535.     proc->setCommunication( Q3Process::Stdin | Q3Process::Stdout| Q3Process::Stderr| Q3Process::DupStderr );
  536.  
  537. //    proc->addArgument( "sh" );
  538. //    proc->addArgument( "-c" );
  539. //    proc->addArgument( "tail -c 100000000000000 -f /data/linuxhome/files/lessig | mplayer -" );
  540.  
  541.     proc->addArgument( "mplayer");
  542.    
  543.     QString file = Q3Url(url).fileName();
  544.     // file == "" matches find below
  545.     if ( file != "" && QString("PLAYLIST").find(Q3Url(url).fileName().right(4), 0, false) != -1 )
  546.     {
  547.        proc->addArgument( "-playlist" );
  548.     }
  549.    
  550.     proc->addArgument( url );
  551.     proc->addArgument( "-dumpstream");
  552.     proc->addArgument( "-dumpfile");
  553.     proc->addArgument( fileName);
  554.  
  555.     connect( proc, SIGNAL(readyReadStdout()), this, SLOT(readFromStdout()) );
  556.     connect( proc, SIGNAL(readyReadStderr()), this, SLOT(readFromStderr()) );
  557.     connect( proc, SIGNAL(processExited()), this, SLOT(streamExited()) );
  558.  
  559.     if ( !proc->start() )
  560.     {
  561.       fprintf( stderr, "error starting player\n" );
  562.       reason = process;
  563.       streamExited();
  564.     }
  565.       else
  566.     {
  567.        isRecording = true;
  568.        recordingStarted(this);
  569.     }
  570. }
  571.  
  572. void Recorder::stopStream()
  573. {
  574.    if ( proc && proc->isRunning() )
  575.    {
  576.       reason = command;
  577.       proc->tryTerminate();
  578.    }
  579. }
  580.  
  581. void Recorder::readFromStdout()
  582. {
  583.    QString val = "";
  584.    QString temp = " ";
  585.    while ( temp != "" )
  586.    {
  587.      temp = QString(proc->readStdout());
  588.      val += temp;
  589.    }
  590.    QStringList lines = QStringList::split( QRegExp("[\r\n|\r]"), val );
  591.    for ( QStringList::iterator line = lines.begin();
  592.          line != lines.end(); ++line )
  593.    parsePlayerOutput(*line);
  594. }
  595.  
  596. void Recorder::readFromStderr()
  597. {
  598.   // is rerouted to stdout
  599. }
  600.  
  601. void Recorder::streamExited()
  602. {
  603.    delete proc;
  604.    proc = NULL;
  605.    //cout << "recorder exited" << endl;
  606.  
  607.    // if the player failed blame it on the recorder
  608.    if (sawPlayerOutput && reason == process)
  609.      reason = recorder;
  610.  
  611.    isRecording = false;
  612.    emit recordingStopped(this);
  613. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement