Merge pull request #121 from glyptodon/improve-lag-compensation

GUAC-1389: Stretch RDP and VNC frames until client has caught up.
This commit is contained in:
James Muehlner 2016-03-08 19:37:44 -08:00
commit b8c5ccb321
4 changed files with 84 additions and 63 deletions

View File

@ -37,7 +37,7 @@
* milliseconds. If the server is silent for at least this amount of time, the * milliseconds. If the server is silent for at least this amount of time, the
* frame will be considered finished. * frame will be considered finished.
*/ */
#define GUAC_RDP_FRAME_TIMEOUT 10 #define GUAC_RDP_FRAME_TIMEOUT 0
/** /**
* The amount of time to wait for a new message from the RDP server when * The amount of time to wait for a new message from the RDP server when

View File

@ -829,9 +829,10 @@ void* guac_rdp_client_thread(void* data) {
/* Connection complete */ /* Connection complete */
rdp_client->rdp_inst = rdp_inst; rdp_client->rdp_inst = rdp_inst;
rdpChannels* channels = rdp_inst->context->channels; rdpChannels* channels = rdp_inst->context->channels;
guac_timestamp last_frame_end = guac_timestamp_current();
/* Handle messages from RDP server while client is running */ /* Handle messages from RDP server while client is running */
while (client->state == GUAC_CLIENT_RUNNING) { while (client->state == GUAC_CLIENT_RUNNING) {
@ -845,72 +846,88 @@ void* guac_rdp_client_thread(void* data) {
/* Wait for data and construct a reasonable frame */ /* Wait for data and construct a reasonable frame */
int wait_result = rdp_guac_client_wait_for_messages(client, int wait_result = rdp_guac_client_wait_for_messages(client,
GUAC_RDP_FRAME_START_TIMEOUT); GUAC_RDP_FRAME_START_TIMEOUT);
guac_timestamp frame_start = guac_timestamp_current(); if (wait_result > 0) {
while (wait_result > 0) {
guac_timestamp frame_end; int processing_lag = guac_client_get_processing_lag(client);
int frame_remaining; guac_timestamp frame_start = guac_timestamp_current();
pthread_mutex_lock(&(rdp_client->rdp_lock)); /* Read server messages until frame is built */
do {
/* Check the libfreerdp fds */ guac_timestamp frame_end;
if (!freerdp_check_fds(rdp_inst)) { int frame_remaining;
guac_client_log(client, GUAC_LOG_DEBUG,
"Error handling RDP file descriptors");
pthread_mutex_unlock(&(rdp_client->rdp_lock));
return NULL;
}
/* Check channel fds */ pthread_mutex_lock(&(rdp_client->rdp_lock));
if (!freerdp_channels_check_fds(channels, rdp_inst)) {
guac_client_log(client, GUAC_LOG_DEBUG,
"Error handling RDP channel file descriptors");
pthread_mutex_unlock(&(rdp_client->rdp_lock));
return NULL;
}
/* Check for channel events */ /* Check the libfreerdp fds */
wMessage* event = freerdp_channels_pop_event(channels); if (!freerdp_check_fds(rdp_inst)) {
if (event) { guac_client_log(client, GUAC_LOG_DEBUG,
"Error handling RDP file descriptors");
pthread_mutex_unlock(&(rdp_client->rdp_lock));
return NULL;
}
/* Handle channel events (clipboard and RAIL) */ /* Check channel fds */
if (!freerdp_channels_check_fds(channels, rdp_inst)) {
guac_client_log(client, GUAC_LOG_DEBUG,
"Error handling RDP channel file descriptors");
pthread_mutex_unlock(&(rdp_client->rdp_lock));
return NULL;
}
/* Check for channel events */
wMessage* event = freerdp_channels_pop_event(channels);
if (event) {
/* Handle channel events (clipboard and RAIL) */
#ifdef LEGACY_EVENT #ifdef LEGACY_EVENT
if (event->event_class == CliprdrChannel_Class) if (event->event_class == CliprdrChannel_Class)
guac_rdp_process_cliprdr_event(client, event); guac_rdp_process_cliprdr_event(client, event);
else if (event->event_class == RailChannel_Class) else if (event->event_class == RailChannel_Class)
guac_rdp_process_rail_event(client, event); guac_rdp_process_rail_event(client, event);
#else #else
if (GetMessageClass(event->id) == CliprdrChannel_Class) if (GetMessageClass(event->id) == CliprdrChannel_Class)
guac_rdp_process_cliprdr_event(client, event); guac_rdp_process_cliprdr_event(client, event);
else if (GetMessageClass(event->id) == RailChannel_Class) else if (GetMessageClass(event->id) == RailChannel_Class)
guac_rdp_process_rail_event(client, event); guac_rdp_process_rail_event(client, event);
#endif #endif
freerdp_event_free(event); freerdp_event_free(event);
} }
/* Handle RDP disconnect */
if (freerdp_shall_disconnect(rdp_inst)) {
guac_client_log(client, GUAC_LOG_INFO,
"RDP server closed connection");
pthread_mutex_unlock(&(rdp_client->rdp_lock));
return NULL;
}
/* Handle RDP disconnect */
if (freerdp_shall_disconnect(rdp_inst)) {
guac_client_log(client, GUAC_LOG_INFO,
"RDP server closed connection");
pthread_mutex_unlock(&(rdp_client->rdp_lock)); pthread_mutex_unlock(&(rdp_client->rdp_lock));
return NULL;
}
pthread_mutex_unlock(&(rdp_client->rdp_lock)); /* Calculate time remaining in frame */
frame_end = guac_timestamp_current();
frame_remaining = frame_start + GUAC_RDP_FRAME_DURATION
- frame_end;
/* Calculate time remaining in frame */ /* Calculate time that client needs to catch up */
frame_end = guac_timestamp_current(); int time_elapsed = frame_end - last_frame_end;
frame_remaining = frame_start + GUAC_RDP_FRAME_DURATION - frame_end; int required_wait = processing_lag - time_elapsed;
/* Wait again if frame remaining */ /* Increase the duration of this frame if client is lagging */
if (frame_remaining > 0) if (required_wait > GUAC_RDP_FRAME_TIMEOUT)
wait_result = rdp_guac_client_wait_for_messages(client, wait_result = rdp_guac_client_wait_for_messages(client,
GUAC_RDP_FRAME_TIMEOUT*1000); required_wait*1000);
else
break;
/* Wait again if frame remaining */
else if (frame_remaining > 0)
wait_result = rdp_guac_client_wait_for_messages(client,
GUAC_RDP_FRAME_TIMEOUT*1000);
else
break;
} while (wait_result > 0);
} }
/* If an error occurred, fail */ /* If an error occurred, fail */
@ -923,6 +940,9 @@ void* guac_rdp_client_thread(void* data) {
guac_client_end_frame(client); guac_client_end_frame(client);
guac_socket_flush(client->socket); guac_socket_flush(client->socket);
/* Record end of frame */
last_frame_end = guac_timestamp_current();
} }
guac_client_log(client, GUAC_LOG_INFO, "Internal RDP client disconnected"); guac_client_log(client, GUAC_LOG_INFO, "Internal RDP client disconnected");

View File

@ -36,7 +36,7 @@
* milliseconds. If the server is silent for at least this amount of time, the * milliseconds. If the server is silent for at least this amount of time, the
* frame will be considered finished. * frame will be considered finished.
*/ */
#define GUAC_VNC_FRAME_TIMEOUT 10 #define GUAC_VNC_FRAME_TIMEOUT 0
/** /**
* The amount of time to wait for a new message from the VNC server when * The amount of time to wait for a new message from the VNC server when

View File

@ -345,6 +345,7 @@ void* guac_vnc_client_thread(void* data) {
GUAC_VNC_FRAME_START_TIMEOUT); GUAC_VNC_FRAME_START_TIMEOUT);
if (wait_result > 0) { if (wait_result > 0) {
int processing_lag = guac_client_get_processing_lag(client);
guac_timestamp frame_start = guac_timestamp_current(); guac_timestamp frame_start = guac_timestamp_current();
/* Read server messages until frame is built */ /* Read server messages until frame is built */
@ -366,8 +367,17 @@ void* guac_vnc_client_thread(void* data) {
frame_remaining = frame_start + GUAC_VNC_FRAME_DURATION frame_remaining = frame_start + GUAC_VNC_FRAME_DURATION
- frame_end; - frame_end;
/* Calculate time that client needs to catch up */
int time_elapsed = frame_end - last_frame_end;
int required_wait = processing_lag - time_elapsed;
/* Increase the duration of this frame if client is lagging */
if (required_wait > GUAC_VNC_FRAME_TIMEOUT)
wait_result = guac_vnc_wait_for_messages(rfb_client,
required_wait*1000);
/* Wait again if frame remaining */ /* Wait again if frame remaining */
if (frame_remaining > 0) else if (frame_remaining > 0)
wait_result = guac_vnc_wait_for_messages(rfb_client, wait_result = guac_vnc_wait_for_messages(rfb_client,
GUAC_VNC_FRAME_TIMEOUT*1000); GUAC_VNC_FRAME_TIMEOUT*1000);
else else
@ -386,17 +396,8 @@ void* guac_vnc_client_thread(void* data) {
guac_client_end_frame(client); guac_client_end_frame(client);
guac_socket_flush(client->socket); guac_socket_flush(client->socket);
/* Calculate time since previous frame ended */
int current_frame_end = guac_timestamp_current();
int time_elapsed = current_frame_end - last_frame_end;
int processing_lag = guac_client_get_processing_lag(client);
/* Insert delays as necessary to allow client to catch up */
if (time_elapsed < processing_lag)
guac_timestamp_msleep(processing_lag - time_elapsed);
/* Record end of frame */ /* Record end of frame */
last_frame_end = current_frame_end; last_frame_end = guac_timestamp_current();
} }