Initial /api/get_frame_stats
This commit is contained in:
@@ -695,6 +695,8 @@ bool ComparingUpdateTracker::compare(bool skipScrollDetection, const Region &ski
|
||||
std::vector<Rect> rects;
|
||||
std::vector<Rect>::iterator i;
|
||||
|
||||
changedPerc = 100;
|
||||
|
||||
if (!enabled)
|
||||
return false;
|
||||
|
||||
@@ -749,8 +751,13 @@ bool ComparingUpdateTracker::compare(bool skipScrollDetection, const Region &ski
|
||||
for (i = rects.begin(); i != rects.end(); i++)
|
||||
totalPixels += i->area();
|
||||
newChanged.get_rects(&rects);
|
||||
for (i = rects.begin(); i != rects.end(); i++)
|
||||
unsigned newchangedarea = 0;
|
||||
for (i = rects.begin(); i != rects.end(); i++) {
|
||||
missedPixels += i->area();
|
||||
newchangedarea += i->area();
|
||||
}
|
||||
|
||||
changedPerc = newchangedarea * 100 / fb->area();
|
||||
|
||||
if (changed.equals(newChanged))
|
||||
return false;
|
||||
|
||||
@@ -48,6 +48,8 @@ namespace rfb {
|
||||
virtual void getUpdateInfo(UpdateInfo* info, const Region& cliprgn);
|
||||
virtual void clear();
|
||||
|
||||
rdr::U8 changedPerc;
|
||||
|
||||
private:
|
||||
void compareRect(const Rect& r, Region* newchanged, const Region &skipCursorArea);
|
||||
PixelBuffer* fb;
|
||||
|
||||
@@ -298,6 +298,11 @@ size_t Congestion::getBandwidth()
|
||||
return congWindow * 1000 / safeBaseRTT;
|
||||
}
|
||||
|
||||
unsigned Congestion::getPingTime() const
|
||||
{
|
||||
return safeBaseRTT;
|
||||
}
|
||||
|
||||
void Congestion::debugTrace(const char* filename, int fd)
|
||||
{
|
||||
#ifdef CONGESTION_TRACE
|
||||
|
||||
@@ -51,6 +51,8 @@ namespace rfb {
|
||||
// per second.
|
||||
size_t getBandwidth();
|
||||
|
||||
unsigned getPingTime() const;
|
||||
|
||||
// debugTrace() writes the current congestion window, as well as the
|
||||
// congestion window of the underlying TCP layer, to the specified
|
||||
// file
|
||||
|
||||
@@ -359,6 +359,8 @@ void EncodeManager::doUpdate(bool allowLossy, const Region& changed_,
|
||||
changed = changed_;
|
||||
|
||||
gettimeofday(&start, NULL);
|
||||
memset(&jpegstats, 0, sizeof(codecstats_t));
|
||||
memset(&webpstats, 0, sizeof(codecstats_t));
|
||||
|
||||
if (allowLossy && activeEncoders[encoderFullColour] == encoderTightWEBP) {
|
||||
const unsigned rate = 1024 * 1000 / rfb::Server::frameRate;
|
||||
@@ -1014,6 +1016,7 @@ void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb,
|
||||
std::vector<uint8_t> isWebp, fromCache;
|
||||
std::vector<Palette> palettes;
|
||||
std::vector<std::vector<uint8_t> > compresseds;
|
||||
std::vector<uint32_t> ms;
|
||||
uint32_t i;
|
||||
|
||||
if (rfb::Server::rectThreads > 0)
|
||||
@@ -1078,6 +1081,7 @@ void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb,
|
||||
palettes.resize(subrects.size());
|
||||
compresseds.resize(subrects.size());
|
||||
scaledrects.resize(subrects.size());
|
||||
ms.resize(subrects.size());
|
||||
|
||||
// In case the current resolution is above the max video res, and video was detected,
|
||||
// scale to that res, keeping aspect ratio
|
||||
@@ -1134,10 +1138,19 @@ void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb,
|
||||
for (i = 0; i < subrects.size(); ++i) {
|
||||
encoderTypes[i] = getEncoderType(subrects[i], pb, &palettes[i], compresseds[i],
|
||||
&isWebp[i], &fromCache[i],
|
||||
scaledpb, scaledrects[i]);
|
||||
scaledpb, scaledrects[i], ms[i]);
|
||||
checkWebpFallback(start);
|
||||
}
|
||||
|
||||
for (i = 0; i < subrects.size(); ++i) {
|
||||
if (encoderTypes[i] == encoderFullColour) {
|
||||
if (isWebp[i])
|
||||
webpstats.ms += ms[i];
|
||||
else
|
||||
jpegstats.ms += ms[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (start) {
|
||||
encodingTime = msSince(start);
|
||||
|
||||
@@ -1178,7 +1191,8 @@ void EncodeManager::writeRects(const Region& changed, const PixelBuffer* pb,
|
||||
uint8_t EncodeManager::getEncoderType(const Rect& rect, const PixelBuffer *pb,
|
||||
Palette *pal, std::vector<uint8_t> &compressed,
|
||||
uint8_t *isWebp, uint8_t *fromCache,
|
||||
const PixelBuffer *scaledpb, const Rect& scaledrect) const
|
||||
const PixelBuffer *scaledpb, const Rect& scaledrect,
|
||||
uint32_t &ms) const
|
||||
{
|
||||
struct RectInfo info;
|
||||
unsigned int maxColours = 256;
|
||||
@@ -1231,9 +1245,12 @@ uint8_t EncodeManager::getEncoderType(const Rect& rect, const PixelBuffer *pb,
|
||||
|
||||
*isWebp = 0;
|
||||
*fromCache = 0;
|
||||
ms = 0;
|
||||
if (type == encoderFullColour) {
|
||||
uint32_t len;
|
||||
const void *data;
|
||||
struct timeval start;
|
||||
gettimeofday(&start, NULL);
|
||||
|
||||
if (encCache->enabled &&
|
||||
(data = encCache->get(activeEncoders[encoderFullColour],
|
||||
@@ -1274,6 +1291,8 @@ uint8_t EncodeManager::getEncoderType(const Rect& rect, const PixelBuffer *pb,
|
||||
compressed,
|
||||
videoDetected);
|
||||
}
|
||||
|
||||
ms = msSince(&start);
|
||||
}
|
||||
|
||||
delete ppb;
|
||||
@@ -1292,10 +1311,15 @@ void EncodeManager::writeSubRect(const Rect& rect, const PixelBuffer *pb,
|
||||
encoder = startRect(rect, type, compressed.size() == 0, isWebp);
|
||||
|
||||
if (compressed.size()) {
|
||||
if (isWebp)
|
||||
if (isWebp) {
|
||||
((TightWEBPEncoder *) encoder)->writeOnly(compressed);
|
||||
else
|
||||
webpstats.area += rect.area();
|
||||
webpstats.rects++;
|
||||
} else {
|
||||
((TightJPEGEncoder *) encoder)->writeOnly(compressed);
|
||||
jpegstats.area += rect.area();
|
||||
jpegstats.rects++;
|
||||
}
|
||||
} else {
|
||||
if (encoder->flags & EncoderUseNativePF) {
|
||||
ppb = preparePixelBuffer(rect, pb, false);
|
||||
|
||||
@@ -72,6 +72,14 @@ namespace rfb {
|
||||
return encodingTime;
|
||||
};
|
||||
|
||||
struct codecstats_t {
|
||||
uint32_t ms;
|
||||
uint32_t area;
|
||||
uint32_t rects;
|
||||
};
|
||||
|
||||
codecstats_t jpegstats, webpstats;
|
||||
|
||||
protected:
|
||||
void doUpdate(bool allowLossy, const Region& changed,
|
||||
const Region& copied, const Point& copy_delta,
|
||||
@@ -105,7 +113,8 @@ namespace rfb {
|
||||
uint8_t getEncoderType(const Rect& rect, const PixelBuffer *pb, Palette *pal,
|
||||
std::vector<uint8_t> &compressed, uint8_t *isWebp,
|
||||
uint8_t *fromCache,
|
||||
const PixelBuffer *scaledpb, const Rect& scaledrect) const;
|
||||
const PixelBuffer *scaledpb, const Rect& scaledrect,
|
||||
uint32_t &ms) const;
|
||||
virtual bool handleTimeout(Timer* t);
|
||||
|
||||
bool checkSolidTile(const Rect& r, const rdr::U8* colourValue,
|
||||
|
||||
@@ -64,6 +64,7 @@ namespace rfb {
|
||||
const rdr::U8* const* data);
|
||||
|
||||
virtual void sendStats(const bool toClient = true) = 0;
|
||||
virtual void handleFrameStats(rdr::U32 all, rdr::U32 render) = 0;
|
||||
|
||||
virtual bool canChangeKasmSettings() const = 0;
|
||||
|
||||
|
||||
@@ -80,6 +80,9 @@ void SMsgReader::readMsg()
|
||||
case msgTypeRequestStats:
|
||||
readRequestStats();
|
||||
break;
|
||||
case msgTypeFrameStats:
|
||||
readFrameStats();
|
||||
break;
|
||||
case msgTypeKeyEvent:
|
||||
readKeyEvent();
|
||||
break;
|
||||
@@ -346,6 +349,14 @@ void SMsgReader::readRequestStats()
|
||||
handler->sendStats();
|
||||
}
|
||||
|
||||
void SMsgReader::readFrameStats()
|
||||
{
|
||||
is->skip(3);
|
||||
rdr::U32 all = is->readU32();
|
||||
rdr::U32 render = is->readU32();
|
||||
handler->handleFrameStats(all, render);
|
||||
}
|
||||
|
||||
void SMsgReader::readQEMUMessage()
|
||||
{
|
||||
int subType = is->readU8();
|
||||
|
||||
@@ -57,6 +57,7 @@ namespace rfb {
|
||||
void readClientCutText();
|
||||
void readExtendedClipboard(rdr::S32 len);
|
||||
void readRequestStats();
|
||||
void readFrameStats();
|
||||
|
||||
void readQEMUMessage();
|
||||
void readQEMUKeyEvent();
|
||||
|
||||
@@ -208,6 +208,12 @@ void SMsgWriter::writeStats(const char* str, int len)
|
||||
endMsg();
|
||||
}
|
||||
|
||||
void SMsgWriter::writeRequestFrameStats()
|
||||
{
|
||||
startMsg(msgTypeRequestFrameStats);
|
||||
endMsg();
|
||||
}
|
||||
|
||||
void SMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[])
|
||||
{
|
||||
if (!cp->supportsFence)
|
||||
|
||||
@@ -65,6 +65,8 @@ namespace rfb {
|
||||
|
||||
void writeStats(const char* str, int len);
|
||||
|
||||
void writeRequestFrameStats();
|
||||
|
||||
// writeFence() sends a new fence request or response to the client.
|
||||
void writeFence(rdr::U32 flags, unsigned len, const char data[]);
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
|
||||
continuousUpdates(false), encodeManager(this, &server_->encCache),
|
||||
needsPermCheck(false), pointerEventTime(0),
|
||||
clientHasCursor(false),
|
||||
accessRights(AccessDefault), startTime(time(0))
|
||||
accessRights(AccessDefault), startTime(time(0)), frameTracking(false)
|
||||
{
|
||||
setStreams(&sock->inStream(), &sock->outStream());
|
||||
peerEndpoint.buf = sock->getPeerEndpoint();
|
||||
@@ -99,6 +99,9 @@ VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
|
||||
gettimeofday(&lastKeyEvent, NULL);
|
||||
|
||||
server->clients.push_front(this);
|
||||
|
||||
if (server->apimessager)
|
||||
server->apimessager->mainUpdateUserInfo(checkOwnerConn(), server->clients.size());
|
||||
}
|
||||
|
||||
|
||||
@@ -129,6 +132,9 @@ VNCSConnectionST::~VNCSConnectionST()
|
||||
server->clients.remove(this);
|
||||
|
||||
delete [] fenceData;
|
||||
|
||||
if (server->apimessager)
|
||||
server->apimessager->mainUpdateUserInfo(checkOwnerConn(), server->clients.size());
|
||||
}
|
||||
|
||||
|
||||
@@ -1230,6 +1236,9 @@ void VNCSConnectionST::writeFramebufferUpdate()
|
||||
// window.
|
||||
sock->cork(true);
|
||||
|
||||
if (frameTracking)
|
||||
writer()->writeRequestFrameStats();
|
||||
|
||||
// First take care of any updates that cannot contain framebuffer data
|
||||
// changes.
|
||||
writeNoDataUpdate();
|
||||
@@ -1507,6 +1516,22 @@ void VNCSConnectionST::sendStats(const bool toClient) {
|
||||
}
|
||||
}
|
||||
|
||||
void VNCSConnectionST::handleFrameStats(rdr::U32 all, rdr::U32 render)
|
||||
{
|
||||
if (server->apimessager) {
|
||||
const char *at = strchr(peerEndpoint.buf, '@');
|
||||
if (!at)
|
||||
at = peerEndpoint.buf;
|
||||
else
|
||||
at++;
|
||||
|
||||
server->apimessager->mainUpdateClientFrameStats(at, render, all,
|
||||
congestion.getPingTime());
|
||||
}
|
||||
|
||||
frameTracking = false;
|
||||
}
|
||||
|
||||
// setCursor() is called whenever the cursor has changed shape or pixel format.
|
||||
// If the client supports local cursor then it will arrange for the cursor to
|
||||
// be sent to the client.
|
||||
@@ -1616,3 +1641,15 @@ int VNCSConnectionST::getStatus()
|
||||
return 4;
|
||||
}
|
||||
|
||||
bool VNCSConnectionST::checkOwnerConn() const
|
||||
{
|
||||
std::list<VNCSConnectionST*>::const_iterator it;
|
||||
|
||||
for (it = server->clients.begin(); it != server->clients.end(); it++) {
|
||||
bool write, owner;
|
||||
if ((*it)->getPerms(write, owner) && owner)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -165,6 +165,26 @@ namespace rfb {
|
||||
int getStatus();
|
||||
|
||||
virtual void sendStats(const bool toClient = true);
|
||||
virtual void handleFrameStats(rdr::U32 all, rdr::U32 render);
|
||||
|
||||
bool is_owner() const {
|
||||
bool write, owner;
|
||||
if (getPerms(write, owner) && owner)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void setFrameTracking() {
|
||||
frameTracking = true;
|
||||
}
|
||||
|
||||
EncodeManager::codecstats_t getJpegStats() const {
|
||||
return encodeManager.jpegstats;
|
||||
}
|
||||
|
||||
EncodeManager::codecstats_t getWebpStats() const {
|
||||
return encodeManager.webpstats;
|
||||
}
|
||||
|
||||
private:
|
||||
// SConnection callbacks
|
||||
@@ -220,6 +240,8 @@ namespace rfb {
|
||||
|
||||
bool getPerms(bool &write, bool &owner) const;
|
||||
|
||||
bool checkOwnerConn() const;
|
||||
|
||||
// Congestion control
|
||||
void writeRTTPing();
|
||||
bool isCongested();
|
||||
@@ -295,6 +317,8 @@ namespace rfb {
|
||||
time_t startTime;
|
||||
|
||||
std::vector<CopyPassRect> copypassed;
|
||||
|
||||
bool frameTracking;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -128,7 +128,7 @@ VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
|
||||
renderedCursorInvalid(false),
|
||||
queryConnectionHandler(0), keyRemapper(&KeyRemapper::defInstance),
|
||||
lastConnectionTime(0), disableclients(false),
|
||||
frameTimer(this), apimessager(NULL)
|
||||
frameTimer(this), apimessager(NULL), trackingFrameStats(0)
|
||||
{
|
||||
lastUserInputTime = lastDisconnectTime = time(0);
|
||||
slog.debug("creating single-threaded server %s", name.buf);
|
||||
@@ -210,6 +210,8 @@ VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
|
||||
if (inotify_add_watch(inotifyfd, kasmpasswdpath, IN_CLOSE_WRITE | IN_DELETE_SELF) < 0)
|
||||
slog.error("Failed to set watch");
|
||||
}
|
||||
|
||||
trackingClient[0] = 0;
|
||||
}
|
||||
|
||||
VNCServerST::~VNCServerST()
|
||||
@@ -774,7 +776,8 @@ int VNCServerST::msToNextUpdate()
|
||||
return frameTimer.getRemainingMs();
|
||||
}
|
||||
|
||||
static void checkAPIMessages(network::GetAPIMessager *apimessager)
|
||||
static void checkAPIMessages(network::GetAPIMessager *apimessager,
|
||||
rdr::U8 &trackingFrameStats, char trackingClient[])
|
||||
{
|
||||
if (pthread_mutex_lock(&apimessager->userMutex))
|
||||
return;
|
||||
@@ -827,6 +830,20 @@ static void checkAPIMessages(network::GetAPIMessager *apimessager)
|
||||
slog.error("Tried to give control to nonexistent user %s", act.data.user);
|
||||
}
|
||||
break;
|
||||
|
||||
case network::GetAPIMessager::WANT_FRAME_STATS_SERVERONLY:
|
||||
trackingFrameStats = act.action;
|
||||
break;
|
||||
case network::GetAPIMessager::WANT_FRAME_STATS_ALL:
|
||||
trackingFrameStats = act.action;
|
||||
break;
|
||||
case network::GetAPIMessager::WANT_FRAME_STATS_OWNER:
|
||||
trackingFrameStats = act.action;
|
||||
break;
|
||||
case network::GetAPIMessager::WANT_FRAME_STATS_SPECIFIC:
|
||||
trackingFrameStats = act.action;
|
||||
memcpy(trackingClient, act.data.password, 128);
|
||||
break;
|
||||
}
|
||||
|
||||
if (set) {
|
||||
@@ -923,6 +940,9 @@ void VNCServerST::writeUpdate()
|
||||
assert(blockCounter == 0);
|
||||
assert(desktopStarted);
|
||||
|
||||
struct timeval start;
|
||||
gettimeofday(&start, NULL);
|
||||
|
||||
if (DLPRegion.enabled) {
|
||||
comparer->enable_copyrect(false);
|
||||
blackOut();
|
||||
@@ -949,6 +969,9 @@ void VNCServerST::writeUpdate()
|
||||
else
|
||||
comparer->disable();
|
||||
|
||||
struct timeval beforeAnalysis;
|
||||
gettimeofday(&beforeAnalysis, NULL);
|
||||
|
||||
// Skip scroll detection if the client is slow, and didn't get the previous one yet
|
||||
if (comparer->compare(clients.size() == 1 && (*clients.begin())->has_copypassed(),
|
||||
cursorReg))
|
||||
@@ -956,6 +979,8 @@ void VNCServerST::writeUpdate()
|
||||
|
||||
comparer->clear();
|
||||
|
||||
const unsigned analysisMs = msSince(&beforeAnalysis);
|
||||
|
||||
encCache.clear();
|
||||
encCache.enabled = clients.size() > 1;
|
||||
|
||||
@@ -984,22 +1009,64 @@ void VNCServerST::writeUpdate()
|
||||
if (apimessager) {
|
||||
apimessager->mainUpdateScreen(pb);
|
||||
|
||||
checkAPIMessages(apimessager);
|
||||
trackingFrameStats = 0;
|
||||
checkAPIMessages(apimessager, trackingFrameStats, trackingClient);
|
||||
}
|
||||
|
||||
EncodeManager::codecstats_t jpegstats, webpstats;
|
||||
memset(&jpegstats, 0, sizeof(EncodeManager::codecstats_t));
|
||||
memset(&webpstats, 0, sizeof(EncodeManager::codecstats_t));
|
||||
|
||||
for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
|
||||
ci_next = ci; ci_next++;
|
||||
|
||||
if (permcheck)
|
||||
(*ci)->recheckPerms();
|
||||
|
||||
if (trackingFrameStats == network::GetAPIMessager::WANT_FRAME_STATS_ALL ||
|
||||
(trackingFrameStats == network::GetAPIMessager::WANT_FRAME_STATS_OWNER &&
|
||||
(*ci)->is_owner()) ||
|
||||
(trackingFrameStats == network::GetAPIMessager::WANT_FRAME_STATS_SPECIFIC &&
|
||||
strstr((*ci)->getPeerEndpoint(), trackingClient))) {
|
||||
|
||||
(*ci)->setFrameTracking();
|
||||
|
||||
// Only one owner
|
||||
if (trackingFrameStats == network::GetAPIMessager::WANT_FRAME_STATS_OWNER)
|
||||
trackingFrameStats = network::GetAPIMessager::WANT_FRAME_STATS_SERVERONLY;
|
||||
}
|
||||
|
||||
(*ci)->add_copied(ui.copied, ui.copy_delta);
|
||||
(*ci)->add_copypassed(ui.copypassed);
|
||||
(*ci)->add_changed(ui.changed);
|
||||
(*ci)->writeFramebufferUpdateOrClose();
|
||||
|
||||
if (apimessager)
|
||||
if (apimessager) {
|
||||
(*ci)->sendStats(false);
|
||||
const EncodeManager::codecstats_t subjpeg = (*ci)->getJpegStats();
|
||||
const EncodeManager::codecstats_t subwebp = (*ci)->getWebpStats();
|
||||
|
||||
jpegstats.ms += subjpeg.ms;
|
||||
jpegstats.area += subjpeg.area;
|
||||
jpegstats.rects += subjpeg.rects;
|
||||
|
||||
webpstats.ms += subwebp.ms;
|
||||
webpstats.area += subwebp.area;
|
||||
webpstats.rects += subwebp.rects;
|
||||
}
|
||||
}
|
||||
|
||||
if (trackingFrameStats) {
|
||||
const unsigned totalMs = msSince(&start);
|
||||
|
||||
if (apimessager)
|
||||
apimessager->mainUpdateServerFrameStats(comparer->changedPerc, totalMs,
|
||||
jpegstats.ms, webpstats.ms,
|
||||
analysisMs,
|
||||
jpegstats.area, webpstats.area,
|
||||
jpegstats.rects, webpstats.rects,
|
||||
pb->getRect().width(),
|
||||
pb->getRect().height());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -267,6 +267,9 @@ namespace rfb {
|
||||
|
||||
network::GetAPIMessager *apimessager;
|
||||
|
||||
rdr::U8 trackingFrameStats;
|
||||
char trackingClient[128];
|
||||
|
||||
struct {
|
||||
bool enabled;
|
||||
int x1, y1, x2, y2;
|
||||
|
||||
@@ -30,6 +30,7 @@ namespace rfb {
|
||||
|
||||
// kasm
|
||||
const int msgTypeStats = 178;
|
||||
const int msgTypeRequestFrameStats = 179;
|
||||
|
||||
const int msgTypeServerFence = 248;
|
||||
|
||||
@@ -47,6 +48,7 @@ namespace rfb {
|
||||
|
||||
// kasm
|
||||
const int msgTypeRequestStats = 178;
|
||||
const int msgTypeFrameStats = 179;
|
||||
|
||||
const int msgTypeClientFence = 248;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user