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