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; }
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(); }
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 }
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(); }
/** \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; } }
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; }
/** \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; }
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; }
/** \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]); }
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; }
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 ); }
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(); }
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); } }
/** \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); }
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; } } }
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 }
/** * \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(); }
/** \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; }
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; }
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; }
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; } } }
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(); }