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,
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  result = true;
413  else if (m_remotefile && m_remotefile->ReOpen(newFilename))
414  result = true;
415 
416  if (result)
417  {
418  m_filename = newFilename;
419  m_posLock.lockForWrite();
420  m_writePos = 0;
421  m_posLock.unlock();
422  }
423 
424  m_rwLock.unlock();
425  return result;
426 }
427 
428 bool FileRingBuffer::IsOpen(void) const
429 {
430  m_rwLock.lockForRead();
431  bool ret = m_tfw || (m_fd2 > -1) || m_remotefile;
432  m_rwLock.unlock();
433  return ret;
434 }
435 
448 int FileRingBuffer::safe_read(int /*fd*/, void *data, uint sz)
449 {
450  unsigned tot = 0;
451  unsigned errcnt = 0;
452  unsigned zerocnt = 0;
453 
454  if (m_fd2 < 0)
455  {
456  LOG(VB_GENERAL, LOG_ERR, LOC +
457  "Invalid file descriptor in 'safe_read()'");
458  return 0;
459  }
460 
461  if (m_stopReads)
462  return 0;
463 
464  struct stat sb;
465 
466  while (tot < sz)
467  {
468  uint toread = sz - tot;
469  bool read_ok = true;
470  bool eof = false;
471 
472  // check that we have some data to read,
473  // so we never attempt to read past the end of file
474  // if fstat errored or isn't a regular file, default to previous behavior
475  int ret = fstat(m_fd2, &sb);
476  if (ret == 0 && S_ISREG(sb.st_mode))
477  {
478  if ((m_internalReadPos + tot) >= sb.st_size)
479  {
480  // We're at the end, don't attempt to read
481  read_ok = false;
482  eof = true;
483  LOG(VB_FILE, LOG_DEBUG, LOC + "not reading, reached EOF");
484  }
485  else
486  {
487  toread =
488  min(sb.st_size - (m_internalReadPos + tot), (long long)toread);
489  if (toread < (sz-tot))
490  {
491  eof = true;
492  LOG(VB_FILE, LOG_DEBUG,
493  LOC + QString("About to reach EOF, reading %1 wanted %2")
494  .arg(toread).arg(sz-tot));
495  }
496  }
497  }
498 
499  if (read_ok)
500  {
501  LOG(VB_FILE, LOG_DEBUG, LOC +
502  QString("read(%1) -- begin").arg(toread));
503  ret = read(m_fd2, (char *)data + tot, toread);
504  LOG(VB_FILE, LOG_DEBUG, LOC +
505  QString("read(%1) -> %2 end").arg(toread).arg(ret));
506  }
507  if (ret < 0)
508  {
509  if (errno == EAGAIN)
510  continue;
511 
512  LOG(VB_GENERAL, LOG_ERR,
513  LOC + "File I/O problem in 'safe_read()'" + ENO);
514 
515  errcnt++;
516  m_numFailures++;
517  if (errcnt == 3)
518  break;
519  }
520  else if (ret > 0)
521  {
522  tot += ret;
523  }
524 
525  if (m_oldfile)
526  break;
527 
528  if (eof)
529  {
530  // we can exit now, if file is still open for writing in another
531  // instance, RingBuffer will retry
532  break;
533  }
534 
535  if (ret == 0)
536  {
537  if (tot > 0)
538  break;
539 
540  zerocnt++;
541 
542  // 0.36 second timeout for livetvchain with usleep(60000),
543  // or 2.4 seconds if it's a new file less than 30 minutes old.
544  if (zerocnt >= (m_liveTVChain ? 6 : 40))
545  {
546  break;
547  }
548  }
549  if (m_stopReads)
550  break;
551  if (tot < sz)
552  usleep(60000);
553  }
554  return tot;
555 }
556 
565 int FileRingBuffer::safe_read(RemoteFile *rf, void *data, uint sz)
566 {
567  int ret = rf->Read(data, sz);
568  if (ret < 0)
569  {
570  LOG(VB_GENERAL, LOG_ERR, LOC +
571  "safe_read(RemoteFile* ...): read failed");
572 
573  m_posLock.lockForRead();
574  rf->Seek(m_internalReadPos - m_readAdjust, SEEK_SET);
575  m_posLock.unlock();
576  m_numFailures++;
577  }
578  else if (ret == 0)
579  {
580  LOG(VB_FILE, LOG_INFO, LOC +
581  "safe_read(RemoteFile* ...): at EOF");
582  }
583 
584  return ret;
585 }
586 
587 long long FileRingBuffer::GetReadPosition(void) const
588 {
589  m_posLock.lockForRead();
590  long long ret = m_readPos;
591  m_posLock.unlock();
592  return ret;
593 }
594 
596 {
597  m_rwLock.lockForRead();
598  long long ret = -1;
599  if (m_remotefile)
600  {
601  ret = m_remotefile->GetRealFileSize();
602  }
603  else
604  {
605  if (m_fd2 >= 0)
606  {
607  struct stat sb;
608 
609  ret = fstat(m_fd2, &sb);
610  if (ret == 0 && S_ISREG(sb.st_mode))
611  {
612  m_rwLock.unlock();
613  return sb.st_size;
614  }
615  }
616  ret = QFileInfo(m_filename).size();
617  }
618  m_rwLock.unlock();
619  return ret;
620 }
621 
622 long long FileRingBuffer::SeekInternal(long long pos, int whence)
623 {
624  long long ret = -1;
625 
626  // Ticket 12128
627  StopReads();
628  StartReads();
629 
630  if (m_writeMode)
631  {
632  ret = WriterSeek(pos, whence, true);
633 
634  return ret;
635  }
636 
637  m_posLock.lockForWrite();
638 
639  // Optimize no-op seeks
640  if (m_readAheadRunning &&
641  ((whence == SEEK_SET && pos == m_readPos) ||
642  (whence == SEEK_CUR && pos == 0)))
643  {
644  ret = m_readPos;
645 
646  m_posLock.unlock();
647 
648  return ret;
649  }
650 
651  // only valid for SEEK_SET & SEEK_CUR
652  long long new_pos = (SEEK_SET==whence) ? pos : m_readPos + pos;
653 
654  // Optimize short seeks where the data for
655  // them is in our ringbuffer already.
656  if (m_readAheadRunning &&
657  (SEEK_SET==whence || SEEK_CUR==whence))
658  {
659  m_rbrLock.lockForWrite();
660  m_rbwLock.lockForRead();
661  LOG(VB_FILE, LOG_INFO, LOC +
662  QString("Seek(): rbrpos: %1 rbwpos: %2"
663  "\n\t\t\treadpos: %3 internalreadpos: %4")
664  .arg(m_rbrPos).arg(m_rbwPos)
665  .arg(m_readPos).arg(m_internalReadPos));
666  bool used_opt = false;
667  if ((new_pos < m_readPos))
668  {
669  // Seeking to earlier than current buffer's start, but still in buffer
670  int min_safety = max(m_fillMin, m_readBlockSize);
671  int free = ((m_rbwPos >= m_rbrPos) ?
673  int internal_backbuf =
675  internal_backbuf = min(internal_backbuf, free - min_safety);
676  long long sba = m_readPos - new_pos;
677  LOG(VB_FILE, LOG_INFO, LOC +
678  QString("Seek(): internal_backbuf: %1 sba: %2")
679  .arg(internal_backbuf).arg(sba));
680  if (internal_backbuf >= sba)
681  {
682  m_rbrPos = (m_rbrPos>=sba) ? m_rbrPos - sba :
683  m_bufferSize + m_rbrPos - sba;
684  used_opt = true;
685  LOG(VB_FILE, LOG_INFO, LOC +
686  QString("Seek(): OPT1 rbrPos: %1 rbwPos: %2"
687  "\n\t\t\treadpos: %3 internalreadpos: %4")
688  .arg(m_rbrPos).arg(m_rbwPos)
689  .arg(new_pos).arg(m_internalReadPos));
690  }
691  }
692  else if ((new_pos >= m_readPos) && (new_pos <= m_internalReadPos))
693  {
694  m_rbrPos = (m_rbrPos + (new_pos - m_readPos)) % m_bufferSize;
695  used_opt = true;
696  LOG(VB_FILE, LOG_INFO, LOC +
697  QString("Seek(): OPT2 rbrPos: %1 sba: %2")
698  .arg(m_rbrPos).arg(m_readPos - new_pos));
699  }
700  m_rbwLock.unlock();
701  m_rbrLock.unlock();
702 
703  if (used_opt)
704  {
705  if (m_ignoreReadPos >= 0)
706  {
707  // seek should always succeed since we were at this position
708  if (m_remotefile)
709  ret = m_remotefile->Seek(m_internalReadPos, SEEK_SET);
710  else
711  {
712  ret = lseek64(m_fd2, m_internalReadPos, SEEK_SET);
713 #ifndef _MSC_VER
715  128*1024, POSIX_FADV_WILLNEED) < 0)
716  {
717  LOG(VB_FILE, LOG_DEBUG, LOC +
718  QString("Seek(): fadvise willneed "
719  "failed: ") + ENO);
720  }
721 #endif
722  }
723  LOG(VB_FILE, LOG_INFO, LOC +
724  QString("Seek to %1 from ignore pos %2 returned %3")
725  .arg(m_internalReadPos).arg(m_ignoreReadPos).arg(ret));
726  m_ignoreReadPos = -1;
727  }
728  // if we are seeking forward we may now be too close to the
729  // end, so we need to recheck if reads are allowed.
730  if (new_pos > m_readPos)
731  {
732  m_ateof = false;
733  m_readsAllowed = false;
734  m_readsDesired = false;
735  m_recentSeek = true;
736  }
737  m_readPos = new_pos;
738  m_posLock.unlock();
739  m_generalWait.wakeAll();
740 
741  return new_pos;
742  }
743  }
744 
745 #if 1
746  // This optimizes the seek end-250000, read, seek 0, read portion
747  // of the pattern ffmpeg performs at the start of playback to
748  // determine the pts.
749  // If the seek is a SEEK_END or is a seek where the position
750  // changes over 100 MB we check the file size and if the
751  // destination point is within 300000 bytes of the end of
752  // the file we enter a special mode where the read ahead
753  // buffer stops reading data and all reads are made directly
754  // until another seek is performed. The point of all this is
755  // to avoid flushing out the buffer that still contains all
756  // the data the final seek 0, read will need just to read the
757  // last 250000 bytes. A further optimization would be to buffer
758  // the 250000 byte read, which is currently performed in 32KB
759  // blocks (inefficient with RemoteFile).
760  if ((m_remotefile || m_fd2 >= 0) && (m_ignoreReadPos < 0))
761  {
762  long long off_end = 0xDEADBEEF;
763  if (SEEK_END == whence)
764  {
765  off_end = pos;
766  if (m_remotefile)
767  {
768  new_pos = m_remotefile->GetFileSize() - off_end;
769  }
770  else
771  {
772  QFileInfo fi(m_filename);
773  new_pos = fi.size() - off_end;
774  }
775  }
776  else
777  {
778  if (m_remotefile)
779  {
780  off_end = m_remotefile->GetFileSize() - new_pos;
781  }
782  else
783  {
784  QFileInfo fi(m_filename);
785  off_end = fi.size() - new_pos;
786  }
787  }
788 
789  if (off_end != 0xDEADBEEF)
790  {
791  LOG(VB_FILE, LOG_INFO, LOC +
792  QString("Seek(): Offset from end: %1").arg(off_end));
793  }
794 
795  if (off_end == 250000)
796  {
797  LOG(VB_FILE, LOG_INFO, LOC +
798  QString("Seek(): offset from end: %1").arg(off_end) +
799  "\n\t\t\t -- ignoring read ahead thread until next seek.");
800 
801  m_ignoreReadPos = new_pos;
802  errno = EINVAL;
803  if (m_remotefile)
804  ret = m_remotefile->Seek(m_ignoreReadPos, SEEK_SET);
805  else
806  ret = lseek64(m_fd2, m_ignoreReadPos, SEEK_SET);
807 
808  if (ret < 0)
809  {
810  int tmp_eno = errno;
811  QString cmd = QString("Seek(%1, SEEK_SET) ign ")
812  .arg(m_ignoreReadPos);
813 
814  m_ignoreReadPos = -1;
815 
816  LOG(VB_GENERAL, LOG_ERR, LOC + cmd + " Failed" + ENO);
817 
818  // try to return to former position..
819  if (m_remotefile)
820  ret = m_remotefile->Seek(m_internalReadPos, SEEK_SET);
821  else
822  ret = lseek64(m_fd2, m_internalReadPos, SEEK_SET);
823  if (ret < 0)
824  {
825  QString cmd2 = QString("Seek(%1, SEEK_SET) int ")
826  .arg(m_internalReadPos);
827  LOG(VB_GENERAL, LOG_ERR, LOC + cmd2 + " Failed" + ENO);
828  }
829  else
830  {
831  QString cmd2 = QString("Seek(%1, %2) int ")
832  .arg(m_internalReadPos)
833  .arg((SEEK_SET == whence) ? "SEEK_SET" :
834  ((SEEK_CUR == whence) ?"SEEK_CUR" : "SEEK_END"));
835  LOG(VB_GENERAL, LOG_ERR, LOC + cmd2 + " succeeded");
836  }
837  ret = -1;
838  errno = tmp_eno;
839  }
840  else
841  {
842  m_ateof = false;
843  m_readsAllowed = false;
844  m_readsDesired = false;
845  m_recentSeek = true;
846  }
847 
848  m_posLock.unlock();
849 
850  m_generalWait.wakeAll();
851 
852  return ret;
853  }
854  }
855 #endif
856 
857  // Here we perform a normal seek. When successful we
858  // need to call ResetReadAhead(). A reset means we will
859  // need to refill the buffer, which takes some time.
860  if (m_remotefile)
861  {
862  ret = m_remotefile->Seek(pos, whence, m_readPos);
863  if (ret<0)
864  errno = EINVAL;
865  }
866  else
867  {
868  ret = lseek64(m_fd2, pos, whence);
869  }
870 
871  if (ret >= 0)
872  {
873  m_readPos = ret;
874 
875  m_ignoreReadPos = -1;
876 
877  if (m_readAheadRunning)
879 
880  m_readAdjust = 0;
881  }
882  else
883  {
884  QString cmd = QString("Seek(%1, %2)").arg(pos)
885  .arg((whence == SEEK_SET) ? "SEEK_SET" :
886  ((whence == SEEK_CUR) ? "SEEK_CUR" : "SEEK_END"));
887  LOG(VB_GENERAL, LOG_ERR, LOC + cmd + " Failed" + ENO);
888  }
889 
890  m_posLock.unlock();
891 
892  m_generalWait.wakeAll();
893 
894  return ret;
895 }
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
unsigned int uint
Definition: compat.h:140
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
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)
int elapsed(void) const
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:90
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
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