Exemple #1
0
int SchemaUpgradeWizard::CompareAndWait(const int seconds)
{
    if (Compare() > 0)  // i.e. if DB is older than expected
    {
        QString message = tr("%1 database schema is old. Waiting to see if DB "
                             "is being upgraded.").arg(m_schemaName);

        LOG(VB_GENERAL, LOG_CRIT, message);

        MSqlQuery query(MSqlQuery::InitCon(MSqlQuery::kDedicatedConnection));
        bool      backupRunning  = false;
        bool      upgradeRunning = false;

        MythTimer elapsedTimer;
        elapsedTimer.start();
        while (versionsBehind && (elapsedTimer.elapsed() < seconds * 1000))
        {
            sleep(1);

            if (IsBackupInProgress())
            {
                LOG(VB_GENERAL, LOG_CRIT,
                         "Waiting for Database Backup to complete.");
                if (!backupRunning)
                {
                    elapsedTimer.restart();
                    backupRunning = true;
                }
                continue;
            }

            if (!lockSchema(query))
            {
                LOG(VB_GENERAL, LOG_CRIT,
                         "Waiting for Database Upgrade to complete.");
                if (!upgradeRunning)
                {
                    elapsedTimer.restart();
                    upgradeRunning = true;
                }
                continue;
            }

            Compare();
            unlockSchema(query);

            if (m_expertMode)
                break;
        }

        if (versionsBehind)
            LOG(VB_GENERAL, LOG_CRIT, "Timed out waiting.");
        else
            LOG(VB_GENERAL, LOG_CRIT,
                     "Schema version was upgraded while we were waiting.");
    }
    // else DB is same version, or newer. Either way, we won't upgrade it

    return versionsBehind;
}
Exemple #2
0
DTC::LiveStreamInfo *HTTPLiveStream::StartStream(void)
{
    if (GetDBStatus() != kHLSStatusQueued)
        return GetLiveStreamInfo();

    HTTPLiveStreamThread *streamThread =
        new HTTPLiveStreamThread(GetStreamID());
    MThreadPool::globalInstance()->startReserved(streamThread,
                                                 "HTTPLiveStream");
    MythTimer statusTimer;
    int       delay = 250000;
    statusTimer.start();

    HTTPLiveStreamStatus status = GetDBStatus();
    while ((status == kHLSStatusQueued) &&
           ((statusTimer.elapsed() / 1000) < 30))
    {
        delay = (int)(delay * 1.5);
        usleep(delay);

        status = GetDBStatus();
    }

    return GetLiveStreamInfo();
}
Exemple #3
0
bool DeviceReadBuffer::Poll(void) const
{
#ifdef USING_MINGW
#warning mingw DeviceReadBuffer::Poll
    VERBOSE(VB_IMPORTANT, LOC_ERR +
            "mingw DeviceReadBuffer::Poll is not implemented");
    return false;
#else
    bool retval = true;
    MythTimer timer;
    timer.start();

    while (true)
    {
        struct pollfd polls;
        polls.fd      = _stream_fd;
        polls.events  = POLLIN;
        polls.revents = 0;

        int ret = poll(&polls, 1 /*number of polls*/, 10 /*msec*/);

        if (polls.revents & (POLLHUP | POLLNVAL))
        {
            VERBOSE(VB_IMPORTANT, LOC + "poll error");
            error = true;
            return true;
        }

        if (!run || !IsOpen() || IsPauseRequested())
        {
            retval = false;
            break; // are we supposed to pause, stop, etc.
        }

        if (ret > 0)
            break; // we have data to read :)
        else if (ret < 0)
        {
            if ((EOVERFLOW == errno))
                break; // we have an error to handle

            if ((EAGAIN == errno) || (EINTR  == errno))
                continue; // errors that tell you to try again

            usleep(2500 /*2.5 ms*/);
        }
        else //  ret == 0
        {
            if ((uint)timer.elapsed() > max_poll_wait)
            {
                VERBOSE(VB_IMPORTANT, LOC_ERR + "Poll giving up");
                QMutexLocker locker(&lock);
                error = true;
                return true;
            }
        }
    }
    return retval;
#endif //!USING_MINGW
}
Exemple #4
0
    void run(void)
    {
        RunProlog();

        MythTimer t;
        t.start();
        QMutexLocker locker(&m_lock);
        while (true)
        {
            if (m_do_run && !m_runnable)
                m_wait.wait(locker.mutex(), m_expiry_timeout+1);

            if (!m_runnable)
            {
                m_do_run = false;

                locker.unlock();
                m_pool.NotifyDone(this);
                locker.relock();
                break;
            }

            if (!m_runnable_name.isEmpty())
                loggingRegisterThread(m_runnable_name);

            bool autodelete = m_runnable->autoDelete();
            m_runnable->run();
            if (autodelete)
                delete m_runnable;
            if (m_reserved)
                m_pool.ReleaseThread();
            m_reserved = false;
            m_runnable = NULL;

            loggingDeregisterThread();
            loggingRegisterThread(objectName());

            GetMythDB()->GetDBManager()->PurgeIdleConnections(false);
            qApp->processEvents();
            qApp->sendPostedEvents(NULL, QEvent::DeferredDelete);

            t.start();

            if (m_do_run)
            {
                locker.unlock();
                m_pool.NotifyAvailable(this);
                locker.relock();
            }
            else
            {
                locker.unlock();
                m_pool.NotifyDone(this);
                locker.relock();
                break;
            }
        }

        RunEpilog();
    }
Exemple #5
0
/** \fn PlayerContext::StartPlaying(int)
 *  \brief Starts player, must be called after StartRecorder().
 *  \param maxWait How long to wait for MythPlayer to start playing.
 *  \return true when successful, false otherwise.
 */
bool PlayerContext::StartPlaying(int maxWait)
{
    if (!player)
        return false;

    player->StartPlaying();

    maxWait = (maxWait <= 0) ? 20000 : maxWait;
#ifdef USING_VALGRIND
    maxWait = (1<<30);
#endif // USING_VALGRIND
    MythTimer t;
    t.start();

    while (!player->IsPlaying(50, true) && (t.elapsed() < maxWait))
        ReloadTVChain();

    if (player->IsPlaying())
    {
        VERBOSE(VB_PLAYBACK, LOC + "StartPlaying(): took "<<t.elapsed()
                <<" ms to start player.");
        return true;
    }
    else
    {
        VERBOSE(VB_IMPORTANT, LOC_ERR + "StartPlaying() "
                "Failed to start player");
        StopPlaying();
        return false;
    }
}
/** \fn PlayerContext::StartPlaying(int)
 *  \brief Starts player, must be called after StartRecorder().
 *  \param maxWait How long to wait for MythPlayer to start playing.
 *  \return true when successful, false otherwise.
 */
