[Spice-devel] [PATCH spice-html5 3/3] Add support for stream reports.

Jeremy White jwhite at codeweavers.com
Wed Jun 3 15:22:55 PDT 2015


This helps video playback do a slightly better job of keeping up
in the browser.  It's not a dramatic effect, but enough to start making
video playback almost tolerable.

Signed-off-by: Jeremy White <jwhite at codeweavers.com>
---
 display.js   | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++----------
 enums.js     |  1 +
 main.js      |  3 +++
 spiceconn.js |  3 ++-
 spicemsg.js  | 54 ++++++++++++++++++++++++++++++++++++++++++++++++
 5 files changed, 116 insertions(+), 12 deletions(-)

diff --git a/display.js b/display.js
index 7027230..7938527 100644
--- a/display.js
+++ b/display.js
@@ -477,31 +477,48 @@ SpiceDisplayConn.prototype.process_channel_message = function(msg)
         return true;
     }
 
-    if (msg.type == SPICE_MSG_DISPLAY_STREAM_DATA)
+    if (msg.type == SPICE_MSG_DISPLAY_STREAM_DATA ||
+        msg.type == SPICE_MSG_DISPLAY_STREAM_DATA_SIZED)
     {
-        var m = new SpiceMsgDisplayStreamData(msg.data);
+        var m;
+        if (msg.type == SPICE_MSG_DISPLAY_STREAM_DATA_SIZED)
+            m = new SpiceMsgDisplayStreamDataSized(msg.data);
+        else
+            m = new SpiceMsgDisplayStreamData(msg.data);
+
         if (!this.streams[m.base.id])
         {
             console.log("no stream for data");
             return false;
         }
+
+        var mmtime = (Date.now() - this.parent.our_mm_time) + this.parent.mm_time;
+        var latency = m.base.multi_media_time - mmtime;
+
         if (this.streams[m.base.id].codec_type === SPICE_VIDEO_CODEC_TYPE_MJPEG)
-        {
-            process_mjpeg_stream_data(this, m);
+            process_mjpeg_stream_data(this, m, latency);
+
+        if ("report" in this.streams[m.base.id])
+            process_stream_data_report(this, m, mmtime, latency);
 
-        }
         return true;
     }
 
-    if (msg.type == SPICE_MSG_DISPLAY_STREAM_DATA_SIZED)
+    if (msg.type == SPICE_MSG_DISPLAY_STREAM_ACTIVATE_REPORT)
     {
-        var m = new SpiceMsgDisplayStreamDataSized(msg.data);
-        if (this.streams[m.base.id].codec_type === SPICE_VIDEO_CODEC_TYPE_MJPEG)
-            process_mjpeg_stream_data(this, m);
+        var m = new SpiceMsgDisplayStreamActivateReport(msg.data);
+
+        var report = new SpiceMsgcDisplayStreamReport(m.stream_id, m.unique_id);
+        if (this.streams[m.stream_id])
+        {
+            this.streams[m.stream_id].report = report;
+            this.streams[m.stream_id].max_window_size = m.max_window_size;
+            this.streams[m.stream_id].timeout_ms = m.timeout_ms
+        }
+
         return true;
     }
 
-
     if (msg.type == SPICE_MSG_DISPLAY_STREAM_CLIP)
     {
         var m = new SpiceMsgDisplayStreamClip(msg.data);
@@ -811,8 +828,15 @@ function handle_draw_jpeg_onload()
     }
 }
 
-function process_mjpeg_stream_data(sc, m)
+function process_mjpeg_stream_data(sc, m, latency)
 {
+    if (latency < 0)
+    {
+        if ("report" in sc.streams[m.base.id])
+            sc.streams[m.base.id].report.num_drops++;
+        return;
+    }
+
     var tmpstr = "data:image/jpeg,";
     var img = new Image;
     var i;
@@ -837,3 +861,24 @@ function process_mjpeg_stream_data(sc, m)
     img.src = tmpstr;
 }
 
