Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

RingBuffer: Make the buffer size dynamic.

- convert the buffer size to a member variable that can be adjusted mid-
- add buffer sizing factors that increase the size of the buffer for
networked playback, matroska streams and streams with an unknown video
bitrate.
- set the matroska and unknown bitrate flags when scanning the streams
in AvFormatDecoder.

RingBuffer behaviour is unchanged for playback of local files and for
RingBuffers operating on backends; the default 4Mb is ample for fast,
local disk access.

For networked playback, the 4Mb is doubled and doubled again for both
matroska files and files of an unknown bitrate - and hence the maximum
networked buffer size (in the frontend) is 32Mb. This is sufficient to
playback the now infamous galactica clip over an 18Mps wifi connection
with minimal pauses (and there are a couple of additional tweaks that
will remove those pauses altogether).

The specific triggers and multipliers used are a little contrived to fix
the worst case scenarios we have now (in many cases the buffer size will
be much larger than needed - improvements are more than welcome) but the
increase in buffer size for networked playback is probably long overdue.
  • Loading branch information...
commit bff85941ca0a0d09c2ea8365dcf097eedb735d15 1 parent 491cc4a
Mark Kendall authored
View
11 mythtv/libs/libmythtv/avformatdecoder.cpp
@@ -1693,6 +1693,7 @@ void AvFormatDecoder::ScanDSMCCStreams(void)
int AvFormatDecoder::ScanStreams(bool novideo)
{
+ bool unknownbitrate = false;
int scanerror = 0;
bitrate = 0;
fps = 0;
@@ -1745,6 +1746,9 @@ int AvFormatDecoder::ScanStreams(bool novideo)
codec_is_mpeg = CODEC_IS_FFMPEG_MPEG(enc->codec_id);
+ if (enc->bit_rate == 0)
+ unknownbitrate = true;
+
// HACK -- begin
// ffmpeg is unable to compute H.264 bitrates in mpegts?
if (CODEC_IS_H264(enc->codec_id) && enc->bit_rate == 0)
@@ -2082,6 +2086,13 @@ int AvFormatDecoder::ScanStreams(bool novideo)
ringBuffer->UpdateRawBitrate(bitrate);
}
+ // update RingBuffer buffer size
+ if (ringBuffer)
+ {
+ ringBuffer->SetBufferSizeFactors(unknownbitrate,
+ QString(ic->iformat->name).contains("matroska"));
+ }
+
PostProcessTracks();
// Select a new track at the next opportunity.
View
6 mythtv/libs/libmythtv/fileringbuffer.cpp
@@ -561,7 +561,7 @@ long long FileRingBuffer::Seek(long long pos, int whence, bool has_lock)
{
int min_safety = max(fill_min, readblocksize);
int free = ((rbwpos >= rbrpos) ?
- rbrpos + kBufferSize : rbrpos) - rbwpos;
+ rbrpos + bufferSize : rbrpos) - rbwpos;
int internal_backbuf =
(rbwpos >= rbrpos) ? rbrpos : rbrpos - rbwpos;
internal_backbuf = min(internal_backbuf, free - min_safety);
@@ -572,7 +572,7 @@ long long FileRingBuffer::Seek(long long pos, int whence, bool has_lock)
if (internal_backbuf >= sba)
{
rbrpos = (rbrpos>=sba) ? rbrpos - sba :
- kBufferSize + rbrpos - sba;
+ bufferSize + rbrpos - sba;
used_opt = true;
VERBOSE(VB_FILE, LOC +
QString("Seek(): OPT1 rbrpos: %1 rbwpos: %2"
@@ -583,7 +583,7 @@ long long FileRingBuffer::Seek(long long pos, int whence, bool has_lock)
}
else if ((new_pos >= readpos) && (new_pos <= internalreadpos))
{
- rbrpos = (rbrpos + (new_pos - readpos)) % kBufferSize;
+ rbrpos = (rbrpos + (new_pos - readpos)) % bufferSize;
used_opt = true;
VERBOSE(VB_FILE, LOC +
QString("Seek(): OPT2 rbrpos: %1 sba: %2")
View
99 mythtv/libs/libmythtv/ringbuffer.cpp
@@ -28,7 +28,11 @@
#include "util.h"
// about one second at 35mbit
-const uint RingBuffer::kBufferSize = 4 * 1024 * 1024;
+#define BUFFER_SIZE_MINIMUM 4 * 1024 * 1024
+#define BUFFER_FACTOR_NETWORK 2
+#define BUFFER_FACTOR_BITRATE 2
+#define BUFFER_FACTOR_MATROSKA 2
+
const int RingBuffer::kDefaultOpenTimeout = 2000; // ms
const int RingBuffer::kLiveTVOpenTimeout = 10000;
@@ -175,6 +179,8 @@ RingBuffer::RingBuffer(void) :
filename(), subtitlefilename(),
tfw(NULL), fd2(-1),
writemode(false), remotefile(NULL),
+ bufferSize(BUFFER_SIZE_MINIMUM),
+ fileismatroska(false), unknownbitrate(false),
startreadahead(false), readAheadBuffer(NULL),
readaheadrunning(false), reallyrunning(false),
request_pause(false), paused(false),
@@ -304,6 +310,20 @@ void RingBuffer::UpdatePlaySpeed(float play_speed)
rwlock.unlock();
}
+/** \fn RingBuffer::SetBufferSizeFactors(bool, bool)
+ * \brief Tells RingBuffer that the raw bitrate may be innacurate and the
+ * underlying container is matroska, both of which may require a larger
+ * buffer size.
+ */
+void RingBuffer::SetBufferSizeFactors(bool estbitrate, bool matroska)
+{
+ rwlock.lockForWrite();
+ unknownbitrate = estbitrate;
+ fileismatroska = matroska;
+ rwlock.unlock();
+ CreateReadAheadBuffer();
+}
+
/** \fn RingBuffer::CalcReadAheadThresh(void)
* \brief Calculates fill_min, fill_threshold, and readblocksize
* from the estimated effective bitrate of the stream.
@@ -319,7 +339,7 @@ void RingBuffer::CalcReadAheadThresh(void)
readblocksize = max(readblocksize, CHUNK);
// loop without sleeping if the buffered data is less than this
- fill_threshold = 3 * kBufferSize / 4;
+ fill_threshold = 3 * bufferSize / 8;
const uint KB32 = 32*1024;
const uint KB64 = 64*1024;
@@ -384,7 +404,7 @@ int RingBuffer::ReadBufFree(void) const
{
rbrlock.lockForRead();
rbwlock.lockForRead();
- int ret = ((rbwpos >= rbrpos) ? rbrpos + kBufferSize : rbrpos) - rbwpos - 1;
+ int ret = ((rbwpos >= rbrpos) ? rbrpos + bufferSize : rbrpos) - rbwpos - 1;
rbwlock.unlock();
rbrlock.unlock();
return ret;
@@ -396,7 +416,7 @@ int RingBuffer::ReadBufAvail(void) const
{
rbrlock.lockForRead();
rbwlock.lockForRead();
- int ret = (rbwpos >= rbrpos) ? rbwpos - rbrpos : kBufferSize - rbrpos + rbwpos;
+ int ret = (rbwpos >= rbrpos) ? rbwpos - rbrpos : bufferSize - rbrpos + rbwpos;
rbwlock.unlock();
rbrlock.unlock();
return ret;
@@ -623,6 +643,53 @@ bool RingBuffer::PauseAndWait(void)
return request_pause || paused;
}
+void RingBuffer::CreateReadAheadBuffer(void)
+{
+ rwlock.lockForWrite();
+ poslock.lockForWrite();
+
+ uint oldsize = bufferSize;
+ uint newsize = BUFFER_SIZE_MINIMUM;
+ if (remotefile)
+ {
+ newsize *= BUFFER_FACTOR_NETWORK;
+ if (fileismatroska)
+ newsize *= BUFFER_FACTOR_MATROSKA;
+ if (unknownbitrate)
+ newsize *= BUFFER_FACTOR_BITRATE;
+ }
+
+ // N.B. Don't try and make it smaller - bad things happen...
+ if (readAheadBuffer && oldsize >= newsize)
+ {
+ poslock.unlock();
+ rwlock.unlock();
+ return;
+ }
+
+ bufferSize = newsize;
+ if (readAheadBuffer)
+ {
+ char* newbuffer = new char[bufferSize + 1024];
+ memcpy(newbuffer, readAheadBuffer + rbwpos, oldsize - rbwpos);
+ memcpy(newbuffer + (oldsize - rbwpos), readAheadBuffer, rbwpos);
+ delete [] readAheadBuffer;
+ readAheadBuffer = newbuffer;
+ rbrpos = rbrpos + oldsize - rbwpos;
+ rbwpos = oldsize;
+ }
+ else
+ {
+ readAheadBuffer = new char[bufferSize + 1024];
+ }
+ CalcReadAheadThresh();
+ poslock.unlock();
+ rwlock.unlock();
+
+ VERBOSE(VB_FILE, LOC + QString("Created readAheadBuffer: %1Mb")
+ .arg(newsize >> 20));
+}
+
void RingBuffer::run(void)
{
// These variables are used to adjust the read block size
@@ -632,10 +699,10 @@ void RingBuffer::run(void)
gettimeofday(&lastread, NULL); // this is just to keep gcc happy
+ CreateReadAheadBuffer();
rwlock.lockForWrite();
poslock.lockForWrite();
request_pause = false;
- readAheadBuffer = new char[kBufferSize + 1024];
ResetReadAhead(0);
readaheadrunning = true;
reallyrunning = true;
@@ -697,7 +764,7 @@ void RingBuffer::run(void)
(now.tv_usec - lastread.tv_usec) / 1000;
readtimeavg = (readtimeavg * 9 + readinterval) / 10;
- if (readtimeavg < 150 && (uint)readblocksize < (kBufferSize>>2))
+ if (readtimeavg < 150 && (uint)readblocksize < (bufferSize>>2))
{
int old_block_size = readblocksize;
readblocksize = 3 * readblocksize / 2;
@@ -726,9 +793,9 @@ void RingBuffer::run(void)
lastread = now;
rbwlock.lockForRead();
- if (rbwpos + totfree > kBufferSize)
+ if (rbwpos + totfree > bufferSize)
{
- totfree = kBufferSize - rbwpos;
+ totfree = bufferSize - rbwpos;
VERBOSE(VB_FILE|VB_EXTRA, LOC +
"Shrinking read, near end of buffer");
}
@@ -769,7 +836,7 @@ void RingBuffer::run(void)
poslock.lockForWrite();
rbwlock.lockForWrite();
internalreadpos += read_return;
- rbwpos = (rbwpos + read_return) % kBufferSize;
+ rbwpos = (rbwpos + read_return) % bufferSize;
VERBOSE(VB_FILE|VB_EXTRA,
LOC + QString("rbwpos += %1K requested %2K in read")
.arg(read_return/1024,3).arg(totfree/1024,3));
@@ -777,7 +844,7 @@ void RingBuffer::run(void)
poslock.unlock();
}
- int used = kBufferSize - ReadBufFree();
+ int used = bufferSize - ReadBufFree();
if ((0 == read_return) || (numfailures > 5) ||
(readsallowed != (used >= fill_min || ateof ||
@@ -816,7 +883,7 @@ void RingBuffer::run(void)
rwlock.unlock();
rwlock.lockForRead();
- used = kBufferSize - ReadBufFree();
+ used = bufferSize - ReadBufFree();
}
VERBOSE(VB_FILE|VB_EXTRA, LOC + "@ end of read ahead loop");
@@ -1148,9 +1215,9 @@ int RingBuffer::ReadPriv(void *buf, int count, bool peek)
VERBOSE(VB_FILE|VB_EXTRA, LOC + loc_desc + " -- copying data");
- if (rbrpos + count > (int) kBufferSize)
+ if (rbrpos + count > (int) bufferSize)
{
- int firstsize = kBufferSize - rbrpos;
+ int firstsize = bufferSize - rbrpos;
int secondsize = count - firstsize;
memcpy(buf, readAheadBuffer + rbrpos, firstsize);
@@ -1165,7 +1232,7 @@ int RingBuffer::ReadPriv(void *buf, int count, bool peek)
if (!peek)
{
- rbrpos = (rbrpos + count) % kBufferSize;
+ rbrpos = (rbrpos + count) % bufferSize;
generalWait.wakeAll();
}
rbrlock.unlock();
@@ -1240,8 +1307,8 @@ QString RingBuffer::GetStorageRate(void)
QString RingBuffer::GetAvailableBuffer(void)
{
- int avail = (rbwpos >= rbrpos) ? rbwpos - rbrpos : kBufferSize - rbrpos + rbwpos;
- return QString("%1%").arg((int)(((float)avail / (float)kBufferSize) * 100.0));
+ int avail = (rbwpos >= rbrpos) ? rbwpos - rbrpos : bufferSize - rbrpos + rbwpos;
+ return QString("%1%").arg((int)(((float)avail / (float)bufferSize) * 100.0));
}
uint64_t RingBuffer::UpdateDecoderRate(uint64_t latest)
View
9 mythtv/libs/libmythtv/ringbuffer.h
@@ -47,6 +47,7 @@ class MTV_PUBLIC RingBuffer : protected QThread
void UpdateRawBitrate(uint rawbitrate);
void UpdatePlaySpeed(float playspeed);
void EnableBitrateMonitor(bool enable) { bitrateMonitorEnabled = enable; }
+ void SetBufferSizeFactors(bool estbitrate, bool matroska);
// Gets
QString GetSafeFilename(void) { return safefilename; }
@@ -141,6 +142,7 @@ class MTV_PUBLIC RingBuffer : protected QThread
RingBuffer();
void run(void); // QThread
+ void CreateReadAheadBuffer(void);
void CalcReadAheadThresh(void);
bool PauseAndWait(void);
virtual int safe_read(void *data, uint sz) = 0;
@@ -187,6 +189,9 @@ class MTV_PUBLIC RingBuffer : protected QThread
RemoteFile *remotefile; // protected by rwlock
+ uint bufferSize; // protected by rwlock
+ bool fileismatroska; // protected by rwlock
+ bool unknownbitrate; // protected by rwlock
bool startreadahead; // protected by rwlock
char *readAheadBuffer; // protected by rwlock
bool readaheadrunning; // protected by rwlock
@@ -233,10 +238,6 @@ class MTV_PUBLIC RingBuffer : protected QThread
static QMutex subExtLock;
static QStringList subExt;
static QStringList subExtNoCheck;
-
- // constants
- public:
- static const uint kBufferSize;
};
#endif // _RINGBUFFER_H_
Please sign in to comment.
Something went wrong with that request. Please try again.