bool PlayerContext::StartPlaying(int maxWait)
{
    if (!player)
        return false;

    player->StartPlaying();

    maxWait = (maxWait <= 0) ? 20000 : maxWait;
#ifdef USING_VALGRIND
    maxWait = (1<<30);
#endif // USING_VALGRIND
    MythTimer t;
    t.start();

    while (!player->IsPlaying(50, true) && (t.elapsed() < maxWait))
        ReloadTVChain();

    if (player->IsPlaying())
    {
        LOG(VB_PLAYBACK, LOG_INFO, LOC +
            QString("StartPlaying(): took %1 ms to start player.")
                .arg(t.elapsed()));
        return true;
    }
    else
    {
        LOG(VB_GENERAL, LOG_ERR, LOC + "StartPlaying() Failed to start player");
        StopPlaying();
        return false;
    }
}
Exemple #7
0
bool ZMClient::readData(unsigned char *data, int dataSize)
{
    qint64 read = 0;
    int errmsgtime = 0;
    MythTimer timer;
    timer.start();
    int elapsed;

    while (dataSize > 0)
    {
        qint64 sret = m_socket->Read(
            (char*) data + read, dataSize, 100 /*ms*/);
        if (sret > 0)
        {
            read += sret;
            dataSize -= sret;
            if (dataSize > 0)
            {
                timer.start();
            }
        }
        else if (sret < 0)
        {
            LOG(VB_GENERAL, LOG_ERR, "readData: Error, readBlock");
            m_socket->DisconnectFromHost();
            return false;
        }
        else if (!m_socket->IsConnected())
        {
            LOG(VB_GENERAL, LOG_ERR,
                "readData: Error, socket went unconnected");
            m_socket->DisconnectFromHost();
            return false;
        }
        else
        {
            elapsed = timer.elapsed();
            if (elapsed  > 10000)
            {
                if ((elapsed - errmsgtime) > 10000)
                {
                    errmsgtime = elapsed;
                    LOG(VB_GENERAL, LOG_ERR,
                        QString("m_socket->: Waiting for data: %1 %2")
                            .arg(read).arg(dataSize));
                }
            }

            if (elapsed > 100000)
            {
                LOG(VB_GENERAL, LOG_ERR, "Error, readData timeout (readBlock)");
                return false;
            }
        }
    }

    return true;
}
Exemple #8
0
/** \fn RecorderBase::WaitForPause(int)
 *  \brief WaitForPause blocks until recorder is actually paused,
 *         or timeout milliseconds elapse.
 *  \param timeout number of milliseconds to wait defaults to 1000.
 *  \return true iff pause happened within timeout period.
 */
bool RecorderBase::WaitForPause(int timeout)
{
    MythTimer t;
    t.start();

    QMutexLocker locker(&pauseLock);
    while (!IsPaused(true) && request_pause)
    {
        int wait = timeout - t.elapsed();
        if (wait <= 0)
            return false;
        pauseWait.wait(&pauseLock, wait);
    }
    return true;
}
Exemple #9
0
int MythDatabase::AddTimer(MythTimer &timer)
{

  m_database_t->Lock();

  int retval=CMYTH->MysqlAddTimer(*m_database_t,timer.ChanID(),timer.m_callsign.Buffer(),timer.m_description.Buffer(),timer.StartTime(), timer.EndTime(),timer.m_title.Buffer(),timer.m_category.Buffer(),
    timer.Type(),timer.m_subtitle.Buffer(),timer.Priority(),timer.StartOffset(),timer.EndOffset(),timer.SearchType(),timer.Inactive()?1:0,timer.DupMethod(),timer.CheckDupIn(),timer.RecGroup().Buffer(),
    timer.StoreGroup().Buffer(),timer.PlayGroup().Buffer(),timer.AutoTranscode(),timer.Userjobs(),timer.AutoCommFlag(),timer.AutoExpire(),timer.MaxEpisodes(),timer.NewExpireOldRecord(),timer.Transcoder());
  timer.m_recordid=retval;
  m_database_t->Unlock();
  return retval;
}
Exemple #10
0
/** \fn DeviceReadBuffer::WaitForUsed(uint,uint) const
 *  \param needed Number of bytes we want to read
 *  \param max_wait Number of milliseconds to wait for the needed data
 *  \return bytes available for reading
 */
