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