Go to the documentation of this file.
16 #include <QReadLocker>
22 #include "libmythbase/mythconfig.h"
41 #include "libavformat/avformat.h"
44 #define LOC QString("RingBuf(%1): ").arg(m_filename)
99 bool UseReadAhead, std::chrono::milliseconds Timeout,
bool StreamOnly)
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");
131 if (httpurl || iptvurl)
137 if (!StreamOnly && mythurl)
139 struct stat fileInfo {};
141 (S_ISDIR(fileInfo.st_mode)))
149 else if (!StreamOnly && !mythurl)
151 if (QFile::exists(
filename +
"/VIDEO_TS"))
153 else if (QFile::exists(
filename +
"/BDMV"))
157 if (!StreamOnly && (dvdurl || dvddir))
162 if (!(mythurl || QFile::exists(
filename)))
164 LOG(VB_PLAYBACK, LOG_INFO,
"Trying DVD at " +
filename);
168 if (!StreamOnly && (bdurl || bddir))
173 if (!(mythurl || QFile::exists(
filename)))
175 LOG(VB_PLAYBACK, LOG_INFO,
"Trying BD at " +
filename);
179 if (!mythurl && imgext &&
filename.startsWith(
"dvd:"))
181 LOG(VB_PLAYBACK, LOG_INFO,
"DVD image at " +
filename);
185 if (!mythurl && lower.endsWith(
".vob") &&
filename.contains(
"/VIDEO_TS/"))
187 LOG(VB_PLAYBACK, LOG_INFO,
"DVD VOB at " +
filename);
189 if (dvdstream && dvdstream->IsOpen())
239 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Reset(%1,%2,%3)")
240 .arg(Full).arg(ToAdjust).arg(ResetInternal));
254 LOG(VB_GENERAL, LOG_ERR,
LOC +
255 QString(
"MythMediaBuffer::Reset() nonzero readpos. toAdjust: %1 "
256 "readpos: %2 readAdjust: %3")
280 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"UpdateRawBitrate(%1Kb)").arg(RawBitrate));
286 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Bitrate too low - setting to 64Kb"));
289 else if (RawBitrate > 100000)
291 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Bitrate too high - setting to 100Mb"));
355 const uint KB2 = 2*1024;
356 const uint KB4 = 4*1024;
357 const uint KB8 = 8*1024;
358 const uint KB16 = 16*1024;
359 const uint KB32 = 32*1024;
360 const uint KB64 = 64*1024;
361 const uint KB128 = 128*1024;
362 const uint KB256 = 256*1024;
363 const uint KB512 = 512*1024;
367 int const rbs = (estbitrate > 18000) ? KB512 :
368 (estbitrate > 9000) ? KB256 :
369 (estbitrate > 5000) ? KB128 :
370 (estbitrate > 2500) ? KB64 :
371 (estbitrate > 1250) ? KB32 :
372 (estbitrate >= 500) ? KB16 :
373 (estbitrate > 250) ? KB8 :
374 (estbitrate > 125) ? KB4 : KB2;
381 float secs_min = 0.3F;
383 m_fillMin =
static_cast<int>((estbitrate * 1000 * secs_min) * 0.125F);
388 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Buffering optimisations disabled.");
396 LOG(VB_GENERAL, LOG_WARNING,
"Enabling buffering optimisations for low bitrate stream.");
399 LOG(VB_FILE, LOG_INFO,
LOC +
400 QString(
"CalcReadAheadThresh(%1 Kb)\n\t\t\t -> "
401 "threshold(%2 KB) min read(%3 KB) blk size(%4 KB)")
422 if (kbitspersec == 0)
425 double readahead_time = size / (kbitspersec * (1000.0 / 8.0));
427 bool near_end = readahead_time <= 1.5;
428 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"IsReallyNearEnd()" +
429 QString(
" br(%1KB)").arg(kbitspersec/8) +
430 QString(
" sz(%1KB)").arg(size / 1000LL) +
431 QString(
" vfl(%1)").arg(Frames) +
432 QString(
" time(%1)").arg(readahead_time) +
434 QString(
" avail(%1)").arg(size) +
436 QString(
" readposition(%1)").arg(readpos) +
438 QString(
" paused(%1)").arg(
m_paused) +
439 QString(
" ne:%1").arg(near_end));
475 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Seek: Position:%1 Type: %2 Locked: %3)")
477 .arg((SEEK_SET == Whence) ?
"SEEK_SET" : ((SEEK_CUR == Whence) ?
"SEEK_CUR" :
"SEEK_END"),
478 HasLock?
"locked":
"unlocked"));
538 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"SetReadInternalMode: %1").arg(
Mode ?
"on" :
"off"));
571 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"ResetReadAhead(internalreadpos = %1->%2)")
622 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Not starting read ahead thread - write only RingBuffer");
627 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Not starting read ahead thread - already running");
665 LOG(VB_FILE, LOG_INFO,
LOC +
"StopReads()");
675 LOG(VB_FILE, LOG_INFO,
LOC +
"StartReads()");
686 LOG(VB_FILE, LOG_INFO,
LOC +
"Pausing read ahead thread");
700 LOG(VB_FILE, LOG_INFO,
LOC +
"Unpausing readahead thread");
722 LOG(VB_GENERAL, LOG_WARNING,
LOC + QString(
"Waited %1 ms for ringbuffer pause")
723 .arg(
t.elapsed().count()));
805 m_rbwPos =
static_cast<int>(oldsize);
815 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Created readAheadBuffer: %1Mb")
816 .arg(newsize >> 20));
824 std::chrono::milliseconds readTimeAvg = 300ms;
825 bool ignoreForReadTiming =
true;
828 auto lastread = nowAsDuration<std::chrono::milliseconds>();
847 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Initial readblocksize %1K fillMin %2K")
858 LOG(VB_FILE, LOG_WARNING,
LOC + QString(
"File not opened, terminating readahead thread"));
867 ignoreForReadTiming =
true;
868 LOG(VB_FILE, LOG_DEBUG,
LOC +
"run: PauseAndWait Not reading continuing");
874 const uint KB32 = 32*1024;
875 const int KB512 = 512*1024;
883 LOG(VB_FILE, LOG_DEBUG,
LOC +
884 QString(
"run: Not reading continuing: totfree(%1) "
885 "readsallowed(%2) ignorereadpos(%3) commserror(%4) "
896 ignoreForReadTiming =
true;
906 totfree = (totfree / KB32) * KB32;
911 auto now = nowAsDuration<std::chrono::milliseconds>();
912 if (!ignoreForReadTiming)
914 readTimeAvg = (readTimeAvg * 9 + (now - lastread)) / 10;
916 if (readTimeAvg < 150ms &&
926 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Avg read interval was %1 msec. "
927 "%2K -> %3K block size")
928 .arg(readTimeAvg.count()).arg(old_block_size/1024).arg(
m_readBlockSize/1024));
934 LOG(VB_FILE, LOG_INFO,
LOC +
935 QString(
"Avg read interval was %1 msec. %2K -> %3K block size")
936 .arg(readTimeAvg.count())
948 LOG(VB_FILE, LOG_DEBUG,
LOC +
"Shrinking read, near end of buffer");
954 LOG(VB_FILE, LOG_DEBUG,
LOC +
"Reading enough data to start playback");
957 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"safe_read(...@%1, %2) -- begin")
971 int sr_elapsed = sr_timer.
elapsed().count();
972 uint64_t bps = !sr_elapsed ? 1000000001 :
973 static_cast<uint64_t
>((readResult * 8000.0) /
static_cast<double>(sr_elapsed));
974 LOG(VB_FILE, LOG_DEBUG,
LOC +
975 QString(
"safe_read(...@%1, %2) -> %3, took %4 ms %5 avg %6 ms")
976 .arg(rbwposcopy).arg(totfree).arg(readResult).arg(sr_elapsed)
977 .arg(QString(
"(%1Mbps)").arg(
static_cast<double>(bps) / 1000000.0))
978 .arg(readTimeAvg.count()));
990 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"rbwpos += %1K requested %2K in read")
991 .arg(readResult/1024,3).arg(totfree/1024,3));
998 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"total read so far: %1 bytes")
1004 LOG(VB_FILE, LOG_DEBUG,
LOC +
1005 QString(
"We are not reading anything (totfree: %1 commserror:%2 ateof:%3 setswitchtonext:%4")
1012 ignoreForReadTiming = (totfree <
m_readBlockSize) || (readResult < totfree);
1030 if ((0 == readResult) && (oldreadposition ==
m_readPos))
1052 LOG(VB_FILE, LOG_DEBUG,
LOC +
1053 QString(
"EOF encountered, but %1 still being written to")
1063 LOG(VB_FILE, LOG_DEBUG,
LOC +
1064 QString(
"EOF encountered, but %1 still being written to")
1076 LOG(VB_FILE, LOG_DEBUG,
LOC +
"Waiting for file to grow large enough to process.");
1081 LOG(VB_FILE, LOG_DEBUG,
LOC +
"setting ateof (readResult == 0)");
1096 LOG(VB_FILE, LOG_DEBUG,
LOC +
"@ end of read ahead loop");
1099 (m_wantToRead <= used && m_wantToRead > 0))
1106 std::this_thread::sleep_for(5ms);
1123 std::this_thread::sleep_for(5ms);
1148 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Exiting readahead thread"));
1168 if (result != Count)
1170 LOG(VB_GENERAL, LOG_WARNING,
LOC + QString(
"Peek requested %1 bytes but only have %2")
1171 .arg(Count).arg(result));
1186 std::chrono::milliseconds timeoutms = 30s;
1194 std::chrono::milliseconds delta = clamp(timeoutms - timer.
elapsed(), 10ms, 100ms);
1196 if (!check && timer.
elapsed() > 1s && (count % 100) == 0)
1197 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Taking too long to be allowed to read..");
1201 if (timer.
elapsed() >= timeoutms)
1203 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Took more than %1 seconds to be allowed to read, aborting.")
1204 .arg(duration_cast<std::chrono::seconds>(timeoutms).count()));
1213 if (available >= Count)
1216 Count = (
m_ateof && available < Count) ? available : Count;
1231 std::chrono::milliseconds delta = clamp(Timeout - timer.
elapsed(), 10ms, 250ms);
1238 if (timer.
elapsed() > Timeout)
1248 long long oldposition = 0;
1259 int elapsed = timer.
elapsed().count();
1260 uint64_t bps = !elapsed ? 1000000001 :
static_cast<uint64_t
>((result * 8000.0) /
static_cast<double>(elapsed));
1269 long long cur_pos = -1;
1272 else if (
m_fd2 >= 0)
1273 cur_pos = lseek64(
m_fd2, oldposition, SEEK_SET);
1276 LOG(VB_FILE, LOG_ERR,
LOC +
"Seek failed repositioning to previous position");
1288 if (
Peek && (result > 0))
1290 if ((
IsDVD() ||
IsBD()) && oldposition != 0)
1292 LOG(VB_GENERAL, LOG_ERR,
LOC +
1293 "DVD and Blu-Ray do not support arbitrary "
1294 "peeks except when read-ahead is enabled."
1295 "\n\t\t\tWill seek to beginning of video.");
1299 long long newposition =
Seek(oldposition, SEEK_SET,
true);
1301 if (newposition != oldposition)
1303 LOG(VB_GENERAL, LOG_ERR,
LOC +
1304 QString(
"Peek() Failed to return from new position %1 to old position %2, now "
1306 .arg(oldposition - result).arg(oldposition).arg(newposition));
1323 QString desc = QString(
"ReadPriv(..%1, %2)").arg(Count).arg(
Peek ?
"peek" :
"normal");
1324 LOG(VB_FILE, LOG_DEBUG,
LOC + desc + QString(
" @%1 -- begin").arg(
m_rbrPos));
1329 LOG(VB_GENERAL, LOG_ERR,
LOC + desc +
": Attempt to read from a write only file");
1347 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1348 quint16 checksum = qChecksum(
reinterpret_cast<char*
>(
Buffer),
static_cast<uint>(Count));
1350 QByteArrayView BufferView(
reinterpret_cast<char*
>(
Buffer), Count);
1351 quint16 checksum = qChecksum(BufferView);
1353 LOG(VB_FILE, LOG_DEBUG,
LOC + desc + QString(
": ReadDirect checksum %1")
1364 LOG(VB_FILE, LOG_NOTICE,
LOC + desc +
": !WaitForReadsAllowed()");
1377 std::chrono::milliseconds timeout_ms = 10s;
1384 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Checking to see if there's a new livetv program to switch to..");
1393 LOG(VB_GENERAL, LOG_WARNING,
LOC + desc + QString(
" -- waited %1 ms for avail(%2) > count(%3)")
1394 .arg(timer.
elapsed().count()).arg(available).arg(Count));
1399 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"ReadPriv: %1 bytes available, %2 left")
1415 LOG(VB_GENERAL, LOG_ERR,
LOC + desc + QString(
" -- timed out waiting for data (%1 ms)")
1416 .arg(timer.
elapsed().count()));
1433 LOG(VB_FILE, LOG_DEBUG,
LOC + desc +
": Copying data");
1435 int readposition = 0;
1441 if (readposition + Count >
static_cast<int>(
m_bufferSize))
1443 int firstsize =
static_cast<int>(
m_bufferSize) - readposition;
1444 int secondsize = Count - firstsize;
1453 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1454 quint16 checksum = qChecksum(
reinterpret_cast<char*
>(
Buffer),
static_cast<uint>(Count));
1456 QByteArrayView BufferView(
reinterpret_cast<char*
>(
Buffer), Count);
1457 quint16 checksum = qChecksum(BufferView);
1459 LOG(VB_FILE, LOG_DEBUG,
LOC + desc + QString(
": Checksum %1").arg(checksum));
1506 if (Rate > 1000000000)
1507 return QObject::tr(
">1Gbps");
1510 auto bitrate =
static_cast<double>(NAN);
1511 auto rate =
static_cast<double>(Rate);
1514 if (Rate >= 1000000)
1516 msg = Hz ? QObject::tr(
"%1MHz") : QObject::tr(
"%1Mbps");
1517 bitrate = rate / 1000000.0;
1520 else if (Rate >= 1000)
1522 msg = Hz ? QObject::tr(
"%1kHz") : QObject::tr(
"%1kbps");
1523 bitrate = rate / 1000.0;
1528 msg = Hz ? QObject::tr(
"%1Hz") : QObject::tr(
"%1bps");
1531 return msg.arg(bitrate, 0,
'f', range);
1551 return QString(
"%1%").arg(lroundf((
static_cast<float>(avail) /
static_cast<float>(
m_bufferSize) * 100.0F)));
1564 auto current = std::chrono::milliseconds(QDateTime::currentDateTime().toMSecsSinceEpoch());
1565 std::chrono::milliseconds expire =
current - 1s;
1571 QMutableMapIterator<std::chrono::milliseconds,uint64_t> it(
m_decoderReads);
1572 while (it.hasNext())
1575 if (it.key() < expire || it.key() >
current)
1578 total += it.value();
1584 auto average =
static_cast<uint64_t
>(
static_cast<double>(total) * 8.0);
1585 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Decoder read speed: %1 %2")
1586 .arg(average).arg(size));
1595 auto current = std::chrono::milliseconds(QDateTime::currentDateTime().toMSecsSinceEpoch());
1602 QMutableMapIterator<std::chrono::milliseconds,uint64_t> it(
m_storageReads);
1603 while (it.hasNext())
1606 if (it.key() < expire || it.key() >
current)
1609 total += it.value();
1615 uint64_t average = size ?
static_cast<uint64_t
>(
static_cast<double>(total) / size) : 0;
1616 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Average storage read speed: %1 (Reads %2")
1632 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Tried to write to a read only file.");
1674 long long result = -1;
1734 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"SetOldFile: %1)").arg(Old));
1863 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
1864 static QMutex s_avnetworkLock(QMutex::Recursive);
1866 static QRecursiveMutex s_avnetworkLock;
1868 static bool s_avnetworkInitialised =
false;
1869 QMutexLocker lock(&s_avnetworkLock);
1870 if (!s_avnetworkInitialised)
1872 avformat_network_init();
1873 s_avnetworkInitialised =
true;
std::chrono::milliseconds elapsed(void)
Returns milliseconds elapsed since last start() or restart()
long long Seek(long long pos, int whence)
Seek to a position within stream; May be unsafe.
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
void Sync(void) const
Flush data written to the file descriptor to disk.
A QElapsedTimer based timer to replace use of QTime as a timer.
static bool TestForHTTPLiveStreaming(const QString &filename)
static bool Exists(const QString &url, struct stat *fileinfo)
bool wait(std::chrono::milliseconds time=std::chrono::milliseconds::max())
Wait for the MThread to exit, with a maximum timeout.
void Flush(void)
Allow DiskLoop() to flush buffer completely ignoring low watermark.
void start(void)
starts measuring elapsed time.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
int Write(const void *data, int size)
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
bool IsRegisteredFileForWrite(const QString &file)
long long Seek(long long pos, int whence, long long curpos=-1)
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
void ReloadAll(const QStringList &data=QStringList())
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
int Write(const void *data, uint count)
Writes data to the end of the write buffer.
static ImageType inspectImage(const QString &path)
void SwitchToNext(bool up)
Sets the recording to switch to.
This is a wrapper around QThread that does several additional things.
bool isRunning(void) const
bool SetBlocking(bool block=true)
Set write blocking mode While in blocking mode, ThreadedFileWriter::Write will wait for buffers to be...
Keeps track of recordings in a current LiveTV instance.