uint DeviceReadBuffer::WaitForUsed(uint needed, uint max_wait) const
{
    MythTimer timer;
    timer.start();

    QMutexLocker locker(&lock);
    size_t avail = used;
    while ((needed > avail) && isRunning() &&
           !request_pause && !error && !eof &&
           (timer.elapsed() < (int)max_wait))
    {
        dataWait.wait(locker.mutex(), 10);
        avail = used;
    }
    return avail;
}
static QString progress_string(
    MythTimer &flagTime, uint64_t m_myFramesPlayed, uint64_t totalFrames)
{
    if (totalFrames == 0ULL)
    {
        return QString("%1 frames processed    \r")
            .arg(m_myFramesPlayed,7);
    }

    double elapsed = flagTime.elapsed() * 0.001;
    double flagFPS = (elapsed > 0.0) ? (m_myFramesPlayed / elapsed) : 0;

    double percentage = m_myFramesPlayed * 100.0 / totalFrames;
    percentage = (percentage > 100.0 && percentage < 101.0) ?
        100.0 : percentage;

    if (flagFPS < 10.0)
    {
        return QString("%1 fps %2%     \r")
            .arg(flagFPS,4,'f',1).arg(percentage,4,'f',1);
    }
    else
    {
        return QString("%1 fps %2%     \r")
            .arg(flagFPS,4,'f',0).arg(percentage,4,'f',1);
    }
}
static QString progress_string(
    MythTimer &flagTime, uint64_t m_myFramesPlayed, uint64_t totalFrames)
{
    if (totalFrames == 0ULL)
    {
        return QString("%1 frames processed    \r")
            .arg(m_myFramesPlayed,7);
    }

    static char const spin_chars[] = "/-\\|";
    static uint spin_cnt = 0;

    double elapsed = flagTime.elapsed() * 0.001;
    double flagFPS = (elapsed > 0.0) ? (m_myFramesPlayed / elapsed) : 0;

    double percentage = m_myFramesPlayed * 100.0 / totalFrames;
    percentage = (percentage > 100.0 && percentage < 101.0) ?
        100.0 : percentage;

    if (m_myFramesPlayed < totalFrames)
        return QString("%1 fps %2%      \r")
          .arg(flagFPS,4,'f', (flagFPS < 10.0 ? 1 : 0)).arg(percentage,4,'f',1);
    else
        return QString("%1 fps %2      \r")
          .arg(flagFPS,4,'f', (flagFPS < 10.0 ? 1 : 0))
          .arg(spin_chars[++spin_cnt % 4]);
}
Exemple #13
0
DTC::LiveStreamInfo *HTTPLiveStream::StopStream(int id)
{
    MSqlQuery query(MSqlQuery::InitCon());
    query.prepare(
        "UPDATE livestream "
        "SET status = :STATUS "
        "WHERE id = :STREAMID; ");
    query.bindValue(":STATUS", (int)kHLSStatusStopping);
    query.bindValue(":STREAMID", id);

    if (!query.exec())
    {
        LOG(VB_GENERAL, LOG_ERR, SLOC +
            QString("Unable to remove mark stream stopped for stream %1.")
                    .arg(id));
        return NULL;
    }

    HTTPLiveStream *hls = new HTTPLiveStream(id);
    if (!hls)
        return NULL;

    MythTimer statusTimer;
    int       delay = 250000;
    statusTimer.start();

    HTTPLiveStreamStatus status = hls->GetDBStatus();
    while ((status != kHLSStatusStopped) &&
           (status != kHLSStatusCompleted) &&
           (status != kHLSStatusErrored) &&
           ((statusTimer.elapsed() / 1000) < 30))
    {
        delay = (int)(delay * 1.5);
        usleep(delay);

        status = hls->GetDBStatus();
    }

    hls->LoadFromDB();
    DTC::LiveStreamInfo *pLiveStreamInfo = hls->GetLiveStreamInfo();

    delete hls;
    return pLiveStreamInfo;
}
Exemple #14
0
void MThreadPool::startReserved(
    QRunnable *runnable, QString debugName, int waitForAvailMS)
{
    QMutexLocker locker(&m_priv->m_lock);
    if (waitForAvailMS > 0 && m_priv->m_avail_threads.empty() &&
        m_priv->m_running_threads.size() >= m_priv->m_max_thread_count)
    {
        MythTimer t;
        t.start();
        int left = waitForAvailMS - t.elapsed();
        while (left > 0 && m_priv->m_avail_threads.empty() &&
               m_priv->m_running_threads.size() >= m_priv->m_max_thread_count)
        {
            m_priv->m_wait.wait(locker.mutex(), left);
            left = waitForAvailMS - t.elapsed();
        }
    }
    TryStartInternal(runnable, debugName, true);
}
void MythConnection::DefaultTimer(MythTimer &timer)
{
  timer.SetAutoTranscode(atoi(GetSetting("NULL", "AutoTranscode").c_str()) > 0);
  timer.SetUserJob(1, atoi(GetSetting("NULL", "AutoRunUserJob1").c_str()) > 0);
  timer.SetUserJob(2, atoi(GetSetting("NULL", "AutoRunUserJob2").c_str()) > 0);
  timer.SetUserJob(3, atoi(GetSetting("NULL", "AutoRunUserJob3").c_str()) > 0);
  timer.SetUserJob(4, atoi(GetSetting("NULL", "AutoRunUserJob4").c_str()) > 0);
  timer.SetAutoCommFlag(atoi(GetSetting("NULL", "AutoCommercialFlag").c_str()) > 0);
  timer.SetAutoExpire(atoi(GetSetting("NULL", "AutoExpireDefault").c_str()) > 0);
  timer.SetTranscoder(atoi(GetSetting("NULL", "DefaultTranscoder").c_str()));
  timer.SetStartOffset(atoi(GetSetting("NULL", "DefaultStartOffset").c_str()));
  timer.SetStartOffset(atoi(GetSetting("NULL", "DefaultEndOffset").c_str()));
}
QString BufferedSocketDevice::ReadLine( int msecs )
{
    MythTimer timer;
    QString   sLine;

    if ( CanReadLine() )
        return( ReadLine() );
        
    // ----------------------------------------------------------------------
    // If the user supplied a timeout, lets loop until we can read a line 
    // or timeout.
    // ----------------------------------------------------------------------

    if ( msecs > 0)
    {
        bool bTimeout = false;

        timer.start();

        while ( !CanReadLine() && !bTimeout )
        {
#if 0
            LOG(VB_HTTP, LOG_DEBUG, "Can't Read Line... Waiting for more." );
#endif

            WaitForMore( msecs, &bTimeout );

            if ( timer.elapsed() >= msecs ) 
            {
                bTimeout = true;
                LOG(VB_HTTP, LOG_INFO, "Exceeded Total Elapsed Wait Time." );
            }
        }
            
        if (CanReadLine())
            sLine = ReadLine();
    }

    return( sLine );
}
Exemple #17
0
void PreviewGenerator::TeardownAll(void)
{
    if (!isConnected)
        return;

    const QString filename = programInfo.pathname + ".png";

    MythTimer t;
    t.start();
    for (bool done = false; !done;)
    {
        previewLock.lock();
        if (isConnected)
            emit previewThreadDone(filename, done);
        else
            done = true;
        previewLock.unlock();
        usleep(5000);
    }
    VERBOSE(VB_PLAYBACK, LOC + "previewThreadDone took "<<t.elapsed()<<"ms");
    disconnectSafe();
}
Exemple #18
0
void MThread::Cleanup(void)
{
    QMutexLocker locker(&s_all_threads_lock);
    QSet<MThread*> badGuys;
    QSet<MThread*>::const_iterator it;
    for (it = s_all_threads.begin(); it != s_all_threads.end(); ++it)
    {
        if ((*it)->isRunning())
        {
            badGuys.insert(*it);
            (*it)->exit(1);
        }
    }

    if (badGuys.empty())
        return;

    // logging has been stopped so we need to use iostream...
    cerr<<"Error: Not all threads were shut down properly: "<<endl;
    for (it = badGuys.begin(); it != badGuys.end(); ++it)
    {
        cerr<<"Thread "<<qPrintable((*it)->objectName())
            <<" is still running"<<endl;
    }
    cerr<<endl;

    static const int kTimeout = 5000;
    MythTimer t;
    t.start();
    for (it = badGuys.begin();
         it != badGuys.end() && t.elapsed() < kTimeout; ++it)
    {
        int left = kTimeout - t.elapsed();
        if (left > 0)
            (*it)->wait(left);
    }
}
Exemple #19
0
/** \fn  SignalMonitor::WaitForLock(int)
 *  \brief Wait for a StatusSignaLock(int) of true.
 *
 *   This can be called whether or not the signal
 *   monitoring thread has been started.
 *
 *  \param timeout maximum time to wait in milliseconds.
 *  \return true if signal was acquired.
 */
