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(filename)
44 
45 FileRingBuffer::FileRingBuffer(const QString &lfilename,
46  bool write, bool readahead, int timeout_ms)
48 {
49  startreadahead = readahead;
50  safefilename = lfilename;
51  filename = lfilename;
52 
53  if (write)
54  {
55  if (filename.startsWith("myth://"))
56  {
57  remotefile = new RemoteFile(filename, true);
58  if (!remotefile->isOpen())
59  {
60  LOG(VB_GENERAL, LOG_ERR,
61  QString("RingBuffer::RingBuffer(): Failed to open "
62  "remote file (%1) for write").arg(filename));
63  delete remotefile;
64  remotefile = nullptr;
65  }
66  else
67  writemode = true;
68  }
69  else
70  {
71  tfw = new ThreadedFileWriter(
72  filename, O_WRONLY|O_TRUNC|O_CREAT|O_LARGEFILE, 0644);
73 
74  if (!tfw->Open())
75  {
76  delete tfw;
77  tfw = nullptr;
78  }
79  else
80  writemode = true;
81  }
82  }
83  else if (timeout_ms >= 0)
84  {
85  OpenFile(filename, timeout_ms);
86  }
87 }
88 
90 {
92 
93  delete remotefile;
94  remotefile = nullptr;
95 
96  delete tfw;
97  tfw = nullptr;
98 
99  if (fd2 >= 0)
100  {
101  close(fd2);
102  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, LOC +
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::subExtLock);
125  bool no_subtitle = false;
126  for (uint i = 0; i < (uint)RingBuffer::subExtNoCheck.size(); i++)
127  {
128  if (extension.contains(RingBuffer::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::subExtLock);
159  QStringList::const_iterator eit = RingBuffer::subExt.begin();
160  for (; eit != RingBuffer::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  rwlock.lockForWrite();
188 
189  filename = lfilename;
190  safefilename = lfilename;
191  subtitlefilename.clear();
192 
193  if (remotefile)
194  {
195  delete remotefile;
196  remotefile = nullptr;
197  }
198 
199  if (fd2 >= 0)
200  {
201  close(fd2);
202  fd2 = -1;
203  }
204 
205  bool is_local =
206  (!filename.startsWith("/dev")) &&
207  ((filename.startsWith("/")) || QFile::exists(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  lasterror = 0;
222 
223  fd2 = open(filename.toLocal8Bit().constData(),
224  O_RDONLY|O_LARGEFILE|O_STREAMING|O_BINARY);
225 
226  if (fd2 < 0)
227  {
229  {
230  lasterror = 3;
231  break;
232  }
233 
234  lasterror = 1;
235  usleep(10 * 1000);
236  }
237  else
238  {
239  int ret = read(fd2, buf, kReadTestSize);
240  if (ret != kReadTestSize)
241  {
242  lasterror = 2;
243  close(fd2);
244  fd2 = -1;
245  if (ret == 0 && openAttempts > 5 &&
247  {
248  // file won't grow, abort early
249  break;
250  }
251 
252  if (oldfile)
253  break; // if it's an old file it won't grow..
254  usleep(10 * 1000);
255  }
256  else
257  {
258  if (0 == lseek(fd2, 0, SEEK_SET))
259  {
260 #ifndef _MSC_VER
261  if (posix_fadvise(fd2, 0, 0,
263  {
264  LOG(VB_FILE, LOG_DEBUG, LOC +
265  QString("OpenFile(): fadvise sequential "
266  "failed: ") + ENO);
267  }
268  if (posix_fadvise(fd2, 0, 128*1024,
269  POSIX_FADV_WILLNEED) < 0)
270  {
271  LOG(VB_FILE, LOG_DEBUG, LOC +
272  QString("OpenFile(): fadvise willneed "
273  "failed: ") + ENO);
274  }
275 #endif
276  lasterror = 0;
277  break;
278  }
279  lasterror = 4;
280  close(fd2);
281  fd2 = -1;
282  }
283  }
284  }
285  while ((uint)openTimer.elapsed() < retry_ms);
286 
287  switch (lasterror)
288  {
289  case 0:
290  {
291  QFileInfo fi(filename);
292  oldfile = fi.lastModified().toUTC()
293  .secsTo(MythDate::current()) > 60;
294  QString extension = fi.completeSuffix().toLower();
295  if (is_subtitle_possible(extension))
297  break;
298  }
299  case 1:
300  LOG(VB_GENERAL, LOG_ERR, LOC +
301  QString("OpenFile(): Could not open."));
302 
303  //: %1 is the filename
304  lastError = tr("Could not open %1").arg(filename);
305  break;
306  case 2:
307  LOG(VB_GENERAL, LOG_ERR, LOC +
308  QString("OpenFile(): File too small (%1B).")
309  .arg(QFileInfo(filename).size()));
310 
311  //: %1 is the file size
312  lastError = tr("File too small (%1B)")
313  .arg(QFileInfo(filename).size());
314  break;
315  case 3:
316  LOG(VB_GENERAL, LOG_ERR, LOC +
317  "OpenFile(): Improper permissions.");
318 
319  lastError = tr("Improper permissions");
320  break;
321  case 4:
322  LOG(VB_GENERAL, LOG_ERR, LOC +
323  "OpenFile(): Cannot seek in file.");
324 
325  lastError = tr("Cannot seek in file");
326  break;
327  default:
328  break;
329  }
330  LOG(VB_FILE, LOG_INFO,
331  LOC + QString("OpenFile() made %1 attempts in %2 ms")
332  .arg(openAttempts).arg(openTimer.elapsed()));
333 
334  }
335  else
336  {
337  QString tmpSubName = filename;
338  QString dirName = ".";
339 
340  int dirPos = filename.lastIndexOf(QChar('/'));
341  if (dirPos > 0)
342  {
343  tmpSubName = filename.mid(dirPos + 1);
344  dirName = filename.left(dirPos);
345  }
346 
347  QString baseName = tmpSubName;
348  QString extension = tmpSubName;
349  QStringList auxFiles;
350 
351  int suffixPos = tmpSubName.lastIndexOf(QChar('.'));
352  if (suffixPos > 0)
353  {
354  baseName = tmpSubName.left(suffixPos);
355  int extnleng = tmpSubName.size() - baseName.size() - 1;
356  extension = tmpSubName.right(extnleng);
357 
358  if (is_subtitle_possible(extension))
359  {
360  QMutexLocker locker(&subExtLock);
361  QStringList::const_iterator eit = subExt.begin();
362  for (; eit != subExt.end(); ++eit)
363  auxFiles += baseName + *eit;
364  }
365  }
366 
367  remotefile = new RemoteFile(filename, false, true,
368  retry_ms, &auxFiles);
369  if (!remotefile->isOpen())
370  {
371  LOG(VB_GENERAL, LOG_ERR, LOC +
372  QString("RingBuffer::RingBuffer(): Failed to open remote "
373  "file (%1)").arg(filename));
374 
375  //: %1 is the filename
376  lastError = tr("Failed to open remote file %1").arg(filename);
377  delete remotefile;
378  remotefile = nullptr;
379  }
380  else
381  {
382  QStringList aux = remotefile->GetAuxiliaryFiles();
383  if (aux.size())
384  subtitlefilename = dirName + "/" + aux[0];
385  }
386  }
387 
388  setswitchtonext = false;
389  ateof = false;
390  commserror = false;
391  numfailures = 0;
392 
394 
395  bool ok = fd2 >= 0 || remotefile;
396 
397  rwlock.unlock();
398 
399  return ok;
400 }
401 
402 bool FileRingBuffer::ReOpen(QString newFilename)
403 {
404  if (!writemode)
405  {
406  LOG(VB_GENERAL, LOG_ERR, LOC + "Tried to ReOpen a read only file.");
407  return false;
408  }
409 
410  bool result = false;
411 
412  rwlock.lockForWrite();
413 
414  if (tfw && tfw->ReOpen(newFilename))
415  result = true;
416  else if (remotefile && remotefile->ReOpen(newFilename))
417  result = true;
418 
419  if (result)
420  {
421  filename = newFilename;
422  poslock.lockForWrite();
423  writepos = 0;
424  poslock.unlock();
425  }
426 
427  rwlock.unlock();
428  return result;
429 }
430 
431 bool FileRingBuffer::IsOpen(void) const
432 {
433  rwlock.lockForRead();
434  bool ret = tfw || (fd2 > -1) || remotefile;
435  rwlock.unlock();
436  return ret;
437 }
438 
451 int FileRingBuffer::safe_read(int /*fd*/, void *data, uint sz)
452 {
453  unsigned tot = 0;
454  unsigned errcnt = 0;
455  unsigned zerocnt = 0;
456 
457  if (fd2 < 0)
458  {
459  LOG(VB_GENERAL, LOG_ERR, LOC +
460  "Invalid file descriptor in 'safe_read()'");
461  return 0;
462  }
463 
464  if (stopreads)
465  return 0;
466 
467  struct stat sb;
468 
469  while (tot < sz)
470  {
471  uint toread = sz - tot;
472  bool read_ok = true;
473  bool eof = false;
474 
475  // check that we have some data to read,
476  // so we never attempt to read past the end of file
477  // if fstat errored or isn't a regular file, default to previous behavior
478  int ret = fstat(fd2, &sb);
479  if (ret == 0 && S_ISREG(sb.st_mode))
480  {
481  if ((internalreadpos + tot) >= sb.st_size)
482  {
483  // We're at the end, don't attempt to read
484  read_ok = false;
485  eof = true;
486  LOG(VB_FILE, LOG_DEBUG, LOC + "not reading, reached EOF");
487  }
488  else
489  {
490  toread =
491  min(sb.st_size - (internalreadpos + tot), (long long)toread);
492  if (toread < (sz-tot))
493  {
494  eof = true;
495  LOG(VB_FILE, LOG_DEBUG,
496  LOC + QString("About to reach EOF, reading %1 wanted %2")
497  .arg(toread).arg(sz-tot));
498  }
499  }
500  }
501 
502  if (read_ok)
503  {
504  LOG(VB_FILE, LOG_DEBUG, LOC +
505  QString("read(%1) -- begin").arg(toread));
506  ret = read(fd2, (char *)data + tot, toread);
507  LOG(VB_FILE, LOG_DEBUG, LOC +
508  QString("read(%1) -> %2 end").arg(toread).arg(ret));
509  }
510  if (ret < 0)
511  {
512  if (errno == EAGAIN)
513  continue;
514 
515  LOG(VB_GENERAL, LOG_ERR,
516  LOC + "File I/O problem in 'safe_read()'" + ENO);
517 
518  errcnt++;
519  numfailures++;
520  if (errcnt == 3)
521  break;
522  }
523  else if (ret > 0)
524  {
525  tot += ret;
526  }
527 
528  if (oldfile)
529  break;
530 
531  if (eof)
532  {
533  // we can exit now, if file is still open for writing in another
534  // instance, RingBuffer will retry
535  break;
536  }
537 
538  if (ret == 0)
539  {
540  if (tot > 0)
541  break;
542 
543  zerocnt++;
544 
545  // 0.36 second timeout for livetvchain with usleep(60000),
546  // or 2.4 seconds if it's a new file less than 30 minutes old.
547  if (zerocnt >= (livetvchain ? 6 : 40))
548  {
549  break;
550  }
551  }
552  if (stopreads)
553  break;
554  if (tot < sz)
555  usleep(60000);
556  }
557  return tot;
558 }
559 
568 int FileRingBuffer::safe_read(RemoteFile *rf, void *data, uint sz)
569 {
570  int ret = rf->Read(data, sz);
571  if (ret < 0)
572  {
573  LOG(VB_GENERAL, LOG_ERR, LOC +
574  "safe_read(RemoteFile* ...): read failed");
575 
576  poslock.lockForRead();
577  rf->Seek(internalreadpos - readAdjust, SEEK_SET);
578  poslock.unlock();
579  numfailures++;
580  }
581  else if (ret == 0)
582  {
583  LOG(VB_FILE, LOG_INFO, LOC +
584  "safe_read(RemoteFile* ...): at EOF");
585  }
586 
587  return ret;
588 }
589 
590 long long FileRingBuffer::GetReadPosition(void) const
591 {
592  poslock.lockForRead();
593  long long ret = readpos;
594  poslock.unlock();
595  return ret;
596 }
597 
599 {
600  rwlock.lockForRead();
601  long long ret = -1;
602  if (remotefile)
603  {
604  ret = remotefile->GetRealFileSize();
605  }
606  else
607  {
608  if (fd2 >= 0)
609  {
610  struct stat sb;
611 
612  ret = fstat(fd2, &sb);
613  if (ret == 0 && S_ISREG(sb.st_mode))
614  {
615  rwlock.unlock();
616  return sb.st_size;
617  }
618  }
619  ret = QFileInfo(filename).size();
620  }
621  rwlock.unlock();
622  return ret;
623 }
624 
625 long long FileRingBuffer::SeekInternal(long long pos, int whence)
626 {
627  long long ret = -1;
628 
629  // Ticket 12128
630  StopReads();
631  StartReads();
632 
633  if (writemode)
634  {
635  ret = WriterSeek(pos, whence, true);
636 
637  return ret;
638  }
639 
640  poslock.lockForWrite();
641 
642  // Optimize no-op seeks
643  if (readaheadrunning &&
644  ((whence == SEEK_SET && pos == readpos) ||
645  (whence == SEEK_CUR && pos == 0)))
646  {
647  ret = readpos;
648 
649  poslock.unlock();
650 
651  return ret;
652  }
653 
654  // only valid for SEEK_SET & SEEK_CUR
655  long long new_pos = (SEEK_SET==whence) ? pos : readpos + pos;
656 
657  // Optimize short seeks where the data for
658  // them is in our ringbuffer already.
659  if (readaheadrunning &&
660  (SEEK_SET==whence || SEEK_CUR==whence))
661  {
662  rbrlock.lockForWrite();
663  rbwlock.lockForRead();
664  LOG(VB_FILE, LOG_INFO, LOC +
665  QString("Seek(): rbrpos: %1 rbwpos: %2"
666  "\n\t\t\treadpos: %3 internalreadpos: %4")
667  .arg(rbrpos).arg(rbwpos)
668  .arg(readpos).arg(internalreadpos));
669  bool used_opt = false;
670  if ((new_pos < readpos))
671  {
672  // Seeking to earlier than current buffer's start, but still in buffer
673  int min_safety = max(fill_min, readblocksize);
674  int free = ((rbwpos >= rbrpos) ?
676  int internal_backbuf =
677  (rbwpos >= rbrpos) ? rbrpos : rbrpos - rbwpos;
678  internal_backbuf = min(internal_backbuf, free - min_safety);
679  long long sba = readpos - new_pos;
680  LOG(VB_FILE, LOG_INFO, LOC +
681  QString("Seek(): internal_backbuf: %1 sba: %2")
682  .arg(internal_backbuf).arg(sba));
683  if (internal_backbuf >= sba)
684  {
685  rbrpos = (rbrpos>=sba) ? rbrpos - sba :
686  bufferSize + rbrpos - sba;
687  used_opt = true;
688  LOG(VB_FILE, LOG_INFO, LOC +
689  QString("Seek(): OPT1 rbrpos: %1 rbwpos: %2"
690  "\n\t\t\treadpos: %3 internalreadpos: %4")
691  .arg(rbrpos).arg(rbwpos)
692  .arg(new_pos).arg(internalreadpos));
693  }
694  }
695  else if ((new_pos >= readpos) && (new_pos <= internalreadpos))
696  {
697  rbrpos = (rbrpos + (new_pos - readpos)) % bufferSize;
698  used_opt = true;
699  LOG(VB_FILE, LOG_INFO, LOC +
700  QString("Seek(): OPT2 rbrpos: %1 sba: %2")
701  .arg(rbrpos).arg(readpos - new_pos));
702  }
703  rbwlock.unlock();
704  rbrlock.unlock();
705 
706  if (used_opt)
707  {
708  if (ignorereadpos >= 0)
709  {
710  // seek should always succeed since we were at this position
711  if (remotefile)
712  ret = remotefile->Seek(internalreadpos, SEEK_SET);
713  else
714  {
715  ret = lseek64(fd2, internalreadpos, SEEK_SET);
716 #ifndef _MSC_VER
718  128*1024, POSIX_FADV_WILLNEED) < 0)
719  {
720  LOG(VB_FILE, LOG_DEBUG, LOC +
721  QString("Seek(): fadvise willneed "
722  "failed: ") + ENO);
723  }
724 #endif
725  }
726  LOG(VB_FILE, LOG_INFO, LOC +
727  QString("Seek to %1 from ignore pos %2 returned %3")
728  .arg(internalreadpos).arg(ignorereadpos).arg(ret));
729  ignorereadpos = -1;
730  }
731  // if we are seeking forward we may now be too close to the
732  // end, so we need to recheck if reads are allowed.
733  if (new_pos > readpos)
734  {
735  ateof = false;
736  readsallowed = false;
737  readsdesired = false;
738  recentseek = true;
739  }
740  readpos = new_pos;
741  poslock.unlock();
742  generalWait.wakeAll();
743 
744  return new_pos;
745  }
746  }
747 
748 #if 1
749  // This optimizes the seek end-250000, read, seek 0, read portion
750  // of the pattern ffmpeg performs at the start of playback to
751  // determine the pts.
752  // If the seek is a SEEK_END or is a seek where the position
753  // changes over 100 MB we check the file size and if the
754  // destination point is within 300000 bytes of the end of
755  // the file we enter a special mode where the read ahead
756  // buffer stops reading data and all reads are made directly
757  // until another seek is performed. The point of all this is
758  // to avoid flushing out the buffer that still contains all
759  // the data the final seek 0, read will need just to read the
760  // last 250000 bytes. A further optimization would be to buffer
761  // the 250000 byte read, which is currently performed in 32KB
762  // blocks (inefficient with RemoteFile).
763  if ((remotefile || fd2 >= 0) && (ignorereadpos < 0))
764  {
765  long long off_end = 0xDEADBEEF;
766  if (SEEK_END == whence)
767  {
768  off_end = pos;
769  if (remotefile)
770  {
771  new_pos = remotefile->GetFileSize() - off_end;
772  }
773  else
774  {
775  QFileInfo fi(filename);
776  new_pos = fi.size() - off_end;
777  }
778  }
779  else
780  {
781  if (remotefile)
782  {
783  off_end = remotefile->GetFileSize() - new_pos;
784  }
785  else
786  {
787  QFileInfo fi(filename);
788  off_end = fi.size() - new_pos;
789  }
790  }
791 
792  if (off_end != 0xDEADBEEF)
793  {
794  LOG(VB_FILE, LOG_INFO, LOC +
795  QString("Seek(): Offset from end: %1").arg(off_end));
796  }
797 
798  if (off_end == 250000)
799  {
800  LOG(VB_FILE, LOG_INFO, LOC +
801  QString("Seek(): offset from end: %1").arg(off_end) +
802  "\n\t\t\t -- ignoring read ahead thread until next seek.");
803 
804  ignorereadpos = new_pos;
805  errno = EINVAL;
806  if (remotefile)
807  ret = remotefile->Seek(ignorereadpos, SEEK_SET);
808  else
809  ret = lseek64(fd2, ignorereadpos, SEEK_SET);
810 
811  if (ret < 0)
812  {
813  int tmp_eno = errno;
814  QString cmd = QString("Seek(%1, SEEK_SET) ign ")
815  .arg(ignorereadpos);
816 
817  ignorereadpos = -1;
818 
819  LOG(VB_GENERAL, LOG_ERR, LOC + cmd + " Failed" + ENO);
820 
821  // try to return to former position..
822  if (remotefile)
823  ret = remotefile->Seek(internalreadpos, SEEK_SET);
824  else
825  ret = lseek64(fd2, internalreadpos, SEEK_SET);
826  if (ret < 0)
827  {
828  QString cmd2 = QString("Seek(%1, SEEK_SET) int ")
829  .arg(internalreadpos);
830  LOG(VB_GENERAL, LOG_ERR, LOC + cmd2 + " Failed" + ENO);
831  }
832  else
833  {
834  QString cmd2 = QString("Seek(%1, %2) int ")
835  .arg(internalreadpos)
836  .arg((SEEK_SET == whence) ? "SEEK_SET" :
837  ((SEEK_CUR == whence) ?"SEEK_CUR" : "SEEK_END"));
838  LOG(VB_GENERAL, LOG_ERR, LOC + cmd2 + " succeeded");
839  }
840  ret = -1;
841  errno = tmp_eno;
842  }
843  else
844  {
845  ateof = false;
846  readsallowed = false;
847  readsdesired = false;
848  recentseek = true;
849  }
850 
851  poslock.unlock();
852 
853  generalWait.wakeAll();
854 
855  return ret;
856  }
857  }
858 #endif
859 
860  // Here we perform a normal seek. When successful we
861  // need to call ResetReadAhead(). A reset means we will
862  // need to refill the buffer, which takes some time.
863  if (remotefile)
864  {
865  ret = remotefile->Seek(pos, whence, readpos);
866  if (ret<0)
867  errno = EINVAL;
868  }
869  else
870  {
871  ret = lseek64(fd2, pos, whence);
872  }
873 
874  if (ret >= 0)
875  {
876  readpos = ret;
877 
878  ignorereadpos = -1;
879 
880  if (readaheadrunning)
882 
883  readAdjust = 0;
884  }
885  else
886  {
887  QString cmd = QString("Seek(%1, %2)").arg(pos)
888  .arg((whence == SEEK_SET) ? "SEEK_SET" :
889  ((whence == SEEK_CUR) ? "SEEK_CUR" : "SEEK_END"));
890  LOG(VB_GENERAL, LOG_ERR, LOC + cmd + " Failed" + ENO);
891  }
892 
893  poslock.unlock();
894 
895  generalWait.wakeAll();
896 
897  return ret;
898 }
def write(text, progress=True)
Definition: mythburn.py:279
bool IsOpen(void) const override
Returns true if open for either reading or writing.
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:757
volatile bool recentseek
#define lseek
unsigned int uint
Definition: compat.h:140
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
QReadWriteLock rwlock
volatile bool stopreads
void CalcReadAheadThresh(void)
Calculates fill_min, fill_threshold, and readblocksize from the estimated effective bitrate of the st...
Definition: ringbuffer.cpp:402
QReadWriteLock rbrlock
#define off_t
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 poslock
#define close
Definition: compat.h:16
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:729
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
#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:947
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 ...
static QString local_sub_filename(QFileInfo &fileInfo)
int elapsed(void) const
Returns milliseconds elapsed since last start() or restart()
Definition: mythtimer.cpp:90
#define POSIX_FADV_WILLNEED
void StopReads(void)
????
Definition: ringbuffer.cpp:746
bool Open(void)
Opens the file we will be writing to.
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
static QStringList subExtNoCheck
LiveTVChain * livetvchain
#define POSIX_FADV_SEQUENTIAL
bool isOpen(void) const
Definition: remotefile.cpp:253
ThreadedFileWriter * tfw
static QMutex subExtLock
bool ReOpen(QString newFilename)
Definition: remotefile.cpp:347
QWaitCondition generalWait
Condition to signal that the read ahead thread is running.
This class supports the writing of recordings to disk.
bool ReOpen(QString newFilename="")
Reopens the file we are writing to or opens a new file.
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:645
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:766
QReadWriteLock rbwlock
static void usleep(unsigned long time)
Definition: mthread.cpp:349
bool IsRegisteredFileForWrite(const QString &file)
static QStringList subExt
void start(void)
starts measuring elapsed time.
Definition: mythtimer.cpp:47
long long GetRealFileSizeInternal(void) const override
#define O_BINARY