handle MSG_STREAM_ACTIVIATE_REPORT and send MSGC_STREAM_REPORT periodically, or when the playback is out of sync. --- gtk/channel-display-priv.h | 10 ++++ gtk/channel-display.c | 116 ++++++++++++++++++++++++++++++++++++++++----- spice-common | 2 +- 3 files changed, 114 insertions(+), 14 deletions(-) diff --git a/gtk/channel-display-priv.h b/gtk/channel-display-priv.h index f57dc6e..49f82fe 100644 --- a/gtk/channel-display-priv.h +++ b/gtk/channel-display-priv.h @@ -88,6 +88,16 @@ typedef struct display_stream { GArray *drops_seqs_stats_arr; uint32_t num_drops_seqs; + /* playback quality report to server */ + gboolean report_is_active; + uint32_t report_id; + uint32_t report_max_window; + uint32_t report_timeout; + uint64_t report_start_time; + uint32_t report_start_frame_time; + uint32_t report_num_frames; + uint32_t report_num_drops; + uint32_t report_drops_seq_len; } display_stream; void stream_get_dimensions(display_stream *st, int *width, int *height); diff --git a/gtk/channel-display.c b/gtk/channel-display.c index ab4d3bd..efb9aec 100644 --- a/gtk/channel-display.c +++ b/gtk/channel-display.c @@ -687,6 +687,7 @@ static void spice_display_channel_reset_capabilities(SpiceChannel *channel) spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_DISPLAY_CAP_MONITORS_CONFIG); spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_DISPLAY_CAP_COMPOSITE); spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_DISPLAY_CAP_A8_SURFACE); + spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_DISPLAY_CAP_STREAM_REPORT); } static void spice_display_channel_init(SpiceDisplayChannel *channel) @@ -1238,6 +1239,65 @@ static gboolean display_stream_render(display_stream *st) return FALSE; } +#define STREAM_REPORT_DROP_SEQ_LEN_LIMIT 3 /* after a sequence of 3 drops, push a report to the server, even + if the report window is bigger */ + +static void display_update_stream_report(SpiceDisplayChannel *channel, uint32_t stream_id, + uint32_t frame_time, int32_t latency) +{ + display_stream *st = channel->priv->streams[stream_id]; + guint64 now; + + if (!st->report_is_active) { + return; + } + now = g_get_monotonic_time(); + + if (st->report_num_frames == 0) { + st->report_start_frame_time = frame_time; + st->report_start_time = now; + } + st->report_num_frames++; + + if (latency < 0) { // drop + st->report_num_drops++; + st->report_drops_seq_len++; + } else { + st->report_drops_seq_len = 0; + } + + if (st->report_num_frames >= st->report_max_window || + now - st->report_start_time >= st->report_timeout || + st->report_drops_seq_len >= STREAM_REPORT_DROP_SEQ_LEN_LIMIT) { + SpiceMsgcDisplayStreamReport report; + SpiceSession *session = spice_channel_get_session(SPICE_CHANNEL(channel)); + SpiceMsgOut *msg; + + report.stream_id = stream_id; + report.unique_id = st->report_id; + report.start_frame_mm_time = st->report_start_frame_time; + report.end_frame_mm_time = frame_time; + report.num_frames = st->report_num_frames; + report.num_drops = st-> report_num_drops; + report.last_frame_delay = latency; + if (spice_session_is_playback_active(session)) { + report.audio_delay = spice_session_get_playback_latency(session); + } else { + report.audio_delay = UINT_MAX; + } + + msg = spice_msg_out_new(SPICE_CHANNEL(channel), SPICE_MSGC_DISPLAY_STREAM_REPORT); + msg->marshallers->msgc_display_stream_report(msg->marshaller, &report); + spice_msg_out_send(msg); + + st->report_start_time = 0; + st->report_start_frame_time = 0; + st->report_num_frames = 0; + st->report_num_drops = 0; + st->report_drops_seq_len = 0; + } +} + /* coroutine context */ static void display_handle_stream_data(SpiceChannel *channel, SpiceMsgIn *in) { @@ -1245,6 +1305,7 @@ static void display_handle_stream_data(SpiceChannel *channel, SpiceMsgIn *in) SpiceStreamDataHeader *op = spice_msg_in_parsed(in); display_stream *st; guint32 mmtime; + int32_t latency; g_return_if_fail(c != NULL); g_return_if_fail(c->streams != NULL); @@ -1266,7 +1327,9 @@ static void display_handle_stream_data(SpiceChannel *channel, SpiceMsgIn *in) st->first_frame_mm_time = op->multi_media_time; } st->num_input_frames++; - if (op->multi_media_time < mmtime) { + + latency = op->multi_media_time - mmtime; + if (latency < 0) { CHANNEL_DEBUG(channel, "stream data too late by %u ms (ts: %u, mmtime: %u), dropin", mmtime - op->multi_media_time, op->multi_media_time, mmtime); st->arrive_late_time += mmtime - op->multi_media_time; @@ -1275,19 +1338,20 @@ static void display_handle_stream_data(SpiceChannel *channel, SpiceMsgIn *in) st->cur_drops_seq_stats.start_mm_time = op->multi_media_time; } st->cur_drops_seq_stats.len++; - return; - } - - spice_msg_in_ref(in); - g_queue_push_tail(st->msgq, in); - display_stream_schedule(st); - if (st->cur_drops_seq_stats.len) { - st->cur_drops_seq_stats.duration = op->multi_media_time - - st->cur_drops_seq_stats.start_mm_time; - g_array_append_val(st->drops_seqs_stats_arr, st->cur_drops_seq_stats); - memset(&st->cur_drops_seq_stats, 0, sizeof(st->cur_drops_seq_stats)); - st->num_drops_seqs++; + } else { + spice_msg_in_ref(in); + g_queue_push_tail(st->msgq, in); + display_stream_schedule(st); + if (st->cur_drops_seq_stats.len) { + st->cur_drops_seq_stats.duration = op->multi_media_time - + st->cur_drops_seq_stats.start_mm_time; + g_array_append_val(st->drops_seqs_stats_arr, st->cur_drops_seq_stats); + memset(&st->cur_drops_seq_stats, 0, sizeof(st->cur_drops_seq_stats)); + st->num_drops_seqs++; + } } + display_update_stream_report(SPICE_DISPLAY_CHANNEL(channel), op->id, + op->multi_media_time, latency); } /* coroutine context */ @@ -1406,6 +1470,31 @@ static void display_handle_stream_destroy_all(SpiceChannel *channel, SpiceMsgIn clear_streams(channel); } +/* coroutine context */ +static void display_handle_stream_activate_report(SpiceChannel *channel, SpiceMsgIn *in) +{ + SpiceDisplayChannelPrivate *c = SPICE_DISPLAY_CHANNEL(channel)->priv; + SpiceMsgDisplayStreamActivateReport *op = spice_msg_in_parsed(in); + display_stream *st; + + g_return_if_fail(c != NULL); + g_return_if_fail(c->streams != NULL); + g_return_if_fail(c->nstreams > op->stream_id); + + st = c->streams[op->stream_id]; + g_return_if_fail(st != NULL); + + st->report_is_active = TRUE; + st->report_id = op->unique_id; + st->report_max_window = op->max_window_size; + st->report_timeout = op->timeout_ms * 1000; + st->report_start_time = 0; + st->report_start_frame_time = 0; + st->report_num_frames = 0; + st->report_num_drops = 0; + st->report_drops_seq_len = 0; +} + /* ------------------------------------------------------------------ */ /* coroutine context */ @@ -1627,6 +1716,7 @@ static const spice_msg_handler display_handlers[] = { [ SPICE_MSG_DISPLAY_STREAM_DESTROY ] = display_handle_stream_destroy, [ SPICE_MSG_DISPLAY_STREAM_DESTROY_ALL ] = display_handle_stream_destroy_all, [ SPICE_MSG_DISPLAY_STREAM_DATA_SIZED ] = display_handle_stream_data, + [ SPICE_MSG_DISPLAY_STREAM_ACTIVATE_REPORT ] = display_handle_stream_activate_report, [ SPICE_MSG_DISPLAY_DRAW_FILL ] = display_handle_draw_fill, [ SPICE_MSG_DISPLAY_DRAW_OPAQUE ] = display_handle_draw_opaque, diff --git a/spice-common b/spice-common index 149bb89..09f88f4 160000 --- a/spice-common +++ b/spice-common @@ -1 +1 @@ -Subproject commit 149bb89adb0d7676c41085b3e41f07113e05c880 +Subproject commit 09f88f4a688a156b48c2058dac7a8c0f35e96abd -- 1.8.1 _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel