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(std::fabs(m_rawBitrate * m_playSpeed),
378 0.5F * m_rawBitrate));
379 estbitrate = std::min(m_rawBitrate * 3, estbitrate);
380 int const rbs = estbitrate_to_rbs(estbitrate);
381
382 if (rbs < DEFAULT_CHUNK_SIZE)
383 m_readBlockSize = rbs;
384 else
386
387 // minimum seconds of buffering before allowing read
388 float secs_min = 0.3F;
389 // set the minimum buffering before allowing ffmpeg read
390 m_fillMin = static_cast<int>((estbitrate * 1000 * secs_min) * 0.125F);
391 // make this a multiple of ffmpeg block size..
393 {
394 if (m_lowBuffers)
395 LOG(VB_GENERAL, LOG_INFO, LOC + "Buffering optimisations disabled.");
396 m_lowBuffers = false;
398 m_fillMin = std::min(m_fillMin, static_cast<int>(m_bufferSize / 2));
399 }
400 else
401 {
402 m_lowBuffers = true;
403 LOG(VB_GENERAL, LOG_WARNING, "Enabling buffering optimisations for low bitrate stream.");
404 }
405
406 LOG(VB_FILE, LOG_INFO, LOC +
407 QString("CalcReadAheadThresh(%1 Kb)\n\t\t\t -> "
408 "threshold(%2 KB) min read(%3 KB) blk size(%4 KB)")
409 .arg(estbitrate).arg(m_fillThreshold/1024)
410 .arg(m_fillMin/1024).arg(m_readBlockSize/1024));
411}
412
413bool MythMediaBuffer::IsNearEnd(double /*Framerate*/, uint Frames) const
414{
415 QReadLocker lock(&m_rwLock);
416
417 // file is still being read, so can't be finished
418 if (!m_ateof && !m_setSwitchToNext)
419 return false;
420
421 m_posLock.lockForRead();
422 long long readpos = m_readPos;
423 long long size = m_internalReadPos - m_readPos;
424 m_posLock.unlock();
425
426 // telecom kilobytes (i.e. 1000 per k not 1024)
427 uint tmp = static_cast<uint>(std::max(std::fabs(m_rawBitrate * m_playSpeed),
428 0.5F * m_rawBitrate));
429 uint kbitspersec = std::min(m_rawBitrate * 3, tmp);
430 if (kbitspersec == 0)
431 return false;
432
433 double readahead_time = size / (kbitspersec * (1000.0 / 8.0));
434
435 bool near_end = readahead_time <= 1.5;
436 LOG(VB_PLAYBACK, LOG_INFO, LOC + "IsReallyNearEnd()" +
437 QString(" br(%1KB)").arg(kbitspersec/8) +
438 QString(" sz(%1KB)").arg(size / 1000LL) +
439 QString(" vfl(%1)").arg(Frames) +
440 QString(" time(%1)").arg(readahead_time) +
441 QString(" rawbitrate(%1)").arg(m_rawBitrate) +
442 QString(" avail(%1)").arg(size) +
443 QString(" internal_size(%1)").arg(m_internalReadPos) +
444 QString(" readposition(%1)").arg(readpos) +
445 QString(" stopreads(%1)").arg(m_stopReads) +
446 QString(" paused(%1)").arg(m_paused) +
447 QString(" ne:%1").arg(near_end));
448 return near_end;
449}
450
454{
455 m_rbrLock.lockForRead();
456 m_rbwLock.lockForRead();
457 int ret = ((m_rbwPos >= m_rbrPos) ? m_rbrPos + static_cast<int>(m_bufferSize) : m_rbrPos) - m_rbwPos - 1;
458 m_rbwLock.unlock();
459 m_rbrLock.unlock();
460 return ret;
461}
462
465{
466 QReadLocker lock(&m_rwLock);
467 return ReadBufAvail();
468}
469
471{
472 {
473 QReadLocker lock(&m_rwLock);
475 return ReadBufAvail();
476 }
477
479}
480
481long long MythMediaBuffer::Seek(long long Position, int Whence, bool HasLock)
482{
483 LOG(VB_FILE, LOG_INFO, LOC + QString("Seek: Position:%1 Type: %2 Locked: %3)")
484 .arg(Position)
485 .arg((SEEK_SET == Whence) ? "SEEK_SET" : ((SEEK_CUR == Whence) ? "SEEK_CUR" : "SEEK_END"),
486 HasLock?"locked":"unlocked"));
487
488 if (!HasLock)
489 m_rwLock.lockForWrite();
490
491 long long ret = 0;
492
494 {
495 m_posLock.lockForWrite();
496 // only valid for SEEK_SET & SEEK_CUR
497 switch (Whence)
498 {
499 case SEEK_SET:
500 m_readPos = Position;
501 break;
502 case SEEK_CUR:
503 m_readPos += Position;
504 break;
505 case SEEK_END:
506 m_readPos = ReadBufAvail() - Position;
507 break;
508 }
509 m_readOffset = static_cast<int>(m_readPos);
510 m_posLock.unlock();
511 ret = m_readPos;
512 }
513 else
514 {
515 ret = SeekInternal(Position, Whence);
516 }
517
518 m_generalWait.wakeAll();
519
520 if (!HasLock)
521 m_rwLock.unlock();
522 return ret;
523}
524
526{
527 QWriteLocker lock(&m_rwLock);
528 bool old = m_readInternalMode;
529
530 if (Mode == old)
531 return old;
532
534
535 if (!Mode)
536 {
537 // adjust real read position in ringbuffer
538 m_rbrLock.lockForWrite();
539 m_rbrPos = (m_rbrPos + m_readOffset) % static_cast<int>(m_bufferSize);
540 m_generalWait.wakeAll();
541 m_rbrLock.unlock();
542 // reset the read offset as we are exiting the internal read mode
543 m_readOffset = 0;
544 }
545
546 LOG(VB_FILE, LOG_DEBUG, LOC + QString("SetReadInternalMode: %1").arg(Mode ? "on" : "off"));
547 return old;
548}
549
551{
552 return m_readInternalMode;
553}
554
558{
559 m_rbrLock.lockForRead();
560 m_rbwLock.lockForRead();
561 int ret = (m_rbwPos >= m_rbrPos) ? m_rbwPos - m_rbrPos : static_cast<int>(m_bufferSize) - m_rbrPos + m_rbwPos;
562 m_rbwLock.unlock();
563 m_rbrLock.unlock();
564 return ret;
565}
566
577void MythMediaBuffer::ResetReadAhead(long long NewInternal)
578{
579 LOG(VB_FILE, LOG_INFO, LOC + QString("ResetReadAhead(internalreadpos = %1->%2)")
580 .arg(m_internalReadPos).arg(NewInternal));
581
582 m_readInternalMode = false;
583 m_readOffset = 0;
584
585 m_rbrLock.lockForWrite();
586 m_rbwLock.lockForWrite();
587
589
590 m_rbrPos = 0;
591 m_rbwPos = 0;
592 m_internalReadPos = NewInternal;
593 m_ateof = false;
594 m_readsAllowed = false;
595 m_readsDesired = false;
596 m_recentSeek = true;
597 m_setSwitchToNext = false;
598
599 m_generalWait.wakeAll();
600
601 m_rbwLock.unlock();
602 m_rbrLock.unlock();
603}
604
620{
621 bool dostart = true;
622
623 m_rwLock.lockForWrite();
624 if (!m_startReadAhead)
625 {
626 dostart = false;
627 }
628 else if (m_writeMode)
629 {
630 LOG(VB_GENERAL, LOG_WARNING, LOC + "Not starting read ahead thread - write only RingBuffer");
631 dostart = false;
632 }
633 else if (m_readAheadRunning)
634 {
635 LOG(VB_GENERAL, LOG_WARNING, LOC + "Not starting read ahead thread - already running");
636 dostart = false;
637 }
638
639 if (!dostart)
640 {
641 m_rwLock.unlock();
642 return;
643 }
644
645 StartReads();
648 m_generalWait.wait(&m_rwLock);
649 m_rwLock.unlock();
650}
651
656{
657 while (isRunning())
658 {
659 m_rwLock.lockForWrite();
660 m_readAheadRunning = false;
661 StopReads();
662 m_generalWait.wakeAll();
663 m_rwLock.unlock();
664 MThread::wait(5s);
665 }
666}
667
672{
673 LOG(VB_FILE, LOG_INFO, LOC + "StopReads()");
674 m_stopReads = true;
675 m_generalWait.wakeAll();
676}
677
682{
683 LOG(VB_FILE, LOG_INFO, LOC + "StartReads()");
684 m_stopReads = false;
685 m_generalWait.wakeAll();
686}
687
693{
694 LOG(VB_FILE, LOG_INFO, LOC + "Pausing read ahead thread");
695 StopReads();
696
697 m_rwLock.lockForWrite();
698 m_requestPause = true;
699 m_rwLock.unlock();
700}
701
707{
708 LOG(VB_FILE, LOG_INFO, LOC + "Unpausing readahead thread");
709 StartReads();
710
711 m_rwLock.lockForWrite();
712 m_requestPause = false;
713 m_generalWait.wakeAll();
714 m_rwLock.unlock();
715}
716
721{
722 MythTimer t;
723 t.start();
724
725 m_rwLock.lockForRead();
727 {
728 m_generalWait.wait(&m_rwLock, 1000);
729 if (m_readAheadRunning && !m_paused && m_requestPause && t.elapsed() > 1s)
730 LOG(VB_GENERAL, LOG_WARNING, LOC + QString("Waited %1 ms for ringbuffer pause")
731 .arg(t.elapsed().count()));
732 }
733 m_rwLock.unlock();
734}
735
737{
738 const uint timeout = 500; // ms
739
740 if (m_requestPause)
741 {
742 if (!m_paused)
743 {
744 m_rwLock.unlock();
745 m_rwLock.lockForWrite();
746
747 if (m_requestPause)
748 {
749 m_paused = true;
750 m_generalWait.wakeAll();
751 }
752
753 m_rwLock.unlock();
754 m_rwLock.lockForRead();
755 }
756
759 }
760
761 if (!m_requestPause && m_paused)
762 {
763 m_rwLock.unlock();
764 m_rwLock.lockForWrite();
765
766 if (!m_requestPause)
767 {
768 m_paused = false;
769 m_generalWait.wakeAll();
770 }
771
772 m_rwLock.unlock();
773 m_rwLock.lockForRead();
774 }
775
776 return m_requestPause || m_paused;
777}
778
780{
781 m_rwLock.lockForWrite();
782 m_posLock.lockForWrite();
783
784 uint oldsize = m_bufferSize;
785 uint newsize = BUFFER_SIZE_MINIMUM;
786 if (m_remotefile)
787 {
788 newsize *= BUFFER_FACTOR_NETWORK;
790 newsize *= BUFFER_FACTOR_MATROSKA;
792 newsize *= BUFFER_FACTOR_BITRATE;
793 }
794
795 // N.B. Don't try and make it smaller - bad things happen...
796 if (m_readAheadBuffer && (oldsize >= newsize))
797 {
798 m_posLock.unlock();
799 m_rwLock.unlock();
800 return;
801 }
802
803 m_bufferSize = newsize;
805 {
806 char* newbuffer = new char[m_bufferSize + 1024];
807 memcpy(newbuffer, m_readAheadBuffer + m_rbwPos, oldsize - static_cast<uint>(m_rbwPos));
808 memcpy(newbuffer + (oldsize - static_cast<uint>(m_rbwPos)), m_readAheadBuffer, static_cast<uint>(m_rbwPos));
809 delete [] m_readAheadBuffer;
810 m_readAheadBuffer = newbuffer;
812 (m_rbrPos + static_cast<int>(oldsize) - m_rbwPos);
813 m_rbwPos = static_cast<int>(oldsize);
814 }
815 else
816 {
817 m_readAheadBuffer = new char[m_bufferSize + 1024];
818 }
820 m_posLock.unlock();
821 m_rwLock.unlock();
822
823 LOG(VB_FILE, LOG_INFO, LOC + QString("Created readAheadBuffer: %1Mb")
824 .arg(newsize >> 20));
825}
826
828{
829 RunProlog();
830
831 // These variables are used to adjust the read block size
832 std::chrono::milliseconds readTimeAvg = 300ms;
833 bool ignoreForReadTiming = true;
834 int eofreads = 0;
835
836 auto lastread = nowAsDuration<std::chrono::milliseconds>();
837
839 m_rwLock.lockForWrite();
840 m_posLock.lockForWrite();
841 m_requestPause = false;
843 m_readAheadRunning = true;
844 m_reallyRunning = true;
845 m_generalWait.wakeAll();
846 m_posLock.unlock();
847 m_rwLock.unlock();
848
849 // NOTE: this must loop at some point hold only
850 // a read lock on rwlock, so that other functions
851 // such as reset and seek can take priority.
852
853 m_rwLock.lockForRead();
854
855 LOG(VB_FILE, LOG_INFO, LOC + QString("Initial readblocksize %1K fillMin %2K")
856 .arg(m_readBlockSize/1024).arg(m_fillMin/1024));
857
858 while (m_readAheadRunning)
859 {
860 m_rwLock.unlock();
861 bool isopened = IsOpen();
862 m_rwLock.lockForRead();
863
864 if (!isopened)
865 {
866 LOG(VB_FILE, LOG_WARNING, LOC + QString("File not opened, terminating readahead thread"));
867 m_posLock.lockForWrite();
868 m_readAheadRunning = false;
869 m_generalWait.wakeAll();
870 m_posLock.unlock();
871 break;
872 }
873 if (PauseAndWait())
874 {
875 ignoreForReadTiming = true;
876 LOG(VB_FILE, LOG_DEBUG, LOC + "run: PauseAndWait Not reading continuing");
877 continue;
878 }
879
880 long long totfree = ReadBufFree();
881
882 const uint KB32 = 32*1024;
883 const int KB512 = 512*1024;
884 // These are conditions where we don't want to go through
885 // the loop if they are true.
886 if (((totfree < KB32) && m_readsAllowed) ||
888 {
889 ignoreForReadTiming |= (m_ignoreReadPos >= 0) || m_commsError || m_stopReads;
890 m_generalWait.wait(&m_rwLock, (m_stopReads) ? 50 : 1000);
891 LOG(VB_FILE, LOG_DEBUG, LOC +
892 QString("run: Not reading continuing: totfree(%1) "
893 "readsallowed(%2) ignorereadpos(%3) commserror(%4) "
894 "stopreads(%5)")
895 .arg(totfree).arg(m_readsAllowed).arg(m_ignoreReadPos)
896 .arg(m_commsError).arg(m_stopReads));
897 continue;
898 }
899
900 // These are conditions where we want to sleep to allow
901 // other threads to do stuff.
903 {
904 ignoreForReadTiming = true;
905 m_generalWait.wait(&m_rwLock, 1000);
906 totfree = ReadBufFree();
907 }
908
909 int readResult = -1;
910 if (totfree >= KB32 && !m_commsError && !m_ateof && !m_setSwitchToNext)
911 {
912 // limit the read size
913 if (m_readBlockSize > totfree)
914 totfree = (totfree / KB32) * KB32; // must be multiple of 32KB
915 else
916 totfree = m_readBlockSize;
917
918 // adapt blocksize
919 auto now = nowAsDuration<std::chrono::milliseconds>();
920 if (!ignoreForReadTiming)
921 {
922 readTimeAvg = (readTimeAvg * 9 + (now - lastread)) / 10;
923
924 if (readTimeAvg < 150ms &&
925 static_cast<uint>(m_readBlockSize) < (BUFFER_SIZE_MINIMUM >>2) &&
926 m_readBlockSize >= DEFAULT_CHUNK_SIZE /* low_buffers */ &&
927 m_readBlockSize <= KB512)
928 {
929 int old_block_size = m_readBlockSize;
932 m_readBlockSize = std::min(m_readBlockSize, KB512);
933 LOG(VB_FILE, LOG_INFO, LOC + QString("Avg read interval was %1 msec. "
934 "%2K -> %3K block size")
935 .arg(readTimeAvg.count()).arg(old_block_size/1024).arg(m_readBlockSize/1024));
936 readTimeAvg = 225ms;
937 }
938 else if (readTimeAvg > 300ms && m_readBlockSize > DEFAULT_CHUNK_SIZE)
939 {
941 LOG(VB_FILE, LOG_INFO, LOC +
942 QString("Avg read interval was %1 msec. %2K -> %3K block size")
943 .arg(readTimeAvg.count())
945 .arg(m_readBlockSize/1024));
946 readTimeAvg = 225ms;
947 }
948 }
949 lastread = now;
950
951 m_rbwLock.lockForRead();
952 if (m_rbwPos + totfree > m_bufferSize)
953 {
954 totfree = m_bufferSize - static_cast<uint>(m_rbwPos);
955 LOG(VB_FILE, LOG_DEBUG, LOC + "Shrinking read, near end of buffer");
956 }
957
958 if (m_internalReadPos == 0)
959 {
960 totfree = std::max(m_fillMin, m_readBlockSize);
961 LOG(VB_FILE, LOG_DEBUG, LOC + "Reading enough data to start playback");
962 }
963
964 LOG(VB_FILE, LOG_DEBUG, LOC + QString("safe_read(...@%1, %2) -- begin")
965 .arg(m_rbwPos).arg(totfree));
966
967 MythTimer sr_timer;
968 sr_timer.start();
969
970 int rbwposcopy = m_rbwPos;
971
972 // MythFileBuffer::SafeRead(RemoteFile*...) acquires poslock;
973 // so we need to unlock this here to preserve locking order.
974 m_rbwLock.unlock();
975
976 readResult = SafeRead(m_readAheadBuffer + rbwposcopy, static_cast<uint>(totfree));
977
978 int sr_elapsed = sr_timer.elapsed().count();
979 uint64_t bps = !sr_elapsed ? 1000000001 :
980 static_cast<uint64_t>((readResult * 8000.0) / static_cast<double>(sr_elapsed));
981 LOG(VB_FILE, LOG_DEBUG, LOC +
982 QString("safe_read(...@%1, %2) -> %3, took %4 ms %5 avg %6 ms")
983 .arg(rbwposcopy).arg(totfree).arg(readResult).arg(sr_elapsed)
984 .arg(QString("(%1Mbps)").arg(static_cast<double>(bps) / 1000000.0))
985 .arg(readTimeAvg.count()));
987
988 if (readResult >= 0)
989 {
990 m_posLock.lockForWrite();
991 m_rbwLock.lockForWrite();
992
993 if (rbwposcopy == m_rbwPos)
994 {
995 m_internalReadPos += readResult;
996 m_rbwPos = (m_rbwPos + readResult) % static_cast<int>(m_bufferSize);
997 LOG(VB_FILE, LOG_DEBUG, LOC + QString("rbwpos += %1K requested %2K in read")
998 .arg(readResult/1024,3).arg(totfree/1024,3));
999 }
1000 m_numFailures = 0;
1001
1002 m_rbwLock.unlock();
1003 m_posLock.unlock();
1004
1005 LOG(VB_FILE, LOG_DEBUG, LOC + QString("total read so far: %1 bytes")
1006 .arg(m_internalReadPos));
1007 }
1008 }
1009 else
1010 {
1011 LOG(VB_FILE, LOG_DEBUG, LOC +
1012 QString("We are not reading anything (totfree: %1 commserror:%2 ateof:%3 setswitchtonext:%4")
1013 .arg(totfree).arg(m_commsError).arg(m_ateof).arg(m_setSwitchToNext));
1014 }
1015
1016 int used = static_cast<int>(m_bufferSize) - ReadBufFree();
1017 bool readsWereAllowed = m_readsAllowed;
1018
1019 ignoreForReadTiming = (totfree < m_readBlockSize) || (readResult < totfree);
1020
1021 if ((0 == readResult) || (m_numFailures > 5) ||
1022 (m_readsAllowed != (used >= 1 || m_ateof || m_setSwitchToNext || m_commsError)) ||
1024 {
1025 // If readpos changes while the lock is released
1026 // we should not handle the 0 read_return now.
1027 long long oldreadposition = m_readPos;
1028
1029 m_rwLock.unlock();
1030 m_rwLock.lockForWrite();
1031
1032 m_commsError |= (m_numFailures > 5);
1033
1036
1037 if ((0 == readResult) && (oldreadposition == m_readPos))
1038 {
1039 eofreads++;
1040 if (eofreads >= 3 && m_readBlockSize >= KB512)
1041 {
1042 // not reading anything
1045 }
1046
1047 if (m_liveTVChain)
1048 {
1050 {
1051 // we receive new livetv chain element event
1052 // before we receive file closed for writing event
1053 // so don't need to test if file is closed for writing
1055 m_setSwitchToNext = true;
1056 }
1058 {
1059 LOG(VB_FILE, LOG_DEBUG, LOC +
1060 QString("EOF encountered, but %1 still being written to")
1061 .arg(m_filename));
1062 // We reached EOF, but file still open for writing and
1063 // no next program in livetvchain
1064 // wait a little bit (60ms same wait as typical safe_read)
1065 m_generalWait.wait(&m_rwLock, 60);
1066 }
1067 }
1069 {
1070 LOG(VB_FILE, LOG_DEBUG, LOC +
1071 QString("EOF encountered, but %1 still being written to")
1072 .arg(m_filename));
1073 // We reached EOF, but file still open for writing,
1074 // typically active in-progress recording
1075 // wait a little bit (60ms same wait as typical safe_read)
1076 m_generalWait.wait(&m_rwLock, 60);
1077 m_beingWritten = true;
1078 }
1079 else
1080 {
1082 {
1083 LOG(VB_FILE, LOG_DEBUG, LOC + "Waiting for file to grow large enough to process.");
1084 m_generalWait.wait(&m_rwLock, 300);
1085 }
1086 else
1087 {
1088 LOG(VB_FILE, LOG_DEBUG, LOC + "setting ateof (readResult == 0)");
1089 m_ateof = true;
1090 }
1091 }
1092 }
1093
1094 m_rwLock.unlock();
1095 m_rwLock.lockForRead();
1096 used = static_cast<int>(m_bufferSize) - ReadBufFree();
1097 }
1098 else
1099 {
1100 eofreads = 0;
1101 }
1102
1103 LOG(VB_FILE, LOG_DEBUG, LOC + "@ end of read ahead loop");
1104
1106 (m_wantToRead <= used && m_wantToRead > 0))
1107 {
1108 // To give other threads a good chance to handle these
1109 // conditions, even if they are only requesting a read lock
1110 // like us, yield (currently implemented with short usleep).
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 QMutableMapIterator<std::chrono::milliseconds,uint64_t> it(m_decoderReads);
1583 while (it.hasNext())
1584 {
1585 it.next();
1586 if (it.key() < expire || it.key() > current)
1587 it.remove();
1588 else
1589 total += it.value();
1590 }
1591
1592 int size = m_decoderReads.size();
1593 m_decoderReadLock.unlock();
1594
1595 auto average = static_cast<uint64_t>(static_cast<double>(total) * 8.0);
1596 LOG(VB_FILE, LOG_INFO, LOC + QString("Decoder read speed: %1 %2")
1597 .arg(average).arg(size));
1598 return average;
1599}
1600
1601uint64_t MythMediaBuffer::UpdateStorageRate(uint64_t Latest)
1602{
1604 return 0;
1605
1606 auto current = std::chrono::milliseconds(QDateTime::currentMSecsSinceEpoch());
1607 auto expire = current - 1s;
1608
1609 m_storageReadLock.lock();
1610 if (Latest)
1611 m_storageReads.insert(current, Latest);
1612 uint64_t total = 0;
1613 QMutableMapIterator<std::chrono::milliseconds,uint64_t> it(m_storageReads);
1614 while (it.hasNext())
1615 {
1616 it.next();
1617 if (it.key() < expire || it.key() > current)
1618 it.remove();
1619 else
1620 total += it.value();
1621 }
1622
1623 int size = m_storageReads.size();
1624 m_storageReadLock.unlock();
1625
1626 uint64_t average = size ? static_cast<uint64_t>(static_cast<double>(total) / size) : 0;
1627 LOG(VB_FILE, LOG_INFO, LOC + QString("Average storage read speed: %1 (Reads %2")
1628 .arg(average).arg(m_storageReads.size()));
1629 return average;
1630}
1631
1636int MythMediaBuffer::Write(const void *Buffer, uint Count)
1637{
1638 m_rwLock.lockForRead();
1639 int result = -1;
1640
1641 if (!m_writeMode)
1642 {
1643 LOG(VB_GENERAL, LOG_ERR, LOC + "Tried to write to a read only file.");
1644 m_rwLock.unlock();
1645 return result;
1646 }
1647
1648 if (!m_tfw && !m_remotefile)
1649 {
1650 m_rwLock.unlock();
1651 return result;
1652 }
1653
1654 if (m_tfw)
1655 result = m_tfw->Write(Buffer, Count);
1656 else
1657 result = m_remotefile->Write(Buffer, static_cast<int>(Count));
1658
1659 if (result > 0)
1660 {
1661 m_posLock.lockForWrite();
1662 m_writePos += result;
1663 m_posLock.unlock();
1664 }
1665
1666 m_rwLock.unlock();
1667 return result;
1668}
1669
1674{
1675 m_rwLock.lockForRead();
1676 if (m_tfw)
1677 m_tfw->Sync();
1678 m_rwLock.unlock();
1679}
1680
1683long long MythMediaBuffer::WriterSeek(long long Position, int Whence, bool HasLock)
1684{
1685 long long result = -1;
1686
1687 if (!HasLock)
1688 m_rwLock.lockForRead();
1689
1690 m_posLock.lockForWrite();
1691
1692 if (m_tfw)
1693 {
1694 result = m_tfw->Seek(Position, Whence);
1695 m_writePos = result;
1696 }
1697
1698 m_posLock.unlock();
1699
1700 if (!HasLock)
1701 m_rwLock.unlock();
1702
1703 return result;
1704}
1705
1710{
1711 m_rwLock.lockForRead();
1712 if (m_tfw)
1713 m_tfw->Flush();
1714 m_rwLock.unlock();
1715}
1716
1721{
1722 QReadLocker lock(&m_rwLock);
1723 if (m_tfw)
1724 return m_tfw->SetBlocking(Lock);
1725 return false;
1726}
1727
1744{
1745 LOG(VB_FILE, LOG_INFO, LOC + QString("SetOldFile: %1)").arg(Old));
1746 m_rwLock.lockForWrite();
1747 m_oldfile = Old;
1748 m_rwLock.unlock();
1749}
1750
1752{
1753 m_rwLock.lockForRead();
1754 QString tmp = m_filename;
1755 m_rwLock.unlock();
1756 return tmp;
1757}
1758
1760{
1761 return m_safeFilename;
1762}
1763
1765{
1766 m_rwLock.lockForRead();
1767 QString tmp = m_subtitleFilename;
1768 m_rwLock.unlock();
1769 return tmp;
1770}
1771
1773{
1774 m_rwLock.lockForRead();
1775 QString tmp = m_lastError;
1776 m_rwLock.unlock();
1777 return tmp;
1778}
1779
1781{
1782 return m_commsError;
1783}
1784
1786{
1787 m_commsError = false;
1788}
1789
1791{
1792 return m_stopReads;
1793}
1794
1799{
1800 m_posLock.lockForRead();
1801 long long ret = m_writePos;
1802 m_posLock.unlock();
1803 return ret;
1804}
1805
1811{
1812 m_rwLock.lockForRead();
1813 bool ret = (m_liveTVChain);
1814 m_rwLock.unlock();
1815 return ret;
1816}
1817
1823{
1824 m_rwLock.lockForWrite();
1825 m_liveTVChain = Chain;
1826 m_rwLock.unlock();
1827}
1828
1831{
1832 m_rwLock.lockForWrite();
1833 m_ignoreLiveEOF = Ignore;
1834 m_rwLock.unlock();
1835}
1836
1838{
1839 return IsDVD() || IsBD();
1840}
1841
1843{
1844 return m_type == kMythBufferDVD;
1845}
1846
1848{
1849 return m_type == kMythBufferBD;
1850}
1851
1853{
1854 return dynamic_cast<const MythDVDBuffer*>(this);
1855}
1856
1858{
1859 return dynamic_cast<const MythBDBuffer*>(this);
1860}
1861
1863{
1864 return dynamic_cast<MythDVDBuffer*>(this);
1865}
1866
1868{
1869 return dynamic_cast<MythBDBuffer*>(this);
1870}
1871
1873{
1874 static QRecursiveMutex s_avnetworkLock;
1875 static bool s_avnetworkInitialised = false;
1876 QMutexLocker lock(&s_avnetworkLock);
1877 if (!s_avnetworkInitialised)
1878 {
1879 avformat_network_init();
1880 s_avnetworkInitialised = true;
1881 }
1882}
#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: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:763
static bool Exists(const QString &url, struct stat *fileinfo)
Definition: remotefile.cpp:465
int Write(const void *data, int size)
Definition: remotefile.cpp:840
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:68
static uint32_t * tmp
Definition: goom_core.cpp:28
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:20