MythTV  master
mythfilebuffer.cpp
Go to the documentation of this file.
1 // QT
2 #include <QFileInfo>
3 #include <QDir>
4 
5 // MythTV
6 #include "threadedfilewriter.h"
7 #include "mythcontext.h"
8 #include "remotefile.h"
9 #include "mythconfig.h"
10 #include "mythtimer.h"
11 #include "mythdate.h"
12 #include "compat.h"
13 #include "mythcorecontext.h"
14 #include "io/mythfilebuffer.h"
15 
16 // Std
17 #include <cstdlib>
18 #include <cerrno>
19 #include <sys/types.h>
20 #include <sys/time.h>
21 #include <sys/stat.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 
25 #if HAVE_POSIX_FADVISE < 1
26 static int posix_fadvise(int, off_t, off_t, int) { return 0; }
27 #define POSIX_FADV_SEQUENTIAL 0
28 #define POSIX_FADV_WILLNEED 0
29 #define POSIX_FADV_DONTNEED 0
30 #endif
31 
32 #ifndef O_STREAMING
33 #define O_STREAMING 0
34 #endif
35 
36 #ifndef O_LARGEFILE
37 #define O_LARGEFILE 0
38 #endif
39 
40 #ifndef O_BINARY
41 #define O_BINARY 0
42 #endif
43 
44 #define LOC QString("FileRingBuf(%1): ").arg(m_filename)
45 
46 static const QStringList kSubExt {".ass", ".srt", ".ssa", ".sub", ".txt"};
47 static const QStringList kSubExtNoCheck {".ass", ".srt", ".ssa", ".sub", ".txt", ".gif", ".png"};
48 
49 
50 MythFileBuffer::MythFileBuffer(const QString &Filename, bool Write, bool UseReadAhead, int Timeout)
52 {
53  m_startReadAhead = UseReadAhead;
54  m_safeFilename = Filename;
55  m_filename = Filename;
56 
57  if (Write)
58  {
59  if (m_filename.startsWith("myth://"))
60  {
61  m_remotefile = new RemoteFile(m_filename, true);
62  if (!m_remotefile->isOpen())
63  {
64  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to open remote file (%1) for write").arg(m_filename));
65  delete m_remotefile;
66  m_remotefile = nullptr;
67  }
68  else
69  {
70  m_writeMode = true;
71  }
72  }
73  else
74  {
75  m_tfw = new ThreadedFileWriter(m_filename, O_WRONLY|O_TRUNC|O_CREAT|O_LARGEFILE, 0644);
76  if (!m_tfw->Open())
77  {
78  delete m_tfw;
79  m_tfw = nullptr;
80  }
81  else
82  {
83  m_writeMode = true;
84  }
85  }
86  }
87  else if (Timeout >= 0)
88  {
89  MythFileBuffer::OpenFile(m_filename, static_cast<uint>(Timeout));
90  }
91 }
92 
94 {
96 
97  delete m_remotefile;
98  m_remotefile = nullptr;
99 
100  delete m_tfw;
101  m_tfw = nullptr;
102 
103  if (m_fd2 >= 0)
104  {
105  close(m_fd2);
106  m_fd2 = -1;
107  }
108 }
109 
114 static bool CheckPermissions(const QString &Filename)
115 {
116  QFileInfo fileInfo(Filename);
117  if (fileInfo.exists() && !fileInfo.isReadable())
118  {
119  LOG(VB_GENERAL, LOG_ERR, QString("FileRingBuf(%1): File exists but is not readable by MythTV!")
120  .arg(Filename));
121  return false;
122  }
123  return true;
124 }
125 
126 static bool IsSubtitlePossible(const QString &Extension)
127 {
128  auto it = std::find_if(kSubExtNoCheck.cbegin(), kSubExtNoCheck.cend(),
129  [Extension] (const QString& ext) -> bool
130  {return ext.contains(Extension);});
131  return (it != nullptr);
132 }
133 
134 static QString LocalSubtitleFilename(QFileInfo &FileInfo)
135 {
136  // Subtitle handling
137  QString vidFileName = FileInfo.fileName();
138  QString dirName = FileInfo.absolutePath();
139 
140  QString baseName = vidFileName;
141  int suffixPos = vidFileName.lastIndexOf(QChar('.'));
142  if (suffixPos > 0)
143  baseName = vidFileName.left(suffixPos);
144 
145  QStringList list;
146  {
147  // The dir listing does not work if the filename has the
148  // following chars "[]()" so we convert them to the wildcard '?'
149  const QString findBaseName = baseName.replace("[", "?")
150  .replace("]", "?")
151  .replace("(", "?")
152  .replace(")", "?");
153 
154  for (const auto & ext : kSubExt)
155  list += findBaseName + ext;
156  }
157 
158  // Some Qt versions do not accept paths in the search string of
159  // entryList() so we have to set the dir first
160  QDir dir;
161  dir.setPath(dirName);
162  const QStringList candidates = dir.entryList(list);
163  for (const auto & candidate : candidates)
164  {
165  QFileInfo file(dirName + "/" + candidate);
166  if (file.exists() && (file.size() >= kReadTestSize))
167  return file.absoluteFilePath();
168  }
169 
170  return QString();
171 }
172 
173 bool MythFileBuffer::OpenFile(const QString &Filename, uint Retry)
174 {
175  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("OpenFile(%1, %2 ms)")
176  .arg(Filename).arg(Retry));
177 
178  m_rwLock.lockForWrite();
179 
180  m_filename = Filename;
181  m_safeFilename = Filename;
182  m_subtitleFilename.clear();
183 
184  if (m_remotefile)
185  {
186  delete m_remotefile;
187  m_remotefile = nullptr;
188  }
189 
190  if (m_fd2 >= 0)
191  {
192  close(m_fd2);
193  m_fd2 = -1;
194  }
195 
196  bool islocal = (!m_filename.startsWith("/dev")) &&
197  ((m_filename.startsWith("/")) || QFile::exists(m_filename));
198 
199  if (islocal)
200  {
201  std::array<char,kReadTestSize> buf {};
202  int lasterror = 0;
203 
204  MythTimer openTimer;
205  openTimer.start();
206 
207  uint openAttempts = 0;
208  do
209  {
210  openAttempts++;
211 
212  m_fd2 = open(m_filename.toLocal8Bit().constData(),
213  O_RDONLY|O_LARGEFILE|O_STREAMING|O_BINARY);
214 
215  if (m_fd2 < 0)
216  {
218  {
219  lasterror = 3;
220  break;
221  }
222 
223  lasterror = 1;
224  usleep(10 * 1000);
225  }
226  else
227  {
228  ssize_t ret = read(m_fd2, buf.data(), buf.size());
229  if (ret != kReadTestSize)
230  {
231  lasterror = 2;
232  close(m_fd2);
233  m_fd2 = -1;
234  if (ret == 0 && openAttempts > 5 && !gCoreContext->IsRegisteredFileForWrite(m_filename))
235  {
236  // file won't grow, abort early
237  break;
238  }
239 
240  if (m_oldfile)
241  break; // if it's an old file it won't grow..
242  usleep(10 * 1000);
243  }
244  else
245  {
246  if (0 == lseek(m_fd2, 0, SEEK_SET))
247  {
248 #ifndef _MSC_VER
249  if (posix_fadvise(m_fd2, 0, 0, POSIX_FADV_SEQUENTIAL) != 0)
250  {
251  LOG(VB_FILE, LOG_DEBUG, LOC +
252  QString("OpenFile(): fadvise sequential "
253  "failed: ") + ENO);
254  }
255  if (posix_fadvise(m_fd2, 0, 128*1024, POSIX_FADV_WILLNEED) != 0)
256  {
257  LOG(VB_FILE, LOG_DEBUG, LOC +
258  QString("OpenFile(): fadvise willneed "
259  "failed: ") + ENO);
260  }
261 #endif
262  lasterror = 0;
263  break;
264  }
265  lasterror = 4;
266  close(m_fd2);
267  m_fd2 = -1;
268  }
269  }
270  } while (static_cast<uint>(openTimer.elapsed()) < Retry);
271 
272  switch (lasterror)
273  {
274  case 0:
275  {
276  QFileInfo file(m_filename);
277  m_oldfile = file.lastModified().toUTC()
278  .secsTo(MythDate::current()) > 60;
279  QString extension = file.completeSuffix().toLower();
280  if (IsSubtitlePossible(extension))
282  break;
283  }
284  case 1:
285  LOG(VB_GENERAL, LOG_ERR, LOC + QString("OpenFile(): Could not open."));
286  //: %1 is the filename
287  m_lastError = tr("Could not open %1").arg(m_filename);
288  break;
289  case 2:
290  LOG(VB_GENERAL, LOG_ERR, LOC + QString("OpenFile(): File too small (%1B).")
291  .arg(QFileInfo(m_filename).size()));
292  //: %1 is the file size
293  m_lastError = tr("File too small (%1B)").arg(QFileInfo(m_filename).size());
294  break;
295  case 3:
296  LOG(VB_GENERAL, LOG_ERR, LOC + "OpenFile(): Improper permissions.");
297  m_lastError = tr("Improper permissions");
298  break;
299  case 4:
300  LOG(VB_GENERAL, LOG_ERR, LOC + "OpenFile(): Cannot seek in file.");
301  m_lastError = tr("Cannot seek in file");
302  break;
303  default: break;
304  }
305  LOG(VB_FILE, LOG_INFO, LOC + QString("OpenFile() made %1 attempts in %2 ms")
306  .arg(openAttempts).arg(openTimer.elapsed()));
307  }
308  else
309  {
310  QString tmpSubName = m_filename;
311  QString dirName = ".";
312 
313  int dirPos = m_filename.lastIndexOf(QChar('/'));
314  if (dirPos > 0)
315  {
316  tmpSubName = m_filename.mid(dirPos + 1);
317  dirName = m_filename.left(dirPos);
318  }
319 
320  QStringList auxFiles;
321 
322  int suffixPos = tmpSubName.lastIndexOf(QChar('.'));
323  if (suffixPos > 0)
324  {
325  QString baseName = tmpSubName.left(suffixPos);
326  int extnleng = tmpSubName.size() - baseName.size() - 1;
327  QString extension = tmpSubName.right(extnleng);
328 
329  if (IsSubtitlePossible(extension))
330  {
331  for (const auto & ext : kSubExt)
332  auxFiles += baseName + ext;
333  }
334  }
335 
336  m_remotefile = new RemoteFile(m_filename, false, true, static_cast<int>(Retry), &auxFiles);
337  if (!m_remotefile->isOpen())
338  {
339  LOG(VB_GENERAL, LOG_ERR, LOC + QString("RingBuffer::RingBuffer(): Failed to open remote file (%1)")
340  .arg(m_filename));
341  //: %1 is the filename
342  m_lastError = tr("Failed to open remote file %1").arg(m_filename);
343  delete m_remotefile;
344  m_remotefile = nullptr;
345  }
346  else
347  {
348  QStringList aux = m_remotefile->GetAuxiliaryFiles();
349  if (!aux.empty())
350  m_subtitleFilename = dirName + "/" + aux[0];
351  }
352  }
353 
354  m_setSwitchToNext = false;
355  m_ateof = false;
356  m_commsError = false;
357  m_numFailures = 0;
359  bool ok = (m_fd2 >= 0) || m_remotefile;
360  m_rwLock.unlock();
361  return ok;
362 }
363 
364 bool MythFileBuffer::ReOpen(const QString& Filename)
365 {
366  if (!m_writeMode)
367  {
368  LOG(VB_GENERAL, LOG_ERR, LOC + "Tried to ReOpen a read only file.");
369  return false;
370  }
371 
372  bool result = false;
373 
374  m_rwLock.lockForWrite();
375 
376  if ((m_tfw && m_tfw->ReOpen(Filename)) || (m_remotefile && m_remotefile->ReOpen(Filename)))
377  result = true;
378 
379  if (result)
380  {
381  m_filename = Filename;
382  m_posLock.lockForWrite();
383  m_writePos = 0;
384  m_posLock.unlock();
385  }
386 
387  m_rwLock.unlock();
388  return result;
389 }
390 
391 bool MythFileBuffer::IsOpen(void) const
392 {
393  m_rwLock.lockForRead();
394  bool ret = m_tfw || (m_fd2 > -1) || m_remotefile;
395  m_rwLock.unlock();
396  return ret;
397 }
398 
400 {
401  if (m_remotefile)
402  return SafeRead(m_remotefile, Buffer, Size);
403  if (m_fd2 >= 0)
404  return SafeRead(m_fd2, Buffer, Size);
405  errno = EBADF;
406  return -1;
407 }
408 
421 int MythFileBuffer::SafeRead(int /*fd*/, void *Buffer, uint Size)
422 {
423  uint tot = 0;
424  uint errcnt = 0;
425  uint zerocnt = 0;
426 
427  if (m_fd2 < 0)
428  {
429  LOG(VB_GENERAL, LOG_ERR, LOC + "Invalid file descriptor in 'safe_read()'");
430  return 0;
431  }
432 
433  if (m_stopReads)
434  return 0;
435 
436  struct stat sb {};
437 
438  while (tot < Size)
439  {
440  uint toread = Size - tot;
441  bool read_ok = true;
442  bool eof = false;
443 
444  // check that we have some data to read,
445  // so we never attempt to read past the end of file
446  // if fstat errored or isn't a regular file, default to previous behavior
447  int ret = fstat(m_fd2, &sb);
448  if (ret == 0 && S_ISREG(sb.st_mode))
449  {
450  if ((m_internalReadPos + tot) >= sb.st_size)
451  {
452  // We're at the end, don't attempt to read
453  read_ok = false;
454  eof = true;
455  LOG(VB_FILE, LOG_DEBUG, LOC + "not reading, reached EOF");
456  }
457  else
458  {
459  toread = static_cast<uint>(std::min(sb.st_size - (m_internalReadPos + tot), static_cast<long long>(toread)));
460  if (toread < (Size - tot))
461  {
462  eof = true;
463  LOG(VB_FILE, LOG_DEBUG, LOC + QString("About to reach EOF, reading %1 wanted %2")
464  .arg(toread).arg(Size-tot));
465  }
466  }
467  }
468 
469  if (read_ok)
470  {
471  LOG(VB_FILE, LOG_DEBUG, LOC + QString("read(%1) -- begin").arg(toread));
472  ret = static_cast<int>(read(m_fd2, static_cast<char*>(Buffer) + tot, toread));
473  LOG(VB_FILE, LOG_DEBUG, LOC + QString("read(%1) -> %2 end").arg(toread).arg(ret));
474  }
475  if (ret < 0)
476  {
477  if (errno == EAGAIN)
478  continue;
479 
480  LOG(VB_GENERAL, LOG_ERR, LOC + "File I/O problem in 'safe_read()'" + ENO);
481  errcnt++;
482  m_numFailures++;
483  if (errcnt == 3)
484  break;
485  }
486  else if (ret > 0)
487  {
488  tot += static_cast<uint>(ret);
489  }
490 
491  if (m_oldfile)
492  break;
493 
494  if (eof)
495  {
496  // we can exit now, if file is still open for writing in another
497  // instance, RingBuffer will retry
498  break;
499  }
500 
501  if (ret == 0)
502  {
503  if (tot > 0)
504  break;
505 
506  zerocnt++;
507 
508  // 0.36 second timeout for livetvchain with usleep(60000),
509  // or 2.4 seconds if it's a new file less than 30 minutes old.
510  if (zerocnt >= (m_liveTVChain ? 6 : 40))
511  {
512  break;
513  }
514  }
515  if (m_stopReads)
516  break;
517  if (tot < Size)
518  usleep(60000);
519  }
520  return static_cast<int>(tot);
521 }
522 
532 {
533  int ret = Remote->Read(Buffer, static_cast<int>(Size));
534  if (ret < 0)
535  {
536  LOG(VB_GENERAL, LOG_ERR, LOC + "safe_read(RemoteFile* ...): read failed");
537  m_posLock.lockForRead();
538  if (Remote->Seek(m_internalReadPos - m_readAdjust, SEEK_SET) < 0)
539  LOG(VB_GENERAL, LOG_ERR, LOC + "safe_read() failed to seek reset");
540  m_posLock.unlock();
541  m_numFailures++;
542  }
543  else if (ret == 0)
544  {
545  LOG(VB_FILE, LOG_INFO, LOC + "safe_read(RemoteFile* ...): at EOF");
546  }
547 
548  return ret;
549 }
550 
551 long long MythFileBuffer::GetReadPosition(void) const
552 {
553  m_posLock.lockForRead();
554  long long ret = m_readPos;
555  m_posLock.unlock();
556  return ret;
557 }
558 
560 {
561  m_rwLock.lockForRead();
562  long long result = -1;
563  if (m_remotefile)
564  {
565  result = m_remotefile->GetRealFileSize();
566  }
567  else
568  {
569  if (m_fd2 >= 0)
570  {
571  struct stat sb {};
572 
573  result = fstat(m_fd2, &sb);
574  if (result == 0 && S_ISREG(sb.st_mode))
575  {
576  m_rwLock.unlock();
577  return sb.st_size;
578  }
579  }
580  result = QFileInfo(m_filename).size();
581  }
582  m_rwLock.unlock();
583  return result;
584 }
585 
586 long long MythFileBuffer::SeekInternal(long long Position, int Whence)
587 {
588  long long ret = -1;
589 
590  // Ticket 12128
591  StopReads();
592  StartReads();
593 
594  if (m_writeMode)
595  return WriterSeek(Position, Whence, true);
596 
597  m_posLock.lockForWrite();
598 
599  // Optimize no-op seeks
600  if (m_readAheadRunning && ((Whence == SEEK_SET && Position == m_readPos) ||
601  (Whence == SEEK_CUR && Position == 0)))
602  {
603  ret = m_readPos;
604  m_posLock.unlock();
605  return ret;
606  }
607 
608  // only valid for SEEK_SET & SEEK_CUR
609  long long newposition = (SEEK_SET==Whence) ? Position : m_readPos + Position;
610 
611  // Optimize short seeks where the data for
612  // them is in our ringbuffer already.
613  if (m_readAheadRunning && (SEEK_SET==Whence || SEEK_CUR==Whence))
614  {
615  m_rbrLock.lockForWrite();
616  m_rbwLock.lockForRead();
617  LOG(VB_FILE, LOG_INFO, LOC +
618  QString("Seek(): rbrpos: %1 rbwpos: %2\n\t\t\treadpos: %3 internalreadpos: %4")
620  bool used_opt = false;
621  if ((newposition < m_readPos))
622  {
623  // Seeking to earlier than current buffer's start, but still in buffer
624  int min_safety = std::max(m_fillMin, m_readBlockSize);
625  int free = ((m_rbwPos >= m_rbrPos) ? m_rbrPos + static_cast<int>(m_bufferSize) : m_rbrPos) - m_rbwPos;
626  int internal_backbuf = (m_rbwPos >= m_rbrPos) ? m_rbrPos : m_rbrPos - m_rbwPos;
627  internal_backbuf = std::min(internal_backbuf, free - min_safety);
628  long long sba = m_readPos - newposition;
629  LOG(VB_FILE, LOG_INFO, LOC + QString("Seek(): internal_backbuf: %1 sba: %2")
630  .arg(internal_backbuf).arg(sba));
631  if (internal_backbuf >= sba)
632  {
633  m_rbrPos = (m_rbrPos>=sba) ? m_rbrPos - static_cast<int>(sba) :
634  static_cast<int>(m_bufferSize) + m_rbrPos - static_cast<int>(sba);
635  used_opt = true;
636  LOG(VB_FILE, LOG_INFO, LOC +
637  QString("Seek(): OPT1 rbrPos: %1 rbwPos: %2"
638  "\n\t\t\treadpos: %3 internalreadpos: %4")
640  .arg(newposition).arg(m_internalReadPos));
641  }
642  }
643  else if ((newposition >= m_readPos) && (newposition <= m_internalReadPos))
644  {
645  m_rbrPos = (m_rbrPos + (newposition - m_readPos)) % static_cast<int>(m_bufferSize);
646  used_opt = true;
647  LOG(VB_FILE, LOG_INFO, LOC + QString("Seek(): OPT2 rbrPos: %1 sba: %2")
648  .arg(m_rbrPos).arg(m_readPos - newposition));
649  }
650  m_rbwLock.unlock();
651  m_rbrLock.unlock();
652 
653  if (used_opt)
654  {
655  if (m_ignoreReadPos >= 0)
656  {
657  // seek should always succeed since we were at this position
658  if (m_remotefile)
659  {
660  ret = m_remotefile->Seek(m_internalReadPos, SEEK_SET);
661  }
662  else
663  {
664  ret = lseek64(m_fd2, m_internalReadPos, SEEK_SET);
665 #ifndef _MSC_VER
667  LOG(VB_FILE, LOG_DEBUG, LOC + QString("Seek(): fadvise willneed failed: ") + ENO);
668 #endif
669  }
670  LOG(VB_FILE, LOG_INFO, LOC + QString("Seek to %1 from ignore pos %2 returned %3")
672  m_ignoreReadPos = -1;
673  }
674  // if we are seeking forward we may now be too close to the
675  // end, so we need to recheck if reads are allowed.
676  if (newposition > m_readPos)
677  {
678  m_ateof = false;
679  m_readsAllowed = false;
680  m_readsDesired = false;
681  m_recentSeek = true;
682  }
683  m_readPos = newposition;
684  m_posLock.unlock();
685  m_generalWait.wakeAll();
686 
687  return newposition;
688  }
689  }
690 
691 #if 1
692  // This optimizes the seek end-250000, read, seek 0, read portion
693  // of the pattern ffmpeg performs at the start of playback to
694  // determine the pts.
695  // If the seek is a SEEK_END or is a seek where the position
696  // changes over 100 MB we check the file size and if the
697  // destination point is within 300000 bytes of the end of
698  // the file we enter a special mode where the read ahead
699  // buffer stops reading data and all reads are made directly
700  // until another seek is performed. The point of all this is
701  // to avoid flushing out the buffer that still contains all
702  // the data the final seek 0, read will need just to read the
703  // last 250000 bytes. A further optimization would be to buffer
704  // the 250000 byte read, which is currently performed in 32KB
705  // blocks (inefficient with RemoteFile).
706  if ((m_remotefile || m_fd2 >= 0) && (m_ignoreReadPos < 0))
707  {
708  long long off_end = 0xDEADBEEF;
709  if (SEEK_END == Whence)
710  {
711  off_end = Position;
712  if (m_remotefile)
713  {
714  newposition = m_remotefile->GetFileSize() - off_end;
715  }
716  else
717  {
718  QFileInfo fi(m_filename);
719  newposition = fi.size() - off_end;
720  }
721  }
722  else
723  {
724  if (m_remotefile)
725  {
726  off_end = m_remotefile->GetFileSize() - newposition;
727  }
728  else
729  {
730  QFileInfo file(m_filename);
731  off_end = file.size() - newposition;
732  }
733  }
734 
735  if (off_end != 0xDEADBEEF)
736  {
737  LOG(VB_FILE, LOG_INFO, LOC + QString("Seek(): Offset from end: %1").arg(off_end));
738  }
739 
740  if (off_end == 250000)
741  {
742  LOG(VB_FILE, LOG_INFO, LOC +
743  QString("Seek(): offset from end: %1").arg(off_end) +
744  "\n\t\t\t -- ignoring read ahead thread until next seek.");
745 
746  m_ignoreReadPos = newposition;
747  errno = EINVAL;
748  if (m_remotefile)
749  ret = m_remotefile->Seek(m_ignoreReadPos, SEEK_SET);
750  else
751  ret = lseek64(m_fd2, m_ignoreReadPos, SEEK_SET);
752 
753  if (ret < 0)
754  {
755  int tmp_eno = errno;
756  QString cmd = QString("Seek(%1, SEEK_SET) ign ")
757  .arg(m_ignoreReadPos);
758 
759  m_ignoreReadPos = -1;
760 
761  LOG(VB_GENERAL, LOG_ERR, LOC + cmd + " Failed" + ENO);
762 
763  // try to return to former position..
764  if (m_remotefile)
765  ret = m_remotefile->Seek(m_internalReadPos, SEEK_SET);
766  else
767  ret = lseek64(m_fd2, m_internalReadPos, SEEK_SET);
768  if (ret < 0)
769  {
770  QString cmd2 = QString("Seek(%1, SEEK_SET) int ")
771  .arg(m_internalReadPos);
772  LOG(VB_GENERAL, LOG_ERR, LOC + cmd2 + " Failed" + ENO);
773  }
774  else
775  {
776  QString cmd2 = QString("Seek(%1, %2) int ")
777  .arg(m_internalReadPos)
778  .arg((SEEK_SET == Whence) ? "SEEK_SET" :
779  ((SEEK_CUR == Whence) ?"SEEK_CUR" : "SEEK_END"));
780  LOG(VB_GENERAL, LOG_ERR, LOC + cmd2 + " succeeded");
781  }
782  ret = -1;
783  errno = tmp_eno;
784  }
785  else
786  {
787  m_ateof = false;
788  m_readsAllowed = false;
789  m_readsDesired = false;
790  m_recentSeek = true;
791  }
792 
793  m_posLock.unlock();
794 
795  m_generalWait.wakeAll();
796 
797  return ret;
798  }
799  }
800 #endif
801 
802  // Here we perform a normal seek. When successful we
803  // need to call ResetReadAhead(). A reset means we will
804  // need to refill the buffer, which takes some time.
805  if (m_remotefile)
806  {
807  ret = m_remotefile->Seek(Position, Whence, m_readPos);
808  if (ret < 0)
809  errno = EINVAL;
810  }
811  else
812  {
813  ret = lseek64(m_fd2, Position, Whence);
814  }
815 
816  if (ret >= 0)
817  {
818  m_readPos = ret;
819  m_ignoreReadPos = -1;
820  if (m_readAheadRunning)
822  m_readAdjust = 0;
823  }
824  else
825  {
826  QString cmd = QString("Seek(%1, %2)").arg(Position)
827  .arg((Whence == SEEK_SET) ? "SEEK_SET" :
828  ((Whence == SEEK_CUR) ? "SEEK_CUR" : "SEEK_END"));
829  LOG(VB_GENERAL, LOG_ERR, LOC + cmd + " Failed" + ENO);
830  }
831 
832  m_posLock.unlock();
833  m_generalWait.wakeAll();
834  return ret;
835 }
FileInfo
Definition: importnative.h:22
MythFileBuffer::ReOpen
bool ReOpen(const QString &Filename="") override
Definition: mythfilebuffer.cpp:364
fileInfo
QFileInfo fileInfo(filename)
ENO
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:72
MythMediaBuffer::m_tfw
ThreadedFileWriter * m_tfw
Definition: mythmediabuffer.h:186
RemoteFile::GetFileSize
long long GetFileSize(void) const
GetFileSize: returns the remote file's size at the time it was first opened Will query the server in ...
Definition: remotefile.cpp:1091
IsSubtitlePossible
static bool IsSubtitlePossible(const QString &Extension)
Definition: mythfilebuffer.cpp:126
MythMediaBuffer::m_rbrPos
int m_rbrPos
Definition: mythmediabuffer.h:170
MythMediaBuffer::m_readAdjust
long long m_readAdjust
Definition: mythmediabuffer.h:218
RemoteFile::ReOpen
bool ReOpen(const QString &newFilename)
Definition: remotefile.cpp:340
MythTimer
A QElapsedTimer based timer to replace use of QTime as a timer.
Definition: mythtimer.h:14
MythMediaBuffer::m_startReadAhead
bool m_startReadAhead
Definition: mythmediabuffer.h:194
MythFileBuffer::GetRealFileSizeInternal
long long GetRealFileSizeInternal(void) const override
Definition: mythfilebuffer.cpp:559
MythMediaBuffer::StopReads
void StopReads(void)
Definition: mythmediabuffer.cpp:665
discid.disc.read
def read(device=None, features=[])
Definition: disc.py:35
MythMediaBuffer::m_commsError
bool m_commsError
Definition: mythmediabuffer.h:214
MythMediaBuffer::m_oldfile
bool m_oldfile
Definition: mythmediabuffer.h:215
CheckPermissions
static bool CheckPermissions(const QString &Filename)
Definition: mythfilebuffer.cpp:114
MythMediaBuffer::m_fd2
int m_fd2
Definition: mythmediabuffer.h:187
MythMediaBuffer::m_safeFilename
QString m_safeFilename
Definition: mythmediabuffer.h:180
O_BINARY
#define O_BINARY
Definition: mythfilebuffer.cpp:41
MythMediaBuffer::CalcReadAheadThresh
void CalcReadAheadThresh(void)
Calculates m_fillMin, m_fillThreshold, and m_readBlockSize from the estimated effective bitrate of th...
Definition: mythmediabuffer.cpp:347
arg
arg(title).arg(filename).arg(doDelete))
RemoteFile
Definition: remotefile.h:18
MythMediaBuffer::Write
int Write(const void *Buffer, uint Count)
Writes buffer to ThreadedFileWriter::Write(const void*,uint)
Definition: mythmediabuffer.cpp:1617
MythMediaBuffer
Definition: mythmediabuffer.h:50
MythTimer::start
void start(void)
starts measuring elapsed time.
Definition: mythtimer.cpp:47
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
MythMediaBuffer::m_lastError
QString m_lastError
Definition: mythmediabuffer.h:185
MythMediaBuffer::m_generalWait
QWaitCondition m_generalWait
Condition to signal that the read ahead thread is running.
Definition: mythmediabuffer.h:236
MythFileBuffer::SafeRead
int SafeRead(void *Buffer, uint Size) override
Definition: mythfilebuffer.cpp:399
RemoteFile::isOpen
bool isOpen(void) const
Definition: remotefile.cpp:246
build_compdb.file
file
Definition: build_compdb.py:55
MythDate::current
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
MythMediaBuffer::m_readsAllowed
bool m_readsAllowed
Definition: mythmediabuffer.h:203
close
#define close
Definition: compat.h:17
POSIX_FADV_WILLNEED
#define POSIX_FADV_WILLNEED
Definition: mythfilebuffer.cpp:28
lseek
#define lseek
Definition: mythiowrapper.cpp:236
MythFileBuffer::SeekInternal
long long SeekInternal(long long Position, int Whence) override
Definition: mythfilebuffer.cpp:586
MythMediaBuffer::m_liveTVChain
LiveTVChain * m_liveTVChain
Definition: mythmediabuffer.h:216
MythCoreContext::IsRegisteredFileForWrite
bool IsRegisteredFileForWrite(const QString &file)
Definition: mythcorecontext.cpp:2138
threadedfilewriter.h
baseName
QString baseName
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:593
mythdate.h
MythMediaBuffer::m_readPos
long long m_readPos
Definition: mythmediabuffer.h:164
MythFileBuffer::GetReadPosition
long long GetReadPosition(void) const override
Definition: mythfilebuffer.cpp:551
RemoteFile::Seek
long long Seek(long long pos, int whence, long long curpos=-1)
Definition: remotefile.cpp:759
LocalSubtitleFilename
static QString LocalSubtitleFilename(QFileInfo &FileInfo)
Definition: mythfilebuffer.cpp:134
remotefile.h
MythMediaBuffer::WriterSeek
long long WriterSeek(long long Position, int Whence, bool HasLock=false)
Calls ThreadedFileWriter::Seek(long long,int).
Definition: mythmediabuffer.cpp:1664
posix_fadvise
static int posix_fadvise(int, off_t, off_t, int)
Definition: mythfilebuffer.cpp:26
compat.h
MythMediaBuffer::m_remotefile
RemoteFile * m_remotefile
Definition: mythmediabuffer.h:189
MythMediaBuffer::m_writePos
long long m_writePos
Definition: mythmediabuffer.h:165
MythMediaBuffer::m_readBlockSize
int m_readBlockSize
Definition: mythmediabuffer.h:211
POSIX_FADV_SEQUENTIAL
#define POSIX_FADV_SEQUENTIAL
Definition: mythfilebuffer.cpp:27
MythMediaBuffer::m_rbwLock
QReadWriteLock m_rbwLock
Definition: mythmediabuffer.h:172
RemoteFile::GetAuxiliaryFiles
QStringList GetAuxiliaryFiles(void) const
Definition: remotefile.h:64
MythMediaBuffer::m_setSwitchToNext
bool m_setSwitchToNext
Definition: mythmediabuffer.h:206
MythFileBuffer::MythFileBuffer
MythFileBuffer(const QString &Filename, bool Write, bool UseReadAhead, int Timeout)
Definition: mythfilebuffer.cpp:50
MythMediaBuffer::m_numFailures
int m_numFailures
Definition: mythmediabuffer.h:213
MythMediaBuffer::m_bufferSize
uint m_bufferSize
Definition: mythmediabuffer.h:190
uint
unsigned int uint
Definition: compat.h:141
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:60
LOC
#define LOC
Definition: mythfilebuffer.cpp:44
MythMediaBuffer::m_filename
QString m_filename
Definition: mythmediabuffer.h:183
O_LARGEFILE
#define O_LARGEFILE
Definition: mythfilebuffer.cpp:37
kSubExt
static const QStringList kSubExt
Definition: mythfilebuffer.cpp:46
O_STREAMING
#define O_STREAMING
Definition: mythfilebuffer.cpp:33
off_t
#define off_t
Definition: mythiowrapper.cpp:238
ThreadedFileWriter
This class supports the writing of recordings to disk.
Definition: threadedfilewriter.h:43
MythMediaBuffer::m_rbrLock
QReadWriteLock m_rbrLock
Definition: mythmediabuffer.h:169
MythMediaBuffer::m_readsDesired
bool m_readsDesired
Definition: mythmediabuffer.h:204
MythTimer::elapsed
int elapsed(void)
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:90
MythMediaBuffer::KillReadAheadThread
void KillReadAheadThread(void)
Stops the read-ahead thread, and waits for it to stop.
Definition: mythmediabuffer.cpp:649
Buffer
Definition: MythExternControl.h:37
MythMediaBuffer::m_recentSeek
volatile bool m_recentSeek
Definition: mythmediabuffer.h:205
kReadTestSize
#define kReadTestSize
Definition: mythmediabuffer.h:22
MythMediaBuffer::ResetReadAhead
void ResetReadAhead(long long NewInternal)
Restart the read-ahead thread at the 'newinternal' position.
Definition: mythmediabuffer.cpp:571
mythcorecontext.h
ThreadedFileWriter::ReOpen
bool ReOpen(const QString &newFilename="")
Reopens the file we are writing to or opens a new file.
Definition: threadedfilewriter.cpp:63
dir
QDir dir
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:1174
kSubExtNoCheck
static const QStringList kSubExtNoCheck
Definition: mythfilebuffer.cpp:47
MythFileBuffer::~MythFileBuffer
~MythFileBuffer() override
Definition: mythfilebuffer.cpp:93
mythcontext.h
kMythBufferFile
@ kMythBufferFile
Definition: mythmediabuffer.h:41
MythFileBuffer::OpenFile
bool OpenFile(const QString &Filename, uint Retry=static_cast< uint >(kDefaultOpenTimeout)) override
Definition: mythfilebuffer.cpp:173
mythtimer.h
MythMediaBuffer::m_subtitleFilename
QString m_subtitleFilename
Definition: mythmediabuffer.h:184
mythfilebuffer.h
MythMediaBuffer::m_writeMode
bool m_writeMode
Definition: mythmediabuffer.h:188
ThreadedFileWriter::Open
bool Open(void)
Opens the file we will be writing to.
Definition: threadedfilewriter.cpp:92
MythMediaBuffer::m_posLock
QReadWriteLock m_posLock
Definition: mythmediabuffer.h:163
MythMediaBuffer::m_stopReads
volatile bool m_stopReads
Definition: mythmediabuffer.h:177
MythFileBuffer::IsOpen
bool IsOpen(void) const override
Definition: mythfilebuffer.cpp:391
MythMediaBuffer::m_rwLock
QReadWriteLock m_rwLock
Definition: mythmediabuffer.h:182
MythMediaBuffer::StartReads
void StartReads(void)
Definition: mythmediabuffer.cpp:675
MythMediaBuffer::m_ateof
bool m_ateof
Definition: mythmediabuffer.h:200
MythMediaBuffer::m_internalReadPos
long long m_internalReadPos
Definition: mythmediabuffer.h:166
MythMediaBuffer::m_rbwPos
int m_rbwPos
Definition: mythmediabuffer.h:173
MThread::usleep
static void usleep(unsigned long time)
Definition: mthread.cpp:342
MythMediaBuffer::m_ignoreReadPos
long long m_ignoreReadPos
Definition: mythmediabuffer.h:167
MythMediaBuffer::m_readAheadRunning
bool m_readAheadRunning
Definition: mythmediabuffer.h:196
RemoteFile::Read
int Read(void *data, int size)
Definition: remotefile.cpp:936
MythMediaBuffer::m_fillMin
int m_fillMin
Definition: mythmediabuffer.h:210
RemoteFile::GetRealFileSize
long long GetRealFileSize(void)
GetRealFileSize: returns the current remote file's size.
Definition: remotefile.cpp:1119