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  QStringList::const_iterator eit = RingBuffer::s_subExt.begin();
160  for (; eit != RingBuffer::s_subExt.end(); ++eit)
161  el += findBaseName + *eit;
162  }
163 
164  // Some Qt versions do not accept paths in the search string of
165  // entryList() so we have to set the dir first
166  QDir dir;
167  dir.setPath(dirName);
168 
169  const QStringList candidates = dir.entryList(el);
170 
171  QStringList::const_iterator cit = candidates.begin();
172  for (; cit != candidates.end(); ++cit)
173  {
174  QFileInfo fi(dirName + "/" + *cit);
175  if (fi.exists() && (fi.size() >= kReadTestSize))
176  return fi.absoluteFilePath();
177  }
178 
179  return QString();
180 }
181 
182 bool FileRingBuffer::OpenFile(const QString &lfilename, uint retry_ms)
183 {
184  LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("OpenFile(%1, %2 ms)")
185  .arg(lfilename).arg(retry_ms));
186 
187  m_rwLock.lockForWrite();
188 
189  m_filename = lfilename;
190  m_safeFilename = lfilename;
191  m_subtitleFilename.clear();
192 
193  if (m_remotefile)
194  {
195  delete m_remotefile;
196  m_remotefile = nullptr;
197  }
198 
199  if (m_fd2 >= 0)
200  {
201  close(m_fd2);
202  m_fd2 = -1;
203  }
204 
205  bool is_local =
206  (!m_filename.startsWith("/dev")) &&
207  ((m_filename.startsWith("/")) || QFile::exists(m_filename));
208 
209  if (is_local)
210  {
211  char buf[kReadTestSize];
212  int lasterror = 0;
213 
214  MythTimer openTimer;
215  openTimer.start();
216 
217  uint openAttempts = 0;
218  do
219  {
220  openAttempts++;
221 
222  m_fd2 = open(m_filename.toLocal8Bit().constData(),
223  O_RDONLY|O_LARGEFILE|O_STREAMING|O_BINARY);
224 
225  if (m_fd2 < 0)
226  {
228  {
229  lasterror = 3;
230  break;
231  }
232 
233  lasterror = 1;
234  usleep(10 * 1000);
235  }
236  else
237  {
238  int ret = read(m_fd2, buf, kReadTestSize);
239  if (ret != kReadTestSize)
240  {
241  lasterror = 2;
242  close(m_fd2);
243  m_fd2 = -1;
244  if (ret == 0 && openAttempts > 5 &&
246  {
247  // file won't grow, abort early
248  break;
249  }
250 
251  if (m_oldfile)
252  break; // if it's an old file it won't grow..
253  usleep(10 * 1000);
254  }
255  else
256  {
257  if (0 == lseek(m_fd2, 0, SEEK_SET))
258  {
259 #ifndef _MSC_VER
260  if (posix_fadvise(m_fd2, 0, 0,
261  POSIX_FADV_SEQUENTIAL) != 0)
262  {
263  LOG(VB_FILE, LOG_DEBUG, LOC +
264  QString("OpenFile(): fadvise sequential "
265  "failed: ") + ENO);
266  }
267  if (posix_fadvise(m_fd2, 0, 128*1024,
268  POSIX_FADV_WILLNEED) != 0)
269  {
270  LOG(VB_FILE, LOG_DEBUG, LOC +
271  QString("OpenFile(): fadvise willneed "
272  "failed: ") + ENO);
273  }
274 #endif
275  lasterror = 0;
276  break;
277  }
278  lasterror = 4;
279  close(m_fd2);
280  m_fd2 = -1;
281  }
282  }
283  }
284  while ((uint)openTimer.elapsed() < retry_ms);
285 
286  switch (lasterror)
287  {
288  case 0:
289  {
290  QFileInfo fi(m_filename);
291  m_oldfile = fi.lastModified().toUTC()
292  .secsTo(MythDate::current()) > 60;
293  QString extension = fi.completeSuffix().toLower();
294  if (is_subtitle_possible(extension))
296  break;
297  }
298  case 1:
299  LOG(VB_GENERAL, LOG_ERR, LOC +
300  QString("OpenFile(): Could not open."));
301 
302  //: %1 is the filename
303  m_lastError = tr("Could not open %1").arg(m_filename);
304  break;
305  case 2:
306  LOG(VB_GENERAL, LOG_ERR, LOC +
307  QString("OpenFile(): File too small (%1B).")
308  .arg(QFileInfo(m_filename).size()));
309 
310  //: %1 is the file size
311  m_lastError = tr("File too small (%1B)")
312  .arg(QFileInfo(m_filename).size());
313  break;
314  case 3:
315  LOG(VB_GENERAL, LOG_ERR, LOC +
316  "OpenFile(): Improper permissions.");
317 
318  m_lastError = tr("Improper permissions");
319  break;
320  case 4:
321  LOG(VB_GENERAL, LOG_ERR, LOC +
322  "OpenFile(): Cannot seek in file.");
323 
324  m_lastError = tr("Cannot seek in file");
325  break;
326  default:
327  break;
328  }
329  LOG(VB_FILE, LOG_INFO,
330  LOC + QString("OpenFile() made %1 attempts in %2 ms")
331  .arg(openAttempts).arg(openTimer.elapsed()));
332 
333  }
334  else
335  {
336  QString tmpSubName = m_filename;
337  QString dirName = ".";
338 
339  int dirPos = m_filename.lastIndexOf(QChar('/'));
340  if (dirPos > 0)
341  {
342  tmpSubName = m_filename.mid(dirPos + 1);
343  dirName = m_filename.left(dirPos);
344  }
345 
346  QStringList auxFiles;
347 
348  int suffixPos = tmpSubName.lastIndexOf(QChar('.'));
349  if (suffixPos > 0)
350  {
351  QString baseName = tmpSubName.left(suffixPos);
352  int extnleng = tmpSubName.size() - baseName.size() - 1;
353  QString extension = tmpSubName.right(extnleng);
354 
355  if (is_subtitle_possible(extension))
356  {
357  QMutexLocker locker(&s_subExtLock);
358  QStringList::const_iterator eit = s_subExt.begin();
359  for (; eit != s_subExt.end(); ++eit)
360  auxFiles += baseName + *eit;
361  }
362  }
363 
364  m_remotefile = new RemoteFile(m_filename, false, true,
365  retry_ms, &auxFiles);
366  if (!m_remotefile->isOpen())
367  {
368  LOG(VB_GENERAL, LOG_ERR, LOC +
369  QString("RingBuffer::RingBuffer(): Failed to open remote "
370  "file (%1)").arg(m_filename));
371 
372  //: %1 is the filename
373  m_lastError = tr("Failed to open remote file %1").arg(m_filename);
374  delete m_remotefile;
375  m_remotefile = nullptr;
376  }
377  else
378  {
379  QStringList aux = m_remotefile->GetAuxiliaryFiles();
380  if (!aux.empty())
381  m_subtitleFilename = dirName + "/" + aux[0];
382  }
383  }
384 
385  m_setSwitchToNext = false;
386  m_ateof = false;
387  m_commsError = false;
388  m_numFailures = 0;
389 
391 
392  bool ok = m_fd2 >= 0 || m_remotefile;
393 
394  m_rwLock.unlock();
395 
396  return ok;
397 }
398 
399 bool FileRingBuffer::ReOpen(QString newFilename)
400 {
401  if (!m_writeMode)
402  {
403  LOG(VB_GENERAL, LOG_ERR, LOC + "Tried to ReOpen a read only file.");
404  return false;
405  }
406 
407  bool result = false;
408 
409  m_rwLock.lockForWrite();
410 
411  if ((m_tfw && m_tfw->ReOpen(newFilename)) ||
412  (m_remotefile && m_remotefile->ReOpen(newFilename)))
413  result = true;
414 
415  if (result)
416  {
417  m_filename = newFilename;
418  m_posLock.lockForWrite();
419  m_writePos = 0;
420  m_posLock.unlock();
421  }
422 
423  m_rwLock.unlock();
424  return result;
425 }
426 
427 bool FileRingBuffer::IsOpen(void) const
428 {
429  m_rwLock.lockForRead();
430  bool ret = m_tfw || (m_fd2 > -1) || m_remotefile;
431  m_rwLock.unlock();
432  return ret;
433 }
434 
447 int FileRingBuffer::safe_read(int /*fd*/, void *data, uint sz)
448 {
449  unsigned tot = 0;
450  unsigned errcnt = 0;
451  unsigned zerocnt = 0;
452 
453  if (m_fd2 < 0)
454  {
455  LOG(VB_GENERAL, LOG_ERR, LOC +
456  "Invalid file descriptor in 'safe_read()'");
457  return 0;
458  }
459 
460  if (m_stopReads)
461  return 0;
462 
463  struct stat sb {};
464 
465  while (tot < sz)
466  {
467  uint toread = sz - tot;
468  bool read_ok = true;
469  bool eof = false;
470 
471  // check that we have some data to read,
472  // so we never attempt to read past the end of file
473  // if fstat errored or isn't a regular file, default to previous behavior
474  int ret = fstat(m_fd2, &sb);
475  if (ret == 0 && S_ISREG(sb.st_mode))
476  {
477  if ((m_internalReadPos + tot) >= sb.st_size)
478  {
479  // We're at the end, don't attempt to read
480  read_ok = false;
481  eof = true;
482  LOG(VB_FILE, LOG_DEBUG, LOC + "not reading, reached EOF");
483  }
484  else
485  {
486  toread =
487  min(sb.st_size - (m_internalReadPos + tot), (long long)toread);
488  if (toread < (sz-tot))
489  {
490  eof = true;
491  LOG(VB_FILE, LOG_DEBUG,
492  LOC + QString("About to reach EOF, reading %1 wanted %2")
493  .arg(toread).arg(sz-tot));
494  }
495  }
496  }
497 
498  if (read_ok)
499  {
500  LOG(VB_FILE, LOG_DEBUG, LOC +
501  QString("read(%1) -- begin").arg(toread));
502  ret = read(m_fd2, (char *)data + tot, toread);
503  LOG(VB_FILE, LOG_DEBUG, LOC +
504  QString("read(%1) -> %2 end").arg(toread).arg(ret));
505  }
506  if (ret < 0)
507  {
508  if (errno == EAGAIN)
509  continue;
510 
511  LOG(VB_GENERAL, LOG_ERR,
512  LOC + "File I/O problem in 'safe_read()'" + ENO);
513 
514  errcnt++;
515  m_numFailures++;
516  if (errcnt == 3)
517  break;
518  }
519  else if (ret > 0)
520  {
521  tot += ret;
522  }
523 
524  if (m_oldfile)
525  break;
526 
527  if (eof)
528  {
529  // we can exit now, if file is still open for writing in another
530  // instance, RingBuffer will retry
531  break;
532  }
533 
534  if (ret == 0)
535  {
536  if (tot > 0)
537  break;
538 
539  zerocnt++;
540 
541  // 0.36 second timeout for livetvchain with usleep(60000),
542  // or 2.4 seconds if it's a new file less than 30 minutes old.
543  if (zerocnt >= (m_liveTVChain ? 6 : 40))
544  {
545  break;
546  }
547  }
548  if (m_stopReads)
549  break;
550  if (tot < sz)
551  usleep(60000);
552  }
553  return tot;
554 }
555 
564 int FileRingBuffer::safe_read(RemoteFile *rf, void *data, uint sz)
565 {
566  int ret = rf->Read(data, sz);
567  if (ret < 0)
568  {
569  LOG(VB_GENERAL, LOG_ERR, LOC +
570  "safe_read(RemoteFile* ...): read failed");
571 
572  m_posLock.lockForRead();
573  rf->Seek(m_internalReadPos - m_readAdjust, SEEK_SET);
574  m_posLock.unlock();
575  m_numFailures++;
576  }
577  else if (ret == 0)
578  {
579  LOG(VB_FILE, LOG_INFO, LOC +
580  "safe_read(RemoteFile* ...): at EOF");
581  }
582 
583  return ret;
584 }
585 
586 long long FileRingBuffer::GetReadPosition(void) const
587 {
588  m_posLock.lockForRead();
589  long long ret = m_readPos;
590  m_posLock.unlock();
591  return ret;
592 }
593 
595 {
596  m_rwLock.lockForRead();
597  long long ret = -1;
598  if (m_remotefile)
599  {
600  ret = m_remotefile->GetRealFileSize();
601  }
602  else
603  {
604  if (m_fd2 >= 0)
605  {
606  struct stat sb {};
607 
608  ret = fstat(m_fd2, &sb);
609  if (ret == 0 && S_ISREG(sb.st_mode))
610  {
611  m_rwLock.unlock();
612  return sb.st_size;
613  }
614  }
615  ret = QFileInfo(m_filename).size();
616  }
617  m_rwLock.unlock();
618  return ret;
619 }
620 
621 long long FileRingBuffer::SeekInternal(long long pos, int whence)
622 {
623  long long ret = -1;
624 
625  // Ticket 12128
626  StopReads();
627  StartReads();
628 
629  if (m_writeMode)
630  {
631  ret = WriterSeek(pos, whence, true);
632 
633  return ret;
634  }
635 
636  m_posLock.lockForWrite();
637 
638  // Optimize no-op seeks
639  if (m_readAheadRunning &&
640  ((whence == SEEK_SET && pos == m_readPos) ||
641  (whence == SEEK_CUR && pos == 0)))
642  {
643  ret = m_readPos;
644 
645  m_posLock.unlock();
646 
647  return ret;
648  }
649 
650  // only valid for SEEK_SET & SEEK_CUR
651  long long new_pos = (SEEK_SET==whence) ? pos : m_readPos + pos;
652 
653  // Optimize short seeks where the data for
654  // them is in our ringbuffer already.
655  if (m_readAheadRunning &&
656  (SEEK_SET==whence || SEEK_CUR==whence))
657  {
658  m_rbrLock.lockForWrite();
659  m_rbwLock.lockForRead();
660  LOG(VB_FILE, LOG_INFO, LOC +
661  QString("Seek(): rbrpos: %1 rbwpos: %2"
662  "\n\t\t\treadpos: %3 internalreadpos: %4")
663  .arg(m_rbrPos).arg(m_rbwPos)
664  .arg(m_readPos).arg(m_internalReadPos));
665  bool used_opt = false;
666  if ((new_pos < m_readPos))
667  {
668  // Seeking to earlier than current buffer's start, but still in buffer
669  int min_safety = max(m_fillMin, m_readBlockSize);
670  int free = ((m_rbwPos >= m_rbrPos) ?
672  int internal_backbuf =
674  internal_backbuf = min(internal_backbuf, free - min_safety);
675  long long sba = m_readPos - new_pos;
676  LOG(VB_FILE, LOG_INFO, LOC +
677  QString("Seek(): internal_backbuf: %1 sba: %2")
678  .arg(internal_backbuf).arg(sba));
679  if (internal_backbuf >= sba)
680  {
681  m_rbrPos = (m_rbrPos>=sba) ? m_rbrPos - sba :
682  m_bufferSize + m_rbrPos - sba;
683  used_opt = true;
684  LOG(VB_FILE, LOG_INFO, LOC +
685  QString("Seek(): OPT1 rbrPos: %1 rbwPos: %2"
686  "\n\t\t\treadpos: %3 internalreadpos: %4")
687  .arg(m_rbrPos).arg(m_rbwPos)
688  .arg(new_pos).arg(m_internalReadPos));
689  }
690  }
691  else if ((new_pos >= m_readPos) && (new_pos <= m_internalReadPos))
692  {
693  m_rbrPos = (m_rbrPos + (new_pos - m_readPos)) % m_bufferSize;
694  used_opt = true;
695  LOG(VB_FILE, LOG_INFO, LOC +
696  QString("Seek(): OPT2 rbrPos: %1 sba: %2")
697  .arg(m_rbrPos).arg(m_readPos - new_pos));
698  }
699  m_rbwLock.unlock();
700  m_rbrLock.unlock();
701 
702  if (used_opt)
703  {
704  if (m_ignoreReadPos >= 0)
705  {
706  // seek should always succeed since we were at this position
707  if (m_remotefile)
708  ret = m_remotefile->Seek(m_internalReadPos, SEEK_SET);
709  else
710  {
711  ret = lseek64(m_fd2, m_internalReadPos, SEEK_SET);
712 #ifndef _MSC_VER
714  128*1024, POSIX_FADV_WILLNEED) != 0)
715  {
716  LOG(VB_FILE, LOG_DEBUG, LOC +
717  QString("Seek(): fadvise willneed "
718  "failed: ") + ENO);
719  }
720 #endif
721  }
722  LOG(VB_FILE, LOG_INFO, LOC +
723  QString("Seek to %1 from ignore pos %2 returned %3")
724  .arg(m_internalReadPos).arg(m_ignoreReadPos).arg(ret));
725  m_ignoreReadPos = -1;
726  }
727  // if we are seeking forward we may now be too close to the
728  // end, so we need to recheck if reads are allowed.
729  if (new_pos > m_readPos)
730  {
731  m_ateof = false;
732  m_readsAllowed = false;
733  m_readsDesired = false;
734  m_recentSeek = true;
735  }
736  m_readPos = new_pos;
737  m_posLock.unlock();
738  m_generalWait.wakeAll();
739 
740  return new_pos;
741  }
742  }
743 
744 #if 1
745  // This optimizes the seek end-250000, read, seek 0, read portion
746  // of the pattern ffmpeg performs at the start of playback to
747  // determine the pts.
748  // If the seek is a SEEK_END or is a seek where the position
749  // changes over 100 MB we check the file size and if the
750  // destination point is within 300000 bytes of the end of
751  // the file we enter a special mode where the read ahead
752  // buffer stops reading data and all reads are made directly
753  // until another seek is performed. The point of all this is
754  // to avoid flushing out the buffer that still contains all
755  // the data the final seek 0, read will need just to read the
756  // last 250000 bytes. A further optimization would be to buffer
757  // the 250000 byte read, which is currently performed in 32KB
758  // blocks (inefficient with RemoteFile).
759  if ((m_remotefile || m_fd2 >= 0) && (m_ignoreReadPos < 0))
760  {
761  long long off_end = 0xDEADBEEF;
762  if (SEEK_END == whence)
763  {
764  off_end = pos;
765  if (m_remotefile)
766  {
767  new_pos = m_remotefile->GetFileSize() - off_end;
768  }
769  else
770  {
771  QFileInfo fi(m_filename);
772  new_pos = fi.size() - off_end;
773  }
774  }
775  else
776  {
777  if (m_remotefile)
778  {
779  off_end = m_remotefile->GetFileSize() - new_pos;
780  }
781  else
782  {
783  QFileInfo fi(m_filename);
784  off_end = fi.size() - new_pos;
785  }
786  }
787 
788  if (off_end != 0xDEADBEEF)
789  {
790  LOG(VB_FILE, LOG_INFO, LOC +
791  QString("Seek(): Offset from end: %1").arg(off_end));
792  }
793 
794  if (off_end == 250000)
795  {
796  LOG(VB_FILE, LOG_INFO, LOC +
797  QString("Seek(): offset from end: %1").arg(off_end) +
798  "\n\t\t\t -- ignoring read ahead thread until next seek.");
799 
800  m_ignoreReadPos = new_pos;
801  errno = EINVAL;
802  if (m_remotefile)
803  ret = m_remotefile->Seek(m_ignoreReadPos, SEEK_SET);
804  else
805  ret = lseek64(m_fd2, m_ignoreReadPos, SEEK_SET);
806 
807  if (ret < 0)
808  {
809  int tmp_eno = errno;
810  QString cmd = QString("Seek(%1, SEEK_SET) ign ")
811  .arg(m_ignoreReadPos);
812 
813  m_ignoreReadPos = -1;
814 
815  LOG(VB_GENERAL, LOG_ERR, LOC + cmd + " Failed" + ENO);
816 
817  // try to return to former position..
818  if (m_remotefile)
819  ret = m_remotefile->Seek(m_internalReadPos, SEEK_SET);
820  else
821  ret = lseek64(m_fd2, m_internalReadPos, SEEK_SET);
822  if (ret < 0)
823  {
824  QString cmd2 = QString("Seek(%1, SEEK_SET) int ")
825  .arg(m_internalReadPos);
826  LOG(VB_GENERAL, LOG_ERR, LOC + cmd2 + " Failed" + ENO);
827  }
828  else
829  {
830  QString cmd2 = QString("Seek(%1, %2) int ")
831  .arg(m_internalReadPos)
832  .arg((SEEK_SET == whence) ? "SEEK_SET" :
833  ((SEEK_CUR == whence) ?"SEEK_CUR" : "SEEK_END"));
834  LOG(VB_GENERAL, LOG_ERR, LOC + cmd2 + " succeeded");
835  }
836  ret = -1;
837  errno = tmp_eno;
838  }
839  else
840  {
841  m_ateof = false;
842  m_readsAllowed = false;
843  m_readsDesired = false;
844  m_recentSeek = true;
845  }
846 
847  m_posLock.unlock();
848 
849  m_generalWait.wakeAll();
850 
851  return ret;
852  }
853  }
854 #endif
855 
856  // Here we perform a normal seek. When successful we
857  // need to call ResetReadAhead(). A reset means we will
858  // need to refill the buffer, which takes some time.
859  if (m_remotefile)
860  {
861  ret = m_remotefile->Seek(pos, whence, m_readPos);
862  if (ret<0)
863  errno = EINVAL;
864  }
865  else
866  {
867  ret = lseek64(m_fd2, pos, whence);
868  }
869 
870  if (ret >= 0)
871  {
872  m_readPos = ret;
873 
874  m_ignoreReadPos = -1;
875 
876  if (m_readAheadRunning)
878 
879  m_readAdjust = 0;
880  }
881  else
882  {
883  QString cmd = QString("Seek(%1, %2)").arg(pos)
884  .arg((whence == SEEK_SET) ? "SEEK_SET" :
885  ((whence == SEEK_CUR) ? "SEEK_CUR" : "SEEK_END"));
886  LOG(VB_GENERAL, LOG_ERR, LOC + cmd + " Failed" + ENO);
887  }
888 
889  m_posLock.unlock();
890 
891  m_generalWait.wakeAll();
892 
893  return ret;
894 }
def write(text, progress=True)
Definition: mythburn.py:279
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
bool ReOpen(QString newFilename="") override
#define O_STREAMING
void StartReads(void)
????
Definition: ringbuffer.cpp:722
QReadWriteLock m_rwLock
#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:367
#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:694
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:936
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:711
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:246
int elapsed(void)
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:90
bool ReOpen(const QString &newFilename)
Definition: remotefile.cpp:340
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:610
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:759
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