diff options
Diffstat (limited to 'x11vnc/rates.c')
-rw-r--r-- | x11vnc/rates.c | 595 |
1 files changed, 595 insertions, 0 deletions
diff --git a/x11vnc/rates.c b/x11vnc/rates.c new file mode 100644 index 0000000..b6516b2 --- /dev/null +++ b/x11vnc/rates.c @@ -0,0 +1,595 @@ +/* -- rates.c -- */ + +#include "x11vnc.h" +#include "xwrappers.h" +#include "scan.h" + +int measure_speeds = 1; +int speeds_net_rate = 0; +int speeds_net_rate_measured = 0; +int speeds_net_latency = 0; +int speeds_net_latency_measured = 0; +int speeds_read_rate = 0; +int speeds_read_rate_measured = 0; + + +int get_cmp_rate(void); +int get_raw_rate(void); +void initialize_speeds(void); +int get_read_rate(void); +int link_rate(int *latency, int *netrate); +int get_net_rate(void); +int get_net_latency(void); +void measure_send_rates(int init); + + +static void measure_display_hook(rfbClientPtr cl); +static int get_rate(int which); +static int get_latency(void); + + +static void measure_display_hook(rfbClientPtr cl) { + ClientData *cd = (ClientData *) cl->clientData; + dtime0(&cd->timer); +} + +static int get_rate(int which) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + int irate, irate_min = 1; /* 1 KB/sec */ + int irate_max = 100000; /* 100 MB/sec */ + int count = 0; + double slowest = -1.0, rate; + static double save_rate = 1000 * NETRATE0; + + if (!screen) { + return 0; + } + + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + ClientData *cd = (ClientData *) cl->clientData; + + if (cl->state != RFB_NORMAL) { + continue; + } + if (cd->send_cmp_rate == 0.0 || cd->send_raw_rate == 0.0) { + continue; + } + count++; + + if (which == 0) { + rate = cd->send_cmp_rate; + } else { + rate = cd->send_raw_rate; + } + if (slowest == -1.0 || rate < slowest) { + slowest = rate; + } + + } + rfbReleaseClientIterator(iter); + + if (! count) { + return NETRATE0; + } + + if (slowest == -1.0) { + slowest = save_rate; + } else { + save_rate = slowest; + } + + irate = (int) (slowest/1000.0); + if (irate < irate_min) { + irate = irate_min; + } + if (irate > irate_max) { + irate = irate_max; + } +if (0) fprintf(stderr, "get_rate(%d) %d %.3f/%.3f\n", which, irate, save_rate, slowest); + + return irate; +} + +static int get_latency(void) { + rfbClientIteratorPtr iter; + rfbClientPtr cl; + int ilat, ilat_min = 1; /* 1 ms */ + int ilat_max = 2000; /* 2 sec */ + double slowest = -1.0, lat; + static double save_lat = ((double) LATENCY0)/1000.0; + int count = 0; + + if (!screen) { + return 0; + } + + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + ClientData *cd = (ClientData *) cl->clientData; + + if (cl->state != RFB_NORMAL) { + continue; + } + if (cd->latency == 0.0) { + continue; + } + count++; + + lat = cd->latency; + if (slowest == -1.0 || lat > slowest) { + slowest = lat; + } + } + rfbReleaseClientIterator(iter); + + if (! count) { + return LATENCY0; + } + + if (slowest == -1.0) { + slowest = save_lat; + } else { + save_lat = slowest; + } + + ilat = (int) (slowest * 1000.0); + if (ilat < ilat_min) { + ilat = ilat_min; + } + if (ilat > ilat_max) { + ilat = ilat_max; + } + + return ilat; +} + +int get_cmp_rate(void) { + return get_rate(0); +} + +int get_raw_rate(void) { + return get_rate(1); +} + +void initialize_speeds(void) { + char *s, *s_in, *p; + int i; + + speeds_read_rate = 0; + speeds_net_rate = 0; + speeds_net_latency = 0; + if (! speeds_str || *speeds_str == '\0') { + s_in = strdup(""); + } else { + s_in = strdup(speeds_str); + } + + if (!strcmp(s_in, "modem")) { + s = strdup("6,4,200"); + } else if (!strcmp(s_in, "dsl")) { + s = strdup("6,100,50"); + } else if (!strcmp(s_in, "lan")) { + s = strdup("6,5000,1"); + } else { + s = strdup(s_in); + } + + p = strtok(s, ","); + i = 0; + while (p) { + double val; + if (*p != '\0') { + val = atof(p); + if (i==0) { + speeds_read_rate = (int) 1000000 * val; + } else if (i==1) { + speeds_net_rate = (int) 1000 * val; + } else if (i==2) { + speeds_net_latency = (int) val; + } + } + i++; + p = strtok(NULL, ","); + } + free(s); + free(s_in); + + if (! speeds_read_rate) { + int n = 0; + double dt, timer; + dtime0(&timer); + if (fullscreen) { + copy_image(fullscreen, 0, 0, 0, 0); + n = fullscreen->bytes_per_line * fullscreen->height; + } else if (scanline) { + copy_image(scanline, 0, 0, 0, 0); + n = scanline->bytes_per_line * scanline->height; + } + dt = dtime(&timer); + if (n && dt > 0.0) { + double rate = ((double) n) / dt; + speeds_read_rate_measured = (int) (rate/1000000.0); + if (speeds_read_rate_measured < 1) { + speeds_read_rate_measured = 1; + } else { + rfbLog("fb read rate: %d MB/sec\n", + speeds_read_rate_measured); + } + } + } +} + +int get_read_rate(void) { + if (speeds_read_rate) { + return speeds_read_rate; + } + if (speeds_read_rate_measured) { + return speeds_read_rate_measured; + } + return 0; +} + +int link_rate(int *latency, int *netrate) { + *latency = get_net_latency(); + *netrate = get_net_rate(); + + if (speeds_str) { + if (!strcmp(speeds_str, "modem")) { + return LR_DIALUP; + } else if (!strcmp(speeds_str, "dsl")) { + return LR_BROADBAND; + } else if (!strcmp(speeds_str, "lan")) { + return LR_LAN; + } + } + + if (*latency == LATENCY0 && *netrate == NETRATE0) { + return LR_UNSET; + } else if (*latency > 150 || *netrate < 20) { + return LR_DIALUP; + } else if (*latency > 50 || *netrate < 150) { + return LR_BROADBAND; + } else if (*latency < 10 && *netrate > 300) { + return LR_LAN; + } else { + return LR_UNKNOWN; + } +} + +int get_net_rate(void) { + int spm = speeds_net_rate_measured; + if (speeds_net_rate) { + return speeds_net_rate; + } + if (! spm || spm == NETRATE0) { + speeds_net_rate_measured = get_cmp_rate(); + } + if (speeds_net_rate_measured) { + return speeds_net_rate_measured; + } + return 0; +} + +int get_net_latency(void) { + int spm = speeds_net_latency_measured; + if (speeds_net_latency) { + return speeds_net_latency; + } + if (! spm || spm == LATENCY0) { + speeds_net_latency_measured = get_latency(); + } + if (speeds_net_latency_measured) { + return speeds_net_latency_measured; + } + return 0; +} + +void measure_send_rates(int init) { + double cmp_rate, raw_rate; + static double now, start = 0.0; + static rfbDisplayHookPtr orig_display_hook = NULL; + double cmp_max = 1.0e+08; /* 100 MB/sec */ + double cmp_min = 1000.0; /* 9600baud */ + double lat_max = 5.0; /* 5 sec */ + double lat_min = .0005; /* 0.5 ms */ + int min_cmp = 10000, nclients; + rfbClientIteratorPtr iter; + rfbClientPtr cl; + int db = 0, msg = 0; + +db = 0; + + if (! measure_speeds) { + return; + } + if (speeds_net_rate && speeds_net_latency) { + return; + } + + if (! orig_display_hook) { + orig_display_hook = screen->displayHook; + } + + if (start == 0.0) { + dtime(&start); + } + dtime0(&now); + now = now - start; + + nclients = 0; + + if (!screen) { + return; + } + + iter = rfbGetClientIterator(screen); + while( (cl = rfbClientIteratorNext(iter)) ) { + int defer, i, cbs, rbs; + char *httpdir; + double dt, dt1 = 0.0, dt2, dt3; + double tm, spin_max = 15.0, spin_lat_max = 1.5; + int got_t2 = 0, got_t3 = 0; + ClientData *cd = (ClientData *) cl->clientData; + + if (cd->send_cmp_rate > 0.0) { + continue; + } + nclients++; + + cbs = 0; + for (i=0; i<MAX_ENCODINGS; i++) { + cbs += cl->bytesSent[i]; + } + rbs = cl->rawBytesEquivalent; + + if (init) { + +if (db) fprintf(stderr, "%d client num rects req: %d mod: %d cbs: %d " + "rbs: %d dt1: %.3f t: %.3f\n", init, + (int) sraRgnCountRects(cl->requestedRegion), + (int) sraRgnCountRects(cl->modifiedRegion), cbs, rbs, dt1, now); + + cd->timer = dnow(); + cd->cmp_bytes_sent = cbs; + cd->raw_bytes_sent = rbs; + continue; + } + + /* first part of the bulk transfer of initial screen */ + dt1 = dtime(&cd->timer); + +if (db) fprintf(stderr, "%d client num rects req: %d mod: %d cbs: %d " + "rbs: %d dt1: %.3f t: %.3f\n", init, + (int) sraRgnCountRects(cl->requestedRegion), + (int) sraRgnCountRects(cl->modifiedRegion), cbs, rbs, dt1, now); + + if (dt1 <= 0.0) { + continue; + } + + cbs = cbs - cd->cmp_bytes_sent; + rbs = rbs - cd->raw_bytes_sent; + + if (cbs < min_cmp) { + continue; + } + + rfbPE(1000); + if (sraRgnCountRects(cl->modifiedRegion)) { + rfbPE(1000); + } + + defer = screen->deferUpdateTime; + httpdir = screen->httpDir; + screen->deferUpdateTime = 0; + screen->httpDir = NULL; + + /* mark a small rectangle: */ + mark_rect_as_modified(0, 0, 16, 16, 1); + + dtime0(&tm); + + dt2 = 0.0; + dt3 = 0.0; + + if (dt1 < 0.25) { + /* try to cut it down to avoid long pauses. */ + spin_max = 5.0; + } + + /* when req1 = 1 mod1 == 0, end of 2nd part of bulk transfer */ + while (1) { + int req0, req1, mod0, mod1; + req0 = sraRgnCountRects(cl->requestedRegion); + mod0 = sraRgnCountRects(cl->modifiedRegion); + if (use_threads) { + usleep(1000); + } else { + if (mod0) { + rfbPE(1000); + } else { + rfbCFD(1000); + } + } + dt = dtime(&tm); + dt2 += dt; + if (dt2 > spin_max) { + break; + } + req1 = sraRgnCountRects(cl->requestedRegion); + mod1 = sraRgnCountRects(cl->modifiedRegion); + +if (db) fprintf(stderr, "dt2 calc: num rects req: %d/%d mod: %d/%d " + "fbu-sent: %d dt: %.4f dt2: %.4f tm: %.4f\n", + req0, req1, mod0, mod1, cl->framebufferUpdateMessagesSent, dt, dt2, tm); + if (req1 != 0 && mod1 == 0) { + got_t2 = 1; + break; + } + } + + if (! got_t2) { + dt2 = 0.0; + } else { + int tr, trm = 3; + double dts[10]; + + /* + * Note: since often select(2) cannot sleep + * less than 1/HZ (e.g. 10ms), the resolution + * of the latency may be messed up by something + * of this order. Effect may occur on both ends, + * i.e. the viewer may not respond immediately. + */ + + for (tr = 0; tr < trm; tr++) { + usleep(5000); + + /* mark a 2nd small rectangle: */ + mark_rect_as_modified(0, 0, 16, 16, 1); + i = 0; + dtime0(&tm); + dt3 = 0.0; + + /* + * when req1 > 0 and mod1 == 0, we say + * that is the "ping" time. + */ + while (1) { + int req0, req1, mod0, mod1; + + req0 = sraRgnCountRects( + cl->requestedRegion); + mod0 = sraRgnCountRects( + cl->modifiedRegion); + + if (i == 0) { + rfbPE(0); + } else { + if (use_threads) { + usleep(1000); + } else { + /* try to get it all */ + rfbCFD(1000*1000); + } + } + dt = dtime(&tm); + i++; + + dt3 += dt; + if (dt3 > spin_lat_max) { + break; + } + req1 = sraRgnCountRects( + cl->requestedRegion); + + mod1 = sraRgnCountRects( + cl->modifiedRegion); + +if (db) fprintf(stderr, "dt3 calc: num rects req: %d/%d mod: %d/%d " + "fbu-sent: %d dt: %.4f dt3: %.4f tm: %.4f\n", + req0, req1, mod0, mod1, cl->framebufferUpdateMessagesSent, dt, dt3, tm); + + if (req1 != 0 && mod1 == 0) { + dts[got_t3++] = dt3; + break; + } + } + } + + if (! got_t3) { + dt3 = 0.0; + } else { + if (got_t3 == 1) { + dt3 = dts[0]; + } else if (got_t3 == 2) { + dt3 = dts[1]; + } else { + if (dts[2] >= 0.0) { + double rat = dts[1]/dts[2]; + if (rat > 0.5 && rat < 2.0) { + dt3 = dts[1]+dts[2]; + dt3 *= 0.5; + } else { + dt3 = dts[1]; + } + } else { + dt3 = dts[1]; + } + } + } + } + + screen->deferUpdateTime = defer; + screen->httpDir = httpdir; + + dt = dt1 + dt2; + + + if (dt3 <= dt2/2.0) { + /* guess only 1/2 a ping for reply... */ + dt = dt - dt3/2.0; + } + + cmp_rate = cbs/dt; + raw_rate = rbs/dt; + + if (cmp_rate > cmp_max) { + cmp_rate = cmp_max; + } + if (cmp_rate <= cmp_min) { + cmp_rate = cmp_min; + } + + cd->send_cmp_rate = cmp_rate; + cd->send_raw_rate = raw_rate; + + if (dt3 > lat_max) { + dt3 = lat_max; + } + if (dt3 <= lat_min) { + dt3 = lat_min; + } + + cd->latency = dt3; + + rfbLog("client %d network rate %.1f KB/sec (%.1f eff KB/sec)\n", + cd->uid, cmp_rate/1000.0, raw_rate/1000.0); + rfbLog("client %d latency: %.1f ms\n", cd->uid, 1000.0*dt3); + rfbLog("dt1: %.4f, dt2: %.4f dt3: %.4f bytes: %d\n", + dt1, dt2, dt3, cbs); + msg = 1; + } + rfbReleaseClientIterator(iter); + + if (msg) { + int link, latency, netrate; + char *str = "error"; + + link = link_rate(&latency, &netrate); + if (link == LR_UNSET) { + str = "LR_UNSET"; + } else if (link == LR_UNKNOWN) { + str = "LR_UNKNOWN"; + } else if (link == LR_DIALUP) { + str = "LR_DIALUP"; + } else if (link == LR_BROADBAND) { + str = "LR_BROADBAND"; + } else if (link == LR_LAN) { + str = "LR_LAN"; + } + rfbLog("link_rate: %s - %d ms, %d KB/s\n", str, latency, + netrate); + } + + if (init) { + if (nclients) { + screen->displayHook = measure_display_hook; + } + } else { + screen->displayHook = orig_display_hook; + } +} + |