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"
29
30#include "Bluray/mythbdbuffer.h"
31#include "DVD/mythdvdbuffer.h"
32#include "DVD/mythdvdstream.h"
34#include "io/mythfilebuffer.h"
35#include "io/mythmediabuffer.h"
37#include "livetvchain.h"
38
39// FFmpeg
40extern "C" {
41#include "libavformat/avformat.h"
42}
43
44#define LOC QString("RingBuf(%1): ").arg(m_filename)
45
46
47/*
48 Locking relations:
49 rwlock->poslock->rbrlock->rbwlock
50
51 A child should never lock any of the parents without locking
52 the parent lock before the child lock.
53 void MythMediaBuffer::Example1()
54 {
55 poslock.lockForWrite();
56 rwlock.lockForRead(); // error!
57 blah(); // <- does not implicitly acquire any locks
58 rwlock.unlock();
59 poslock.unlock();
60 }
61 void MythMediaBuffer::Example2()
62 {
63 rwlock.lockForRead();
64 rbrlock.lockForWrite(); // ok!
65 blah(); // <- does not implicitly acquire any locks
66 rbrlock.unlock();
67 rwlock.unlock();
68 }
69*/
70
98MythMediaBuffer *MythMediaBuffer::Create(const QString &Filename, bool Write,
99 bool UseReadAhead, std::chrono::milliseconds Timeout, bool StreamOnly)
100{
101 QString filename = Filename;
102 QString lower = filename.toLower();
103
104 if (Write)
105 return new MythFileBuffer(filename, Write, UseReadAhead, Timeout);
106
107 bool dvddir = false;
108 bool bddir = false;
109 bool httpurl = lower.startsWith("http://") || lower.startsWith("https://");
110 bool iptvurl = lower.startsWith("rtp://") || lower.startsWith("tcp://") ||
111 lower.startsWith("udp://");
112 bool mythurl = lower.startsWith("myth://");
113 bool bdurl = lower.startsWith("bd:");
114 bool dvdurl = lower.startsWith("dvd:");
115 bool imgext = lower.endsWith(".img") || lower.endsWith(".iso");
116
117 if (imgext)
118 {
120 {
122 bdurl = true;
123 break;
124 case MythCDROM::kDVD:
125 dvdurl = true;
126 break;
127 default: break;
128 }
129 }
130
131 if (httpurl || iptvurl)
132 {
134 return new HLSRingBuffer(filename);
135 return new MythStreamingBuffer(filename);
136 }
137 if (!StreamOnly && mythurl)
138 {
139 struct stat fileInfo {};
140 if ((RemoteFile::Exists(filename, &fileInfo)) &&
141 (S_ISDIR(fileInfo.st_mode)))
142 {
143 if (RemoteFile::Exists(filename + "/VIDEO_TS"))
144 dvddir = true;
145 else if (RemoteFile::Exists(filename + "/BDMV"))
146 bddir = true;
147 }
148 }
149 else if (!StreamOnly && !mythurl)
150 {
151 if (QFile::exists(filename + "/VIDEO_TS"))
152 dvddir = true;
153 else if (QFile::exists(filename + "/BDMV"))
154 bddir = true;
155 }
156
157 if (!StreamOnly && (dvdurl || dvddir))
158 {
159 if (filename.startsWith("dvd:")) // URI "dvd:" + path
160 filename.remove(0,4); // e.g. "dvd:/dev/dvd"
161
162 if (!(mythurl || QFile::exists(filename)))
163 filename = "/dev/dvd";
164 LOG(VB_PLAYBACK, LOG_INFO, "Trying DVD at " + filename);
165 return new MythDVDBuffer(filename);
166 }
167
168 if (!StreamOnly && (bdurl || bddir))
169 {
170 if (filename.startsWith("bd:")) // URI "bd:" + path
171 filename.remove(0,3); // e.g. "bd:/videos/ET"
172
173 if (!(mythurl || QFile::exists(filename)))
174 filename = "/dev/dvd";
175 LOG(VB_PLAYBACK, LOG_INFO, "Trying BD at " + filename);
176 return new MythBDBuffer(filename);
177 }
178
179 if (!mythurl && imgext && filename.startsWith("dvd:"))
180 {
181 LOG(VB_PLAYBACK, LOG_INFO, "DVD image at " + filename);
182 return new MythDVDStream(filename);
183 }
184
185 if (!mythurl && lower.endsWith(".vob") && filename.contains("/VIDEO_TS/"))
186 {
187 LOG(VB_PLAYBACK, LOG_INFO, "DVD VOB at " + filename);
188 auto *dvdstream = new MythDVDStream(filename);
189 if (dvdstream && dvdstream->IsOpen())
190 return dvdstream;
191 delete dvdstream;
192 }
193
194 return new MythFileBuffer(filename, Write, UseReadAhead, Timeout);
195}
196
198 : MThread("RingBuffer"),
199 m_type(Type)
200{
201}
202
204{
205 return m_type;
206}
207
208#undef NDEBUG
209#include <cassert>
210
219{
220 assert(!isRunning());
221 wait();
222
223 delete [] m_readAheadBuffer;
224 m_readAheadBuffer = nullptr;
225
226 if (m_tfw)
227 {
228 m_tfw->Flush();
229 delete m_tfw;
230 m_tfw = nullptr;
231 }
232}
233
237void MythMediaBuffer::Reset(bool Full, bool ToAdjust, bool ResetInternal)
238{
239 LOG(VB_FILE, LOG_INFO, LOC + QString("Reset(%1,%2,%3)")
240 .arg(Full).arg(ToAdjust).arg(ResetInternal));
241
242 m_rwLock.lockForWrite();
243 m_posLock.lockForWrite();
244
245 m_numFailures = 0;
246 m_commsError = false;
247 m_setSwitchToNext = false;
248
249 m_writePos = 0;
250 m_readPos = (ToAdjust) ? (m_readPos - m_readAdjust) : 0;
251
252 if (m_readPos != 0)
253 {
254 LOG(VB_GENERAL, LOG_ERR, LOC +
255 QString("MythMediaBuffer::Reset() nonzero readpos. toAdjust: %1 "
256 "readpos: %2 readAdjust: %3")
257 .arg(ToAdjust).arg(m_readPos).arg(m_readAdjust));
258 }
259
260 m_readAdjust = 0;
261 m_readPos = (m_readPos < 0) ? 0 : m_readPos;
262
263 if (Full)
265
266 if (ResetInternal)
268
269 m_generalWait.wakeAll();
270 m_posLock.unlock();
271 m_rwLock.unlock();
272}
273
279{
280 LOG(VB_FILE, LOG_INFO, LOC + QString("UpdateRawBitrate(%1Kb)").arg(RawBitrate));
281
282 // an audio only stream could be as low as 64Kb (DVB radio) and
283 // an MHEG only stream is likely to be reported as 0Kb
284 if (RawBitrate < 64)
285 {
286 LOG(VB_FILE, LOG_INFO, LOC + QString("Bitrate too low - setting to 64Kb"));
287 RawBitrate = 64;
288 }
289 else if (RawBitrate > 100000)
290 {
291 LOG(VB_FILE, LOG_INFO, LOC + QString("Bitrate too high - setting to 100Mb"));
292 RawBitrate = 100000;
293 }
294
295 m_rwLock.lockForWrite();
296 m_rawBitrate = RawBitrate;
299 m_rwLock.unlock();
300}
301
307{
308 m_rwLock.lockForWrite();
309 m_playSpeed = PlaySpeed;
311 m_rwLock.unlock();
312}
313
315{
317}
318
320{
321 m_waitForWrite = true;
322}
323
329void MythMediaBuffer::SetBufferSizeFactors(bool EstBitrate, bool Matroska)
330{
331 m_rwLock.lockForWrite();
332 m_unknownBitrate = EstBitrate;
333 m_fileIsMatroska = Matroska;
334 m_rwLock.unlock();
336}
337
338static inline uint estbitrate_to_rbs(uint estbitrate)
339{
340 if (estbitrate > 18000)
341 return 512*1024;
342 if (estbitrate > 9000)
343 return 256*1024;
344 if (estbitrate > 5000)
345 return 128*1024;
346 if (estbitrate > 2500)
347 return 64*1024;
348 if (estbitrate > 1250)
349 return 32*1024;
350 if (estbitrate >= 500)
351 return 16*1024;
352 if (estbitrate > 250)
353 return 8*1024;
354 if (estbitrate > 125)
355 return 4*1024;
356 return 2*1024;
357}
358
367{
368 uint estbitrate = 0;
369
370 m_readsAllowed = false;
371 m_readsDesired = false;
372
373 // loop without sleeping if the buffered data is less than this
375
376 estbitrate = static_cast<uint>(std::max(std::fabs(m_rawBitrate * m_playSpeed),
377 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(std::fabs(m_rawBitrate * m_playSpeed),
427 0.5F * m_rawBitrate));
428 uint kbitspersec = std::min(m_rawBitrate * 3, tmp);
429 if (kbitspersec == 0)
430 return false;
431
432 double readahead_time = size / (kbitspersec * (1000.0 / 8.0));
433
434 bool near_end = readahead_time <= 1.5;
435 LOG(VB_PLAYBACK, LOG_INFO, LOC + "IsReallyNearEnd()" +
436 QString(" br(%1KB)").arg(kbitspersec/8) +
437 QString(" sz(%1KB)").arg(size / 1000LL) +
438 QString(" vfl(%1)").arg(Frames) +
439 QString(" time(%1)").arg(readahead_time) +
440 QString(" rawbitrate(%1)").arg(m_rawBitrate) +
441 QString(" avail(%1)").arg(size) +
442 QString(" internal_size(%1)").arg(m_internalReadPos) +
443 QString(" readposition(%1)").arg(readpos) +
444 QString(" stopreads(%1)").arg(m_stopReads) +
445 QString(" paused(%1)").arg(m_paused) +
446 QString(" ne:%1").arg(near_end));
447 return near_end;
448}
449
453{
454 m_rbrLock.lockForRead();
455 m_rbwLock.lockForRead();
456 int ret = ((m_rbwPos >= m_rbrPos) ? m_rbrPos + static_cast<int>(m_bufferSize) : m_rbrPos) - m_rbwPos - 1;
457 m_rbwLock.unlock();
458 m_rbrLock.unlock();
459 return ret;
460}
461
464{
465 QReadLocker lock(&m_rwLock);
466 return ReadBufAvail();
467}
468
470{
471 {
472 QReadLocker lock(&m_rwLock);
474 return ReadBufAvail();
475 }
476
478}
479
480long long MythMediaBuffer::Seek(long long Position, int Whence, bool HasLock)
481{
482 LOG(VB_FILE, LOG_INFO, LOC + QString("Seek: Position:%1 Type: %2 Locked: %3)")
483 .arg(Position)
484 .arg((SEEK_SET == Whence) ? "SEEK_SET" : ((SEEK_CUR == Whence) ? "SEEK_CUR" : "SEEK_END"),
485 HasLock?"locked":"unlocked"));
486
487 if (!HasLock)
488 m_rwLock.lockForWrite();
489
490 long long ret = 0;
491
493 {
494 m_posLock.lockForWrite();
495 // only valid for SEEK_SET & SEEK_CUR
496 switch (Whence)
497 {
498 case SEEK_SET:
499 m_readPos = Position;
500 break;
501 case SEEK_CUR:
502 m_readPos += Position;
503 break;
504 case SEEK_END:
505 m_readPos = ReadBufAvail() - Position;
506 break;
507 }
508 m_readOffset = static_cast<int>(m_readPos);
509 m_posLock.unlock();
510 ret = m_readPos;
511 }
512 else
513 {
514 ret = SeekInternal(Position, Whence);
515 }
516
517 m_generalWait.wakeAll();
518
519 if (!HasLock)
520 m_rwLock.unlock();
521 return ret;
522}
523
525{
526 QWriteLocker lock(&m_rwLock);
527 bool old = m_readInternalMode;
528
529 if (Mode == old)
530 return old;
531
533
534 if (!Mode)
535 {
536 // adjust real read position in ringbuffer
537 m_rbrLock.lockForWrite();
538 m_rbrPos = (m_rbrPos + m_readOffset) % static_cast<int>(m_bufferSize);
539 m_generalWait.wakeAll();
540 m_rbrLock.unlock();
541 // reset the read offset as we are exiting the internal read mode
542 m_readOffset = 0;
543 }
544
545 LOG(VB_FILE, LOG_DEBUG, LOC + QString("SetReadInternalMode: %1").arg(Mode ? "on" : "off"));
546 return old;
547}
548
550{
551 return m_readInternalMode;
552}
553
557{
558 m_rbrLock.lockForRead();
559 m_rbwLock.lockForRead();
560 int ret = (m_rbwPos >= m_rbrPos) ? m_rbwPos - m_rbrPos : static_cast<int>(m_bufferSize) - m_rbrPos + m_rbwPos;
561 m_rbwLock.unlock();
562 m_rbrLock.unlock();
563 return ret;
564}
565
576void MythMediaBuffer::ResetReadAhead(long long NewInternal)
577{
578 LOG(VB_FILE, LOG_INFO, LOC + QString("ResetReadAhead(internalreadpos = %1->%2)")
579 .arg(m_internalReadPos).arg(NewInternal));
580
581 m_readInternalMode = false;
582 m_readOffset = 0;
583
584 m_rbrLock.lockForWrite();
585 m_rbwLock.lockForWrite();
586
588
589 m_rbrPos = 0;
590 m_rbwPos = 0;
591 m_internalReadPos = NewInternal;
592 m_ateof = false;
593 m_readsAllowed = false;
594 m_readsDesired = false;
595 m_recentSeek = true;
596 m_setSwitchToNext = false;
597
598 m_generalWait.wakeAll();
599
600 m_rbwLock.unlock();
601 m_rbrLock.unlock();
602}
603
619{
620 bool dostart = true;
621
622 m_rwLock.lockForWrite();
623 if (!m_startReadAhead)
624 {
625 dostart = false;
626 }
627 else if (m_writeMode)
628 {
629 LOG(VB_GENERAL, LOG_WARNING, LOC + "Not starting read ahead thread - write only RingBuffer");
630 dostart = false;
631 }
632 else if (m_readAheadRunning)
633 {
634 LOG(VB_GENERAL, LOG_WARNING, LOC + "Not starting read ahead thread - already running");
635 dostart = false;
636 }
637
638 if (!dostart)
639 {
640 m_rwLock.unlock();
641 return;
642 }
643
644 StartReads();
647 m_generalWait.wait(&m_rwLock);
648 m_rwLock.unlock();
649}
650
655{
656 while (isRunning())
657 {
658 m_rwLock.lockForWrite();
659 m_readAheadRunning = false;
660 StopReads();
661 m_generalWait.wakeAll();
662 m_rwLock.unlock();
663 MThread::wait(5s);
664 }
665}
666
671{
672 LOG(VB_FILE, LOG_INFO, LOC + "StopReads()");
673 m_stopReads = true;
674 m_generalWait.wakeAll();
675}
676
681{
682 LOG(VB_FILE, LOG_INFO, LOC + "StartReads()");
683 m_stopReads = false;
684 m_generalWait.wakeAll();
685}
686
692{
693 LOG(VB_FILE, LOG_INFO, LOC + "Pausing read ahead thread");
694 StopReads();
695
696 m_rwLock.lockForWrite();
697 m_requestPause = true;
698 m_rwLock.unlock();
699}
700
706{
707 LOG(VB_FILE, LOG_INFO, LOC + "Unpausing readahead thread");
708 StartReads();
709
710 m_rwLock.lockForWrite();
711 m_requestPause = false;
712 m_generalWait.wakeAll();
713 m_rwLock.unlock();
714}
715
720{
721 MythTimer t;
722 t.start();
723
724 m_rwLock.lockForRead();
726 {
727 m_generalWait.wait(&m_rwLock, 1000);
728 if (m_readAheadRunning && !m_paused && m_requestPause && t.elapsed() > 1s)
729 LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Waited %1 ms for ringbuffer pause")
730 .arg(t.elapsed().count()));
731 }
732 m_rwLock.unlock();
733}
734
736{
737 const uint timeout = 500; // ms
738
739 if (m_requestPause)
740 {
741 if (!m_paused)
742 {
743 m_rwLock.unlock();
744 m_rwLock.lockForWrite();
745
746 if (m_requestPause)
747 {
748 m_paused = true;
749 m_generalWait.wakeAll();
750 }
751
752 m_rwLock.unlock();
753 m_rwLock.lockForRead();
754 }
755
758 }
759
760 if (!m_requestPause && m_paused)
761 {
762 m_rwLock.unlock();
763 m_rwLock.lockForWrite();
764
765 if (!m_requestPause)
766 {
767 m_paused = false;
768 m_generalWait.wakeAll();
769 }
770
771 m_rwLock.unlock();
772 m_rwLock.lockForRead();
773 }
774
775 return m_requestPause || m_paused;
776}
777
779{
780 m_rwLock.lockForWrite();
781 m_posLock.lockForWrite();
782
783 uint oldsize = m_bufferSize;
784 uint newsize = BUFFER_SIZE_MINIMUM;
785 if (m_remotefile)
786 {
787 newsize *= BUFFER_FACTOR_NETWORK;
789 newsize *= BUFFER_FACTOR_MATROSKA;
791 newsize *= BUFFER_FACTOR_BITRATE;
792 }
793
794 // N.B. Don't try and make it smaller - bad things happen...
795 if (m_readAheadBuffer && (oldsize >= newsize))
796 {
797 m_posLock.unlock();
798 m_rwLock.unlock();
799 return;
800 }
801
802 m_bufferSize = newsize;
804 {
805 char* newbuffer = new char[m_bufferSize + 1024];
806 memcpy(newbuffer, m_readAheadBuffer + m_rbwPos, oldsize - static_cast<uint>(m_rbwPos));
807 memcpy(newbuffer + (oldsize - static_cast<uint>(m_rbwPos)), m_readAheadBuffer, static_cast<uint>(m_rbwPos));
808 delete [] m_readAheadBuffer;
809 m_readAheadBuffer = newbuffer;
811 (m_rbrPos + static_cast<int>(oldsize) - m_rbwPos);
812 m_rbwPos = static_cast<int>(oldsize);
813 }
814 else
815 {
816 m_readAheadBuffer = new char[m_bufferSize + 1024];
817 }
819 m_posLock.unlock();
820 m_rwLock.unlock();
821
822 LOG(VB_FILE, LOG_INFO, LOC + QString("Created readAheadBuffer: %1Mb")
823 .arg(newsize >> 20));
824}
825
827{
828 RunProlog();
829
830 // These variables are used to adjust the read block size
831 std::chrono::milliseconds readTimeAvg = 300ms;
832 bool ignoreForReadTiming = true;
833 int eofreads = 0;
834
835 auto lastread = nowAsDuration<std::chrono::milliseconds>();
836
838 m_rwLock.lockForWrite();
839 m_posLock.lockForWrite();
840 m_requestPause = false;
842 m_readAheadRunning = true;
843 m_reallyRunning = true;
844 m_generalWait.wakeAll();
845 m_posLock.unlock();
846 m_rwLock.unlock();
847
848 // NOTE: this must loop at some point hold only
849 // a read lock on rwlock, so that other functions
850 // such as reset and seek can take priority.
851
852 m_rwLock.lockForRead();
853
854 LOG(VB_FILE, LOG_INFO, LOC + QString("Initial readblocksize %1K fillMin %2K")
855 .arg(m_readBlockSize/1024).arg(m_fillMin/1024));
856
857 while (m_readAheadRunning)
858 {
859 m_rwLock.unlock();
860 bool isopened = IsOpen();
861 m_rwLock.lockForRead();
862
863 if (!isopened)
864 {
865 LOG(VB_FILE, LOG_WARNING, LOC + QString("File not opened, terminating readahead thread"));
866 m_posLock.lockForWrite();
867 m_readAheadRunning = false;
868 m_generalWait.wakeAll();
869 m_posLock.unlock();
870 break;
871 }
872 if (PauseAndWait())
873 {
874 ignoreForReadTiming = true;
875 LOG(VB_FILE, LOG_DEBUG, LOC + "run: PauseAndWait Not reading continuing");
876 continue;
877 }
878
879 long long totfree = ReadBufFree();
880
881 const uint KB32 = 32*1024;
882 const int KB512 = 512*1024;
883 // These are conditions where we don't want to go through
884 // the loop if they are true.
885 if (((totfree < KB32) && m_readsAllowed) ||
887 {
888 ignoreForReadTiming |= (m_ignoreReadPos >= 0) || m_commsError || m_stopReads;
889 m_generalWait.wait(&m_rwLock, (m_stopReads) ? 50 : 1000);
890 LOG(VB_FILE, LOG_DEBUG, LOC +
891 QString("run: Not reading continuing: totfree(%1) "
892 "readsallowed(%2) ignorereadpos(%3) commserror(%4) "
893 "stopreads(%5)")
894 .arg(totfree).arg(m_readsAllowed).arg(m_ignoreReadPos)
895 .arg(m_commsError).arg(m_stopReads));
896 continue;
897 }
898
899 // These are conditions where we want to sleep to allow
900 // other threads to do stuff.
902 {
903 ignoreForReadTiming = true;
904 m_generalWait.wait(&m_rwLock, 1000);
905 totfree = ReadBufFree();
906 }
907
908 int readResult = -1;
909 if (totfree >= KB32 && !m_commsError && !m_ateof && !m_setSwitchToNext)
910 {
911 // limit the read size
912 if (m_readBlockSize > totfree)
913 totfree = (totfree / KB32) * KB32; // must be multiple of 32KB
914 else
915 totfree = m_readBlockSize;
916
917 // adapt blocksize
918 auto now = nowAsDuration<std::chrono::milliseconds>();
919 if (!ignoreForReadTiming)
920 {
921 readTimeAvg = (readTimeAvg * 9 + (now - lastread)) / 10;
922
923 if (readTimeAvg < 150ms &&
924 static_cast<uint>(m_readBlockSize) < (BUFFER_SIZE_MINIMUM >>2) &&
925 m_readBlockSize >= DEFAULT_CHUNK_SIZE /* low_buffers */ &&
926 m_readBlockSize <= KB512)
927 {
928 int old_block_size = m_readBlockSize;
931 m_readBlockSize = std::min(m_readBlockSize, KB512);
932 LOG(VB_FILE, LOG_INFO, LOC + QString("Avg read interval was %1 msec. "
933 "%2K -> %3K block size")
934 .arg(readTimeAvg.count()).arg(old_block_size/1024).arg(m_readBlockSize/1024));
935 readTimeAvg = 225ms;
936 }
937 else if (readTimeAvg > 300ms && m_readBlockSize > DEFAULT_CHUNK_SIZE)
938 {
940 LOG(VB_FILE, LOG_INFO, LOC +
941 QString("Avg read interval was %1 msec. %2K -> %3K block size")
942 .arg(readTimeAvg.count())
944 .arg(m_readBlockSize/1024));
945 readTimeAvg = 225ms;
946 }
947 }
948 lastread = now;
949
950 m_rbwLock.lockForRead();
951 if (m_rbwPos + totfree > m_bufferSize)
952 {
953 totfree = m_bufferSize - static_cast<uint>(m_rbwPos);
954 LOG(VB_FILE, LOG_DEBUG, LOC + "Shrinking read, near end of buffer");
955 }
956
957 if (m_internalReadPos == 0)
958 {
959 totfree = std::max(m_fillMin, m_readBlockSize);
960 LOG(VB_FILE, LOG_DEBUG, LOC + "Reading enough data to start playback");
961 }
962
963 LOG(VB_FILE, LOG_DEBUG, LOC + QString("safe_read(...@%1, %2) -- begin")
964 .arg(m_rbwPos).arg(totfree));
965
966 MythTimer sr_timer;
967 sr_timer.start();
968
969 int rbwposcopy = m_rbwPos;
970
971 // MythFileBuffer::SafeRead(RemoteFile*...) acquires poslock;
972 // so we need to unlock this here to preserve locking order.
973 m_rbwLock.unlock();
974
975 readResult = SafeRead(m_readAheadBuffer + rbwposcopy, static_cast<uint>(totfree));
976
977 int sr_elapsed = sr_timer.elapsed().count();
978 uint64_t bps = !sr_elapsed ? 1000000001 :
979 static_cast<uint64_t>((readResult * 8000.0) / static_cast<double>(sr_elapsed));
980 LOG(VB_FILE, LOG_DEBUG, LOC +
981 QString("safe_read(...@%1, %2) -> %3, took %4 ms %5 avg %6 ms")
982 .arg(rbwposcopy).arg(totfree).arg(readResult).arg(sr_elapsed)
983 .arg(QString("(%1Mbps)").arg(static_cast<double>(bps) / 1000000.0))
984 .arg(readTimeAvg.count()));
986
987 if (readResult >= 0)
988 {
989 m_posLock.lockForWrite();
990 m_rbwLock.lockForWrite();
991
992 if (rbwposcopy == m_rbwPos)
993 {
994 m_internalReadPos += readResult;
995 m_rbwPos = (m_rbwPos + readResult) % static_cast<int>(m_bufferSize);
996 LOG(VB_FILE, LOG_DEBUG, LOC + QString("rbwpos += %1K requested %2K in read")
997 .arg(readResult/1024,3).arg(totfree/1024,3));
998 }
999 m_numFailures = 0;
1000
1001 m_rbwLock.unlock();
1002 m_posLock.unlock();
1003
1004 LOG(VB_FILE, LOG_DEBUG, LOC + QString("total read so far: %1 bytes")
1005 .arg(m_internalReadPos));
1006 }
1007 }
1008 else
1009 {
1010 LOG(VB_FILE, LOG_DEBUG, LOC +
1011 QString("We are not reading anything (totfree: %1 commserror:%2 ateof:%3 setswitchtonext:%4")
1012 .arg(totfree).arg(m_commsError).arg(m_ateof).arg(m_setSwitchToNext));
1013 }
1014
1015 int used = static_cast<int>(m_bufferSize) - ReadBufFree();
1016 bool readsWereAllowed = m_readsAllowed;
1017
1018 ignoreForReadTiming = (totfree < m_readBlockSize) || (readResult < totfree);
1019
1020 if ((0 == readResult) || (m_numFailures > 5) ||
1021 (m_readsAllowed != (used >= 1 || m_ateof || m_setSwitchToNext || m_commsError)) ||
1023 {
1024 // If readpos changes while the lock is released
1025 // we should not handle the 0 read_return now.
1026 long long oldreadposition = m_readPos;
1027
1028 m_rwLock.unlock();
1029 m_rwLock.lockForWrite();
1030
1031 m_commsError |= (m_numFailures > 5);
1032
1035
1036 if ((0 == readResult) && (oldreadposition == m_readPos))
1037 {
1038 eofreads++;
1039 if (eofreads >= 3 && m_readBlockSize >= KB512)
1040 {
1041 // not reading anything
1044 }
1045
1046 if (m_liveTVChain)
1047 {
1049 {
1050 // we receive new livetv chain element event
1051 // before we receive file closed for writing event
1052 // so don't need to test if file is closed for writing
1054 m_setSwitchToNext = true;
1055 }
1057 {
1058 LOG(VB_FILE, LOG_DEBUG, LOC +
1059 QString("EOF encountered, but %1 still being written to")
1060 .arg(m_filename));
1061 // We reached EOF, but file still open for writing and
1062 // no next program in livetvchain
1063 // wait a little bit (60ms same wait as typical safe_read)
1064 m_generalWait.wait(&m_rwLock, 60);
1065 }
1066 }
1068 {
1069 LOG(VB_FILE, LOG_DEBUG, LOC +
1070 QString("EOF encountered, but %1 still being written to")
1071 .arg(m_filename));
1072 // We reached EOF, but file still open for writing,
1073 // typically active in-progress recording
1074 // wait a little bit (60ms same wait as typical safe_read)
1075 m_generalWait.wait(&m_rwLock, 60);
1076 m_beingWritten = true;
1077 }
1078 else
1079 {
1081 {
1082 LOG(VB_FILE, LOG_DEBUG, LOC + "Waiting for file to grow large enough to process.");
1083 m_generalWait.wait(&m_rwLock, 300);
1084 }
1085 else
1086 {
1087 LOG(VB_FILE, LOG_DEBUG, LOC + "setting ateof (readResult == 0)");
1088 m_ateof = true;
1089 }
1090 }
1091 }
1092
1093 m_rwLock.unlock();
1094 m_rwLock.lockForRead();
1095 used = static_cast<int>(m_bufferSize) - ReadBufFree();
1096 }
1097 else
1098 {
1099 eofreads = 0;
1100 }
1101
1102 LOG(VB_FILE, LOG_DEBUG, LOC + "@ end of read ahead loop");
1103
1105 (m_wantToRead <= used && m_wantToRead > 0))
1106 {
1107 // To give other threads a good chance to handle these
1108 // conditions, even if they are only requesting a read lock
1109 // like us, yield (currently implemented with short
1110 // std::this_thread::sleep_for).
1111 m_generalWait.wakeAll();
1112 m_rwLock.unlock();
1113 std::this_thread::sleep_for(5ms);
1114 m_rwLock.lockForRead();
1115 }
1116 else
1117 {
1118 // yield if we have nothing to do...
1119 if (!m_requestPause && readsWereAllowed &&
1121 {
1122 m_generalWait.wait(&m_rwLock, 50);
1123 }
1124 else if (m_readsAllowed)
1125 {
1126 // if reads are allowed release the lock and yield so the
1127 // reader gets a chance to read before the buffer is full.
1128 m_generalWait.wakeAll();
1129 m_rwLock.unlock();
1130 std::this_thread::sleep_for(5ms);
1131 m_rwLock.lockForRead();
1132 }
1133 }
1134 }
1135
1136 m_rwLock.unlock();
1137
1138 m_rwLock.lockForWrite();
1139 m_rbrLock.lockForWrite();
1140 m_rbwLock.lockForWrite();
1141
1142 delete [] m_readAheadBuffer;
1143
1144 m_readAheadBuffer = nullptr;
1145 m_rbrPos = 0;
1146 m_rbwPos = 0;
1147 m_reallyRunning = false;
1148 m_readsAllowed = false;
1149 m_readsDesired = false;
1150
1151 m_rbwLock.unlock();
1152 m_rbrLock.unlock();
1153 m_rwLock.unlock();
1154
1155 LOG(VB_FILE, LOG_INFO, LOC + QString("Exiting readahead thread"));
1156
1157 RunEpilog();
1158}
1159
1161{
1162 m_rwLock.lockForWrite();
1163 m_posLock.lockForRead();
1165 long long readadjust = m_readAdjust;
1166 m_posLock.unlock();
1167 m_rwLock.unlock();
1168 return readadjust;
1169}
1170
1172int MythMediaBuffer::Peek(void *Buffer, int Count)
1173{
1174 int result = ReadPriv(Buffer, Count, true);
1175 if (result != Count)
1176 {
1177 LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Peek requested %1 bytes but only have %2")
1178 .arg(Count).arg(result));
1179 }
1180 return result;
1181}
1182
1183int MythMediaBuffer::Peek(std::vector<char>& Buffer)
1184{
1185 return Peek(Buffer.data(), Buffer.size());
1186};
1187
1189{
1190 // Wait up to 30000 ms for reads allowed (or readsdesired if post seek/open)
1192 m_recentSeek = false;
1193 std::chrono::milliseconds timeoutms = 30s;
1194 int count = 0;
1195 MythTimer timer;
1196 timer.start();
1197
1198 while ((timer.elapsed() < timeoutms) && !check && !m_stopReads &&
1200 {
1201 std::chrono::milliseconds delta = clamp(timeoutms - timer.elapsed(), 10ms, 100ms);
1202 m_generalWait.wait(&m_rwLock, delta.count());
1203 if (!check && timer.elapsed() > 1s && (count % 100) == 0)
1204 LOG(VB_GENERAL, LOG_WARNING, LOC + "Taking too long to be allowed to read..");
1205 count++;
1206 }
1207
1208 if (timer.elapsed() >= timeoutms)
1209 {
1210 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Took more than %1 seconds to be allowed to read, aborting.")
1211 .arg(duration_cast<std::chrono::seconds>(timeoutms).count()));
1212 return false;
1213 }
1214 return check;
1215}
1216
1217int MythMediaBuffer::WaitForAvail(int Count, std::chrono::milliseconds Timeout)
1218{
1219 int available = ReadBufAvail();
1220 if (available >= Count)
1221 return available;
1222
1223 if (m_ateof)
1224 {
1225 m_wantToRead = 0;
1226 return available;
1227 }
1228
1230 return available;
1231
1232 // Make sure that if the read ahead thread is sleeping and
1233 // it should be reading that we start reading right away.
1235 m_generalWait.wakeAll();
1236
1237 MythTimer timer;
1238 timer.start();
1239 while ((available < Count) && !m_stopReads && !m_requestPause && !m_commsError && m_readAheadRunning)
1240 {
1241 m_wantToRead = Count;
1242 std::chrono::milliseconds delta = clamp(Timeout - timer.elapsed(), 10ms, 250ms);
1243 m_generalWait.wait(&m_rwLock, delta.count());
1244 available = ReadBufAvail();
1245 if (m_ateof)
1246 break;
1247 if (m_lowBuffers && available >= m_fillMin)
1248 break;
1249 if (timer.elapsed() > Timeout)
1250 break;
1251 }
1252
1253 m_wantToRead = 0;
1254 return available;
1255}
1256
1257int MythMediaBuffer::ReadDirect(void *Buffer, int Count, bool Peek)
1258{
1259 long long oldposition = 0;
1260 if (Peek)
1261 {
1262 m_posLock.lockForRead();
1263 oldposition = (m_ignoreReadPos >= 0) ? m_ignoreReadPos : m_readPos;
1264 m_posLock.unlock();
1265 }
1266
1267 MythTimer timer;
1268 timer.start();
1269 int result = SafeRead(Buffer, static_cast<uint>(Count));
1270 int elapsed = timer.elapsed().count();
1271 uint64_t bps = !elapsed ? 1000000001 : static_cast<uint64_t>((result * 8000.0) / static_cast<double>(elapsed));
1272 UpdateStorageRate(bps);
1273
1274 m_posLock.lockForWrite();
1275 if (m_ignoreReadPos >= 0 && result > 0)
1276 {
1277 if (Peek)
1278 {
1279 // seek should always succeed since we were at this position
1280 long long cur_pos = -1;
1281 if (m_remotefile)
1282 cur_pos = m_remotefile->Seek(oldposition, SEEK_SET);
1283 else if (m_fd2 >= 0)
1284 cur_pos = lseek(m_fd2, oldposition, SEEK_SET);
1285 if (cur_pos < 0)
1286 {
1287 LOG(VB_FILE, LOG_ERR, LOC + "Seek failed repositioning to previous position");
1288 }
1289 }
1290 else
1291 {
1292 m_ignoreReadPos += result;
1293 }
1294 m_posLock.unlock();
1295 return result;
1296 }
1297 m_posLock.unlock();
1298
1299 if (Peek && (result > 0))
1300 {
1301 if ((IsDVD() || IsBD()) && oldposition != 0)
1302 {
1303 LOG(VB_GENERAL, LOG_ERR, LOC +
1304 "DVD and Blu-Ray do not support arbitrary "
1305 "peeks except when read-ahead is enabled."
1306 "\n\t\t\tWill seek to beginning of video.");
1307 oldposition = 0;
1308 }
1309
1310 long long newposition = Seek(oldposition, SEEK_SET, true);
1311
1312 if (newposition != oldposition)
1313 {
1314 LOG(VB_GENERAL, LOG_ERR, LOC +
1315 QString("Peek() Failed to return from new position %1 to old position %2, now "
1316 "at position %3")
1317 .arg(oldposition - result).arg(oldposition).arg(newposition));
1318 }
1319 }
1320
1321 return result;
1322}
1323
1332int MythMediaBuffer::ReadPriv(void *Buffer, int Count, bool Peek)
1333{
1334 QString desc = QString("ReadPriv(..%1, %2)").arg(Count).arg(Peek ? "peek" : "normal");
1335 LOG(VB_FILE, LOG_DEBUG, LOC + desc + QString(" @%1 -- begin").arg(m_rbrPos));
1336
1337 m_rwLock.lockForRead();
1338 if (m_writeMode)
1339 {
1340 LOG(VB_GENERAL, LOG_ERR, LOC + desc + ": Attempt to read from a write only file");
1341 errno = EBADF;
1342 m_rwLock.unlock();
1343 return -1;
1344 }
1345
1347 {
1348 m_rwLock.unlock();
1349 m_rwLock.lockForWrite();
1350 // we need a write lock so the read-ahead thread
1351 // can't start mucking with the read position.
1352 // If the read ahead thread was started while we
1353 // didn't hold the lock, we proceed with a normal
1354 // read from the buffer, otherwise we read directly.
1356 {
1357 int result = ReadDirect(Buffer, Count, Peek);
1358#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1359 quint16 checksum = qChecksum(reinterpret_cast<char*>(Buffer), static_cast<uint>(Count));
1360#else
1361 QByteArrayView BufferView(reinterpret_cast<char*>(Buffer), Count);
1362 quint16 checksum = qChecksum(BufferView);
1363#endif
1364 LOG(VB_FILE, LOG_DEBUG, LOC + desc + QString(": ReadDirect checksum %1")
1365 .arg(checksum));
1366 m_rwLock.unlock();
1367 return result;
1368 }
1369 m_rwLock.unlock();
1370 m_rwLock.lockForRead();
1371 }
1372
1373 if (!WaitForReadsAllowed())
1374 {
1375 LOG(VB_FILE, LOG_NOTICE, LOC + desc + ": !WaitForReadsAllowed()");
1376 m_rwLock.unlock();
1377 m_stopReads = true; // this needs to be outside the lock
1378 m_rwLock.lockForWrite();
1379 m_wantToRead = 0;
1380 m_rwLock.unlock();
1381 return 0;
1382 }
1383
1384 int available = ReadBufAvail();
1386
1387 // Wait up to 10000 ms for any data
1388 std::chrono::milliseconds timeout_ms = 10s;
1389 while (!m_readInternalMode && !m_ateof && (timer.elapsed() < timeout_ms) && m_readAheadRunning &&
1391 {
1392 available = WaitForAvail(Count, std::min(timeout_ms - timer.elapsed(), 100ms));
1393 if (m_liveTVChain && m_setSwitchToNext && available < Count)
1394 {
1395 LOG(VB_GENERAL, LOG_INFO, LOC + "Checking to see if there's a new livetv program to switch to..");
1397 break;
1398 }
1399 if (available > 0)
1400 break;
1401 }
1402 if (timer.elapsed() > 6s)
1403 {
1404 LOG(VB_GENERAL, LOG_WARNING, LOC + desc + QString(" -- waited %1 ms for avail(%2) > count(%3)")
1405 .arg(timer.elapsed().count()).arg(available).arg(Count));
1406 }
1407
1409 {
1410 LOG(VB_FILE, LOG_DEBUG, LOC + QString("ReadPriv: %1 bytes available, %2 left")
1411 .arg(available).arg(available-m_readOffset));
1412 }
1413 Count = std::min(available - m_readOffset, Count);
1414
1415 if ((Count <= 0) && (m_ateof || m_readInternalMode))
1416 {
1417 // If we're at the end of file return 0 bytes
1418 m_rwLock.unlock();
1419 return Count;
1420 }
1421
1422 if (Count <= 0)
1423 {
1424 // If we're not at the end of file but have no data
1425 // at this point time out and shutdown read ahead.
1426 LOG(VB_GENERAL, LOG_ERR, LOC + desc + QString(" -- timed out waiting for data (%1 ms)")
1427 .arg(timer.elapsed().count()));
1428
1429 m_rwLock.unlock();
1430 m_stopReads = true; // this needs to be outside the lock
1431 m_rwLock.lockForWrite();
1432 m_ateof = true;
1433 m_wantToRead = 0;
1434 m_generalWait.wakeAll();
1435 m_rwLock.unlock();
1436 return Count;
1437 }
1438
1439 if (Peek || m_readInternalMode)
1440 m_rbrLock.lockForRead();
1441 else
1442 m_rbrLock.lockForWrite();
1443
1444 LOG(VB_FILE, LOG_DEBUG, LOC + desc + ": Copying data");
1445
1446 int readposition = 0;
1447 if (m_rbrPos + m_readOffset > static_cast<int>(m_bufferSize))
1448 readposition = (m_rbrPos + m_readOffset) - static_cast<int>(m_bufferSize);
1449 else
1450 readposition = m_rbrPos + m_readOffset;
1451
1452 if (readposition + Count > static_cast<int>(m_bufferSize))
1453 {
1454 int firstsize = static_cast<int>(m_bufferSize) - readposition;
1455 int secondsize = Count - firstsize;
1456
1457 memcpy(Buffer, m_readAheadBuffer + readposition, static_cast<size_t>(firstsize));
1458 memcpy(reinterpret_cast<char*>(Buffer) + firstsize, m_readAheadBuffer, static_cast<size_t>(secondsize));
1459 }
1460 else
1461 {
1462 memcpy(Buffer, m_readAheadBuffer + readposition, static_cast<uint>(Count));
1463 }
1464#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1465 quint16 checksum = qChecksum(reinterpret_cast<char*>(Buffer), static_cast<uint>(Count));
1466#else
1467 QByteArrayView BufferView(reinterpret_cast<char*>(Buffer), Count);
1468 quint16 checksum = qChecksum(BufferView);
1469#endif
1470 LOG(VB_FILE, LOG_DEBUG, LOC + desc + QString(": Checksum %1").arg(checksum));
1471
1472 if (!Peek)
1473 {
1475 {
1476 m_readOffset += Count;
1477 }
1478 else
1479 {
1480 m_rbrPos = (m_rbrPos + Count) % static_cast<int>(m_bufferSize);
1481 m_generalWait.wakeAll();
1482 }
1483 }
1484 m_rbrLock.unlock();
1485 m_rwLock.unlock();
1486
1487 return Count;
1488}
1489
1498int MythMediaBuffer::Read(void *Buffer, int Count)
1499{
1500 int ret = ReadPriv(Buffer, Count, false);
1501 if (ret > 0)
1502 {
1503 m_posLock.lockForWrite();
1504 m_readPos += ret;
1505 m_posLock.unlock();
1506 UpdateDecoderRate(static_cast<uint64_t>(ret));
1507 }
1508
1509 return ret;
1510}
1511
1512QString MythMediaBuffer::BitrateToString(uint64_t Rate, bool Hz)
1513{
1514 if (Rate < 1)
1515 return "-";
1516
1517 if (Rate > 1000000000)
1518 return QObject::tr(">1Gbps");
1519
1520 QString msg;
1521 auto bitrate = static_cast<double>(NAN);
1522 auto rate = static_cast<double>(Rate);
1523 int range = 0;
1524
1525 if (Rate >= 1000000)
1526 {
1527 msg = Hz ? QObject::tr("%1MHz") : QObject::tr("%1Mbps");
1528 bitrate = rate / 1000000.0;
1529 range = Hz ? 3 : 1;
1530 }
1531 else if (Rate >= 1000)
1532 {
1533 msg = Hz ? QObject::tr("%1kHz") : QObject::tr("%1kbps");
1534 bitrate = rate / 1000.0;
1535 range = Hz ? 1 : 0;
1536 }
1537 else
1538 {
1539 msg = Hz ? QObject::tr("%1Hz") : QObject::tr("%1bps");
1540 bitrate = rate;
1541 }
1542 return msg.arg(bitrate, 0, 'f', range);
1543}
1544
1546{
1548}
1549
1551{
1553}
1554
1556{
1558 return "N/A";
1559
1560 int avail = (m_rbwPos >= m_rbrPos) ? m_rbwPos - m_rbrPos
1561 : static_cast<int>(m_bufferSize) - m_rbrPos + m_rbwPos;
1562 return QString("%1%").arg(lroundf((static_cast<float>(avail) / static_cast<float>(m_bufferSize) * 100.0F)));
1563}
1564
1566{
1567 return m_bufferSize;
1568}
1569
1570uint64_t MythMediaBuffer::UpdateDecoderRate(uint64_t Latest)
1571{
1573 return 0;
1574
1575 auto current = std::chrono::milliseconds(QDateTime::currentMSecsSinceEpoch());
1576 std::chrono::milliseconds expire = current - 1s;
1577
1578 m_decoderReadLock.lock();
1579 if (Latest)
1580 m_decoderReads.insert(current, Latest);
1581 uint64_t total = 0;
1582 for (auto it = m_decoderReads.begin();
1583 it != m_decoderReads.end();
1584 /* no inc */)
1585 {
1586 if (it.key() < expire || it.key() > current)
1587 {
1588 it = m_decoderReads.erase(it);
1589 }
1590 else
1591 {
1592 total += it.value();
1593 ++it;
1594 }
1595 }
1596
1597 int size = m_decoderReads.size();
1598 m_decoderReadLock.unlock();
1599
1600 auto average = static_cast<uint64_t>(static_cast<double>(total) * 8.0);
1601 LOG(VB_FILE, LOG_INFO, LOC + QString("Decoder read speed: %1 %2")
1602 .arg(average).arg(size));
1603 return average;
1604}
1605
1606uint64_t MythMediaBuffer::UpdateStorageRate(uint64_t Latest)
1607{
1609 return 0;
1610
1611 auto current = std::chrono::milliseconds(QDateTime::currentMSecsSinceEpoch());
1612 auto expire = current - 1s;
1613
1614 m_storageReadLock.lock();
1615 if (Latest)
1616 m_storageReads.insert(current, Latest);
1617 uint64_t total = 0;
1618 for (auto it = m_storageReads.begin();
1619 it != m_storageReads.end();
1620 /* no inc */)
1621 {
1622 if (it.key() < expire || it.key() > current)
1623 {
1624 it = m_storageReads.erase(it);
1625 }
1626 else
1627 {
1628 total += it.value();
1629 ++it;
1630 }
1631 }
1632
1633 int size = m_storageReads.size();
1634 m_storageReadLock.unlock();
1635
1636 uint64_t average = size ? static_cast<uint64_t>(static_cast<double>(total) / size) : 0;
1637 LOG(VB_FILE, LOG_INFO, LOC + QString("Average storage read speed: %1 (Reads %2")
1638 .arg(average).arg(m_storageReads.size()));
1639 return average;
1640}
1641
1646int MythMediaBuffer::Write(const void *Buffer, uint Count)
1647{
1648 m_rwLock.lockForRead();
1649 int result = -1;
1650
1651 if (!m_writeMode)
1652 {
1653 LOG(VB_GENERAL, LOG_ERR, LOC + "Tried to write to a read only file.");
1654 m_rwLock.unlock();
1655 return result;
1656 }
1657
1658 if (!m_tfw && !m_remotefile)
1659 {
1660 m_rwLock.unlock();
1661 return result;
1662 }
1663
1664 if (m_tfw)
1665 result = m_tfw->Write(Buffer, Count);
1666 else
1667 result = m_remotefile->Write(Buffer, static_cast<int>(Count));
1668
1669 if (result > 0)
1670 {
1671 m_posLock.lockForWrite();
1672 m_writePos += result;
1673 m_posLock.unlock();
1674 }
1675
1676 m_rwLock.unlock();
1677 return result;
1678}
1679
1684{
1685 m_rwLock.lockForRead();
1686 if (m_tfw)
1687 m_tfw->Sync();
1688 m_rwLock.unlock();
1689}
1690
1693long long MythMediaBuffer::WriterSeek(long long Position, int Whence, bool HasLock)
1694{
1695 long long result = -1;
1696
1697 if (!HasLock)
1698 m_rwLock.lockForRead();
1699
1700 m_posLock.lockForWrite();
1701
1702 if (m_tfw)
1703 {
1704 result = m_tfw->Seek(Position, Whence);
1705 m_writePos = result;
1706 }
1707
1708 m_posLock.unlock();
1709
1710 if (!HasLock)
1711 m_rwLock.unlock();
1712
1713 return result;
1714}
1715
1720{
1721 m_rwLock.lockForRead();
1722 if (m_tfw)
1723 m_tfw->Flush();
1724 m_rwLock.unlock();
1725}
1726
1731{
1732 QReadLocker lock(&m_rwLock);
1733 if (m_tfw)
1734 return m_tfw->SetBlocking(Lock);
1735 return false;
1736}
1737
1754{
1755 LOG(VB_FILE, LOG_INFO, LOC + QString("SetOldFile: %1)").arg(Old));
1756 m_rwLock.lockForWrite();
1757 m_oldfile = Old;
1758 m_rwLock.unlock();
1759}
1760
1762{
1763 m_rwLock.lockForRead();
1764 QString tmp = m_filename;
1765 m_rwLock.unlock();
1766 return tmp;
1767}
1768
1770{
1771 return m_safeFilename;
1772}
1773
1775{
1776 m_rwLock.lockForRead();
1777 QString tmp = m_subtitleFilename;
1778 m_rwLock.unlock();
1779 return tmp;
1780}
1781
1783{
1784 m_rwLock.lockForRead();
1785 QString tmp = m_lastError;
1786 m_rwLock.unlock();
1787 return tmp;
1788}
1789
1791{
1792 return m_commsError;
1793}
1794
1796{
1797 m_commsError = false;
1798}
1799
1801{
1802 return m_stopReads;
1803}
1804
1809{
1810 m_posLock.lockForRead();
1811 long long ret = m_writePos;
1812 m_posLock.unlock();
1813 return ret;
1814}
1815
1821{
1822 m_rwLock.lockForRead();
1823 bool ret = (m_liveTVChain);
1824 m_rwLock.unlock();
1825 return ret;
1826}
1827
1833{
1834 m_rwLock.lockForWrite();
1835 m_liveTVChain = Chain;
1836 m_rwLock.unlock();
1837}
1838
1841{
1842 m_rwLock.lockForWrite();
1843 m_ignoreLiveEOF = Ignore;
1844 m_rwLock.unlock();
1845}
1846
1848{
1849 return IsDVD() || IsBD();
1850}
1851
1853{
1854 return m_type == kMythBufferDVD;
1855}
1856
1858{
1859 return m_type == kMythBufferBD;
1860}
1861
1863{
1864 return dynamic_cast<const MythDVDBuffer*>(this);
1865}
1866
1868{
1869 return dynamic_cast<const MythBDBuffer*>(this);
1870}
1871
1873{
1874 return dynamic_cast<MythDVDBuffer*>(this);
1875}
1876
1878{
1879 return dynamic_cast<MythBDBuffer*>(this);
1880}
1881
1883{
1884 static QRecursiveMutex s_avnetworkLock;
1885 static bool s_avnetworkInitialised = false;
1886 QMutexLocker lock(&s_avnetworkLock);
1887 if (!s_avnetworkInitialised)
1888 {
1889 avformat_network_init();
1890 s_avnetworkInitialised = true;
1891 }
1892}
#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:261
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:194
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:281
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:207
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:298
A class to allow a MythMediaBuffer to read from BDs.
Definition: mythbdbuffer.h:20
static ImageType inspectImage(const QString &path)
Definition: mythcdrom.cpp:189
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:761
static bool Exists(const QString &url, struct stat *fileinfo)
Definition: remotefile.cpp:463
int Write(const void *data, int size)
Definition: remotefile.cpp:838
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: compat.h:60
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:201
Mode
Definition: synaesthesia.h:20