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, std::chrono::milliseconds 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 >= 0ms)
88  {
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, std::chrono::milliseconds Retry)
174 {
175  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("OpenFile(%1, %2 ms)")
176  .arg(Filename).arg(Retry.count()));
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(10ms);
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(10ms);
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 (openTimer.elapsed() < Retry);
271 
272  switch (lasterror)
273  {
274  case 0:
275  {
276  QFileInfo file(m_filename);
277  m_oldfile = MythDate::secsInPast(file.lastModified().toUTC()) > 60s;
278  QString extension = file.completeSuffix().toLower();
279  if (IsSubtitlePossible(extension))
281  break;
282  }
283  case 1:
284  LOG(VB_GENERAL, LOG_ERR, LOC + QString("OpenFile(): Could not open."));
285  //: %1 is the filename
286  m_lastError = tr("Could not open %1").arg(m_filename);
287  break;
288  case 2:
289  LOG(VB_GENERAL, LOG_ERR, LOC + QString("OpenFile(): File too small (%1B).")
290  .arg(QFileInfo(m_filename).size()));
291  //: %1 is the file size
292  m_lastError = tr("File too small (%1B)").arg(QFileInfo(m_filename).size());
293  break;
294  case 3:
295  LOG(VB_GENERAL, LOG_ERR, LOC + "OpenFile(): Improper permissions.");
296  m_lastError = tr("Improper permissions");
297  break;
298  case 4:
299  LOG(VB_GENERAL, LOG_ERR, LOC + "OpenFile(): Cannot seek in file.");
300  m_lastError = tr("Cannot seek in file");
301  break;
302  default: break;
303  }
304  LOG(VB_FILE, LOG_INFO, LOC + QString("OpenFile() made %1 attempts in %2 ms")
305  .arg(openAttempts).arg(openTimer.elapsed().count()));
306  }
307  else
308  {
309  QString tmpSubName = m_filename;
310  QString dirName = ".";
311 
312  int dirPos = m_filename.lastIndexOf(QChar('/'));
313  if (dirPos > 0)
314  {
315  tmpSubName = m_filename.mid(dirPos + 1);
316  dirName = m_filename.left(dirPos);
317  }
318 
319  QStringList auxFiles;
320 
321  int suffixPos = tmpSubName.lastIndexOf(QChar('.'));
322  if (suffixPos > 0)
323  {
324  QString baseName = tmpSubName.left(suffixPos);
325  int extnleng = tmpSubName.size() - baseName.size() - 1;
326  QString extension = tmpSubName.right(extnleng);
327 
328  if (IsSubtitlePossible(extension))
329  {
330  for (const auto & ext : kSubExt)
331  auxFiles += baseName + ext;
332  }
333  }
334 
335  m_remotefile = new RemoteFile(m_filename, false, true, Retry, &auxFiles);
336  if (!m_remotefile->isOpen())
337  {
338  LOG(VB_GENERAL, LOG_ERR, LOC + QString("RingBuffer::RingBuffer(): Failed to open remote file (%1)")
339  .arg(m_filename));
340  //: %1 is the filename
341  m_lastError = tr("Failed to open remote file %1").arg(m_filename);
342  delete m_remotefile;
343  m_remotefile = nullptr;
344  }
345  else
346  {
347  QStringList aux = m_remotefile->GetAuxiliaryFiles();
348  if (!aux.empty())
349  m_subtitleFilename = dirName + "/" + aux[0];
350  }
351  }
352 
353  m_setSwitchToNext = false;
354  m_ateof = false;
355  m_commsError = false;
356  m_numFailures = 0;
358  bool ok = (m_fd2 >= 0) || m_remotefile;
359  m_rwLock.unlock();
360  return ok;
361 }
362 
363 bool MythFileBuffer::ReOpen(const QString& Filename)
364 {
365  if (!m_writeMode)
366  {
367  LOG(VB_GENERAL, LOG_ERR, LOC + "Tried to ReOpen a read only file.");
368  return false;
369  }
370 
371  bool result = false;
372 
373  m_rwLock.lockForWrite();
374 
375  if ((m_tfw && m_tfw->ReOpen(Filename)) || (m_remotefile && m_remotefile->ReOpen(Filename)))
376  result = true;
377 
378  if (result)
379  {
380  m_filename = Filename;
381  m_posLock.lockForWrite();
382  m_writePos = 0;
383  m_posLock.unlock();
384  }
385 
386  m_rwLock.unlock();
387  return result;
388 }
389 
390 bool MythFileBuffer::IsOpen(void) const
391 {
392  m_rwLock.lockForRead();
393  bool ret = m_tfw || (m_fd2 > -1) || m_remotefile;
394  m_rwLock.unlock();
395  return ret;
396 }
397 
399 {
400  if (m_remotefile)
401  return SafeRead(m_remotefile, Buffer, Size);
402  if (m_fd2 >= 0)
403  return SafeRead(m_fd2, Buffer, Size);
404  errno = EBADF;
405  return -1;
406 }
407 
420 int MythFileBuffer::SafeRead(int /*fd*/, void *Buffer, uint Size)
421 {
422  uint tot = 0;
423  uint errcnt = 0;
424  uint zerocnt = 0;
425 
426  if (m_fd2 < 0)
427  {
428  LOG(VB_GENERAL, LOG_ERR, LOC + "Invalid file descriptor in 'safe_read()'");
429  return 0;
430  }
431 
432  if (m_stopReads)
433  return 0;
434 
435  struct stat sb {};
436 
437  while (tot < Size)
438  {
439  uint toread = Size - tot;
440  bool read_ok = true;
441  bool eof = false;
442 
443  // check that we have some data to read,
444  // so we never attempt to read past the end of file
445  // if fstat errored or isn't a regular file, default to previous behavior
446  int ret = fstat(m_fd2, &sb);
447  if (ret == 0 && S_ISREG(sb.st_mode))
448  {
449  if ((m_internalReadPos + tot) >= sb.st_size)
450  {
451  // We're at the end, don't attempt to read
452  read_ok = false;
453  eof = true;
454  LOG(VB_FILE, LOG_DEBUG, LOC + "not reading, reached EOF");
455  }
456  else
457  {
458  toread = static_cast<uint>(std::min(sb.st_size - (m_internalReadPos + tot), static_cast<long long>(toread)));
459  if (toread < (Size - tot))
460  {
461  eof = true;
462  LOG(VB_FILE, LOG_DEBUG, LOC + QString("About to reach EOF, reading %1 wanted %2")
463  .arg(toread).arg(Size-tot));
464  }
465  }
466  }
467 
468  if (read_ok)
469  {
470  LOG(VB_FILE, LOG_DEBUG, LOC + QString("read(%1) -- begin").arg(toread));
471  ret = static_cast<int>(read(m_fd2, static_cast<char*>(Buffer) + tot, toread));
472  LOG(VB_FILE, LOG_DEBUG, LOC + QString("read(%1) -> %2 end").arg(toread).arg(ret));
473  }
474  if (ret < 0)
475  {
476  if (errno == EAGAIN)
477  continue;
478 
479  LOG(VB_GENERAL, LOG_ERR, LOC + "File I/O problem in 'safe_read()'" + ENO);
480  errcnt++;
481  m_numFailures++;
482  if (errcnt == 3)
483  break;
484  }
485  else if (ret > 0)
486  {
487  tot += static_cast<uint>(ret);
488  }
489 
490  if (m_oldfile)
491  break;
492 
493  if (eof)
494  {
495  // we can exit now, if file is still open for writing in another
496  // instance, RingBuffer will retry
497  break;
498  }
499 
500  if (ret == 0)
501  {
502  if (tot > 0)
503  break;
504 
505  zerocnt++;
506 
507  // 0.36 second timeout for livetvchain with usleep(60000),
508  // or 2.4 seconds if it's a new file less than 30 minutes old.
509  if (zerocnt >= (m_liveTVChain ? 6 : 40))
510  {
511  break;
512  }
513  }
514  if (m_stopReads)
515  break;
516  if (tot < Size)
517  usleep(60ms);
518  }
519  return static_cast<int>(tot);
520 }
521 
531 {
532  int ret = Remote->Read(Buffer, static_cast<int>(Size));
533  if (ret < 0)
534  {
535  LOG(VB_GENERAL, LOG_ERR, LOC + "safe_read(RemoteFile* ...): read failed");
536  m_posLock.lockForRead();
537  if (Remote->Seek(m_internalReadPos - m_readAdjust, SEEK_SET) < 0)
538  LOG(VB_GENERAL, LOG_ERR, LOC + "safe_read() failed to seek reset");
539  m_posLock.unlock();
540  m_numFailures++;
541  }
542  else if (ret == 0)
543  {
544  LOG(VB_FILE, LOG_INFO, LOC + "safe_read(RemoteFile* ...): at EOF");
545  }
546 
547  return ret;
548 }
549 
550 long long MythFileBuffer::GetReadPosition(void) const
551 {
552  m_posLock.lockForRead();
553  long long ret = m_readPos;
554  m_posLock.unlock();
555  return ret;
556 }
557 
559 {
560  m_rwLock.lockForRead();
561  long long result = -1;
562  if (m_remotefile)
563  {
564  result = m_remotefile->GetRealFileSize();
565  }
566  else
567  {
568  if (m_fd2 >= 0)
569  {
570  struct stat sb {};
571 
572  result = fstat(m_fd2, &sb);
573  if (result == 0 && S_ISREG(sb.st_mode))
574  {
575  m_rwLock.unlock();
576  return sb.st_size;
577  }
578  }
579  result = QFileInfo(m_filename).size();
580  }
581  m_rwLock.unlock();
582  return result;
583 }
584 
585 long long MythFileBuffer::SeekInternal(long long Position, int Whence)
586 {
587  long long ret = -1;
588 
589  // Ticket 12128
590  StopReads();
591  StartReads();
592 
593  if (m_writeMode)
594  return WriterSeek(Position, Whence, true);
595 
596  m_posLock.lockForWrite();
597 
598  // Optimize no-op seeks
599  if (m_readAheadRunning && ((Whence == SEEK_SET && Position == m_readPos) ||
600  (Whence == SEEK_CUR && Position == 0)))
601  {
602  ret = m_readPos;
603  m_posLock.unlock();
604  return ret;
605  }
606 
607  // only valid for SEEK_SET & SEEK_CUR
608  long long newposition = (SEEK_SET==Whence) ? Position : m_readPos + Position;
609 
610  // Optimize short seeks where the data for
611  // them is in our ringbuffer already.
612  if (m_readAheadRunning && (SEEK_SET==Whence || SEEK_CUR==Whence))
613  {
614  m_rbrLock.lockForWrite();
615  m_rbwLock.lockForRead();
616  LOG(VB_FILE, LOG_INFO, LOC +
617  QString("Seek(): rbrpos: %1 rbwpos: %2\n\t\t\treadpos: %3 internalreadpos: %4")
619  bool used_opt = false;
620  if ((newposition < m_readPos))
621  {
622  // Seeking to earlier than current buffer's start, but still in buffer
623  int min_safety = std::max(m_fillMin, m_readBlockSize);
624  int free = ((m_rbwPos >= m_rbrPos) ? m_rbrPos + static_cast<int>(m_bufferSize) : m_rbrPos) - m_rbwPos;
625  int internal_backbuf = (m_rbwPos >= m_rbrPos) ? m_rbrPos : m_rbrPos - m_rbwPos;
626  internal_backbuf = std::min(internal_backbuf, free - min_safety);
627  long long sba = m_readPos - newposition;
628  LOG(VB_FILE, LOG_INFO, LOC + QString("Seek(): internal_backbuf: %1 sba: %2")
629  .arg(internal_backbuf).arg(sba));
630  if (internal_backbuf >= sba)
631  {
632  m_rbrPos = (m_rbrPos>=sba) ? m_rbrPos - static_cast<int>(sba) :
633  static_cast<int>(m_bufferSize) + m_rbrPos - static_cast<int>(sba);
634  used_opt = true;
635  LOG(VB_FILE, LOG_INFO, LOC +
636  QString("Seek(): OPT1 rbrPos: %1 rbwPos: %2"
637  "\n\t\t\treadpos: %3 internalreadpos: %4")
639  .arg(newposition).arg(m_internalReadPos));
640  }
641  }
642  else if ((newposition >= m_readPos) && (newposition <= m_internalReadPos))
643  {
644  m_rbrPos = (m_rbrPos + (newposition - m_readPos)) % static_cast<int>(m_bufferSize);
645  used_opt = true;
646  LOG(VB_FILE, LOG_INFO, LOC + QString("Seek(): OPT2 rbrPos: %1 sba: %2")
647  .arg(m_rbrPos).arg(m_readPos - newposition));
648  }
649  m_rbwLock.unlock();
650  m_rbrLock.unlock();
651 
652  if (used_opt)
653  {
654  if (m_ignoreReadPos >= 0)
655  {
656  // seek should always succeed since we were at this position
657  if (m_remotefile)
658  {
659  ret = m_remotefile->Seek(m_internalReadPos, SEEK_SET);
660  }
661  else
662  {
663  ret = lseek64(m_fd2, m_internalReadPos, SEEK_SET);
664 #ifndef _MSC_VER
666  LOG(VB_FILE, LOG_DEBUG, LOC + QString("Seek(): fadvise willneed failed: ") + ENO);
667 #endif
668  }
669  LOG(VB_FILE, LOG_INFO, LOC + QString("Seek to %1 from ignore pos %2 returned %3")
671  m_ignoreReadPos = -1;
672  }
673  // if we are seeking forward we may now be too close to the
674  // end, so we need to recheck if reads are allowed.
675  if (newposition > m_readPos)
676  {
677  m_ateof = false;
678  m_readsAllowed = false;
679  m_readsDesired = false;
680  m_recentSeek = true;
681  }
682  m_readPos = newposition;
683  m_posLock.unlock();
684  m_generalWait.wakeAll();
685 
686  return newposition;
687  }
688  }
689 
690 #if 1
691  // This optimizes the seek end-250000, read, seek 0, read portion
692  // of the pattern ffmpeg performs at the start of playback to
693  // determine the pts.
694  // If the seek is a SEEK_END or is a seek where the position
695  // changes over 100 MB we check the file size and if the
696  // destination point is within 300000 bytes of the end of
697  // the file we enter a special mode where the read ahead
698  // buffer stops reading data and all reads are made directly
699  // until another seek is performed. The point of all this is
700  // to avoid flushing out the buffer that still contains all
701  // the data the final seek 0, read will need just to read the
702  // last 250000 bytes. A further optimization would be to buffer
703  // the 250000 byte read, which is currently performed in 32KB
704  // blocks (inefficient with RemoteFile).
705  if ((m_remotefile || m_fd2 >= 0) && (m_ignoreReadPos < 0))
706  {
707  long long off_end = 0xDEADBEEF;
708  if (SEEK_END == Whence)
709  {
710  off_end = Position;
711  if (m_remotefile)
712  {
713  newposition = m_remotefile->GetFileSize() - off_end;
714  }
715  else
716  {
717  QFileInfo fi(m_filename);
718  newposition = fi.size() - off_end;
719  }
720  }
721  else
722  {
723  if (m_remotefile)
724  {
725  off_end = m_remotefile->GetFileSize() - newposition;
726  }
727  else
728  {
729  QFileInfo file(m_filename);
730  off_end = file.size() - newposition;
731  }
732  }
733 
734  if (off_end != 0xDEADBEEF)
735  {
736  LOG(VB_FILE, LOG_INFO, LOC + QString("Seek(): Offset from end: %1").arg(off_end));
737  }
738 
739  if (off_end == 250000)
740  {
741  LOG(VB_FILE, LOG_INFO, LOC +
742  QString("Seek(): offset from end: %1").arg(off_end) +
743  "\n\t\t\t -- ignoring read ahead thread until next seek.");
744 
745  m_ignoreReadPos = newposition;
746  errno = EINVAL;
747  if (m_remotefile)
748  ret = m_remotefile->Seek(m_ignoreReadPos, SEEK_SET);
749  else
750  ret = lseek64(m_fd2, m_ignoreReadPos, SEEK_SET);
751 
752  if (ret < 0)
753  {
754  int tmp_eno = errno;
755  QString cmd = QString("Seek(%1, SEEK_SET) ign ")
756  .arg(m_ignoreReadPos);
757 
758  m_ignoreReadPos = -1;
759 
760  LOG(VB_GENERAL, LOG_ERR, LOC + cmd + " Failed" + ENO);
761 
762  // try to return to former position..
763  if (m_remotefile)
764  ret = m_remotefile->Seek(m_internalReadPos, SEEK_SET);
765  else
766  ret = lseek64(m_fd2, m_internalReadPos, SEEK_SET);
767  if (ret < 0)
768  {
769  QString cmd2 = QString("Seek(%1, SEEK_SET) int ")
770  .arg(m_internalReadPos);
771  LOG(VB_GENERAL, LOG_ERR, LOC + cmd2 + " Failed" + ENO);
772  }
773  else
774  {
775  QString cmd2 = QString("Seek(%1, %2) int ")
776  .arg(m_internalReadPos)
777  .arg((SEEK_SET == Whence) ? "SEEK_SET" :
778  ((SEEK_CUR == Whence) ?"SEEK_CUR" : "SEEK_END"));
779  LOG(VB_GENERAL, LOG_ERR, LOC + cmd2 + " succeeded");
780  }
781  ret = -1;
782  errno = tmp_eno;
783  }
784  else
785  {
786  m_ateof = false;
787  m_readsAllowed = false;
788  m_readsDesired = false;
789  m_recentSeek = true;
790  }
791 
792  m_posLock.unlock();
793 
794  m_generalWait.wakeAll();
795 
796  return ret;
797  }
798  }
799 #endif
800 
801  // Here we perform a normal seek. When successful we
802  // need to call ResetReadAhead(). A reset means we will
803  // need to refill the buffer, which takes some time.
804  if (m_remotefile)
805  {
806  ret = m_remotefile->Seek(Position, Whence, m_readPos);
807  if (ret < 0)
808  errno = EINVAL;
809  }
810  else
811  {
812  ret = lseek64(m_fd2, Position, Whence);
813  }
814 
815  if (ret >= 0)
816  {
817  m_readPos = ret;
818  m_ignoreReadPos = -1;
819  if (m_readAheadRunning)
821  m_readAdjust = 0;
822  }
823  else
824  {
825  QString cmd = QString("Seek(%1, %2)").arg(Position)
826  .arg((Whence == SEEK_SET) ? "SEEK_SET" :
827  ((Whence == SEEK_CUR) ? "SEEK_CUR" : "SEEK_END"));
828  LOG(VB_GENERAL, LOG_ERR, LOC + cmd + " Failed" + ENO);
829  }
830 
831  m_posLock.unlock();
832  m_generalWait.wakeAll();
833  return ret;
834 }
FileInfo
Definition: importnative.h:21
MythTimer::elapsed
std::chrono::milliseconds elapsed(void)
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:91
MythFileBuffer::ReOpen
bool ReOpen(const QString &Filename="") override
Definition: mythfilebuffer.cpp:363
MythFileBuffer::MythFileBuffer
MythFileBuffer(const QString &Filename, bool Write, bool UseReadAhead, std::chrono::milliseconds Timeout)
Definition: mythfilebuffer.cpp:50
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:187
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:171
MythMediaBuffer::m_readAdjust
long long m_readAdjust
Definition: mythmediabuffer.h:219
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:13
MythMediaBuffer::m_startReadAhead
bool m_startReadAhead
Definition: mythmediabuffer.h:195
MythFileBuffer::GetRealFileSizeInternal
long long GetRealFileSizeInternal(void) const override
Definition: mythfilebuffer.cpp:558
MythMediaBuffer::StopReads
void StopReads(void)
Definition: mythmediabuffer.cpp:662
discid.disc.read
def read(device=None, features=[])
Definition: disc.py:35
MythMediaBuffer::m_commsError
bool m_commsError
Definition: mythmediabuffer.h:215
MythMediaBuffer::m_oldfile
bool m_oldfile
Definition: mythmediabuffer.h:216
CheckPermissions
static bool CheckPermissions(const QString &Filename)
Definition: mythfilebuffer.cpp:114
MythMediaBuffer::m_fd2
int m_fd2
Definition: mythmediabuffer.h:188
MythMediaBuffer::m_safeFilename
QString m_safeFilename
Definition: mythmediabuffer.h:181
O_BINARY
#define O_BINARY
Definition: mythfilebuffer.cpp:41
MThread::usleep
static void usleep(std::chrono::microseconds time)
Definition: mthread.cpp:338
MythMediaBuffer::CalcReadAheadThresh
void CalcReadAheadThresh(void)
Calculates m_fillMin, m_fillThreshold, and m_readBlockSize from the estimated effective bitrate of th...
Definition: mythmediabuffer.cpp:344
arg
arg(title).arg(filename).arg(doDelete))
RemoteFile
Definition: remotefile.h:17
MythMediaBuffer::Write
int Write(const void *Buffer, uint Count)
Writes buffer to ThreadedFileWriter::Write(const void*,uint)
Definition: mythmediabuffer.cpp:1613
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:186
MythMediaBuffer::m_generalWait
QWaitCondition m_generalWait
Condition to signal that the read ahead thread is running.
Definition: mythmediabuffer.h:237
MythFileBuffer::SafeRead
int SafeRead(void *Buffer, uint Size) override
Definition: mythfilebuffer.cpp:398
RemoteFile::isOpen
bool isOpen(void) const
Definition: remotefile.cpp:246
build_compdb.file
file
Definition: build_compdb.py:55
MythMediaBuffer::m_readsAllowed
bool m_readsAllowed
Definition: mythmediabuffer.h:204
MythFileBuffer::OpenFile
bool OpenFile(const QString &Filename, std::chrono::milliseconds Retry=kDefaultOpenTimeout) override
Definition: mythfilebuffer.cpp:173
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:585
MythMediaBuffer::m_liveTVChain
LiveTVChain * m_liveTVChain
Definition: mythmediabuffer.h:217
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:165
MythFileBuffer::GetReadPosition
long long GetReadPosition(void) const override
Definition: mythfilebuffer.cpp:550
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:1660
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:190
MythMediaBuffer::m_writePos
long long m_writePos
Definition: mythmediabuffer.h:166
MythMediaBuffer::m_readBlockSize
int m_readBlockSize
Definition: mythmediabuffer.h:212
POSIX_FADV_SEQUENTIAL
#define POSIX_FADV_SEQUENTIAL
Definition: mythfilebuffer.cpp:27
MythMediaBuffer::m_rbwLock
QReadWriteLock m_rbwLock
Definition: mythmediabuffer.h:173
RemoteFile::GetAuxiliaryFiles
QStringList GetAuxiliaryFiles(void) const
Definition: remotefile.h:64
MythMediaBuffer::m_setSwitchToNext
bool m_setSwitchToNext
Definition: mythmediabuffer.h:207
MythMediaBuffer::m_numFailures
int m_numFailures
Definition: mythmediabuffer.h:214
MythMediaBuffer::m_bufferSize
uint m_bufferSize
Definition: mythmediabuffer.h:191
uint
unsigned int uint
Definition: compat.h:140
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:184
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:42
MythMediaBuffer::m_rbrLock
QReadWriteLock m_rbrLock
Definition: mythmediabuffer.h:170
MythMediaBuffer::m_readsDesired
bool m_readsDesired
Definition: mythmediabuffer.h:205
MythMediaBuffer::KillReadAheadThread
void KillReadAheadThread(void)
Stops the read-ahead thread, and waits for it to stop.
Definition: mythmediabuffer.cpp:646
Buffer
Definition: MythExternControl.h:36
MythMediaBuffer::m_recentSeek
volatile bool m_recentSeek
Definition: mythmediabuffer.h:206
kReadTestSize
#define kReadTestSize
Definition: mythmediabuffer.h:23
MythDate::secsInPast
std::chrono::seconds secsInPast(const QDateTime &past)
Definition: mythdate.cpp:199
MythMediaBuffer::ResetReadAhead
void ResetReadAhead(long long NewInternal)
Restart the read-ahead thread at the 'newinternal' position.
Definition: mythmediabuffer.cpp:568
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:42
mythtimer.h
MythMediaBuffer::m_subtitleFilename
QString m_subtitleFilename
Definition: mythmediabuffer.h:185
mythfilebuffer.h
MythMediaBuffer::m_writeMode
bool m_writeMode
Definition: mythmediabuffer.h:189
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:164
MythMediaBuffer::m_stopReads
volatile bool m_stopReads
Definition: mythmediabuffer.h:178
MythFileBuffer::IsOpen
bool IsOpen(void) const override
Definition: mythfilebuffer.cpp:390
MythMediaBuffer::m_rwLock
QReadWriteLock m_rwLock
Definition: mythmediabuffer.h:183
MythMediaBuffer::StartReads
void StartReads(void)
Definition: mythmediabuffer.cpp:672
MythMediaBuffer::m_ateof
bool m_ateof
Definition: mythmediabuffer.h:201
MythMediaBuffer::m_internalReadPos
long long m_internalReadPos
Definition: mythmediabuffer.h:167
MythMediaBuffer::m_rbwPos
int m_rbwPos
Definition: mythmediabuffer.h:174
MythMediaBuffer::m_ignoreReadPos
long long m_ignoreReadPos
Definition: mythmediabuffer.h:168
MythMediaBuffer::m_readAheadRunning
bool m_readAheadRunning
Definition: mythmediabuffer.h:197
RemoteFile::Read
int Read(void *data, int size)
Definition: remotefile.cpp:936
MythMediaBuffer::m_fillMin
int m_fillMin
Definition: mythmediabuffer.h:211
RemoteFile::GetRealFileSize
long long GetRealFileSize(void)
GetRealFileSize: returns the current remote file's size.
Definition: remotefile.cpp:1119