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