FdInStream::FdInStream(int fd_, int timeoutms_,
bool closeWhenDone_)
: fd(fd_), closeWhenDone(closeWhenDone_),
- timeoutms(timeoutms_), blockCallback(0),
- timing(false), timeWaitedIn100us(5), timedKbits(0)
+ timeoutms(timeoutms_), blockCallback(0)
{
}
FdInStream::FdInStream(int fd_, FdInStreamBlockCallback* blockCallback_)
- : fd(fd_), timeoutms(0), blockCallback(blockCallback_),
- timing(false), timeWaitedIn100us(5), timedKbits(0)
+ : fd(fd_), timeoutms(0), blockCallback(blockCallback_)
{
}
size_t FdInStream::readWithTimeoutOrCallback(void* buf, size_t len, bool wait)
{
- struct timeval before, after;
- if (timing)
- gettimeofday(&before, 0);
-
int n;
while (true) {
do {
if (n < 0) throw SystemException("read",errno);
if (n == 0) throw EndOfStream();
- if (timing) {
- gettimeofday(&after, 0);
- int newTimeWaited = ((after.tv_sec - before.tv_sec) * 10000 +
- (after.tv_usec - before.tv_usec) / 100);
- int newKbits = n * 8 / 1000;
-
- // limit rate to between 10kbit/s and 40Mbit/s
-
- if (newTimeWaited > newKbits*1000) newTimeWaited = newKbits*1000;
- if (newTimeWaited < newKbits/4) newTimeWaited = newKbits/4;
-
- timeWaitedIn100us += newTimeWaited;
- timedKbits += newKbits;
- }
-
return n;
}
-
-void FdInStream::startTiming()
-{
- timing = true;
-
- // Carry over up to 1s worth of previous rate for smoothing.
-
- if (timeWaitedIn100us > 10000) {
- timedKbits = timedKbits * 10000 / timeWaitedIn100us;
- timeWaitedIn100us = 10000;
- }
-}
-
-void FdInStream::stopTiming()
-{
- timing = false;
- if (timeWaitedIn100us < timedKbits/2)
- timeWaitedIn100us = timedKbits/2; // upper limit 20Mbit/s
-}
-
-unsigned int FdInStream::kbitsPerSecond()
-{
- // The following calculation will overflow 32-bit arithmetic if we have
- // received more than about 50Mbytes (400Mbits) since we started timing, so
- // it should be OK for a single RFB update.
-
- return timedKbits * 10000 / timeWaitedIn100us;
-}
CConn::CConn(const char* vncServerName, network::Socket* socket=NULL)
: serverHost(0), serverPort(0), desktop(NULL),
updateCount(0), pixelCount(0),
- lastServerEncoding((unsigned int)-1)
+ lastServerEncoding((unsigned int)-1), bpsEstimate(20000000)
{
setShared(::shared);
sock = socket;
strcat(infoText, "\n");
snprintf(scratch, sizeof(scratch),
- _("Line speed estimate: %d kbit/s"), sock->inStream().kbitsPerSecond());
+ _("Line speed estimate: %d kbit/s"), (int)(bpsEstimate/1000));
strcat(infoText, scratch);
strcat(infoText, "\n");
{
CConnection::framebufferUpdateStart();
+ // For bandwidth estimate
+ gettimeofday(&updateStartTime, NULL);
+ updateStartPos = sock->inStream().pos();
+
// Update the screen prematurely for very slow updates
Fl::add_timeout(1.0, handleUpdateTimeout, this);
}
// appropriately, and then request another incremental update.
void CConn::framebufferUpdateEnd()
{
+ unsigned long long elapsed, bps;
+ struct timeval now;
+
CConnection::framebufferUpdateEnd();
updateCount++;
+ // Calculate bandwidth everything managed to maintain during this update
+ gettimeofday(&now, NULL);
+ elapsed = (now.tv_sec - updateStartTime.tv_sec) * 1000000;
+ elapsed += now.tv_usec - updateStartTime.tv_usec;
+ if (elapsed == 0)
+ elapsed = 1;
+ bps = (unsigned long long)(sock->inStream().pos() -
+ updateStartPos) * 8 *
+ 1000000 / elapsed;
+ // Allow this update to influence things more the longer it took, to a
+ // maximum of 20% of the new value.
+ if (elapsed > 2000000)
+ elapsed = 2000000;
+ bpsEstimate = ((bpsEstimate * (10000000 - elapsed)) +
+ (bps * elapsed)) / 10000000;
+
Fl::remove_timeout(handleUpdateTimeout, this);
desktop->updateWindow();
void CConn::dataRect(const Rect& r, int encoding)
{
- sock->inStream().startTiming();
-
if (encoding != encodingCopyRect)
lastServerEncoding = encoding;
CConnection::dataRect(r, encoding);
- sock->inStream().stopTiming();
-
pixelCount += r.area();
}
//
void CConn::autoSelectFormatAndEncoding()
{
- int kbitsPerSecond = sock->inStream().kbitsPerSecond();
- unsigned int timeWaited = sock->inStream().timeWaited();
bool newFullColour = fullColour;
int newQualityLevel = ::qualityLevel;
// Always use Tight
setPreferredEncoding(encodingTight);
- // Check that we have a decent bandwidth measurement
- if ((kbitsPerSecond == 0) || (timeWaited < 10000))
- return;
-
// Select appropriate quality level
if (!noJpeg) {
- if (kbitsPerSecond > 16000)
+ if (bpsEstimate > 16000000)
newQualityLevel = 8;
else
newQualityLevel = 6;
if (newQualityLevel != ::qualityLevel) {
vlog.info(_("Throughput %d kbit/s - changing to quality %d"),
- kbitsPerSecond, newQualityLevel);
+ (int)(bpsEstimate/1000), newQualityLevel);
::qualityLevel.setParam(newQualityLevel);
setQualityLevel(newQualityLevel);
}
}
// Select best color level
- newFullColour = (kbitsPerSecond > 256);
+ newFullColour = (bpsEstimate > 256000);
if (newFullColour != fullColour) {
if (newFullColour)
vlog.info(_("Throughput %d kbit/s - full color is now enabled"),
- kbitsPerSecond);
+ (int)(bpsEstimate/1000));
else
vlog.info(_("Throughput %d kbit/s - full color is now disabled"),
- kbitsPerSecond);
+ (int)(bpsEstimate/1000));
fullColour.setParam(newFullColour);
updatePixelFormat();
}