bool SignalMonitor::WaitForLock(int timeout)
{
    statusLock.lock();
    if (-1 == timeout)
        timeout = signalLock.GetTimeout();
    statusLock.unlock();
    if (timeout<0)
        return false;

    MythTimer t;
    t.start();
    if (running)
    {
        while (t.elapsed()<timeout && running)
        {
            Kick();
            statusLock.lock();
            bool ok = signalLock.IsGood();
            statusLock.unlock();
            if (ok)
                return true;

            usleep(50);
        }
        if (!running)
            return WaitForLock(timeout-t.elapsed());
    }
    else
    {
        while (t.elapsed()<timeout && !running)
        {
            UpdateValues();
            statusLock.lock();
            bool ok = signalLock.IsGood();
            statusLock.unlock();
            if (ok)
                return true;

            usleep(50);
        }
        if (running)
            return WaitForLock(timeout-t.elapsed());
    }
    return false;
}
void MythSocket::ReadReal(char *data, int size, int max_wait_ms, int *ret)
{
    MythTimer t; t.start();
    while ((m_tcpSocket->state() == QAbstractSocket::ConnectedState) &&
           (m_tcpSocket->bytesAvailable() < size) &&
           (t.elapsed() < max_wait_ms))
    {
        m_tcpSocket->waitForReadyRead(max(2, max_wait_ms - t.elapsed()));
    }
    *ret = m_tcpSocket->read(data, size);

    if (t.elapsed() > 50)
    {
        LOG(VB_GENERAL, LOG_INFO,
            QString("ReadReal(?, %1, %2) -> %3 took %4 ms")
            .arg(size).arg(max_wait_ms).arg(*ret)
            .arg(t.elapsed()));
    }

    m_dataAvailable.fetchAndStoreOrdered(
        (m_tcpSocket->bytesAvailable() > 0) ? 1 : 0);
}
Exemple #21
0
void IconData::UpdateSourceIcons(uint sourceid)
{
    VERBOSE(VB_GENERAL, LOC +
            QString("Updating icons for sourceid: %1").arg(sourceid));

    QString fileprefix = SetupIconCacheDirectory();

    MSqlQuery query(MSqlQuery::InitCon());
    query.prepare(
        "SELECT ch.chanid, nim.url "
        "FROM (channel ch, callsignnetworkmap csm) "
        "RIGHT JOIN networkiconmap nim ON csm.network = nim.network "
        "WHERE ch.callsign = csm.callsign AND "
        "      (icon = :NOICON OR icon = '') AND "
        "      ch.sourceid = :SOURCEID");
    query.bindValue(":SOURCEID", sourceid);
    query.bindValue(":NOICON", "none");

    if (!query.exec())
    {
        MythDB::DBError("Looking for icons to fetch", query);
        return;
    }

    unsigned int count = 0;
    while (query.next())
    {
        count++;

        QString icon_url = expandURLString(query.value(1).toString());
        QFileInfo qfi(icon_url);
        QFile localfile(fileprefix + "/" + qfi.fileName());

        if (!localfile.exists() || 0 == localfile.size())
        {
            VERBOSE(VB_GENERAL, LOC +
                    QString("Attempting to fetch icon at '%1'")
                    .arg(icon_url));

            FI fi;
            fi.filename = localfile.fileName();
            fi.chanid = query.value(0).toUInt();

            bool add_request = false;
            {
                QMutexLocker locker(&m_u2fl_lock);
                add_request = m_u2fl[icon_url].empty();
                m_u2fl[icon_url].push_back(fi);
            }

            if (add_request)
                MythHttpPool::GetSingleton()->AddUrlRequest(icon_url, this);

            // HACK -- begin
            // This hack is needed because we don't enter the event loop
            // before running this code via qApp->exec()
            qApp->processEvents();
            // HACK -- end
        }
    }

    MythTimer tm; tm.start();
    while (true)
    {
        // HACK -- begin
        // This hack is needed because we don't enter the event loop
        // before running this code via qApp->exec()
        qApp->processEvents();
        // HACK -- end

        QMutexLocker locker(&m_u2fl_lock);
        if (m_u2fl.empty())
            break;

        if ((uint)tm.elapsed() > (count * 500) + 2000)
        {
            VERBOSE(VB_IMPORTANT, LOC_WARN +
                    "Timed out waiting for some icons to download, "
                    "you may wish to try again later.");
            break;
        }
    }
}
Exemple #22
0
bool DeviceReadBuffer::Poll(void) const
{
#ifdef USING_MINGW
# ifdef _MSC_VER
#  pragma message( "mingw DeviceReadBuffer::Poll" )
# else
#  warning mingw DeviceReadBuffer::Poll
# endif
    LOG(VB_GENERAL, LOG_ERR, LOC +
        "mingw DeviceReadBuffer::Poll is not implemented");
    return false;
#else
    bool retval = true;
    MythTimer timer;
    timer.start();

    int poll_cnt = 1;
    struct pollfd polls[2];
    memset(polls, 0, sizeof(polls));

    polls[0].fd      = _stream_fd;
    polls[0].events  = POLLIN | POLLPRI;
    polls[0].revents = 0;

    if (wake_pipe[0] >= 0)
    {
        poll_cnt = 2;
        polls[1].fd      = wake_pipe[0];
        polls[1].events  = POLLIN;
        polls[1].revents = 0;
    }

    while (true)
    {
        polls[0].revents = 0;
        polls[1].revents = 0;
        poll_cnt = (wake_pipe[0] >= 0) ? poll_cnt : 1;

        int timeout = max_poll_wait;
        if (1 == poll_cnt)
            timeout = 10;
        else if (poll_timeout_is_error)
            timeout = max((int)max_poll_wait - timer.elapsed(), 10);

        int ret = poll(polls, poll_cnt, timeout);

        if (polls[0].revents & POLLHUP)
        {
            LOG(VB_GENERAL, LOG_ERR, LOC + "poll eof (POLLHUP)");
            break;
        }
        else if (polls[0].revents & POLLNVAL)
        {
            LOG(VB_GENERAL, LOG_ERR, LOC + "poll error");
            error = true;
            return true;
        }

        if (!dorun || !IsOpen() || IsPauseRequested())
        {
            retval = false;
            break; // are we supposed to pause, stop, etc.
        }

        if (polls[0].revents & POLLPRI)
        {
            readerCB->PriorityEvent(polls[0].fd);
        }

        if (polls[0].revents & POLLIN)
        {
            if (ret > 0)
                break; // we have data to read :)
            else if (ret < 0)
            {
                if ((EOVERFLOW == errno))
                    break; // we have an error to handle

                if ((EAGAIN == errno) || (EINTR  == errno))
                    continue; // errors that tell you to try again

                usleep(2500 /*2.5 ms*/);
            }
            else //  ret == 0
            {
                if (poll_timeout_is_error &&
                    (timer.elapsed() >= (int)max_poll_wait))
                {
                    LOG(VB_GENERAL, LOG_ERR, LOC + "Poll giving up 1");
                    QMutexLocker locker(&lock);
                    error = true;
                    return true;
                }
            }
        }

        // Clear out any pending pipe reads
        if ((poll_cnt > 1) && (polls[1].revents & POLLIN))
        {
            char dummy[128];
            int cnt = (wake_pipe_flags[0] & O_NONBLOCK) ? 128 : 1;
            cnt = ::read(wake_pipe[0], dummy, cnt);
        }

        if (poll_timeout_is_error && (timer.elapsed() >= (int)max_poll_wait))
        {
            LOG(VB_GENERAL, LOG_ERR, LOC + "Poll giving up 2");
            QMutexLocker locker(&lock);
            error = true;
            return true;
        }
    }

    int e = timer.elapsed();
    if (e > (int)max_poll_wait)
    {
        LOG(VB_GENERAL, LOG_WARNING, LOC +
            QString("Poll took an unusually long time %1 ms")
            .arg(timer.elapsed()));
    }

    return retval;
#endif //!USING_MINGW
}
Exemple #23
0
/**
 *  \brief This runs the event loop for EITScanner until 'exitThread' is true.
 */
void EITScanner::run(void)
{
    static const uint  sz[] = { 2000, 1800, 1600, 1400, 1200, };
    static const float rt[] = { 0.0f, 0.2f, 0.4f, 0.6f, 0.8f, };

    lock.lock();

    MythTimer t;
    uint eitCount = 0;

    while (!exitThread)
    {
        lock.unlock();
        uint list_size = eitHelper->GetListSize();

        float rate = 1.0f;
        for (uint i = 0; i < 5; i++)
        {
            if (list_size >= sz[i])
            {
                rate = rt[i];
                break;
            }
        }

        lock.lock();
        if (eitSource)
            eitSource->SetEITRate(rate);
        lock.unlock();

        if (list_size)
        {
            eitCount += eitHelper->ProcessEvents();
            t.start();
        }

        // Tell the scheduler to run if
        // we are in passive scan
        // and there have been updated events since the last scheduler run
        // but not in the last 60 seconds
        if (!activeScan && eitCount && (t.elapsed() > 60 * 1000))
        {
            LOG(VB_EIT, LOG_INFO,
                LOC_ID + QString("Added %1 EIT Events").arg(eitCount));
            eitCount = 0;
            RescheduleRecordings();
        }

        // Is it time to move to the next transport in active scan?
        if (activeScan && (MythDate::current() > activeScanNextTrig))
        {
            // if there have been any new events, tell scheduler to run.
            if (eitCount)
            {
                LOG(VB_EIT, LOG_INFO,
                    LOC_ID + QString("Added %1 EIT Events").arg(eitCount));
                eitCount = 0;
                RescheduleRecordings();
            }

            if (activeScanNextChan == activeScanChannels.end())
                activeScanNextChan = activeScanChannels.begin();

            if (!(*activeScanNextChan).isEmpty())
            {
                eitHelper->WriteEITCache();
                if (rec->QueueEITChannelChange(*activeScanNextChan))
                {
                    eitHelper->SetChannelID(ChannelUtil::GetChanID(
                        rec->GetSourceID(), *activeScanNextChan));
                    LOG(VB_EIT, LOG_INFO,
                        LOC_ID + QString("Now looking for EIT data on "
                                         "multiplex of channel %1")
                        .arg(*activeScanNextChan));
                }
            }

            activeScanNextTrig = MythDate::current()
                .addSecs(activeScanTrigTime);
            ++activeScanNextChan;

            // 24 hours ago
            eitHelper->PruneEITCache(activeScanNextTrig.toTime_t() - 86400);
        }

        lock.lock();
        if ((activeScan || activeScanStopped) && !exitThread)
            exitThreadCond.wait(&lock, 400); // sleep up to 400 ms.

        if (!activeScan && !activeScanStopped)
        {
            activeScanStopped = true;
            activeScanCond.wakeAll();
        }
    }

    if (eitCount) /* some events have been handled since the last schedule request */
    {
        eitCount = 0;
        RescheduleRecordings();
    }

    activeScanStopped = true;
    activeScanCond.wakeAll();
    lock.unlock();
}
Exemple #24
0
/** \fn EITScanner::RunEventLoop(void)
 *  \brief This runs the event loop for EITScanner until 'exitThread' is true.
 */
