MythTV  master
mythfilebuffer.cpp
Go to the documentation of this file.
1 // QT
2 #include <QFileInfo>
3 #include <QDir>
4 
5 // MythTV
6 #include "libmyth/mythcontext.h"
7 #include "libmythbase/compat.h"
8 #include "libmythbase/mythconfig.h"
10 #include "libmythbase/mythdate.h"
11 #include "libmythbase/mythtimer.h"
12 #include "libmythbase/remotefile.h"
14 
15 #include "io/mythfilebuffer.h"
16 
17 // Std
18 #include <array>
19 #include <cstdlib>
20 #include <cerrno>
21 #include <sys/types.h>
22 #include <sys/time.h>
23 #include <sys/stat.h>
24 #include <unistd.h>
25 #include <fcntl.h>
26 
27 #if HAVE_POSIX_FADVISE < 1
28 static int posix_fadvise(int, off_t, off_t, int) { return 0; }
29 static constexpr int8_t POSIX_FADV_SEQUENTIAL { 0 };
30 static constexpr int8_t POSIX_FADV_WILLNEED { 0 };
31 #endif
32 
33 #ifndef O_STREAMING
34 static constexpr int8_t O_STREAMING { 0 };
35 #endif
36 
37 #ifndef O_LARGEFILE
38 static constexpr int8_t O_LARGEFILE { 0 };
39 #endif
40 
41 #ifndef O_BINARY
42 static constexpr int8_t O_BINARY { 0 };
43 #endif
44 
45 #define LOC QString("FileRingBuf(%1): ").arg(m_filename)
46 
47 static const QStringList kSubExt {".ass", ".srt", ".ssa", ".sub", ".txt"};
48 static const QStringList kSubExtNoCheck {".ass", ".srt", ".ssa", ".sub", ".txt", ".gif", ".png"};
49 
50 
51 MythFileBuffer::MythFileBuffer(const QString &Filename, bool Write, bool UseReadAhead, std::chrono::milliseconds Timeout)
53 {
54  m_startReadAhead = UseReadAhead;
55  m_safeFilename = Filename;
56  m_filename = Filename;
57 
58  if (Write)
59  {
60  if (m_filename.startsWith("myth://"))
61  {
62  m_remotefile = new RemoteFile(m_filename, true);
63  if (!m_remotefile->isOpen())
64  {
65  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to open remote file (%1) for write").arg(m_filename));
66  delete m_remotefile;
67  m_remotefile = nullptr;
68  }
69  else
70  {
71  m_writeMode = true;
72  }
73  }
74  else
75  {
76  m_tfw = new ThreadedFileWriter(m_filename, O_WRONLY|O_TRUNC|O_CREAT|O_LARGEFILE, 0644);
77  if (!m_tfw->Open())
78  {
79  delete m_tfw;
80  m_tfw = nullptr;
81  }
82  else
83  {
84  m_writeMode = true;
85  }
86  }
87  }
88  else if (Timeout >= 0ms)
89  {
91  }
92 }
93 
95 {
97 
98  delete m_remotefile;
99  m_remotefile = nullptr;
100 
101  delete m_tfw;
102  m_tfw = nullptr;
103 
104  if (m_fd2 >= 0)
105  {
106  close(m_fd2);
107  m_fd2 = -1;
108  }
109 }
110 
115 static bool CheckPermissions(const QString &Filename)
116 {
117  QFileInfo fileInfo(Filename);
118  if (fileInfo.exists() && !fileInfo.isReadable())
119  {
120  LOG(VB_GENERAL, LOG_ERR, QString("FileRingBuf(%1): File exists but is not readable by MythTV!")
121  .arg(Filename));
122  return false;
123  }
124  return true;
125 }
126 
127 static bool IsSubtitlePossible(const QString &Extension)
128 {
129  auto it = std::find_if(kSubExtNoCheck.cbegin(), kSubExtNoCheck.cend(),
130  [Extension] (const QString& ext) -> bool
131  {return ext.contains(Extension);});
132  return (it != nullptr);
133 }
134 
135 static QString LocalSubtitleFilename(QFileInfo &FileInfo)
136 {
137  // Subtitle handling
138  QString vidFileName = FileInfo.fileName();
139  QString dirName = FileInfo.absolutePath();
140 
141  QString baseName = vidFileName;
142  int suffixPos = vidFileName.lastIndexOf(QChar('.'));
143  if (suffixPos > 0)
144  baseName = vidFileName.left(suffixPos);
145 
146  QStringList list;
147  {
148  // The dir listing does not work if the filename has the
149  // following chars "[]()" so we convert them to the wildcard '?'
150  const QString findBaseName = baseName.replace("[", "?")
151  .replace("]", "?")
152  .replace("(", "?")
153  .replace(")", "?");
154 
155  for (const auto & ext : kSubExt)
156  list += findBaseName + ext;
157  }
158 
159  // Some Qt versions do not accept paths in the search string of
160  // entryList() so we have to set the dir first
161  QDir dir;
162  dir.setPath(dirName);
163  const QStringList candidates = dir.entryList(list);
164  for (const auto & candidate : candidates)
165  {
166  QFileInfo file(dirName + "/" + candidate);
167  if (file.exists() && (file.size() >= kReadTestSize))
168  return file.absoluteFilePath();
169  }
170 
171  return {};
172 }
173 
174 bool MythFileBuffer::OpenFile(const QString &Filename, std::chrono::milliseconds Retry)
175 {
176  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("OpenFile(%1, %2 ms)")
177  .arg(Filename).arg(Retry.count()));
178 
179  m_rwLock.lockForWrite();
180 
181  m_filename = Filename;
182  m_safeFilename = Filename;
183  m_subtitleFilename.clear();
184 
185  if (m_remotefile)
186  {
187  delete m_remotefile;
188  m_remotefile = nullptr;
189  }
190 
191  if (m_fd2 >= 0)
192  {
193  close(m_fd2);
194  m_fd2 = -1;
195  }
196 
197  bool islocal = (!m_filename.startsWith("/dev")) &&
198  ((m_filename.startsWith("/")) || QFile::exists(m_filename));
199 
200  if (islocal)
201  {
202  std::array<char,kReadTestSize> buf {};
203  int lasterror = 0;
204 
205  MythTimer openTimer;
206  openTimer.start();
207 
208  uint openAttempts = 0;
209  do
210  {
211  openAttempts++;
212 
213  m_fd2 = open(m_filename.toLocal8Bit().constData(),
214  O_RDONLY|O_LARGEFILE|O_STREAMING|O_BINARY);
215 
216  if (m_fd2 < 0)
217  {
219  {
220  lasterror = 3;
221  break;
222  }
223 
224  lasterror = 1;
225  usleep(10ms);
226  }
227  else
228  {
229  ssize_t ret = read(m_fd2, buf.data(), buf.size());
230  if (ret != kReadTestSize)
231  {
232  lasterror = 2;
233  close(m_fd2);
234  m_fd2 = -1;
235  if (ret == 0 && openAttempts > 5 && !gCoreContext->IsRegisteredFileForWrite(m_filename))
236  {
237  // file won't grow, abort early
238  break;
239  }
240 
241  if (m_oldfile)
242  break; // if it's an old file it won't grow..
243  usleep(10ms);
244  }
245  else
246  {
247  if (0 == lseek(m_fd2, 0, SEEK_SET))
248  {
249 #ifndef _MSC_VER
250  if (posix_fadvise(m_fd2, 0, 0, POSIX_FADV_SEQUENTIAL) != 0)
251  {
252  LOG(VB_FILE, LOG_DEBUG, LOC +
253  QString("OpenFile(): fadvise sequential "
254  "failed: ") + ENO);
255  }
256  if (posix_fadvise(m_fd2, 0, static_cast<off_t>(128)*1024, POSIX_FADV_WILLNEED) != 0)
257  {
258  LOG(VB_FILE, LOG_DEBUG, LOC +
259  QString("OpenFile(): fadvise willneed "
260  "failed: ") + ENO);
261  }
262 #endif
263  lasterror = 0;
264  break;
265  }
266  lasterror = 4;
267  close(m_fd2);
268  m_fd2 = -1;
269  }
270  }
271  } while (openTimer.elapsed() < Retry);
272 
273  switch (lasterror)
274  {
275  case 0:
276  {
277  QFileInfo file(m_filename);
278  m_oldfile = MythDate::secsInPast(file.lastModified().toUTC()) > 60s;
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().count()));
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, 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(60ms);
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")
619  .arg(m_rbrPos).arg(m_rbwPos).arg(m_readPos).arg(m_internalReadPos));
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")
639  .arg(m_rbrPos).arg(m_rbwPos)
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
666  if (posix_fadvise(m_fd2, m_internalReadPos, static_cast<off_t>(128)*1024, POSIX_FADV_WILLNEED) != 0)
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")
671  .arg(m_internalReadPos).arg(m_ignoreReadPos).arg(ret));
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: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:364
MythFileBuffer::MythFileBuffer
MythFileBuffer(const QString &Filename, bool Write, bool UseReadAhead, std::chrono::milliseconds Timeout)
Definition: mythfilebuffer.cpp:51
ENO
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:73
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:1084
IsSubtitlePossible
static bool IsSubtitlePossible(const QString &Extension)
Definition: mythfilebuffer.cpp:127
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:332
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
O_LARGEFILE
static constexpr int8_t O_LARGEFILE
Definition: mythfilebuffer.cpp:38
MythFileBuffer::GetRealFileSizeInternal
long long GetRealFileSizeInternal(void) const override
Definition: mythfilebuffer.cpp:559
MythMediaBuffer::StopReads
void StopReads(void)
Definition: mythmediabuffer.cpp:663
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:115
MythMediaBuffer::m_fd2
int m_fd2
Definition: mythmediabuffer.h:188
MythMediaBuffer::m_safeFilename
QString m_safeFilename
Definition: mythmediabuffer.h:181
MThread::usleep
static void usleep(std::chrono::microseconds time)
Definition: mthread.cpp:335
MythMediaBuffer::CalcReadAheadThresh
void CalcReadAheadThresh(void)
Calculates m_fillMin, m_fillThreshold, and m_readBlockSize from the estimated effective bitrate of th...
Definition: mythmediabuffer.cpp:345
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:1625
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:39
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:399
RemoteFile::isOpen
bool isOpen(void) const
Definition: remotefile.cpp:238
O_STREAMING
static constexpr int8_t O_STREAMING
Definition: mythfilebuffer.cpp:34
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:174
close
#define close
Definition: compat.h:43
lseek
#define lseek
Definition: mythiowrapper.cpp:239
MythFileBuffer::SeekInternal
long long SeekInternal(long long Position, int Whence) override
Definition: mythfilebuffer.cpp:586
MythMediaBuffer::m_liveTVChain
LiveTVChain * m_liveTVChain
Definition: mythmediabuffer.h:217
MythCoreContext::IsRegisteredFileForWrite
bool IsRegisteredFileForWrite(const QString &file)
Definition: mythcorecontext.cpp:2127
threadedfilewriter.h
mythdate.h
MythMediaBuffer::m_readPos
long long m_readPos
Definition: mythmediabuffer.h:165
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:752
LocalSubtitleFilename
static QString LocalSubtitleFilename(QFileInfo &FileInfo)
Definition: mythfilebuffer.cpp:135
remotefile.h
MythMediaBuffer::WriterSeek
long long WriterSeek(long long Position, int Whence, bool HasLock=false)
Calls ThreadedFileWriter::Seek(long long,int).
Definition: mythmediabuffer.cpp:1672
posix_fadvise
static int posix_fadvise(int, off_t, off_t, int)
Definition: mythfilebuffer.cpp:28
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
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
POSIX_FADV_SEQUENTIAL
static constexpr int8_t POSIX_FADV_SEQUENTIAL
Definition: mythfilebuffer.cpp:29
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:79
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:54
LOC
#define LOC
Definition: mythfilebuffer.cpp:45
MythMediaBuffer::m_filename
QString m_filename
Definition: mythmediabuffer.h:184
kSubExt
static const QStringList kSubExt
Definition: mythfilebuffer.cpp:47
kReadTestSize
static constexpr qint64 kReadTestSize
Definition: mythmediabuffer.h:23
off_t
#define off_t
Definition: mythiowrapper.cpp:241
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:647
Buffer
Definition: MythExternControl.h:36
MythMediaBuffer::m_recentSeek
volatile bool m_recentSeek
Definition: mythmediabuffer.h:206
MythDate::secsInPast
std::chrono::seconds secsInPast(const QDateTime &past)
Definition: mythdate.cpp:203
MythMediaBuffer::ResetReadAhead
void ResetReadAhead(long long NewInternal)
Restart the read-ahead thread at the 'newinternal' position.
Definition: mythmediabuffer.cpp:569
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
kSubExtNoCheck
static const QStringList kSubExtNoCheck
Definition: mythfilebuffer.cpp:48
MythFileBuffer::~MythFileBuffer
~MythFileBuffer() override
Definition: mythfilebuffer.cpp:94
mythcontext.h
POSIX_FADV_WILLNEED
static constexpr int8_t POSIX_FADV_WILLNEED
Definition: mythfilebuffer.cpp:30
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
O_BINARY
static constexpr int8_t O_BINARY
Definition: mythfilebuffer.cpp:42
MythFileBuffer::IsOpen
bool IsOpen(void) const override
Definition: mythfilebuffer.cpp:391
MythMediaBuffer::m_rwLock
QReadWriteLock m_rwLock
Definition: mythmediabuffer.h:183
MythMediaBuffer::StartReads
void StartReads(void)
Definition: mythmediabuffer.cpp:673
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:929
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:1112