MythTV master
mythmediabuffer.cpp
Go to the documentation of this file.
1// Std
2#include <algorithm>
3#include <cmath>
4#include <cstdio>
5#include <cstdlib>
6#include <cerrno>
7#include <chrono>
8#include <thread>
9#include <sys/types.h>
10#include <sys/time.h>
11#include <unistd.h>
12#include <fcntl.h>
13
14// Qt
15#include <QFile>
16#include <QDateTime>
17#include <QReadLocker>
18
19// MythTV
20#include "libmythbase/compat.h"
22#include "libmythbase/mythconfig.h"
30
31#include "Bluray/mythbdbuffer.h"
32#include "DVD/mythdvdbuffer.h"
33#include "DVD/mythdvdstream.h"
35#include "io/mythfilebuffer.h"
36#include "io/mythmediabuffer.h"
38#include "livetvchain.h"
39
40// FFmpeg
41extern "C" {
42#include "libavformat/avformat.h"
43}
44
45#define LOC QString("RingBuf(%1): ").arg(m_filename)
46
47
48/*
49 Locking relations:
50 rwlock->poslock->rbrlock->rbwlock
51
52 A child should never lock any of the parents without locking
53 the parent lock before the child lock.
54 void MythMediaBuffer::Example1()
55 {
56 poslock.lockForWrite();
57 rwlock.lockForRead(); // error!
58 blah(); // <- does not implicitly acquire any locks
59 rwlock.unlock();
60 poslock.unlock();
61 }
62 void MythMediaBuffer::Example2()
63 {
64 rwlock.lockForRead();
65 rbrlock.lockForWrite(); // ok!
66 blah(); // <- does not implicitly acquire any locks
67 rbrlock.unlock();
68 rwlock.unlock();
69 }
70*/
71
99MythMediaBuffer *MythMediaBuffer::Create(const QString &Filename, bool Write,
100 bool UseReadAhead, std::chrono::milliseconds Timeout, bool StreamOnly)
101{
102 QString filename = Filename;
103 QString lower = filename.toLower();
104
105 if (Write)
106 return new MythFileBuffer(filename, Write, UseReadAhead, Timeout);
107
108 bool dvddir = false;
109 bool bddir = false;
110 bool httpurl = lower.startsWith("http://") || lower.startsWith("https://");
111 bool iptvurl = lower.startsWith("rtp://") || lower.startsWith("tcp://") ||
112 lower.startsWith("udp://");
113 bool mythurl = lower.startsWith("myth://");
114 bool bdurl = lower.startsWith("bd:");
115 bool dvdurl = lower.startsWith("dvd:");
116 bool imgext = lower.endsWith(".img") || lower.endsWith(".iso");
117
118 if (imgext)
119 {
121 {
123 bdurl = true;
124 break;
125 case MythCDROM::kDVD:
126 dvdurl = true;
127 break;
128 default: break;
129 }
130 }
131
132 if (httpurl || iptvurl)
133 {
135 return new HLSRingBuffer(filename);
136 return new MythStreamingBuffer(filename);
137 }
138 if (!StreamOnly && mythurl)
139 {
140 struct stat fileInfo {};
141 if ((RemoteFile::Exists(filename, &fileInfo)) &&
142 (S_ISDIR(fileInfo.st_mode)))
143 {
144 if (RemoteFile::Exists(filename + "/VIDEO_TS"))
145 dvddir = true;
146 else if (RemoteFile::Exists(filename + "/BDMV"))
147 bddir = true;
148 }
149 }
150 else if (!StreamOnly && !mythurl)
151 {
152 if (QFile::exists(filename + "/VIDEO_TS"))
153 dvddir = true;
154 else if (QFile::exists(filename + "/BDMV"))
155 bddir = true;
156 }
157
158 if (!StreamOnly && (dvdurl || dvddir))
159 {
160 if (filename.startsWith("dvd:")) // URI "dvd:" + path
161 filename.remove(0,4); // e.g. "dvd:/dev/dvd"
162
163 if (!(mythurl || QFile::exists(filename)))
164 filename = "/dev/dvd";
165 LOG(VB_PLAYBACK, LOG_INFO, "Trying DVD at " + filename);
166 return new MythDVDBuffer(filename);
167 }
168
169 if (!StreamOnly && (bdurl || bddir))
170 {
171 if (filename.startsWith("bd:")) // URI "bd:" + path
172 filename.remove(0,3); // e.g. "bd:/videos/ET"
173
174 if (!(mythurl || QFile::exists(filename)))
175 filename = "/dev/dvd";
176 LOG(VB_PLAYBACK, LOG_INFO, "Trying BD at " + filename);
177 return new MythBDBuffer(filename);
178 }
179
180 if (!mythurl && imgext && filename.startsWith("dvd:"))
181 {
182 LOG(VB_PLAYBACK, LOG_INFO, "DVD image at " + filename);
183 return new MythDVDStream(filename);
184 }
185
186 if (!mythurl && lower.endsWith(".vob") && filename.contains("/VIDEO_TS/"))
187 {
188 LOG(VB_PLAYBACK, LOG_INFO, "DVD VOB at " + filename);
189 auto *dvdstream = new MythDVDStream(filename);
190 if (dvdstream && dvdstream->IsOpen())
191 return dvdstream;
192 delete dvdstream;
193 }
194
195 return new MythFileBuffer(filename, Write, UseReadAhead, Timeout);
196}
197
199 : MThread("RingBuffer"),
200 m_type(Type)
201{
202}
203
205{
206 return m_type;
207}
208
209#undef NDEBUG
210#include <cassert>
211
220{
221 assert(!isRunning());
222 wait();
223
224 delete [] m_readAheadBuffer;
225 m_readAheadBuffer = nullptr;
226
227 if (m_tfw)
228 {
229 m_tfw->Flush();
230 delete m_tfw;
231 m_tfw = nullptr;
232 }
233}
234
238void MythMediaBuffer::Reset(bool Full, bool ToAdjust, bool ResetInternal)
239{
240 LOG(VB_FILE, LOG_INFO, LOC + QString("Reset(%1,%2,%3)")
241 .arg(Full).arg(ToAdjust).arg(ResetInternal));
242
243 m_rwLock.lockForWrite();
244 m_posLock.lockForWrite();
245
246 m_numFailures = 0;
247 m_commsError = false;
248 m_setSwitchToNext = false;
249
250 m_writePos = 0;
251 m_readPos = (ToAdjust) ? (m_readPos - m_readAdjust) : 0;
252
253 if (m_readPos != 0)
254 {
255 LOG(VB_GENERAL, LOG_ERR, LOC +
256 QString("MythMediaBuffer::Reset() nonzero readpos. toAdjust: %1 "
257 "readpos: %2 readAdjust: %3")
258 .arg(ToAdjust).arg(m_readPos).arg(m_readAdjust));
259 }
260
261 m_readAdjust = 0;
262 m_readPos = (m_readPos < 0) ? 0 : m_readPos;
263
264 if (Full)
266
267 if (ResetInternal)
269
270 m_generalWait.wakeAll();
271 m_posLock.unlock();
272 m_rwLock.unlock();
273}
274
280{
281 LOG(VB_FILE, LOG_INFO, LOC + QString("UpdateRawBitrate(%1Kb)").arg(RawBitrate));
282
283 // an audio only stream could be as low as 64Kb (DVB radio) and
284 // an MHEG only stream is likely to be reported as 0Kb
285 if (RawBitrate < 64)
286 {
287 LOG(VB_FILE, LOG_INFO, LOC + QString("Bitrate too low - setting to 64Kb"));
288 RawBitrate = 64;
289 }
290 else if (RawBitrate > 100000)
291 {
292 LOG(VB_FILE, LOG_INFO, LOC + QString("Bitrate too high - setting to 100Mb"));
293 RawBitrate = 100000;
294 }
295
296 m_rwLock.lockForWrite();
297 m_rawBitrate = RawBitrate;
300 m_rwLock.unlock();
301}
302
308{
309 m_rwLock.lockForWrite();
310 m_playSpeed = PlaySpeed;
312 m_rwLock.unlock();
313}
314
316{
318}
319
321{
322 m_waitForWrite = true;
323}
324
330void MythMediaBuffer::SetBufferSizeFactors(bool EstBitrate, bool Matroska)
331{
332 m_rwLock.lockForWrite();
333 m_unknownBitrate = EstBitrate;
334 m_fileIsMatroska = Matroska;
335 m_rwLock.unlock();
337}
338
339static inline uint estbitrate_to_rbs(uint estbitrate)
340{
341 if (estbitrate > 18000)
342 return 512*1024;
343 if (estbitrate > 9000)
344 return 256*1024;
345 if (estbitrate > 5000)
346 return 128*1024;
347 if (estbitrate > 2500)
348 return 64*1024;
349 if (estbitrate > 1250)
350 return 32*1024;
351 if (estbitrate >= 500)
352 return 16*1024;
353 if (estbitrate > 250)
354 return 8*1024;
355 if (estbitrate > 125)
356 return 4*1024;
357 return 2*1024;
358}
359
368{
369 uint estbitrate = 0;
370
371 m_readsAllowed = false;
372 m_readsDesired = false;
373
374 // loop without sleeping if the buffered data is less than this
376
377 estbitrate = static_cast<uint>(std::max(abs(m_rawBitrate * m_playSpeed), 0.5F * m_rawBitrate));
378 estbitrate = std::min(m_rawBitrate * 3, estbitrate);
379 int const rbs = estbitrate_to_rbs(estbitrate);
380
381 if (rbs < DEFAULT_CHUNK_SIZE)
382 m_readBlockSize = rbs;
383 else
385
386 // minimum seconds of buffering before allowing read
387 float secs_min = 0.3F;
388 // set the minimum buffering before allowing ffmpeg read
389 m_fillMin = static_cast<int>((estbitrate * 1000 * secs_min) * 0.125F);
390 // make this a multiple of ffmpeg block size..
392 {
393 if (m_lowBuffers)
394 LOG(VB_GENERAL, LOG_INFO, LOC + "Buffering optimisations disabled.");
395 m_lowBuffers = false;
397 m_fillMin = std::min(m_fillMin, static_cast<int>(m_bufferSize / 2));
398 }
399 else
400 {
401 m_lowBuffers = true;
402 LOG(VB_GENERAL, LOG_WARNING, "Enabling buffering optimisations for low bitrate stream.");
403 }
404
405 LOG(VB_FILE, LOG_INFO, LOC +
406 QString("CalcReadAheadThresh(%1 Kb)\n\t\t\t -> "
407 "threshold(%2 KB) min read(%3 KB) blk size(%4 KB)")
408 .arg(estbitrate).arg(m_fillThreshold/1024)
409 .arg(m_fillMin/1024).arg(m_readBlockSize/1024));
410}
411
412bool MythMediaBuffer::IsNearEnd(double /*Framerate*/, uint Frames) const
413{
414 QReadLocker lock(&m_rwLock);
415
416 // file is still being read, so can't be finished
417 if (!m_ateof && !m_setSwitchToNext)
418 return false;
419
420 m_posLock.lockForRead();
421 long long readpos = m_readPos;
422 long long size = m_internalReadPos - m_readPos;
423 m_posLock.unlock();
424
425 // telecom kilobytes (i.e. 1000 per k not 1024)
426 uint tmp = static_cast<uint>(std::max(abs(m_rawBitrate * m_playSpeed), 0.5F * m_rawBitrate));
427 uint kbitspersec = std::min(m_rawBitrate * 3, tmp);
428 if (kbitspersec == 0)
429 return false;
430
431 double readahead_time = size / (kbitspersec * (1000.0 / 8.0));
432
433 bool near_end = readahead_time <= 1.5;
434 LOG(VB_PLAYBACK, LOG_INFO, LOC + "IsReallyNearEnd()" +
435 QString(" br(%1KB)").arg(kbitspersec/8) +
436 QString(" sz(%1KB)").arg(size / 1000LL) +
437 QString(" vfl(%1)").arg(Frames) +
438 QString(" time(%1)").arg(readahead_time) +
439 QString(" rawbitrate(%1)").arg(m_rawBitrate) +
440 QString(" avail(%1)").arg(size) +
441 QString(" internal_size(%1)").arg(m_internalReadPos) +
442 QString(" readposition(%1)").arg(readpos) +
443 QString(" stopreads(%1)").arg(m_stopReads) +
444 QString(" paused(%1)").arg(m_paused) +
445 QString(" ne:%1").arg(near_end));
446 return near_end;
447}
448
452{
453 m_rbrLock.lockForRead();
454 m_rbwLock.lockForRead();
455 int ret = ((m_rbwPos >= m_rbrPos) ? m_rbrPos + static_cast<int>(m_bufferSize) : m_rbrPos) - m_rbwPos - 1;
456 m_rbwLock.unlock();
457 m_rbrLock.unlock();
458 return ret;
459}
460
463{
464 QReadLocker lock(&m_rwLock);
465 return ReadBufAvail();
466}
467
469{
470 {
471 QReadLocker lock(&m_rwLock);
473 return ReadBufAvail();
474 }
475
477}
478
479long long MythMediaBuffer::Seek(long long Position, int Whence, bool HasLock)
480{
481 LOG(VB_FILE, LOG_INFO, LOC + QString("Seek: Position:%1 Type: %2 Locked: %3)")
482 .arg(Position)
483 .arg((SEEK_SET == Whence) ? "SEEK_SET" : ((SEEK_CUR == Whence) ? "SEEK_CUR" : "SEEK_END"),
484 HasLock?"locked":"unlocked"));
485
486 if (!HasLock)
487 m_rwLock.lockForWrite();
488
489 long long ret = 0;
490
492 {
493 m_posLock.lockForWrite();
494 // only valid for SEEK_SET & SEEK_CUR
495 switch (Whence)
496 {
497 case SEEK_SET:
498 m_readPos = Position;
499 break;
500 case SEEK_CUR:
501 m_readPos += Position;
502 break;
503 case SEEK_END:
504 m_readPos = ReadBufAvail() - Position;
505 break;
506 }
507 m_readOffset = static_cast<int>(m_readPos);
508 m_posLock.unlock();
509 ret = m_readPos;
510 }
511 else
512 {
513 ret = SeekInternal(Position, Whence);
514 }
515
516 m_generalWait.wakeAll();
517
518 if (!HasLock)
519 m_rwLock.unlock();
520 return ret;
521}
522
524{
525 QWriteLocker lock(&m_rwLock);
526 bool old = m_readInternalMode;
527
528 if (Mode == old)
529 return old;
530
532
533 if (!Mode)
534 {
535 // adjust real read position in ringbuffer
536 m_rbrLock.lockForWrite();
537 m_rbrPos = (m_rbrPos + m_readOffset) % static_cast<int>(m_bufferSize);
538 m_generalWait.wakeAll();
539 m_rbrLock.unlock();
540 // reset the read offset as we are exiting the internal read mode
541 m_readOffset = 0;
542 }
543
544 LOG(VB_FILE, LOG_DEBUG, LOC + QString("SetReadInternalMode: %1").arg(Mode ? "on" : "off"));
545 return old;
546}
547
549{
550 return m_readInternalMode;
551}
552
556{
557 m_rbrLock.lockForRead();
558 m_rbwLock.lockForRead();
559 int ret = (m_rbwPos >= m_rbrPos) ? m_rbwPos - m_rbrPos : static_cast<int>(m_bufferSize) - m_rbrPos + m_rbwPos;
560 m_rbwLock.unlock();
561 m_rbrLock.unlock();
562 return ret;
563}
564
575void MythMediaBuffer::ResetReadAhead(long long NewInternal)
576{
577 LOG(VB_FILE, LOG_INFO, LOC + QString("ResetReadAhead(internalreadpos = %1->%2)")
578 .arg(m_internalReadPos).arg(NewInternal));
579
580 m_readInternalMode = false;
581 m_readOffset = 0;
582
583 m_rbrLock.lockForWrite();
584 m_rbwLock.lockForWrite();
585
587
588 m_rbrPos = 0;
589 m_rbwPos = 0;
590 m_internalReadPos = NewInternal;
591 m_ateof = false;
592 m_readsAllowed = false;
593 m_readsDesired = false;
594 m_recentSeek = true;
595 m_setSwitchToNext = false;
596
597 m_generalWait.wakeAll();
598
599 m_rbwLock.unlock();
600 m_rbrLock.unlock();
601}
602
618{
619 bool dostart = true;
620
621 m_rwLock.lockForWrite();
622 if (!m_startReadAhead)
623 {
624 dostart = false;
625 }
626 else if (m_writeMode)
627 {
628 LOG(VB_GENERAL, LOG_WARNING, LOC + "Not starting read ahead thread - write only RingBuffer");
629 dostart = false;
630 }
631 else if (m_readAheadRunning)
632 {
633 LOG(VB_GENERAL, LOG_WARNING, LOC + "Not starting read ahead thread - already running");
634 dostart = false;
635 }
636
637 if (!dostart)
638 {
639 m_rwLock.unlock();
640 return;
641 }
642
643 StartReads();
646 m_generalWait.wait(&m_rwLock);
647 m_rwLock.unlock();
648}
649
654{
655 while (isRunning())
656 {
657 m_rwLock.lockForWrite();
658 m_readAheadRunning = false;
659 StopReads();
660 m_generalWait.wakeAll();
661 m_rwLock.unlock();
662 MThread::wait(5s);
663 }
664}
665
670{
671 LOG(VB_FILE, LOG_INFO, LOC + "StopReads()");
672 m_stopReads = true;
673 m_generalWait.wakeAll();
674}
675
680{
681 LOG(VB_FILE, LOG_INFO, LOC + "StartReads()");
682 m_stopReads = false;
683 m_generalWait.wakeAll();
684}
685
691{
692 LOG(VB_FILE, LOG_INFO, LOC + "Pausing read ahead thread");
693 StopReads();
694
695 m_rwLock.lockForWrite();
696 m_requestPause = true;
697 m_rwLock.unlock();
698}
699
705{
706 LOG(VB_FILE, LOG_INFO, LOC + "Unpausing readahead thread");
707 StartReads();
708
709 m_rwLock.lockForWrite();
710 m_requestPause = false;
711 m_generalWait.wakeAll();
712 m_rwLock.unlock();
713}
714
719{
720 MythTimer t;
721 t.start();
722
723 m_rwLock.lockForRead();
725 {
726 m_generalWait.wait(&m_rwLock, 1000);
727 if (m_readAheadRunning && !m_paused && m_requestPause && t.elapsed() > 1s)
728 LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Waited %1 ms for ringbuffer pause")
729 .arg(t.elapsed().count()));
730 }
731 m_rwLock.unlock();
732}
733
735{
736 const uint timeout = 500; // ms
737
738 if (m_requestPause)
739 {
740 if (!m_paused)
741 {
742 m_rwLock.unlock();
743 m_rwLock.lockForWrite();
744
745 if (m_requestPause)
746 {
747 m_paused = true;
748 m_generalWait.wakeAll();
749 }
750
751 m_rwLock.unlock();
752 m_rwLock.lockForRead();
753 }
754
757 }
758
759 if (!m_requestPause && m_paused)
760 {
761 m_rwLock.unlock();
762 m_rwLock.lockForWrite();
763
764 if (!m_requestPause)
765 {
766 m_paused = false;
767 m_generalWait.wakeAll();
768 }
769
770 m_rwLock.unlock();
771 m_rwLock.lockForRead();
772 }
773
774 return m_requestPause || m_paused;
775}
776
778{
779 m_rwLock.lockForWrite();
780 m_posLock.lockForWrite();
781
782 uint oldsize = m_bufferSize;
783 uint newsize = BUFFER_SIZE_MINIMUM;
784 if (m_remotefile)
785 {
786 newsize *= BUFFER_FACTOR_NETWORK;
788 newsize *= BUFFER_FACTOR_MATROSKA;
790 newsize *= BUFFER_FACTOR_BITRATE;
791 }
792
793 // N.B. Don't try and make it smaller - bad things happen...
794 if (m_readAheadBuffer && (oldsize >= newsize))
795 {
796 m_posLock.unlock();
797 m_rwLock.unlock();
798 return;
799 }
800
801 m_bufferSize = newsize;
803 {
804 char* newbuffer = new char[m_bufferSize + 1024];
805 memcpy(newbuffer, m_readAheadBuffer + m_rbwPos, oldsize - static_cast<uint>(m_rbwPos));
806 memcpy(newbuffer + (oldsize - static_cast<uint>(m_rbwPos)), m_readAheadBuffer, static_cast<uint>(m_rbwPos));
807 delete [] m_readAheadBuffer;
808 m_readAheadBuffer = newbuffer;
810 (m_rbrPos + static_cast<int>(oldsize) - m_rbwPos);
811 m_rbwPos = static_cast<int>(oldsize);
812 }
813 else
814 {
815 m_readAheadBuffer = new char[m_bufferSize + 1024];
816 }
818 m_posLock.unlock();
819 m_rwLock.unlock();
820
821 LOG(VB_FILE, LOG_INFO, LOC + QString("Created readAheadBuffer: %1Mb")
822 .arg(newsize >> 20));
823}
824
826{
827 RunProlog();
828
829 // These variables are used to adjust the read block size
830 std::chrono::milliseconds readTimeAvg = 300ms;
831 bool ignoreForReadTiming = true;
832 int eofreads = 0;
833
834 auto lastread = nowAsDuration<std::chrono::milliseconds>();
835
837 m_rwLock.lockForWrite();
838 m_posLock.lockForWrite();
839 m_requestPause = false;
841 m_readAheadRunning = true;
842 m_reallyRunning = true;
843 m_generalWait.wakeAll();
844 m_posLock.unlock();
845 m_rwLock.unlock();
846
847 // NOTE: this must loop at some point hold only
848 // a read lock on rwlock, so that other functions
849 // such as reset and seek can take priority.
850
851 m_rwLock.lockForRead();
852
853 LOG(VB_FILE, LOG_INFO, LOC + QString("Initial readblocksize %1K fillMin %2K")
854 .arg(m_readBlockSize/1024).arg(m_fillMin/1024));
855
856 while (m_readAheadRunning)
857 {
858 m_rwLock.unlock();
859 bool isopened = IsOpen();
860 m_rwLock.lockForRead();
861
862 if (!isopened)
863 {
864 LOG(VB_FILE, LOG_WARNING, LOC + QString("File not opened, terminating readahead thread"));
865 m_posLock.lockForWrite();
866 m_readAheadRunning = false;
867 m_generalWait.wakeAll();
868 m_posLock.unlock();
869 break;
870 }
871 if (PauseAndWait())
872 {
873 ignoreForReadTiming = true;
874 LOG(VB_FILE, LOG_DEBUG, LOC + "run: PauseAndWait Not reading continuing");
875 continue;
876 }
877
878 long long totfree = ReadBufFree();
879
880 const uint KB32 = 32*1024;
881 const int KB512 = 512*1024;
882 // These are conditions where we don't want to go through
883 // the loop if they are true.
884 if (((totfree < KB32) && m_readsAllowed) ||
886 {
887 ignoreForReadTiming |= (m_ignoreReadPos >= 0) || m_commsError || m_stopReads;
888 m_generalWait.wait(&m_rwLock, (m_stopReads) ? 50 : 1000);
889 LOG(VB_FILE, LOG_DEBUG, LOC +
890 QString("run: Not reading continuing: totfree(%1) "
891 "readsallowed(%2) ignorereadpos(%3) commserror(%4) "
892 "stopreads(%5)")
893 .arg(totfree).arg(m_readsAllowed).arg(m_ignoreReadPos)
894 .arg(m_commsError).arg(m_stopReads));
895 continue;
896 }
897
898 // These are conditions where we want to sleep to allow
899 // other threads to do stuff.
901 {
902 ignoreForReadTiming = true;
903 m_generalWait.wait(&m_rwLock, 1000);
904 totfree = ReadBufFree();
905 }
906
907 int readResult = -1;
908 if (totfree >= KB32 && !m_commsError && !m_ateof && !m_setSwitchToNext)
909 {
910 // limit the read size
911 if (m_readBlockSize > totfree)
912 totfree = (totfree / KB32) * KB32; // must be multiple of 32KB
913 else
914 totfree = m_readBlockSize;
915
916 // adapt blocksize
917 auto now = nowAsDuration<std::chrono::milliseconds>();
918 if (!ignoreForReadTiming)
919 {
920 readTimeAvg = (readTimeAvg * 9 + (now - lastread)) / 10;
921
922 if (readTimeAvg < 150ms &&
923 static_cast<uint>(m_readBlockSize) < (BUFFER_SIZE_MINIMUM >>2) &&
924 m_readBlockSize >= DEFAULT_CHUNK_SIZE /* low_buffers */ &&
925 m_readBlockSize <= KB512)
926 {
927 int old_block_size = m_readBlockSize;
930 m_readBlockSize = std::min(m_readBlockSize, KB512);
931 LOG(VB_FILE, LOG_INFO, LOC + QString("Avg read interval was %1 msec. "
932 "%2K -> %3K block size")
933 .arg(readTimeAvg.count()).arg(old_block_size/1024).arg(m_readBlockSize/1024));
934 readTimeAvg = 225ms;
935 }
936 else if (readTimeAvg > 300ms && m_readBlockSize > DEFAULT_CHUNK_SIZE)
937 {
939 LOG(VB_FILE, LOG_INFO, LOC +
940 QString("Avg read interval was %1 msec. %2K -> %3K block size")
941 .arg(readTimeAvg.count())
943 .arg(m_readBlockSize/1024));
944 readTimeAvg = 225ms;
945 }
946 }
947 lastread = now;
948
949 m_rbwLock.lockForRead();
950 if (m_rbwPos + totfree > m_bufferSize)
951 {
952 totfree = m_bufferSize - static_cast<uint>(m_rbwPos);
953 LOG(VB_FILE, LOG_DEBUG, LOC + "Shrinking read, near end of buffer");
954 }
955
956 if (m_internalReadPos == 0)
957 {
958 totfree = std::max(m_fillMin, m_readBlockSize);
959 LOG(VB_FILE, LOG_DEBUG, LOC + "Reading enough data to start playback");
960 }
961
962 LOG(VB_FILE, LOG_DEBUG, LOC + QString("safe_read(...@%1, %2) -- begin")
963 .arg(m_rbwPos).arg(totfree));
964
965 MythTimer sr_timer;
966 sr_timer.start();
967
968 int rbwposcopy = m_rbwPos;
969
970 // MythFileBuffer::SafeRead(RemoteFile*...) acquires poslock;
971 // so we need to unlock this here to preserve locking order.
972 m_rbwLock.unlock();
973
974 readResult = SafeRead(m_readAheadBuffer + rbwposcopy, static_cast<uint>(totfree));
975
976 int sr_elapsed = sr_timer.elapsed().count();
977 uint64_t bps = !sr_elapsed ? 1000000001 :
978 static_cast<uint64_t>((readResult * 8000.0) / static_cast<double>(sr_elapsed));
979 LOG(VB_FILE, LOG_DEBUG, LOC +
980 QString("safe_read(...@%1, %2) -> %3, took %4 ms %5 avg %6 ms")
981 .arg(rbwposcopy).arg(totfree).arg(readResult).arg(sr_elapsed)
982 .arg(QString("(%1Mbps)").arg(static_cast<double>(bps) / 1000000.0))
983 .arg(readTimeAvg.count()));
985
986 if (readResult >= 0)
987 {
988 m_posLock.lockForWrite();
989 m_rbwLock.lockForWrite();
990
991 if (rbwposcopy == m_rbwPos)
992 {
993 m_internalReadPos += readResult;
994 m_rbwPos = (m_rbwPos + readResult) % static_cast<int>(m_bufferSize);
995 LOG(VB_FILE, LOG_DEBUG, LOC + QString("rbwpos += %1K requested %2K in read")
996 .arg(readResult/1024,3).arg(totfree/1024,3));
997 }
998 m_numFailures = 0;
999
1000 m_rbwLock.unlock();
1001 m_posLock.unlock();
1002
1003 LOG(VB_FILE, LOG_DEBUG, LOC + QString("total read so far: %1 bytes")
1004 .arg(m_internalReadPos));
1005 }
1006 }
1007 else
1008 {
1009 LOG(VB_FILE, LOG_DEBUG, LOC +
1010 QString("We are not reading anything (totfree: %1 commserror:%2 ateof:%3 setswitchtonext:%4")
1011 .arg(totfree).arg(m_commsError).arg(m_ateof).arg(m_setSwitchToNext));
1012 }
1013
1014 int used = static_cast<int>(m_bufferSize) - ReadBufFree();
1015 bool readsWereAllowed = m_readsAllowed;
1016
1017 ignoreForReadTiming = (totfree < m_readBlockSize) || (readResult < totfree);
1018
1019 if ((0 == readResult) || (m_numFailures > 5) ||
1020 (m_readsAllowed != (used >= 1 || m_ateof || m_setSwitchToNext || m_commsError)) ||
1022 {
1023 // If readpos changes while the lock is released
1024 // we should not handle the 0 read_return now.
1025 long long oldreadposition = m_readPos;
1026
1027 m_rwLock.unlock();
1028 m_rwLock.lockForWrite();
1029
1030 m_commsError |= (m_numFailures > 5);
1031
1034
1035 if ((0 == readResult) && (oldreadposition == m_readPos))
1036 {
1037 eofreads++;
1038 if (eofreads >= 3 && m_readBlockSize >= KB512)
1039 {
1040 // not reading anything
1043 }
1044
1045 if (m_liveTVChain)
1046 {
1048 {
1049 // we receive new livetv chain element event
1050 // before we receive file closed for writing event
1051 // so don't need to test if file is closed for writing
1053 m_setSwitchToNext = true;
1054 }
1056 {
1057 LOG(VB_FILE, LOG_DEBUG, LOC +
1058 QString("EOF encountered, but %1 still being written to")
1059 .arg(m_filename));
1060 // We reached EOF, but file still open for writing and
1061 // no next program in livetvchain
1062 // wait a little bit (60ms same wait as typical safe_read)
1063 m_generalWait.wait(&m_rwLock, 60);
1064 }
1065 }
1067 {
1068 LOG(VB_FILE, LOG_DEBUG, LOC +
1069 QString("EOF encountered, but %1 still being written to")
1070 .arg(m_filename));
1071 // We reached EOF, but file still open for writing,
1072 // typically active in-progress recording
1073 // wait a little bit (60ms same wait as typical safe_read)
1074 m_generalWait.wait(&m_rwLock, 60);
1075 m_beingWritten = true;
1076 }
1077 else
1078 {
1080 {
1081 LOG(VB_FILE, LOG_DEBUG, LOC + "Waiting for file to grow large enough to process.");
1082 m_generalWait.wait(&m_rwLock, 300);
1083 }
1084 else
1085 {
1086 LOG(VB_FILE, LOG_DEBUG, LOC + "setting ateof (readResult == 0)");
1087 m_ateof = true;
1088 }
1089 }
1090 }
1091
1092 m_rwLock.unlock();
1093 m_rwLock.lockForRead();
1094 used = static_cast<int>(m_bufferSize) - ReadBufFree();
1095 }
1096 else
1097 {
1098 eofreads = 0;
1099 }
1100
1101 LOG(VB_FILE, LOG_DEBUG, LOC + "@ end of read ahead loop");
1102
1104 (m_wantToRead <= used && m_wantToRead > 0))
1105 {
1106 // To give other threads a good chance to handle these
1107 // conditions, even if they are only requesting a read lock
1108 // like us, yield (currently implemented with short usleep).
1109 m_generalWait.wakeAll();
1110 m_rwLock.unlock();
1111 std::this_thread::sleep_for(5ms);
1112 m_rwLock.lockForRead();
1113 }
1114 else
1115 {
1116 // yield if we have nothing to do...
1117 if (!m_requestPause && readsWereAllowed &&
1119 {
1120 m_generalWait.wait(&m_rwLock, 50);
1121 }
1122 else if (m_readsAllowed)
1123 {
1124 // if reads are allowed release the lock and yield so the
1125 // reader gets a chance to read before the buffer is full.
1126 m_generalWait.wakeAll();
1127 m_rwLock.unlock();
1128 std::this_thread::sleep_for(5ms);
1129 m_rwLock.lockForRead();
1130 }
1131 }
1132 }
1133
1134 m_rwLock.unlock();
1135
1136 m_rwLock.lockForWrite();
1137 m_rbrLock.lockForWrite();
1138 m_rbwLock.lockForWrite();
1139
1140 delete [] m_readAheadBuffer;
1141
1142 m_readAheadBuffer = nullptr;
1143 m_rbrPos = 0;
1144 m_rbwPos = 0;
1145 m_reallyRunning = false;
1146 m_readsAllowed = false;
1147 m_readsDesired = false;
1148
1149 m_rbwLock.unlock();
1150 m_rbrLock.unlock();
1151 m_rwLock.unlock();
1152
1153 LOG(VB_FILE, LOG_INFO, LOC + QString("Exiting readahead thread"));
1154
1155 RunEpilog();
1156}
1157
1159{
1160 m_rwLock.lockForWrite();
1161 m_posLock.lockForRead();
1163 long long readadjust = m_readAdjust;
1164 m_posLock.unlock();
1165 m_rwLock.unlock();
1166 return readadjust;
1167}
1168
1170int MythMediaBuffer::Peek(void *Buffer, int Count)
1171{
1172 int result = ReadPriv(Buffer, Count, true);
1173 if (result != Count)
1174 {
1175 LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Peek requested %1 bytes but only have %2")
1176 .arg(Count).arg(result));
1177 }
1178 return result;
1179}
1180
1181int MythMediaBuffer::Peek(std::vector<char>& Buffer)
1182{
1183 return Peek(Buffer.data(), Buffer.size());
1184};
1185
1187{
1188 // Wait up to 30000 ms for reads allowed (or readsdesired if post seek/open)
1190 m_recentSeek = false;
1191 std::chrono::milliseconds timeoutms = 30s;
1192 int count = 0;
1193 MythTimer timer;
1194 timer.start();
1195
1196 while ((timer.elapsed() < timeoutms) && !check && !m_stopReads &&
1198 {
1199 std::chrono::milliseconds delta = clamp(timeoutms - timer.elapsed(), 10ms, 100ms);
1200 m_generalWait.wait(&m_rwLock, delta.count());
1201 if (!check && timer.elapsed() > 1s && (count % 100) == 0)
1202 LOG(VB_GENERAL, LOG_WARNING, LOC + "Taking too long to be allowed to read..");
1203 count++;
1204 }
1205
1206 if (timer.elapsed() >= timeoutms)
1207 {
1208 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Took more than %1 seconds to be allowed to read, aborting.")
1209 .arg(duration_cast<std::chrono::seconds>(timeoutms).count()));
1210 return false;
1211 }
1212 return check;
1213}
1214
1215int MythMediaBuffer::WaitForAvail(int Count, std::chrono::milliseconds Timeout)
1216{
1217 int available = ReadBufAvail();
1218 if (available >= Count)
1219 return available;
1220
1221 if (m_ateof)
1222 {
1223 m_wantToRead = 0;
1224 return available;
1225 }
1226
1228 return available;
1229
1230 // Make sure that if the read ahead thread is sleeping and
1231 // it should be reading that we start reading right away.
1233 m_generalWait.wakeAll();
1234
1235 MythTimer timer;
1236 timer.start();
1237 while ((available < Count) && !m_stopReads && !m_requestPause && !m_commsError && m_readAheadRunning)
1238 {
1239 m_wantToRead = Count;
1240 std::chrono::milliseconds delta = clamp(Timeout - timer.elapsed(), 10ms, 250ms);
1241 m_generalWait.wait(&m_rwLock, delta.count());
1242 available = ReadBufAvail();
1243 if (m_ateof)
1244 break;
1245 if (m_lowBuffers && available >= m_fillMin)
1246 break;
1247 if (timer.elapsed() > Timeout)
1248 break;
1249 }
1250
1251 m_wantToRead = 0;
1252 return available;
1253}
1254
1255int MythMediaBuffer::ReadDirect(void *Buffer, int Count, bool Peek)
1256{
1257 long long oldposition = 0;
1258 if (Peek)
1259 {
1260 m_posLock.lockForRead();
1261 oldposition = (m_ignoreReadPos >= 0) ? m_ignoreReadPos : m_readPos;
1262 m_posLock.unlock();
1263 }
1264
1265 MythTimer timer;
1266 timer.start();
1267 int result = SafeRead(Buffer, static_cast<uint>(Count));
1268 int elapsed = timer.elapsed().count();
1269 uint64_t bps = !elapsed ? 1000000001 : static_cast<uint64_t>((result * 8000.0) / static_cast<double>(elapsed));
1270 UpdateStorageRate(bps);
1271
1272 m_posLock.lockForWrite();
1273 if (m_ignoreReadPos >= 0 && result > 0)
1274 {
1275 if (Peek)
1276 {
1277 // seek should always succeed since we were at this position
1278 long long cur_pos = -1;
1279 if (m_remotefile)
1280 cur_pos = m_remotefile->Seek(oldposition, SEEK_SET);
1281 else if (m_fd2 >= 0)
1282 cur_pos = lseek64(m_fd2, oldposition, SEEK_SET);
1283 if (cur_pos < 0)
1284 {
1285 LOG(VB_FILE, LOG_ERR, LOC + "Seek failed repositioning to previous position");
1286 }
1287 }
1288 else
1289 {
1290 m_ignoreReadPos += result;
1291 }
1292 m_posLock.unlock();
1293 return result;
1294 }
1295 m_posLock.unlock();
1296
1297 if (Peek && (result > 0))
1298 {
1299 if ((IsDVD() || IsBD()) && oldposition != 0)
1300 {
1301 LOG(VB_GENERAL, LOG_ERR, LOC +
1302 "DVD and Blu-Ray do not support arbitrary "
1303 "peeks except when read-ahead is enabled."
1304 "\n\t\t\tWill seek to beginning of video.");
1305 oldposition = 0;
1306 }
1307
1308 long long newposition = Seek(oldposition, SEEK_SET, true);
1309
1310 if (newposition != oldposition)
1311 {
1312 LOG(VB_GENERAL, LOG_ERR, LOC +
1313 QString("Peek() Failed to return from new position %1 to old position %2, now "
1314 "at position %3")
1315 .arg(oldposition - result).arg(oldposition).arg(newposition));
1316 }
1317 }
1318
1319 return result;
1320}
1321
1330int MythMediaBuffer::ReadPriv(void *Buffer, int Count, bool Peek)
1331{
1332 QString desc = QString("ReadPriv(..%1, %2)").arg(Count).arg(Peek ? "peek" : "normal");
1333 LOG(VB_FILE, LOG_DEBUG, LOC + desc + QString(" @%1 -- begin").arg(m_rbrPos));
1334
1335 m_rwLock.lockForRead();
1336 if (m_writeMode)
1337 {
1338 LOG(VB_GENERAL, LOG_ERR, LOC + desc + ": Attempt to read from a write only file");
1339 errno = EBADF;
1340 m_rwLock.unlock();
1341 return -1;
1342 }
1343
1345 {
1346 m_rwLock.unlock();
1347 m_rwLock.lockForWrite();
1348 // we need a write lock so the read-ahead thread
1349 // can't start mucking with the read position.
1350 // If the read ahead thread was started while we
1351 // didn't hold the lock, we proceed with a normal
1352 // read from the buffer, otherwise we read directly.
1354 {
1355 int result = ReadDirect(Buffer, Count, Peek);
1356#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1357 quint16 checksum = qChecksum(reinterpret_cast<char*>(Buffer), static_cast<uint>(Count));
1358#else
1359 QByteArrayView BufferView(reinterpret_cast<char*>(Buffer), Count);
1360 quint16 checksum = qChecksum(BufferView);
1361#endif
1362 LOG(VB_FILE, LOG_DEBUG, LOC + desc + QString(": ReadDirect checksum %1")
1363 .arg(checksum));
1364 m_rwLock.unlock();
1365 return result;
1366 }
1367 m_rwLock.unlock();
1368 m_rwLock.lockForRead();
1369 }
1370
1371 if (!WaitForReadsAllowed())
1372 {
1373 LOG(VB_FILE, LOG_NOTICE, LOC + desc + ": !WaitForReadsAllowed()");
1374 m_rwLock.unlock();
1375 m_stopReads = true; // this needs to be outside the lock
1376 m_rwLock.lockForWrite();
1377 m_wantToRead = 0;
1378 m_rwLock.unlock();
1379 return 0;
1380 }
1381
1382 int available = ReadBufAvail();
1384
1385 // Wait up to 10000 ms for any data
1386 std::chrono::milliseconds timeout_ms = 10s;
1387 while (!m_readInternalMode && !m_ateof && (timer.elapsed() < timeout_ms) && m_readAheadRunning &&
1389 {
1390 available = WaitForAvail(Count, std::min(timeout_ms - timer.elapsed(), 100ms));
1391 if (m_liveTVChain && m_setSwitchToNext && available < Count)
1392 {
1393 LOG(VB_GENERAL, LOG_INFO, LOC + "Checking to see if there's a new livetv program to switch to..");
1395 break;
1396 }
1397 if (available > 0)
1398 break;
1399 }
1400 if (timer.elapsed() > 6s)
1401 {
1402 LOG(VB_GENERAL, LOG_WARNING, LOC + desc + QString(" -- waited %1 ms for avail(%2) > count(%3)")
1403 .arg(timer.elapsed().count()).arg(available).arg(Count));
1404 }
1405
1407 {
1408 LOG(VB_FILE, LOG_DEBUG, LOC + QString("ReadPriv: %1 bytes available, %2 left")
1409 .arg(available).arg(available-m_readOffset));
1410 }
1411 Count = std::min(available - m_readOffset, Count);
1412
1413 if ((Count <= 0) && (m_ateof || m_readInternalMode))
1414 {
1415 // If we're at the end of file return 0 bytes
1416 m_rwLock.unlock();
1417 return Count;
1418 }
1419
1420 if (Count <= 0)
1421 {
1422 // If we're not at the end of file but have no data
1423 // at this point time out and shutdown read ahead.
1424 LOG(VB_GENERAL, LOG_ERR, LOC + desc + QString(" -- timed out waiting for data (%1 ms)")
1425 .arg(timer.elapsed().count()));
1426
1427 m_rwLock.unlock();
1428 m_stopReads = true; // this needs to be outside the lock
1429 m_rwLock.lockForWrite();
1430 m_ateof = true;
1431 m_wantToRead = 0;
1432 m_generalWait.wakeAll();
1433 m_rwLock.unlock();
1434 return Count;
1435 }
1436
1437 if (Peek || m_readInternalMode)
1438 m_rbrLock.lockForRead();
1439 else
1440 m_rbrLock.lockForWrite();
1441
1442 LOG(VB_FILE, LOG_DEBUG, LOC + desc + ": Copying data");
1443
1444 int readposition = 0;
1445 if (m_rbrPos + m_readOffset > static_cast<int>(m_bufferSize))
1446 readposition = (m_rbrPos + m_readOffset) - static_cast<int>(m_bufferSize);
1447 else
1448 readposition = m_rbrPos + m_readOffset;
1449
1450 if (readposition + Count > static_cast<int>(m_bufferSize))
1451 {
1452 int firstsize = static_cast<int>(m_bufferSize) - readposition;
1453 int secondsize = Count - firstsize;
1454
1455 memcpy(Buffer, m_readAheadBuffer + readposition, static_cast<size_t>(firstsize));
1456 memcpy(reinterpret_cast<char*>(Buffer) + firstsize, m_readAheadBuffer, static_cast<size_t>(secondsize));
1457 }
1458 else
1459 {
1460 memcpy(Buffer, m_readAheadBuffer + readposition, static_cast<uint>(Count));
1461 }
1462#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1463 quint16 checksum = qChecksum(reinterpret_cast<char*>(Buffer), static_cast<uint>(Count));
1464#else
1465 QByteArrayView BufferView(reinterpret_cast<char*>(Buffer), Count);
1466 quint16 checksum = qChecksum(BufferView);
1467#endif
1468 LOG(VB_FILE, LOG_DEBUG, LOC + desc + QString(": Checksum %1").arg(checksum));
1469
1470 if (!Peek)
1471 {
1473 {
1474 m_readOffset += Count;
1475 }
1476 else
1477 {
1478 m_rbrPos = (m_rbrPos + Count) % static_cast<int>(m_bufferSize);
1479 m_generalWait.wakeAll();
1480 }
1481 }
1482 m_rbrLock.unlock();
1483 m_rwLock.unlock();
1484
1485 return Count;
1486}
1487
1496int MythMediaBuffer::Read(void *Buffer, int Count)
1497{
1498 int ret = ReadPriv(Buffer, Count, false);
1499 if (ret > 0)
1500 {
1501 m_posLock.lockForWrite();
1502 m_readPos += ret;
1503 m_posLock.unlock();
1504 UpdateDecoderRate(static_cast<uint64_t>(ret));
1505 }
1506
1507 return ret;
1508}
1509
1510QString MythMediaBuffer::BitrateToString(uint64_t Rate, bool Hz)
1511{
1512 if (Rate < 1)
1513 return "-";
1514
1515 if (Rate > 1000000000)
1516 return QObject::tr(">1Gbps");
1517
1518 QString msg;
1519 auto bitrate = static_cast<double>(NAN);
1520 auto rate = static_cast<double>(Rate);
1521 int range = 0;
1522
1523 if (Rate >= 1000000)
1524 {
1525 msg = Hz ? QObject::tr("%1MHz") : QObject::tr("%1Mbps");
1526 bitrate = rate / 1000000.0;
1527 range = Hz ? 3 : 1;
1528 }
1529 else if (Rate >= 1000)
1530 {
1531 msg = Hz ? QObject::tr("%1kHz") : QObject::tr("%1kbps");
1532 bitrate = rate / 1000.0;
1533 range = Hz ? 1 : 0;
1534 }
1535 else
1536 {
1537 msg = Hz ? QObject::tr("%1Hz") : QObject::tr("%1bps");
1538 bitrate = rate;
1539 }
1540 return msg.arg(bitrate, 0, 'f', range);
1541}
1542
1544{
1546}
1547
1549{
1551}
1552
1554{
1556 return "N/A";
1557
1558 int avail = (m_rbwPos >= m_rbrPos) ? m_rbwPos - m_rbrPos
1559 : static_cast<int>(m_bufferSize) - m_rbrPos + m_rbwPos;
1560 return QString("%1%").arg(lroundf((static_cast<float>(avail) / static_cast<float>(m_bufferSize) * 100.0F)));
1561}
1562
1564{
1565 return m_bufferSize;
1566}
1567
1568uint64_t MythMediaBuffer::UpdateDecoderRate(uint64_t Latest)
1569{
1571 return 0;
1572
1573 auto current = std::chrono::milliseconds(QDateTime::currentMSecsSinceEpoch());
1574 std::chrono::milliseconds expire = current - 1s;
1575
1576 m_decoderReadLock.lock();
1577 if (Latest)
1578 m_decoderReads.insert(current, Latest);
1579 uint64_t total = 0;
1580 QMutableMapIterator<std::chrono::milliseconds,uint64_t> it(m_decoderReads);
1581 while (it.hasNext())
1582 {
1583 it.next();
1584 if (it.key() < expire || it.key() > current)
1585 it.remove();
1586 else
1587 total += it.value();
1588 }
1589
1590 int size = m_decoderReads.size();
1591 m_decoderReadLock.unlock();
1592
1593 auto average = static_cast<uint64_t>(static_cast<double>(total) * 8.0);
1594 LOG(VB_FILE, LOG_INFO, LOC + QString("Decoder read speed: %1 %2")
1595 .arg(average).arg(size));
1596 return average;
1597}
1598
1599uint64_t MythMediaBuffer::UpdateStorageRate(uint64_t Latest)
1600{
1602 return 0;
1603
1604 auto current = std::chrono::milliseconds(QDateTime::currentMSecsSinceEpoch());
1605 auto expire = current - 1s;
1606
1607 m_storageReadLock.lock();
1608 if (Latest)
1609 m_storageReads.insert(current, Latest);
1610 uint64_t total = 0;
1611 QMutableMapIterator<std::chrono::milliseconds,uint64_t> it(m_storageReads);
1612 while (it.hasNext())
1613 {
1614 it.next();
1615 if (it.key() < expire || it.key() > current)
1616 it.remove();
1617 else
1618 total += it.value();
1619 }
1620
1621 int size = m_storageReads.size();
1622 m_storageReadLock.unlock();
1623
1624 uint64_t average = size ? static_cast<uint64_t>(static_cast<double>(total) / size) : 0;
1625 LOG(VB_FILE, LOG_INFO, LOC + QString("Average storage read speed: %1 (Reads %2")
1626 .arg(average).arg(m_storageReads.size()));
1627 return average;
1628}
1629
1634int MythMediaBuffer::Write(const void *Buffer, uint Count)
1635{
1636 m_rwLock.lockForRead();
1637 int result = -1;
1638
1639 if (!m_writeMode)
1640 {
1641 LOG(VB_GENERAL, LOG_ERR, LOC + "Tried to write to a read only file.");
1642 m_rwLock.unlock();
1643 return result;
1644 }
1645
1646 if (!m_tfw && !m_remotefile)
1647 {
1648 m_rwLock.unlock();
1649 return result;
1650 }
1651
1652 if (m_tfw)
1653 result = m_tfw->Write(Buffer, Count);
1654 else
1655 result = m_remotefile->Write(Buffer, static_cast<int>(Count));
1656
1657 if (result > 0)
1658 {
1659 m_posLock.lockForWrite();
1660 m_writePos += result;
1661 m_posLock.unlock();
1662 }
1663
1664 m_rwLock.unlock();
1665 return result;
1666}
1667
1672{
1673 m_rwLock.lockForRead();
1674 if (m_tfw)
1675 m_tfw->Sync();
1676 m_rwLock.unlock();
1677}
1678
1681long long MythMediaBuffer::WriterSeek(long long Position, int Whence, bool HasLock)
1682{
1683 long long result = -1;
1684
1685 if (!HasLock)
1686 m_rwLock.lockForRead();
1687
1688 m_posLock.lockForWrite();
1689
1690 if (m_tfw)
1691 {
1692 result = m_tfw->Seek(Position, Whence);
1693 m_writePos = result;
1694 }
1695
1696 m_posLock.unlock();
1697
1698 if (!HasLock)
1699 m_rwLock.unlock();
1700
1701 return result;
1702}
1703
1708{
1709 m_rwLock.lockForRead();
1710 if (m_tfw)
1711 m_tfw->Flush();
1712 m_rwLock.unlock();
1713}
1714
1719{
1720 QReadLocker lock(&m_rwLock);
1721 if (m_tfw)
1722 return m_tfw->SetBlocking(Lock);
1723 return false;
1724}
1725
1742{
1743 LOG(VB_FILE, LOG_INFO, LOC + QString("SetOldFile: %1)").arg(Old));
1744 m_rwLock.lockForWrite();
1745 m_oldfile = Old;
1746 m_rwLock.unlock();
1747}
1748
1750{
1751 m_rwLock.lockForRead();
1752 QString tmp = m_filename;
1753 m_rwLock.unlock();
1754 return tmp;
1755}
1756
1758{
1759 return m_safeFilename;
1760}
1761
1763{
1764 m_rwLock.lockForRead();
1765 QString tmp = m_subtitleFilename;
1766 m_rwLock.unlock();
1767 return tmp;
1768}
1769
1771{
1772 m_rwLock.lockForRead();
1773 QString tmp = m_lastError;
1774 m_rwLock.unlock();
1775 return tmp;
1776}
1777
1779{
1780 return m_commsError;
1781}
1782
1784{
1785 m_commsError = false;
1786}
1787
1789{
1790 return m_stopReads;
1791}
1792
1797{
1798 m_posLock.lockForRead();
1799 long long ret = m_writePos;
1800 m_posLock.unlock();
1801 return ret;
1802}
1803
1809{
1810 m_rwLock.lockForRead();
1811 bool ret = (m_liveTVChain);
1812 m_rwLock.unlock();
1813 return ret;
1814}
1815
1821{
1822 m_rwLock.lockForWrite();
1823 m_liveTVChain = Chain;
1824 m_rwLock.unlock();
1825}
1826
1829{
1830 m_rwLock.lockForWrite();
1831 m_ignoreLiveEOF = Ignore;
1832 m_rwLock.unlock();
1833}
1834
1836{
1837 return IsDVD() || IsBD();
1838}
1839
1841{
1842 return m_type == kMythBufferDVD;
1843}
1844
1846{
1847 return m_type == kMythBufferBD;
1848}
1849
1851{
1852 return dynamic_cast<const MythDVDBuffer*>(this);
1853}
1854
1856{
1857 return dynamic_cast<const MythBDBuffer*>(this);
1858}
1859
1861{
1862 return dynamic_cast<MythDVDBuffer*>(this);
1863}
1864
1866{
1867 return dynamic_cast<MythBDBuffer*>(this);
1868}
1869
1871{
1872 static QRecursiveMutex s_avnetworkLock;
1873 static bool s_avnetworkInitialised = false;
1874 QMutexLocker lock(&s_avnetworkLock);
1875 if (!s_avnetworkInitialised)
1876 {
1877 avformat_network_init();
1878 s_avnetworkInitialised = true;
1879 }
1880}
#define assert(x)
static bool TestForHTTPLiveStreaming(const QString &filename)
Keeps track of recordings in a current LiveTV instance.
Definition: livetvchain.h:33
bool HasNext(void) const
void SwitchToNext(bool up)
Sets the recording to switch to.
void ReloadAll(const QStringList &data=QStringList())
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:49
bool isRunning(void) const
Definition: mthread.cpp:263
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:196
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:283
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:209
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:300
A class to allow a MythMediaBuffer to read from BDs.
Definition: mythbdbuffer.h:20
static ImageType inspectImage(const QString &path)
Definition: mythcdrom.cpp:188
bool IsRegisteredFileForWrite(const QString &file)
long long GetRealFileSize(void) const
QString GetStorageRate(void)
void Reset(bool Full=false, bool ToAdjust=false, bool ResetInternal=false)
Resets the read-ahead thread and our position in the file.
MythBufferType GetType() const
void ResetCommsError(void)
virtual long long GetRealFileSizeInternal(void) const
void KillReadAheadThread(void)
Stops the read-ahead thread, and waits for it to stop.
long long GetWritePosition(void) const
Returns how far into a ThreadedFileWriter file we have written.
bool GetCommsError(void) const
void SetLiveMode(LiveTVChain *Chain)
Assigns a LiveTVChain to this RingBuffer.
void UpdateRawBitrate(uint RawBitrate)
Set the raw bit rate, to allow RingBuffer adjust effective bitrate.
long long m_internalReadPos
void IgnoreLiveEOF(bool Ignore)
Tells RingBuffer whether to ignore the end-of-file.
QReadWriteLock m_rbwLock
int ReadDirect(void *Buffer, int Count, bool Peek)
int Peek(void *Buffer, int Count)
long long m_ignoreReadPos
bool WriterSetBlocking(bool Lock=true)
Calls ThreadedFileWriter::SetBlocking(bool)
QString GetSafeFilename(void)
QString GetLastError(void) const
bool WaitForReadsAllowed(void)
QReadWriteLock m_rbrLock
bool IsDisc(void) const
void SetBufferSizeFactors(bool EstBitrate, bool Matroska)
Tells RingBuffer that the raw bitrate may be inaccurate and the underlying container is matroska,...
MythMediaBuffer(MythBufferType Type)
void EnableBitrateMonitor(bool Enable)
MythBufferType m_type
LiveTVChain * m_liveTVChain
void run(void) override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
void SetOldFile(bool Old)
Tell RingBuffer if this is an old file or not.
int ReadBufFree(void) const
Returns number of bytes available for reading into buffer.
uint64_t UpdateDecoderRate(uint64_t Latest=0)
int ReadPriv(void *Buffer, int Count, bool Peek)
When possible reads from the read-ahead buffer, otherwise reads directly from the device.
void Pause(void)
Pauses the read-ahead thread.
volatile bool m_recentSeek
void Start(void)
Starts the read-ahead thread.
void WaitForPause(void)
Waits for Pause(void) to take effect.
QString m_subtitleFilename
volatile bool m_stopReads
uint GetBufferSize(void) const
void WriterFlush(void)
Calls ThreadedFileWriter::Flush(void)
uint64_t UpdateStorageRate(uint64_t Latest=0)
bool IsDVD(void) const
int ReadBufAvail(void) const
Returns number of bytes available for reading from buffer.
QString GetAvailableBuffer(void)
long long m_readAdjust
int Read(void *Buffer, int Count)
This is the public method for reading from a file, it calls the appropriate read method if the file i...
QString GetDecoderRate(void)
bool PauseAndWait(void)
int WaitForAvail(int Count, std::chrono::milliseconds Timeout)
virtual bool IsOpen(void) const =0
void Sync(void)
Calls ThreadedFileWriter::Sync(void)
void UpdatePlaySpeed(float PlaySpeed)
Set the play speed, to allow RingBuffer adjust effective bitrate.
bool SetReadInternalMode(bool Mode)
static MythMediaBuffer * Create(const QString &Filename, bool Write, bool UseReadAhead=true, std::chrono::milliseconds Timeout=kDefaultOpenTimeout, bool StreamOnly=false)
Creates a RingBuffer instance.
ThreadedFileWriter * m_tfw
RemoteFile * m_remotefile
void SetWaitForWrite(void)
QReadWriteLock m_posLock
long long Seek(long long Position, int Whence, bool HasLock=false)
QMap< std::chrono::milliseconds, uint64_t > m_decoderReads
long long WriterSeek(long long Position, int Whence, bool HasLock=false)
Calls ThreadedFileWriter::Seek(long long,int).
static QString BitrateToString(uint64_t Rate, bool Hz=false)
const MythDVDBuffer * DVD(void) const
bool GetStopReads(void) const
long long SetAdjustFilesize(void)
int Write(const void *Buffer, uint Count)
Writes buffer to ThreadedFileWriter::Write(const void*,uint)
const MythBDBuffer * BD(void) const
void CreateReadAheadBuffer(void)
void Unpause(void)
Unpauses the read-ahead thread.
~MythMediaBuffer() override=0
Deletes.
virtual long long SeekInternal(long long Position, int Whence)=0
static void AVFormatInitNetwork(void)
int GetReadBufAvail(void) const
Returns number of bytes available for reading from buffer.
QString GetSubtitleFilename(void) const
bool IsBD(void) const
long long m_writePos
QMap< std::chrono::milliseconds, uint64_t > m_storageReads
QReadWriteLock m_rwLock
bool IsReadInternalMode(void) const
bool IsNearEnd(double Framerate, uint Frames) const
void ResetReadAhead(long long NewInternal)
Restart the read-ahead thread at the 'newinternal' position.
virtual int SafeRead(void *Buffer, uint Size)=0
bool LiveMode(void) const
Returns true if this RingBuffer has been assigned a LiveTVChain.
void CalcReadAheadThresh(void)
Calculates m_fillMin, m_fillThreshold, and m_readBlockSize from the estimated effective bitrate of th...
QString GetFilename(void) const
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
@ kStartRunning
Definition: mythtimer.h:17
void start(void)
starts measuring elapsed time.
Definition: mythtimer.cpp:47
long long Seek(long long pos, int whence, long long curpos=-1)
Definition: remotefile.cpp:759
static bool Exists(const QString &url, struct stat *fileinfo)
Definition: remotefile.cpp:461
int Write(const void *data, int size)
Definition: remotefile.cpp:836
void Sync(void) const
Flush data written to the file descriptor to disk.
bool SetBlocking(bool block=true)
Set write blocking mode While in blocking mode, ThreadedFileWriter::Write will wait for buffers to be...
long long Seek(long long pos, int whence)
Seek to a position within stream; May be unsafe.
void Flush(void)
Allow DiskLoop() to flush buffer completely ignoring low watermark.
int Write(const void *data, uint count)
Writes data to the end of the write buffer.
unsigned int uint
Definition: freesurround.h:24
static guint32 * tmp
Definition: goom_core.cpp:26
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
#define LOC
static uint estbitrate_to_rbs(uint estbitrate)
MythBufferType
@ kMythBufferDVD
@ kMythBufferBD
static constexpr int32_t DEFAULT_CHUNK_SIZE
static constexpr uint32_t BUFFER_SIZE_MINIMUM
static constexpr uint8_t BUFFER_FACTOR_MATROSKA
static constexpr uint8_t BUFFER_FACTOR_BITRATE
static constexpr uint8_t BUFFER_FACTOR_NETWORK
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:15
bool exists(str path)
Definition: xbmcvfs.py:51
static eu8 clamp(eu8 value, eu8 low, eu8 high)
Definition: pxsup2dast.c:206
Mode
Definition: synaesthesia.h:23