void EITScanner::RunEventLoop(void)
{
    static const uint  sz[] = { 2000, 1800, 1600, 1400, 1200, };
    static const float rt[] = { 0.0f, 0.2f, 0.4f, 0.6f, 0.8f, };

    lock.lock();
    exitThread = false;

    MythTimer t;
    uint eitCount = 0;

    while (!exitThread)
    {
        lock.unlock();
        uint list_size = eitHelper->GetListSize();

        float rate = 1.0f;
        for (uint i = 0; i < 5; i++)
        {
            if (list_size >= sz[i])
            {
                rate = rt[i];
                break;
            }
        }

        lock.lock();
        if (eitSource)
            eitSource->SetEITRate(rate);
        lock.unlock();

        if (list_size)
        {
            eitCount += eitHelper->ProcessEvents();
            t.start();
        }

        // If there have been any new events and we haven't
        // seen any in a while, tell scheduler to run.
        if (eitCount && (t.elapsed() > 60 * 1000))
        {
            VERBOSE(VB_EIT, LOC_ID + "Added "<<eitCount<<" EIT Events");
            eitCount = 0;
            RescheduleRecordings();
        }

        if (activeScan && (QDateTime::currentDateTime() > activeScanNextTrig))
        {
            // if there have been any new events, tell scheduler to run.
            if (eitCount)
            {
                VERBOSE(VB_EIT, LOC_ID + "Added "<<eitCount<<" EIT Events");
                eitCount = 0;
                RescheduleRecordings();
            }

            if (activeScanNextChan == activeScanChannels.end())
                activeScanNextChan = activeScanChannels.begin();

            if (!(*activeScanNextChan).isEmpty())
            {
                eitHelper->WriteEITCache();
                rec->SetChannel(*activeScanNextChan, TVRec::kFlagEITScan);
                VERBOSE(VB_EIT, LOC_ID +
                        QString("Now looking for EIT data on "
                                "multiplex of channel %1")
                        .arg(*activeScanNextChan));
            }

            activeScanNextTrig = QDateTime::currentDateTime()
                .addSecs(activeScanTrigTime);
            activeScanNextChan++;

            // 24 hours ago
            eitHelper->PruneEITCache(activeScanNextTrig.toTime_t() - 86400);
        }

        lock.lock();
        if (!exitThread)
            exitThreadCond.wait(&lock, 400); // sleep up to 400 ms.
    }
    lock.unlock();
}
bool AudioOutputOSS::OpenDevice()
{
    numbadioctls = 0;

    MythTimer timer;
    timer.start();

    VBAUDIO(QString("Opening OSS audio device '%1'.").arg(main_device));

    while (timer.elapsed() < 2000 && audiofd == -1)
    {
        QByteArray device = main_device.toLatin1();
        audiofd = open(device.constData(), O_WRONLY);
        if (audiofd < 0 && errno != EAGAIN && errno != EINTR)
        {
            if (errno == EBUSY)
            {
                VBWARN(QString("Something is currently using: %1.")
                      .arg(main_device));
                return false;
            }
            VBERRENO(QString("Error opening audio device (%1)")
                         .arg(main_device));
        }
        if (audiofd < 0)
            usleep(50);
    }

    if (audiofd == -1)
    {
        Error(QObject::tr("Error opening audio device (%1)").arg(main_device));
        VBERRENO(QString("Error opening audio device (%1)").arg(main_device));
        return false;
    }

    if (fcntl(audiofd, F_SETFL, fcntl(audiofd, F_GETFL) & ~O_NONBLOCK) == -1)
    {
        VBERRENO(QString("Error removing the O_NONBLOCK flag from audio device FD (%1)").arg(main_device));
    }

    bool err = false;
    int  format;

    switch (output_format)
    {
        case FORMAT_U8:  format = AFMT_U8;      break;
        case FORMAT_S16: format = AFMT_S16_NE;  break;
        default:
            VBERROR(QString("Unknown sample format: %1").arg(output_format));
            close(audiofd);
            audiofd = -1;
            return false;
    }

#if defined(AFMT_AC3) && defined(SNDCTL_DSP_GETFMTS)
    if (passthru)
    {
        int format_support = 0;
        if (!ioctl(audiofd, SNDCTL_DSP_GETFMTS, &format_support) &&
            (format_support & AFMT_AC3))
        {
            format = AFMT_AC3;
        }
    }
#endif

    if (channels > 2)
    {
        if (ioctl(audiofd, SNDCTL_DSP_CHANNELS, &channels) < 0 ||
            ioctl(audiofd, SNDCTL_DSP_SPEED, &samplerate) < 0  ||
            ioctl(audiofd, SNDCTL_DSP_SETFMT, &format) < 0)
            err = true;
    }
    else
    {
        int stereo = channels - 1;
        if (ioctl(audiofd, SNDCTL_DSP_STEREO, &stereo) < 0     ||
            ioctl(audiofd, SNDCTL_DSP_SPEED, &samplerate) < 0  ||
            ioctl(audiofd, SNDCTL_DSP_SETFMT, &format) < 0)
            err = true;
    }

    if (err)
    {
        VBERRENO(QString("Unable to set audio device (%1) to %2 kHz, %3 bits, "
                         "%4 channels")
                     .arg(main_device).arg(samplerate)
                     .arg(AudioOutputSettings::FormatToBits(output_format))
                     .arg(channels));

        close(audiofd);
        audiofd = -1;
        return false;
    }

    audio_buf_info info;
    if (ioctl(audiofd, SNDCTL_DSP_GETOSPACE, &info) < 0)
        VBERRENO("Error retrieving card buffer size");
    // align by frame size
    fragment_size = info.fragsize - (info.fragsize % output_bytes_per_frame);

    soundcard_buffer_size = info.bytes;

    int caps;

    if (ioctl(audiofd, SNDCTL_DSP_GETCAPS, &caps) == 0)
    {
        if (!(caps & DSP_CAP_REALTIME))
            VBWARN("The audio device cannot report buffer state "
                   "accurately! audio/video sync will be bad, continuing...");
    }
    else
        VBERRENO("Unable to get audio card capabilities");

    // Setup volume control
    if (internal_vol)
        VolumeInit();

    // Device opened successfully
    return true;
}
Exemple #26
0
int RemoteFile::Read(void *data, int size)
{
    int recv = 0;
    int sent = 0;
    bool error = false;
    bool response = false;

    QMutexLocker locker(&lock);
    if (!sock)
    {
        LOG(VB_NETWORK, LOG_ERR, "RemoteFile::Read(): Called with no socket");
        return -1;
    }

    if (!sock->IsConnected() || !controlSock->IsConnected())
        return -1;

    if (sock->IsDataAvailable())
    {
        LOG(VB_NETWORK, LOG_ERR,
            "RemoteFile::Read(): Read socket not empty to start!");
        sock->Reset();
    }

    while (controlSock->IsDataAvailable())
    {
        LOG(VB_NETWORK, LOG_ERR,
            "RemoteFile::Read(): Control socket not empty to start!");
        controlSock->Reset();
    }

    QStringList strlist( QString(query).arg(recordernum) );
    strlist << "REQUEST_BLOCK";
    strlist << QString::number(size);
    bool ok = controlSock->WriteStringList(strlist);
    if (!ok)
    {
        LOG(VB_NETWORK, LOG_ERR, "RemoteFile::Read(): Block request failed");
        return -1;
    }

    sent = size;

    int waitms = 10;
    MythTimer mtimer;
    mtimer.start();

    while (recv < sent && !error && mtimer.elapsed() < 10000)
    {
        int ret = sock->Read(((char *)data) + recv, sent - recv, waitms);

        if (ret > 0)
            recv += ret;
        else if (ret < 0)
            error = true;

        waitms += (waitms < 200) ? 20 : 0;

        if (controlSock->IsDataAvailable() &&
                controlSock->ReadStringList(strlist, MythSocket::kShortTimeout) &&
                !strlist.empty())
        {
            sent = strlist[0].toInt(); // -1 on backend error
            response = true;
        }
    }

    if (!error && !response)
    {
        if (controlSock->ReadStringList(strlist, MythSocket::kShortTimeout) &&
                !strlist.empty())
        {
            sent = strlist[0].toInt(); // -1 on backend error
        }
        else
        {
            LOG(VB_GENERAL, LOG_ERR,
                "RemoteFile::Read(): No response from control socket.");
            sent = -1;
        }
    }

    LOG(VB_NETWORK, LOG_DEBUG,
        QString("Read(): reqd=%1, rcvd=%2, rept=%3, error=%4")
        .arg(size).arg(recv).arg(sent).arg(error));

    if (sent < 0)
        return sent;

    if (error || sent != recv)
        recv = -1;

    return recv;
}
Exemple #27
0
bool AudioOutputOSS::OpenDevice()
{
    numbadioctls = 0;

    MythTimer timer;
    timer.start();

    VERBOSE(VB_GENERAL, QString("Opening OSS audio device '%1'.")
            .arg(audio_main_device));
    
    while (timer.elapsed() < 2000 && audiofd == -1)
    {
        audiofd = open(audio_main_device.ascii(), O_WRONLY | O_NONBLOCK);
        if (audiofd < 0 && errno != EAGAIN && errno != EINTR)
        {
            if (errno == EBUSY)
            {
                Error(QString("WARNING: something is currently"
                              " using: %1, retrying.").arg(audio_main_device));
                return false;
            }
            VERBOSE(VB_IMPORTANT, QString("Error opening audio device (%1), the"
                    " error was: %2").arg(audio_main_device).arg(strerror(errno)));
            perror(audio_main_device.ascii());
        }
        if (audiofd < 0)
            usleep(50);
    }

    if (audiofd == -1)
    {
        Error(QString("Error opening audio device (%1), the error was: %2")
              .arg(audio_main_device).arg(strerror(errno)));
        return false;
    }

    fcntl(audiofd, F_SETFL, fcntl(audiofd, F_GETFL) & ~O_NONBLOCK);

    SetFragSize();

    bool err = false;
    int  format;

    switch (audio_bits)
    {
        case 8:
            format = AFMT_S8;
            break;
        case 16:
#ifdef WORDS_BIGENDIAN
            format = AFMT_S16_BE;
#else
            format = AFMT_S16_LE;
#endif
            break;
        default: Error(QString("AudioOutputOSS() - Illegal bitsize of %1")
                       .arg(audio_bits));
    }

#if defined(AFMT_AC3) && defined(SNDCTL_DSP_GETFMTS)
    if (audio_passthru)
    {
        int format_support;
        if (!ioctl(audiofd, SNDCTL_DSP_GETFMTS, &format_support) &&
            (format_support & AFMT_AC3))
        {
            format = AFMT_AC3;
        }
    }
#endif

    if (audio_channels > 2)
    {
        if (ioctl(audiofd, SNDCTL_DSP_SAMPLESIZE, &audio_bits) < 0 ||
            ioctl(audiofd, SNDCTL_DSP_CHANNELS, &audio_channels) < 0 ||
            ioctl(audiofd, SNDCTL_DSP_SPEED, &audio_samplerate) < 0 ||
            ioctl(audiofd, SNDCTL_DSP_SETFMT, &format) < 0)
            err = true;
    }
    else
    {
        int stereo = audio_channels - 1;
        if (ioctl(audiofd, SNDCTL_DSP_SAMPLESIZE, &audio_bits) < 0 ||
            ioctl(audiofd, SNDCTL_DSP_STEREO, &stereo) < 0 ||
            ioctl(audiofd, SNDCTL_DSP_SPEED, &audio_samplerate) < 0 ||
            ioctl(audiofd, SNDCTL_DSP_SETFMT, &format) < 0)
            err = true;
    }

    if (err)
    {
        Error(QString("Unable to set audio device (%1) to %2 kHz / %3 bits"
                      " / %4 channels").arg(audio_main_device).arg(audio_samplerate)
                      .arg(audio_bits).arg(audio_channels));
        close(audiofd);
        audiofd = -1;
        return false;
    }

    audio_buf_info info;
    ioctl(audiofd, SNDCTL_DSP_GETOSPACE, &info);
    fragment_size = info.fragsize;

    audio_buffer_unused = info.bytes - (fragment_size * 4);
    soundcard_buffer_size = info.bytes;

    int caps;
    
    if (ioctl(audiofd, SNDCTL_DSP_GETCAPS, &caps) == 0)
    {
        if (!(caps & DSP_CAP_REALTIME))
        {
            VERBOSE(VB_IMPORTANT, "The audio device cannot report buffer state"
                    " accurately! audio/video sync will be bad, continuing...");
        }
    } else {
        VERBOSE(VB_IMPORTANT, QString("Unable to get audio card capabilities,"
                " the error was: %1").arg(strerror(errno)));
    }

    // Setup volume control
    if (internal_vol)
        VolumeInit();

    // Device opened successfully
    return true;
}
Exemple #28
0
bool FileRingBuffer::OpenFile(const QString &lfilename, uint retry_ms)
{
    LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("OpenFile(%1, %2 ms)")
            .arg(lfilename).arg(retry_ms));

    rwlock.lockForWrite();

    filename = lfilename;

    if (remotefile)
    {
        delete remotefile;
        remotefile = NULL;
    }

    if (fd2 >= 0)
    {
        close(fd2);
        fd2 = -1;
    }

    bool is_local = 
        (filename.left(4) != "/dev") &&
        ((filename.left(1) == "/") || QFile::exists(filename));

    if (is_local)
    {
        char buf[kReadTestSize];
        int lasterror = 0;

        MythTimer openTimer;
        openTimer.start();

        uint openAttempts = 0;
        do
        {
            openAttempts++;
            lasterror = 0;

            fd2 = open(filename.toLocal8Bit().constData(),
                       O_RDONLY|O_LARGEFILE|O_STREAMING|O_BINARY);

            if (fd2 < 0)
            {
                if (!check_permissions(filename))
                {
                    lasterror = 3;
                    break;
                }

                lasterror = 1;
                usleep(10 * 1000);
            }
            else
            {
                int ret = read(fd2, buf, kReadTestSize);
                if (ret != (int)kReadTestSize)
                {
                    lasterror = 2;
                    close(fd2);
                    fd2 = -1;
                    if (oldfile)
                        break; // if it's an old file it won't grow..
                    usleep(10 * 1000);
                }
                else
                {
                    if (0 == lseek(fd2, 0, SEEK_SET))
                    {
                        posix_fadvise(fd2, 0, 0,        POSIX_FADV_SEQUENTIAL);
                        posix_fadvise(fd2, 0, 128*1024, POSIX_FADV_WILLNEED);
                        lasterror = 0;
                        break;
                    }
                    lasterror = 4;
                    close(fd2);
                    fd2 = -1;
                }
            }
        }
        while ((uint)openTimer.elapsed() < retry_ms);

        switch (lasterror)
        {
            case 0:
            {
                QFileInfo fi(filename);
                oldfile = fi.lastModified()
                    .secsTo(QDateTime::currentDateTime()) > 60;
                QString extension = fi.completeSuffix().toLower();
                if (is_subtitle_possible(extension))
                    subtitlefilename = local_sub_filename(fi);
                break;
            }
            case 1:
                LOG(VB_GENERAL, LOG_ERR, LOC +
                        QString("OpenFile(): Could not open."));
                break;
            case 2:
                LOG(VB_GENERAL, LOG_ERR, LOC +
                        QString("OpenFile(): File too small (%1B).")
                        .arg(QFileInfo(filename).size()));
                break;
            case 3:
                LOG(VB_GENERAL, LOG_ERR, LOC +
                        "OpenFile(): Improper permissions.");
                break;
            case 4:
                LOG(VB_GENERAL, LOG_ERR, LOC +
                        "OpenFile(): Cannot seek in file.");
                break;
            default:
                break;
        }
        LOG(VB_FILE, LOG_INFO, 
            LOC + QString("OpenFile() made %1 attempts in %2 ms")
                .arg(openAttempts).arg(openTimer.elapsed()));

    }
    else
    {
        QString tmpSubName = filename;
        QString dirName  = ".";

        int dirPos = filename.lastIndexOf(QChar('/'));
        if (dirPos > 0)
        {
            tmpSubName = filename.mid(dirPos + 1);
            dirName = filename.left(dirPos);
        }

        QString baseName  = tmpSubName;
        QString extension = tmpSubName;
        QStringList auxFiles;

        int suffixPos = tmpSubName.lastIndexOf(QChar('.'));
        if (suffixPos > 0)
        {
            baseName = tmpSubName.left(suffixPos);
            extension = tmpSubName.right(suffixPos-1);
            if (is_subtitle_possible(extension))
            {
                QMutexLocker locker(&subExtLock);
                QStringList::const_iterator eit = subExt.begin();
                for (; eit != subExt.end(); ++eit)
                    auxFiles += baseName + *eit;
            }
        }

        remotefile = new RemoteFile(filename, false, true,
                                    retry_ms, &auxFiles);
        if (!remotefile->isOpen())
        {
            LOG(VB_GENERAL, LOG_ERR, LOC +
                    QString("RingBuffer::RingBuffer(): Failed to open remote "
                            "file (%1)").arg(filename));
            delete remotefile;
            remotefile = NULL;
        }
        else
        {
            QStringList aux = remotefile->GetAuxiliaryFiles();
            if (aux.size())
                subtitlefilename = dirName + "/" + aux[0];
        }
    }

    setswitchtonext = false;
    ateof = false;
    commserror = false;
    numfailures = 0;

    rawbitrate = 8000;
    CalcReadAheadThresh();

    bool ok = fd2 >= 0 || remotefile;

    rwlock.unlock();

    return ok;
}
/** \fn ThreadedFileWriter::DiskLoop(void)
 *  \brief The thread run method that actually calls writes to disk.
 */
