MythTV master
mythfilebuffer.cpp
Go to the documentation of this file.
1// QT
2#include <QFileInfo>
3#include <QDir>
4
5// MythTV
7#include "libmythbase/mythconfig.h"
14
15#include "io/mythfilebuffer.h"
16
17// Std
18#include <array>
19#include <cstdlib>
20#include <cerrno>
21#include <sys/types.h>
22#include <sys/time.h>
23#include <sys/stat.h>
24#include <unistd.h>
25#include <fcntl.h>
26
27#if HAVE_POSIX_FADVISE < 1
28static int posix_fadvise(int, off_t, off_t, int) { return 0; }
29static constexpr int8_t POSIX_FADV_SEQUENTIAL { 0 };
30static constexpr int8_t POSIX_FADV_WILLNEED { 0 };
31#endif
32
33#ifndef O_STREAMING
34static constexpr int8_t O_STREAMING { 0 };
35#endif
36
37#ifndef O_LARGEFILE
38static constexpr int8_t O_LARGEFILE { 0 };
39#endif
40
41#ifndef O_BINARY
42static constexpr int8_t O_BINARY { 0 };
43#endif
44
45#define LOC QString("FileRingBuf(%1): ").arg(m_filename)
46
47static const QStringList kSubExt {".ass", ".srt", ".ssa", ".sub", ".txt"};
48static const QStringList kSubExtNoCheck {".ass", ".srt", ".ssa", ".sub", ".txt", ".gif", ".png"};
49
50
51MythFileBuffer::MythFileBuffer(const QString &Filename, bool Write, bool UseReadAhead, std::chrono::milliseconds Timeout)
53{
54 m_startReadAhead = UseReadAhead;
55 m_safeFilename = Filename;
56 m_filename = Filename;
57
58 if (Write)
59 {
60 if (m_filename.startsWith("myth://"))
61 {
63 if (!m_remotefile->isOpen())
64 {
65 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Failed to open remote file (%1) for write").arg(m_filename));
66 delete m_remotefile;
67 m_remotefile = nullptr;
68 }
69 else
70 {
71 m_writeMode = true;
72 }
73 }
74 else
75 {
76 m_tfw = new ThreadedFileWriter(m_filename, O_WRONLY|O_TRUNC|O_CREAT|O_LARGEFILE, 0644);
77 if (!m_tfw->Open())
78 {
79 delete m_tfw;
80 m_tfw = nullptr;
81 }
82 else
83 {
84 m_writeMode = true;
85 }
86 }
87 }
88 else if (Timeout >= 0ms)
89 {
91 }
92}
93
95{
97
98 delete m_remotefile;
99 m_remotefile = nullptr;
100
101 delete m_tfw;
102 m_tfw = nullptr;
103
104 if (m_fd2 >= 0)
105 {
106 close(m_fd2);
107 m_fd2 = -1;
108 }
109}
110
115static bool CheckPermissions(const QString &Filename)
116{
117 QFileInfo fileInfo(Filename);
118 if (fileInfo.exists() && !fileInfo.isReadable())
119 {
120 LOG(VB_GENERAL, LOG_ERR, QString("FileRingBuf(%1): File exists but is not readable by MythTV!")
121 .arg(Filename));
122 return false;
123 }
124 return true;
125}
126
127static bool IsSubtitlePossible(const QString &Extension)
128{
129 auto it = std::find_if(kSubExtNoCheck.cbegin(), kSubExtNoCheck.cend(),
130 [Extension] (const QString& ext) -> bool
131 {return ext.contains(Extension);});
132 return (it != nullptr);
133}
134
135static QString LocalSubtitleFilename(QFileInfo &FileInfo)
136{
137 // Subtitle handling
138 QString vidFileName = FileInfo.fileName();
139 QString dirName = FileInfo.absolutePath();
140
141 QString baseName = vidFileName;
142 int suffixPos = vidFileName.lastIndexOf(QChar('.'));
143 if (suffixPos > 0)
144 baseName = vidFileName.left(suffixPos);
145
146 QStringList list;
147 {
148 // The dir listing does not work if the filename has the
149 // following chars "[]()" so we convert them to the wildcard '?'
150 const QString findBaseName = baseName.replace("[", "?")
151 .replace("]", "?")
152 .replace("(", "?")
153 .replace(")", "?");
154
155 for (const auto & ext : kSubExt)
156 list += findBaseName + ext;
157 }
158
159 // Some Qt versions do not accept paths in the search string of
160 // entryList() so we have to set the dir first
161 QDir dir;
162 dir.setPath(dirName);
163 const QStringList candidates = dir.entryList(list);
164 for (const auto & candidate : candidates)
165 {
166 QFileInfo file(dirName + "/" + candidate);
167 if (file.exists() && (file.size() >= kReadTestSize))
168 return file.absoluteFilePath();
169 }
170
171 return {};
172}
173
174bool MythFileBuffer::OpenFile(const QString &Filename, std::chrono::milliseconds Retry)
175{
176 LOG(VB_PLAYBACK, LOG_INFO, LOC + QString("OpenFile(%1, %2 ms)")
177 .arg(Filename).arg(Retry.count()));
178
179 m_rwLock.lockForWrite();
180
181 m_filename = Filename;
182 m_safeFilename = Filename;
183 m_subtitleFilename.clear();
184
185 if (m_remotefile)
186 {
187 delete m_remotefile;
188 m_remotefile = nullptr;
189 }
190
191 if (m_fd2 >= 0)
192 {
193 close(m_fd2);
194 m_fd2 = -1;
195 }
196
197 bool islocal = (!m_filename.startsWith("/dev")) &&
198 ((m_filename.startsWith("/")) || QFile::exists(m_filename));
199
200 if (islocal)
201 {
202 std::array<char,kReadTestSize> buf {};
203 int lasterror = 0;
204
205 MythTimer openTimer;
206 openTimer.start();
207
208 uint openAttempts = 0;
209 while ((openTimer.elapsed() < Retry) || (openAttempts == 0))
210 {
211 openAttempts++;
212
213 m_fd2 = open(m_filename.toLocal8Bit().constData(),
214 // NOLINTNEXTLINE(misc-redundant-expression)
216
217 if (m_fd2 < 0)
218 {
220 {
221 lasterror = 3;
222 break;
223 }
224
225 lasterror = 1;
226 usleep(10ms);
227 continue;
228 }
229
230 ssize_t ret = read(m_fd2, buf.data(), buf.size());
231 if (ret != kReadTestSize)
232 {
233 lasterror = 2;
234 close(m_fd2);
235 m_fd2 = -1;
236 if (ret == 0 && openAttempts > 5 && !gCoreContext->IsRegisteredFileForWrite(m_filename))
237 {
238 // file won't grow, abort early
239 break;
240 }
241
242 if (m_oldfile)
243 break; // if it's an old file it won't grow..
244 usleep(10ms);
245 continue;
246 }
247
248 if (0 == lseek(m_fd2, 0, SEEK_SET))
249 {
250#ifndef _MSC_VER
252 {
253 LOG(VB_FILE, LOG_DEBUG, LOC +
254 QString("OpenFile(): fadvise sequential "
255 "failed: ") + ENO);
256 }
257 if (posix_fadvise(m_fd2, 0, static_cast<off_t>(128)*1024, POSIX_FADV_WILLNEED) != 0)
258 {
259 LOG(VB_FILE, LOG_DEBUG, LOC +
260 QString("OpenFile(): fadvise willneed "
261 "failed: ") + ENO);
262 }
263#endif
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 with usleep(60000),
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 usleep(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#ifndef _MSC_VER
665 if (posix_fadvise(m_fd2, m_internalReadPos, static_cast<off_t>(128)*1024, POSIX_FADV_WILLNEED) != 0)
666 LOG(VB_FILE, LOG_DEBUG, LOC + QString("Seek(): fadvise willneed failed: ") + ENO);
667#endif
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}
static void usleep(std::chrono::microseconds time)
Definition: mthread.cpp:335
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:339
int Read(void *data, int size)
Definition: remotefile.cpp:938
long long Seek(long long pos, int whence, long long curpos=-1)
Definition: remotefile.cpp:759
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:245
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:68
#define close
Definition: compat.h:30
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