Go to the documentation of this file.
17 #include <QReadLocker>
23 #include "libmythbase/mythconfig.h"
42 #include "libavformat/avformat.h"
45 #define LOC QString("RingBuf(%1): ").arg(m_filename)
100 bool UseReadAhead, std::chrono::milliseconds Timeout,
bool StreamOnly)
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");
132 if (httpurl || iptvurl)
138 if (!StreamOnly && mythurl)
140 struct stat fileInfo {};
142 (S_ISDIR(fileInfo.st_mode)))
150 else if (!StreamOnly && !mythurl)
158 if (!StreamOnly && (dvdurl || dvddir))
165 LOG(VB_PLAYBACK, LOG_INFO,
"Trying DVD at " +
filename);
169 if (!StreamOnly && (bdurl || bddir))
176 LOG(VB_PLAYBACK, LOG_INFO,
"Trying BD at " +
filename);
180 if (!mythurl && imgext &&
filename.startsWith(
"dvd:"))
182 LOG(VB_PLAYBACK, LOG_INFO,
"DVD image at " +
filename);
186 if (!mythurl && lower.endsWith(
".vob") &&
filename.contains(
"/VIDEO_TS/"))
188 LOG(VB_PLAYBACK, LOG_INFO,
"DVD VOB at " +
filename);
190 if (dvdstream && dvdstream->IsOpen())
240 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Reset(%1,%2,%3)")
241 .arg(Full).arg(ToAdjust).arg(ResetInternal));
255 LOG(VB_GENERAL, LOG_ERR,
LOC +
256 QString(
"MythMediaBuffer::Reset() nonzero readpos. toAdjust: %1 "
257 "readpos: %2 readAdjust: %3")
281 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"UpdateRawBitrate(%1Kb)").arg(RawBitrate));
287 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Bitrate too low - setting to 64Kb"));
290 else if (RawBitrate > 100000)
292 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Bitrate too high - setting to 100Mb"));
341 if (estbitrate > 18000)
343 if (estbitrate > 9000)
345 if (estbitrate > 5000)
347 if (estbitrate > 2500)
349 if (estbitrate > 1250)
351 if (estbitrate >= 500)
353 if (estbitrate > 250)
355 if (estbitrate > 125)
387 float secs_min = 0.3F;
389 m_fillMin =
static_cast<int>((estbitrate * 1000 * secs_min) * 0.125F);
394 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Buffering optimisations disabled.");
402 LOG(VB_GENERAL, LOG_WARNING,
"Enabling buffering optimisations for low bitrate stream.");
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)")
428 if (kbitspersec == 0)
431 double readahead_time = size / (kbitspersec * (1000.0 / 8.0));
433 bool near_end = readahead_time <= 1.5;
434 LOG(VB_PLAYBACK, LOG_INFO,
LOC +
"IsReallyNearEnd()" +
435 QString(
" br(%1KB)").arg(kbitspersec/8) +
436 QString(
" sz(%1KB)").arg(size / 1000LL) +
437 QString(
" vfl(%1)").arg(Frames) +
438 QString(
" time(%1)").arg(readahead_time) +
440 QString(
" avail(%1)").arg(size) +
442 QString(
" readposition(%1)").arg(readpos) +
444 QString(
" paused(%1)").arg(
m_paused) +
445 QString(
" ne:%1").arg(near_end));
481 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Seek: Position:%1 Type: %2 Locked: %3)")
483 .arg((SEEK_SET == Whence) ?
"SEEK_SET" : ((SEEK_CUR == Whence) ?
"SEEK_CUR" :
"SEEK_END"),
484 HasLock?
"locked":
"unlocked"));
544 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"SetReadInternalMode: %1").arg(
Mode ?
"on" :
"off"));
577 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"ResetReadAhead(internalreadpos = %1->%2)")
628 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Not starting read ahead thread - write only RingBuffer");
633 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Not starting read ahead thread - already running");
671 LOG(VB_FILE, LOG_INFO,
LOC +
"StopReads()");
681 LOG(VB_FILE, LOG_INFO,
LOC +
"StartReads()");
692 LOG(VB_FILE, LOG_INFO,
LOC +
"Pausing read ahead thread");
706 LOG(VB_FILE, LOG_INFO,
LOC +
"Unpausing readahead thread");
728 LOG(VB_GENERAL, LOG_WARNING,
LOC + QString(
"Waited %1 ms for ringbuffer pause")
729 .arg(
t.elapsed().count()));
811 m_rbwPos =
static_cast<int>(oldsize);
821 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Created readAheadBuffer: %1Mb")
822 .arg(newsize >> 20));
830 std::chrono::milliseconds readTimeAvg = 300ms;
831 bool ignoreForReadTiming =
true;
834 auto lastread = nowAsDuration<std::chrono::milliseconds>();
853 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Initial readblocksize %1K fillMin %2K")
864 LOG(VB_FILE, LOG_WARNING,
LOC + QString(
"File not opened, terminating readahead thread"));
873 ignoreForReadTiming =
true;
874 LOG(VB_FILE, LOG_DEBUG,
LOC +
"run: PauseAndWait Not reading continuing");
880 const uint KB32 = 32*1024;
881 const int KB512 = 512*1024;
889 LOG(VB_FILE, LOG_DEBUG,
LOC +
890 QString(
"run: Not reading continuing: totfree(%1) "
891 "readsallowed(%2) ignorereadpos(%3) commserror(%4) "
902 ignoreForReadTiming =
true;
912 totfree = (totfree / KB32) * KB32;
917 auto now = nowAsDuration<std::chrono::milliseconds>();
918 if (!ignoreForReadTiming)
920 readTimeAvg = (readTimeAvg * 9 + (now - lastread)) / 10;
922 if (readTimeAvg < 150ms &&
931 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Avg read interval was %1 msec. "
932 "%2K -> %3K block size")
933 .arg(readTimeAvg.count()).arg(old_block_size/1024).arg(
m_readBlockSize/1024));
939 LOG(VB_FILE, LOG_INFO,
LOC +
940 QString(
"Avg read interval was %1 msec. %2K -> %3K block size")
941 .arg(readTimeAvg.count())
953 LOG(VB_FILE, LOG_DEBUG,
LOC +
"Shrinking read, near end of buffer");
959 LOG(VB_FILE, LOG_DEBUG,
LOC +
"Reading enough data to start playback");
962 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"safe_read(...@%1, %2) -- begin")
976 int sr_elapsed = sr_timer.
elapsed().count();
977 uint64_t bps = !sr_elapsed ? 1000000001 :
978 static_cast<uint64_t
>((readResult * 8000.0) /
static_cast<double>(sr_elapsed));
979 LOG(VB_FILE, LOG_DEBUG,
LOC +
980 QString(
"safe_read(...@%1, %2) -> %3, took %4 ms %5 avg %6 ms")
981 .arg(rbwposcopy).arg(totfree).arg(readResult).arg(sr_elapsed)
982 .arg(QString(
"(%1Mbps)").arg(
static_cast<double>(bps) / 1000000.0))
983 .arg(readTimeAvg.count()));
995 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"rbwpos += %1K requested %2K in read")
996 .arg(readResult/1024,3).arg(totfree/1024,3));
1003 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"total read so far: %1 bytes")
1009 LOG(VB_FILE, LOG_DEBUG,
LOC +
1010 QString(
"We are not reading anything (totfree: %1 commserror:%2 ateof:%3 setswitchtonext:%4")
1017 ignoreForReadTiming = (totfree <
m_readBlockSize) || (readResult < totfree);
1035 if ((0 == readResult) && (oldreadposition ==
m_readPos))
1057 LOG(VB_FILE, LOG_DEBUG,
LOC +
1058 QString(
"EOF encountered, but %1 still being written to")
1068 LOG(VB_FILE, LOG_DEBUG,
LOC +
1069 QString(
"EOF encountered, but %1 still being written to")
1081 LOG(VB_FILE, LOG_DEBUG,
LOC +
"Waiting for file to grow large enough to process.");
1086 LOG(VB_FILE, LOG_DEBUG,
LOC +
"setting ateof (readResult == 0)");
1101 LOG(VB_FILE, LOG_DEBUG,
LOC +
"@ end of read ahead loop");
1104 (m_wantToRead <= used && m_wantToRead > 0))
1111 std::this_thread::sleep_for(5ms);
1128 std::this_thread::sleep_for(5ms);
1153 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Exiting readahead thread"));
1173 if (result != Count)
1175 LOG(VB_GENERAL, LOG_WARNING,
LOC + QString(
"Peek requested %1 bytes but only have %2")
1176 .arg(Count).arg(result));
1191 std::chrono::milliseconds timeoutms = 30s;
1199 std::chrono::milliseconds delta =
clamp(timeoutms - timer.
elapsed(), 10ms, 100ms);
1201 if (!check && timer.
elapsed() > 1s && (count % 100) == 0)
1202 LOG(VB_GENERAL, LOG_WARNING,
LOC +
"Taking too long to be allowed to read..");
1206 if (timer.
elapsed() >= timeoutms)
1208 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Took more than %1 seconds to be allowed to read, aborting.")
1209 .arg(duration_cast<std::chrono::seconds>(timeoutms).count()));
1218 if (available >= Count)
1221 Count = (
m_ateof && available < Count) ? available : Count;
1236 std::chrono::milliseconds delta =
clamp(Timeout - timer.
elapsed(), 10ms, 250ms);
1243 if (timer.
elapsed() > Timeout)
1253 long long oldposition = 0;
1264 int elapsed = timer.
elapsed().count();
1265 uint64_t bps = !elapsed ? 1000000001 :
static_cast<uint64_t
>((result * 8000.0) /
static_cast<double>(elapsed));
1274 long long cur_pos = -1;
1277 else if (
m_fd2 >= 0)
1278 cur_pos = lseek64(
m_fd2, oldposition, SEEK_SET);
1281 LOG(VB_FILE, LOG_ERR,
LOC +
"Seek failed repositioning to previous position");
1293 if (
Peek && (result > 0))
1295 if ((
IsDVD() ||
IsBD()) && oldposition != 0)
1297 LOG(VB_GENERAL, LOG_ERR,
LOC +
1298 "DVD and Blu-Ray do not support arbitrary "
1299 "peeks except when read-ahead is enabled."
1300 "\n\t\t\tWill seek to beginning of video.");
1304 long long newposition =
Seek(oldposition, SEEK_SET,
true);
1306 if (newposition != oldposition)
1308 LOG(VB_GENERAL, LOG_ERR,
LOC +
1309 QString(
"Peek() Failed to return from new position %1 to old position %2, now "
1311 .arg(oldposition - result).arg(oldposition).arg(newposition));
1328 QString desc = QString(
"ReadPriv(..%1, %2)").arg(Count).arg(
Peek ?
"peek" :
"normal");
1329 LOG(VB_FILE, LOG_DEBUG,
LOC + desc + QString(
" @%1 -- begin").arg(
m_rbrPos));
1334 LOG(VB_GENERAL, LOG_ERR,
LOC + desc +
": Attempt to read from a write only file");
1352 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1353 quint16 checksum = qChecksum(
reinterpret_cast<char*
>(
Buffer),
static_cast<uint>(Count));
1355 QByteArrayView BufferView(
reinterpret_cast<char*
>(
Buffer), Count);
1356 quint16 checksum = qChecksum(BufferView);
1358 LOG(VB_FILE, LOG_DEBUG,
LOC + desc + QString(
": ReadDirect checksum %1")
1369 LOG(VB_FILE, LOG_NOTICE,
LOC + desc +
": !WaitForReadsAllowed()");
1382 std::chrono::milliseconds timeout_ms = 10s;
1389 LOG(VB_GENERAL, LOG_INFO,
LOC +
"Checking to see if there's a new livetv program to switch to..");
1398 LOG(VB_GENERAL, LOG_WARNING,
LOC + desc + QString(
" -- waited %1 ms for avail(%2) > count(%3)")
1399 .arg(timer.
elapsed().count()).arg(available).arg(Count));
1404 LOG(VB_FILE, LOG_DEBUG,
LOC + QString(
"ReadPriv: %1 bytes available, %2 left")
1420 LOG(VB_GENERAL, LOG_ERR,
LOC + desc + QString(
" -- timed out waiting for data (%1 ms)")
1421 .arg(timer.
elapsed().count()));
1438 LOG(VB_FILE, LOG_DEBUG,
LOC + desc +
": Copying data");
1440 int readposition = 0;
1446 if (readposition + Count >
static_cast<int>(
m_bufferSize))
1448 int firstsize =
static_cast<int>(
m_bufferSize) - readposition;
1449 int secondsize = Count - firstsize;
1458 #if QT_VERSION < QT_VERSION_CHECK(6,0,0)
1459 quint16 checksum = qChecksum(
reinterpret_cast<char*
>(
Buffer),
static_cast<uint>(Count));
1461 QByteArrayView BufferView(
reinterpret_cast<char*
>(
Buffer), Count);
1462 quint16 checksum = qChecksum(BufferView);
1464 LOG(VB_FILE, LOG_DEBUG,
LOC + desc + QString(
": Checksum %1").arg(checksum));
1511 if (Rate > 1000000000)
1512 return QObject::tr(
">1Gbps");
1515 auto bitrate =
static_cast<double>(NAN);
1516 auto rate =
static_cast<double>(Rate);
1519 if (Rate >= 1000000)
1521 msg = Hz ? QObject::tr(
"%1MHz") : QObject::tr(
"%1Mbps");
1522 bitrate = rate / 1000000.0;
1525 else if (Rate >= 1000)
1527 msg = Hz ? QObject::tr(
"%1kHz") : QObject::tr(
"%1kbps");
1528 bitrate = rate / 1000.0;
1533 msg = Hz ? QObject::tr(
"%1Hz") : QObject::tr(
"%1bps");
1536 return msg.arg(bitrate, 0,
'f', range);
1556 return QString(
"%1%").arg(lroundf((
static_cast<float>(avail) /
static_cast<float>(
m_bufferSize) * 100.0F)));
1569 auto current = std::chrono::milliseconds(QDateTime::currentMSecsSinceEpoch());
1570 std::chrono::milliseconds expire =
current - 1s;
1576 QMutableMapIterator<std::chrono::milliseconds,uint64_t> it(
m_decoderReads);
1577 while (it.hasNext())
1580 if (it.key() < expire || it.key() >
current)
1583 total += it.value();
1589 auto average =
static_cast<uint64_t
>(
static_cast<double>(total) * 8.0);
1590 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Decoder read speed: %1 %2")
1591 .arg(average).arg(size));
1600 auto current = std::chrono::milliseconds(QDateTime::currentMSecsSinceEpoch());
1607 QMutableMapIterator<std::chrono::milliseconds,uint64_t> it(
m_storageReads);
1608 while (it.hasNext())
1611 if (it.key() < expire || it.key() >
current)
1614 total += it.value();
1620 uint64_t average = size ?
static_cast<uint64_t
>(
static_cast<double>(total) / size) : 0;
1621 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"Average storage read speed: %1 (Reads %2")
1637 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Tried to write to a read only file.");
1679 long long result = -1;
1739 LOG(VB_FILE, LOG_INFO,
LOC + QString(
"SetOldFile: %1)").arg(Old));
1868 static QRecursiveMutex s_avnetworkLock;
1869 static bool s_avnetworkInitialised =
false;
1870 QMutexLocker lock(&s_avnetworkLock);
1871 if (!s_avnetworkInitialised)
1873 avformat_network_init();
1874 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.
static eu8 clamp(eu8 value, eu8 low, eu8 high)
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.