+function process_stream_data_report(sc, m, mmtime, latency)
+{
+    sc.streams[m.base.id].report.num_frames++;
+    if (sc.streams[m.base.id].report.start_frame_mm_time == 0)
+        sc.streams[m.base.id].report.start_frame_mm_time = m.base.multi_media_time;
+
+    if (sc.streams[m.base.id].report.num_frames > sc.streams[m.base.id].max_window_size ||
+        (m.base.multi_media_time - sc.streams[m.base.id].report.start_frame_mm_time) > sc.streams[m.base.id].timeout_ms)
+    {
+        sc.streams[m.base.id].report.end_frame_mm_time = m.base.multi_media_time;
+        sc.streams[m.base.id].report.last_frame_delay = latency;
+
+        var msg = new SpiceMiniData();
+        msg.build_msg(SPICE_MSGC_DISPLAY_STREAM_REPORT, sc.streams[m.base.id].report);
+        sc.send_msg(msg);
+
+        sc.streams[m.base.id].report.start_frame_mm_time = 0;
+        sc.streams[m.base.id].report.num_frames = 0;
+        sc.streams[m.base.id].report.num_drops = 0;
+    }
+}
diff --git a/enums.js b/enums.js
index 7f55e46..6e94025 100644
--- a/enums.js
+++ b/enums.js
@@ -133,6 +133,7 @@ var SPICE_MSG_DISPLAY_DRAW_COMPOSITE    = 318;
 var SPICE_MSG_DISPLAY_STREAM_ACTIVATE_REPORT = 319;
 
 var SPICE_MSGC_DISPLAY_INIT             = 101;
+var SPICE_MSGC_DISPLAY_STREAM_REPORT    = 102;
 
 var SPICE_MSG_INPUTS_INIT               = 101;
 var SPICE_MSG_INPUTS_KEY_MODIFIERS      = 102;
diff --git a/main.js b/main.js
index bfc102a..99b2274 100644
--- a/main.js
+++ b/main.js
@@ -86,6 +86,9 @@ SpiceMainConn.prototype.process_channel_message = function(msg)
                           " ; ram_hint "                + this.main_init.ram_hint);
         }
 
+        this.our_mm_time = Date.now();
+        this.mm_time = this.main_init.multi_media_time;
+
         this.handle_mouse_mode(this.main_init.current_mouse_mode,
                                this.main_init.supported_mouse_modes);
 
diff --git a/spiceconn.js b/spiceconn.js
index b7043c0..f19b109 100644
--- a/spiceconn.js
+++ b/spiceconn.js
@@ -135,7 +135,8 @@ SpiceConn.prototype =
             );
         else if (msg.channel_type == SPICE_CHANNEL_DISPLAY)
             msg.channel_caps.push(
-                (1 << SPICE_DISPLAY_CAP_SIZED_STREAM)
+                (1 << SPICE_DISPLAY_CAP_SIZED_STREAM) |
+                (1 << SPICE_DISPLAY_CAP_STREAM_REPORT)
             );
 
         hdr.size = msg.buffer_size();
diff --git a/spicemsg.js b/spicemsg.js
index 4b78d94..db6625a 100644
--- a/spicemsg.js
+++ b/spicemsg.js
@@ -1201,6 +1201,60 @@ SpiceMsgDisplayStreamDestroy.prototype =
     },
 }
 
+function SpiceMsgDisplayStreamActivateReport(a, at)
+{
+    this.from_buffer(a, at);
+}
+
+SpiceMsgDisplayStreamActivateReport.prototype =
+{
+    from_buffer: function(a, at)
+    {
+        at = at || 0;
+        var dv = new SpiceDataView(a);
+        this.stream_id = dv.getUint32(at, true); at += 4;
+        this.unique_id = dv.getUint32(at, true); at += 4;
+        this.max_window_size = dv.getUint32(at, true); at += 4;
+        this.timeout_ms = dv.getUint32(at, true); at += 4;
+    },
+}
+
+function SpiceMsgcDisplayStreamReport(stream_id, unique_id)
+{
+    this.stream_id = stream_id;
+    this.unique_id = unique_id;
+    this.start_frame_mm_time = 0;
+    this.end_frame_mm_time = 0;
+    this.num_frames = 0;
+    this.num_drops = 0;
+    this.last_frame_delay = 0;
+
+    // TODO - Implement audio delay
+    this.audio_delay = -1;
+}
+
+SpiceMsgcDisplayStreamReport.prototype =
+{
+    to_buffer: function(a, at)
+    {
+        at = at || 0;
+        var dv = new SpiceDataView(a);
+        dv.setUint32(at, this.stream_id, true); at += 4;
+        dv.setUint32(at, this.unique_id, true); at += 4;
+        dv.setUint32(at, this.start_frame_mm_time, true); at += 4;
+        dv.setUint32(at, this.end_frame_mm_time, true); at += 4;
+        dv.setUint32(at, this.num_frames, true); at += 4;
+        dv.setUint32(at, this.num_drops, true); at += 4;
+        dv.setUint32(at, this.last_frame_delay, true); at += 4;
+        dv.setUint32(at, this.audio_delay, true); at += 4;
+        return at;
+    },
+    buffer_size: function()
+    {
+        return 8 * 4;
+    }
+}
+
 function SpiceMsgDisplayInvalList(a, at)
 {
     this.count = 0;
-- 
2.1.4



More information about the Spice-devel mailing list