void ThreadedFileWriter::DiskLoop(void)
{
#ifndef USING_MINGW
    // don't exit program if file gets larger than quota limit..
    signal(SIGXFSZ, SIG_IGN);
#endif

    QMutexLocker locker(&buflock);

    // Even if the bytes buffered is less than the minimum write
    // size we do want to write to the OS buffers periodically.
    // This timer makes sure we do.
    MythTimer minWriteTimer;
    minWriteTimer.start();

    while (!in_dtor)
    {
        if (ignore_writes)
        {
            while (!writeBuffers.empty())
            {
                delete writeBuffers.front();
                writeBuffers.pop_front();
            }
            while (!emptyBuffers.empty())
            {
                delete emptyBuffers.front();
                emptyBuffers.pop_front();
            }
            bufferEmpty.wakeAll();
            bufferHasData.wait(locker.mutex());
            continue;
        }

        if (writeBuffers.empty())
        {
            bufferEmpty.wakeAll();
            bufferHasData.wait(locker.mutex(), 1000);
            TrimEmptyBuffers();
            continue;
        }

        int mwte = minWriteTimer.elapsed();
        if (!flush && (mwte < 250) && (totalBufferUse < kMinWriteSize))
        {
            bufferHasData.wait(locker.mutex(), 250 - mwte);
            TrimEmptyBuffers();
            continue;
        }

        TFWBuffer *buf = writeBuffers.front();
        writeBuffers.pop_front();
        totalBufferUse -= buf->data.size();
        minWriteTimer.start();

        //////////////////////////////////////////

        const void *data = &(buf->data[0]);
        uint sz = buf->data.size();

        bool write_ok = true;
        uint tot = 0;
        uint errcnt = 0;

        LOG(VB_FILE, LOG_DEBUG, LOC + QString("write(%1) cnt %2 total %3")
                .arg(sz).arg(writeBuffers.size())
                .arg(totalBufferUse));

        MythTimer writeTimer;
        writeTimer.start();

        while ((tot < sz) && !in_dtor)
        {
            locker.unlock();

            int ret = write(fd, (char *)data + tot, sz - tot);

            if (ret < 0)
            {
                if (errno == EAGAIN)
                {
                    LOG(VB_GENERAL, LOG_WARNING, LOC + "Got EAGAIN.");
                }
                else
                {
                    errcnt++;
                    LOG(VB_GENERAL, LOG_ERR, LOC + "File I/O " +
                        QString(" errcnt: %1").arg(errcnt) + ENO);
                }

                if ((errcnt >= 3) || (ENOSPC == errno) || (EFBIG == errno))
                {
                    locker.relock();
                    write_ok = false;
                    break;
                }
            }
            else
            {
                tot += ret;
            }

            locker.relock();

            if (!in_dtor)
                bufferHasData.wait(locker.mutex(), 50);
        }

        //////////////////////////////////////////

        buf->lastUsed = QDateTime::currentDateTime();
        emptyBuffers.push_back(buf);

        if (writeTimer.elapsed() > 1000)
        {
            LOG(VB_GENERAL, LOG_WARNING, LOC +
                QString("write(%1) cnt %2 total %3 -- took a long time, %4 ms")
                    .arg(sz).arg(writeBuffers.size())
                    .arg(totalBufferUse).arg(writeTimer.elapsed()));
        }

        if (!write_ok && ((EFBIG == errno) || (ENOSPC == errno)))
        {
            QString msg;
            switch (errno)
            {
                case EFBIG:
                    msg =
                        "Maximum file size exceeded by '%1'"
                        "\n\t\t\t"
                        "You must either change the process ulimits, configure"
                        "\n\t\t\t"
                        "your operating system with \"Large File\" support, "
                        "or use"
                        "\n\t\t\t"
                        "a filesystem which supports 64-bit or 128-bit files."
                        "\n\t\t\t"
                        "HINT: FAT32 is a 32-bit filesystem.";
                    break;
                case ENOSPC:
                    msg =
                        "No space left on the device for file '%1'"
                        "\n\t\t\t"
                        "file will be truncated, no further writing "
                        "will be done.";
                    break;
            }

            LOG(VB_GENERAL, LOG_ERR, LOC + msg.arg(filename));
            ignore_writes = true;
        }
    }
}
Exemple #30
0
void MpegRecorder::run(void)
{
    if (!Open())
    {
        if (_error.isEmpty())
            _error = "Failed to open V4L device";
        return;
    }

    bool has_select = true;

#if defined(__FreeBSD__)
    // HACK. FreeBSD PVR150/500 driver doesn't currently support select()
    has_select = false;
#endif

    if (driver == "hdpvr")
    {
        int progNum = 1;
        MPEGStreamData *sd = new MPEGStreamData
                             (progNum, tvrec ? tvrec->GetCaptureCardNum() : -1,
                              true);
        sd->SetRecordingType(_recording_type);
        SetStreamData(sd);

        _stream_data->AddAVListener(this);
        _stream_data->AddWritingListener(this);

        // Make sure the first things in the file are a PAT & PMT
        HandleSingleProgramPAT(_stream_data->PATSingleProgram(), true);
        HandleSingleProgramPMT(_stream_data->PMTSingleProgram(), true);
        _wait_for_keyframe_option = true;
    }

    {
        QMutexLocker locker(&pauseLock);
        request_recording = true;
        request_helper = true;
        recording = true;
        recordingWait.wakeAll();
    }

    unsigned char *buffer = new unsigned char[bufferSize + 1];
    int len;
    int remainder = 0;

    bool      good_data = false;
    bool      gap = false;
    QDateTime gap_start;

    MythTimer elapsedTimer;
    float elapsed;
    long long bytesRead = 0;
    int dummyBPS = 0;  // Bytes per second, but env var is BITS PER SECOND

    if (getenv("DUMMYBPS"))
    {
        dummyBPS = atoi(getenv("DUMMYBPS")) / 8;
        LOG(VB_GENERAL, LOG_INFO,
            LOC + QString("Throttling dummy recorder to %1 bits per second")
                .arg(dummyBPS * 8));
    }

    struct timeval tv;
    fd_set rdset;

    if (deviceIsMpegFile)
        elapsedTimer.start();
    else if (_device_read_buffer)
    {
        LOG(VB_RECORD, LOG_INFO, LOC + "Initial startup of recorder");
        StartEncoding();
    }

    QByteArray vdevice = videodevice.toLatin1();
    while (IsRecordingRequested() && !IsErrored())
    {
        if (PauseAndWait(100))
            continue;

        if (deviceIsMpegFile)
        {
            if (dummyBPS && bytesRead)
            {
                elapsed = (elapsedTimer.elapsed() / 1000.0) + 1;
                while ((bytesRead / elapsed) > dummyBPS)
                {
                    usleep(50000);
                    elapsed = (elapsedTimer.elapsed() / 1000.0) + 1;
                }
            }
            else if (GetFramesWritten())
            {
                elapsed = (elapsedTimer.elapsed() / 1000.0) + 1;
                while ((GetFramesWritten() / elapsed) > 30)
                {
                    usleep(50000);
                    elapsed = (elapsedTimer.elapsed() / 1000.0) + 1;
                }
            }
        }

        if (_device_read_buffer)
        {
            len = _device_read_buffer->Read
                  (&(buffer[remainder]), bufferSize - remainder);

            // Check for DRB errors
            if (_device_read_buffer->IsErrored())
            {
                LOG(VB_GENERAL, LOG_ERR, LOC + "Device error detected");

                if (good_data)
                {
                    if (gap)
                    {
                        /* Already processing a gap, which means
                         * restarting the encoding didn't work! */
                        SetRecordingStatus(rsFailing, __FILE__, __LINE__);
                    }
                    else
                        gap = true;
                }

                RestartEncoding();
            }
            else if (_device_read_buffer->IsEOF() &&
                     IsRecordingRequested())
            {
                LOG(VB_GENERAL, LOG_ERR, LOC + "Device EOF detected");
                _error = "Device EOF detected";
            }
            else
            {
                // If we have seen good data, but now have a gap, note it
                if (good_data)
                {
                    if (gap)
                    {
                        QMutexLocker locker(&statisticsLock);
                        QDateTime gap_end(MythDate::current());

                        recordingGaps.push_back(RecordingGap
                                                (gap_start, gap_end));
                        LOG(VB_RECORD, LOG_DEBUG,
                            LOC + QString("Inserted gap %1 dur %2")
                            .arg(recordingGaps.back().toString())
                            .arg(gap_start.secsTo(gap_end)));
                        gap = false;
                    }
                    else
                        gap_start = MythDate::current();
                }
                else
                    good_data = true;
            }
        }
        else
        {
            if (has_select)
            {
                tv.tv_sec = 5;
                tv.tv_usec = 0;
                FD_ZERO(&rdset);
                FD_SET(readfd, &rdset);

                switch (select(readfd + 1, &rdset, NULL, NULL, &tv))
                {
                    case -1:
                        if (errno == EINTR)
                            continue;

                        LOG(VB_GENERAL, LOG_ERR, LOC + "Select error" + ENO);
                        continue;

                    case 0:
                        LOG(VB_GENERAL, LOG_ERR, LOC + "select timeout - "
                                "driver has stopped responding");

                        if (close(readfd) != 0)
                        {
                            LOG(VB_GENERAL, LOG_ERR, LOC + "Close error" + ENO);
                        }

                        // Force card to be reopened on next iteration..
                        readfd = -1;
                        continue;

                    default:
                        break;
                }
            }

            len = read(readfd, &(buffer[remainder]), bufferSize - remainder);

            if (len < 0 && !has_select)
            {
                QMutexLocker locker(&pauseLock);
                if (request_recording && !request_pause)
                    unpauseWait.wait(&pauseLock, 25);
                continue;
            }

            if ((len == 0) && (deviceIsMpegFile))
            {
                close(readfd);
                readfd = open(vdevice.constData(), O_RDONLY);

                if (readfd >= 0)
                {
                    len = read(readfd,
                               &(buffer[remainder]), bufferSize - remainder);
                }

                if (len <= 0)
                {
                    _error = "Failed to read from video file";
                    continue;
                }
            }
            else if (len < 0 && errno != EAGAIN)
            {
                LOG(VB_GENERAL, LOG_ERR, LOC + QString("error reading from: %1")
                        .arg(videodevice) + ENO);
                continue;
            }
        }

        if (len > 0)
        {
            bytesRead += len;
            len += remainder;

            if (driver == "hdpvr")
            {
                remainder = _stream_data->ProcessData(buffer, len);
                int start_remain = len - remainder;
                if (remainder && (start_remain >= remainder))
                    memcpy(buffer, buffer+start_remain, remainder);
                else if (remainder)
                    memmove(buffer, buffer+start_remain, remainder);
            }
            else
            {
                FindPSKeyFrames(buffer, len);
            }
        }
    }

    LOG(VB_RECORD, LOG_INFO, LOC + "run finishing up");

    StopEncoding();

    {
        QMutexLocker locker(&pauseLock);
        request_helper = false;
    }

    if (vbi_thread)
    {
        vbi_thread->wait();
        delete vbi_thread;
        vbi_thread = NULL;
        CloseVBIDevice();
    }

    FinishRecording();

    delete[] buffer;

    if (driver == "hdpvr")
    {
        _stream_data->RemoveWritingListener(this);
        _stream_data->RemoveAVListener(this);
        SetStreamData(NULL);
    }

    QMutexLocker locker(&pauseLock);
    request_recording = false;
    recording = false;
    recordingWait.wakeAll();
}