1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2024-12-24 15:15:22 +00:00

spice-html5 updated.

This commit is contained in:
catborise 2018-08-08 14:00:35 +03:00
parent 12c80c5021
commit b05a252d7c
36 changed files with 1356 additions and 1415 deletions

View file

@ -31,6 +31,9 @@
<script src="{% static "js/spice-html5/resize.js" %}"></script> <script src="{% static "js/spice-html5/resize.js" %}"></script>
<script src="{% static "js/spice-html5/filexfer.js" %}"></script> <script src="{% static "js/spice-html5/filexfer.js" %}"></script>
<link rel="stylesheet" type="text/css" href="{% static "js/spice-html5/spice.css" %}" />
<script src="{% static "js/spice-html5/port.js" %}"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@ -61,7 +64,7 @@
{ {
console.log(e); console.log(e);
disconnect(); disconnect();
if (e.message != undefined) { if (e.message !== undefined) {
log_error(e.message); log_error(e.message);
} }
else { else {
@ -73,6 +76,7 @@
log_info(msg); log_info(msg);
} }
function connect(uri,password) function connect(uri,password)
{ {
// If a token variable is passed in, set the parameter in a cookie. // If a token variable is passed in, set the parameter in a cookie.
@ -90,6 +94,7 @@
{ {
sc = new SpiceMainConn({uri: uri, password: password, screen_id: "spice_container", sc = new SpiceMainConn({uri: uri, password: password, screen_id: "spice_container",
onsuccess: spice_success, onerror: spice_error, onagent: agent_connected }); onsuccess: spice_success, onerror: spice_error, onagent: agent_connected });
} }
catch (e) catch (e)
{ {

0
static/js/spice-html5/atKeynames.js Normal file → Executable file
View file

20
static/js/spice-html5/bitmap.js Normal file → Executable file
View file

@ -26,25 +26,31 @@
function convert_spice_bitmap_to_web(context, spice_bitmap) function convert_spice_bitmap_to_web(context, spice_bitmap)
{ {
var ret; var ret;
var offset, x; var offset, x, src_offset = 0, src_dec = 0;
var u8 = new Uint8Array(spice_bitmap.data); var u8 = new Uint8Array(spice_bitmap.data);
if (spice_bitmap.format != SPICE_BITMAP_FMT_32BIT && if (spice_bitmap.format != SPICE_BITMAP_FMT_32BIT &&
spice_bitmap.format != SPICE_BITMAP_FMT_RGBA) spice_bitmap.format != SPICE_BITMAP_FMT_RGBA)
return undefined; return undefined;
if (!(spice_bitmap.flags & SPICE_BITMAP_FLAGS_TOP_DOWN))
{
src_offset = (spice_bitmap.y - 1 ) * spice_bitmap.stride;
src_dec = 2 * spice_bitmap.stride;
}
ret = context.createImageData(spice_bitmap.x, spice_bitmap.y); ret = context.createImageData(spice_bitmap.x, spice_bitmap.y);
for (offset = 0; offset < (spice_bitmap.y * spice_bitmap.stride); ) for (offset = 0; offset < (spice_bitmap.y * spice_bitmap.stride); src_offset -= src_dec)
for (x = 0; x < spice_bitmap.x; x++, offset += 4) for (x = 0; x < spice_bitmap.x; x++, offset += 4, src_offset += 4)
{ {
ret.data[offset + 0 ] = u8[offset + 2]; ret.data[offset + 0 ] = u8[src_offset + 2];
ret.data[offset + 1 ] = u8[offset + 1]; ret.data[offset + 1 ] = u8[src_offset + 1];
ret.data[offset + 2 ] = u8[offset + 0]; ret.data[offset + 2 ] = u8[src_offset + 0];
// FIXME - We effectively treat all images as having SPICE_IMAGE_FLAGS_HIGH_BITS_SET // FIXME - We effectively treat all images as having SPICE_IMAGE_FLAGS_HIGH_BITS_SET
if (spice_bitmap.format == SPICE_BITMAP_FMT_32BIT) if (spice_bitmap.format == SPICE_BITMAP_FMT_32BIT)
ret.data[offset + 3] = 255; ret.data[offset + 3] = 255;
else else
ret.data[offset + 3] = u8[offset]; ret.data[offset + 3] = u8[src_offset];
} }
return ret; return ret;

20
static/js/spice-html5/cursor.js Normal file → Executable file
View file

@ -73,6 +73,12 @@ SpiceCursorConn.prototype.process_channel_message = function(msg)
return true; return true;
} }
if (msg.type == SPICE_MSG_CURSOR_MOVE)
{
this.known_unimplemented(msg.type, "Cursor Move");
return true;
}
if (msg.type == SPICE_MSG_CURSOR_HIDE) if (msg.type == SPICE_MSG_CURSOR_HIDE)
{ {
DEBUG > 1 && console.log("SpiceMsgCursorHide"); DEBUG > 1 && console.log("SpiceMsgCursorHide");
@ -80,6 +86,12 @@ SpiceCursorConn.prototype.process_channel_message = function(msg)
return true; return true;
} }
if (msg.type == SPICE_MSG_CURSOR_TRAIL)
{
this.known_unimplemented(msg.type, "Cursor Trail");
return true;
}
if (msg.type == SPICE_MSG_CURSOR_RESET) if (msg.type == SPICE_MSG_CURSOR_RESET)
{ {
DEBUG > 1 && console.log("SpiceMsgCursorReset"); DEBUG > 1 && console.log("SpiceMsgCursorReset");
@ -87,6 +99,12 @@ SpiceCursorConn.prototype.process_channel_message = function(msg)
return true; return true;
} }
if (msg.type == SPICE_MSG_CURSOR_INVAL_ONE)
{
this.known_unimplemented(msg.type, "Cursor Inval One");
return true;
}
if (msg.type == SPICE_MSG_CURSOR_INVAL_ALL) if (msg.type == SPICE_MSG_CURSOR_INVAL_ALL)
{ {
DEBUG > 1 && console.log("SpiceMsgCursorInvalAll"); DEBUG > 1 && console.log("SpiceMsgCursorInvalAll");
@ -100,7 +118,7 @@ SpiceCursorConn.prototype.process_channel_message = function(msg)
SpiceCursorConn.prototype.set_cursor = function(cursor) SpiceCursorConn.prototype.set_cursor = function(cursor)
{ {
var pngstr = create_rgba_png(cursor.header.height, cursor.header.width, cursor.data); var pngstr = create_rgba_png(cursor.header.height, cursor.header.width, cursor.data);
var curstr = 'url(data:image/png,' + pngstr + ') ' + var curstr = 'url(data:image/png,' + pngstr + ') ' +
cursor.header.hot_spot_x + ' ' + cursor.header.hot_spot_y + ", default"; cursor.header.hot_spot_x + ' ' + cursor.header.hot_spot_y + ", default";
var screen = document.getElementById(this.parent.screen_id); var screen = document.getElementById(this.parent.screen_id);
screen.style.cursor = 'auto'; screen.style.cursor = 'auto';

530
static/js/spice-html5/display.js Normal file → Executable file
View file

@ -62,6 +62,12 @@ function SpiceDisplayConn()
SpiceDisplayConn.prototype = Object.create(SpiceConn.prototype); SpiceDisplayConn.prototype = Object.create(SpiceConn.prototype);
SpiceDisplayConn.prototype.process_channel_message = function(msg) SpiceDisplayConn.prototype.process_channel_message = function(msg)
{ {
if (msg.type == SPICE_MSG_DISPLAY_MODE)
{
this.known_unimplemented(msg.type, "Display Mode");
return true;
}
if (msg.type == SPICE_MSG_DISPLAY_MARK) if (msg.type == SPICE_MSG_DISPLAY_MARK)
{ {
// FIXME - DISPLAY_MARK not implemented (may be hard or impossible) // FIXME - DISPLAY_MARK not implemented (may be hard or impossible)
@ -136,7 +142,7 @@ SpiceDisplayConn.prototype.process_channel_message = function(msg)
{ base: draw_copy.base, { base: draw_copy.base,
src_area: draw_copy.data.src_area, src_area: draw_copy.data.src_area,
image_data: this.cache[draw_copy.data.src_bitmap.descriptor.id], image_data: this.cache[draw_copy.data.src_bitmap.descriptor.id],
tag: "copycache." + draw_copy.data.src_bitmap.descriptor.id, tag: "copycache." + draw_copy.data.src_bitmap.descriptor.id,
has_alpha: true, /* FIXME - may want this to be false... */ has_alpha: true, /* FIXME - may want this to be false... */
descriptor : draw_copy.data.src_bitmap.descriptor descriptor : draw_copy.data.src_bitmap.descriptor
}); });
@ -171,8 +177,6 @@ SpiceDisplayConn.prototype.process_channel_message = function(msg)
has_alpha: this.surfaces[draw_copy.data.src_bitmap.surface_id].format == SPICE_SURFACE_FMT_32_xRGB ? false : true, has_alpha: this.surfaces[draw_copy.data.src_bitmap.surface_id].format == SPICE_SURFACE_FMT_32_xRGB ? false : true,
descriptor : draw_copy.data.src_bitmap.descriptor descriptor : draw_copy.data.src_bitmap.descriptor
}); });
return true;
} }
else if (draw_copy.data.src_bitmap.descriptor.type == SPICE_IMAGE_TYPE_JPEG) else if (draw_copy.data.src_bitmap.descriptor.type == SPICE_IMAGE_TYPE_JPEG)
{ {
@ -196,7 +200,7 @@ SpiceDisplayConn.prototype.process_channel_message = function(msg)
tmpstr += qdv[i].toString(16); tmpstr += qdv[i].toString(16);
} }
img.o = img.o =
{ base: draw_copy.base, { base: draw_copy.base,
tag: "jpeg." + draw_copy.data.src_bitmap.surface_id, tag: "jpeg." + draw_copy.data.src_bitmap.surface_id,
descriptor : draw_copy.data.src_bitmap.descriptor, descriptor : draw_copy.data.src_bitmap.descriptor,
@ -229,7 +233,7 @@ SpiceDisplayConn.prototype.process_channel_message = function(msg)
tmpstr += qdv[i].toString(16); tmpstr += qdv[i].toString(16);
} }
img.o = img.o =
{ base: draw_copy.base, { base: draw_copy.base,
tag: "jpeg." + draw_copy.data.src_bitmap.surface_id, tag: "jpeg." + draw_copy.data.src_bitmap.surface_id,
descriptor : draw_copy.data.src_bitmap.descriptor, descriptor : draw_copy.data.src_bitmap.descriptor,
@ -261,7 +265,7 @@ SpiceDisplayConn.prototype.process_channel_message = function(msg)
draw_copy.data.src_bitmap.bitmap); draw_copy.data.src_bitmap.bitmap);
if (! source_img) if (! source_img)
{ {
this.log_warn("FIXME: Unable to interpret bitmap of format: " + this.log_warn("FIXME: Unable to interpret bitmap of format: " +
draw_copy.data.src_bitmap.bitmap.format); draw_copy.data.src_bitmap.bitmap.format);
return false; return false;
} }
@ -284,14 +288,11 @@ SpiceDisplayConn.prototype.process_channel_message = function(msg)
return false; return false;
} }
if (draw_copy.data.src_bitmap.lz_rgb.top_down != 1)
this.log_warn("FIXME: Implement non top down support for lz_rgb");
var source_img = convert_spice_lz_to_web(canvas.context, var source_img = convert_spice_lz_to_web(canvas.context,
draw_copy.data.src_bitmap.lz_rgb); draw_copy.data.src_bitmap.lz_rgb);
if (! source_img) if (! source_img)
{ {
this.log_warn("FIXME: Unable to interpret bitmap of type: " + this.log_warn("FIXME: Unable to interpret bitmap of type: " +
draw_copy.data.src_bitmap.lz_rgb.type); draw_copy.data.src_bitmap.lz_rgb.type);
return false; return false;
} }
@ -355,7 +356,7 @@ SpiceDisplayConn.prototype.process_channel_message = function(msg)
draw_fill.base.box.bottom - draw_fill.base.box.top); draw_fill.base.box.bottom - draw_fill.base.box.top);
document.getElementById(this.parent.dump_id).appendChild(debug_canvas); document.getElementById(this.parent.dump_id).appendChild(debug_canvas);
} }
this.surfaces[draw_fill.base.surface_id].draw_count++; this.surfaces[draw_fill.base.surface_id].draw_count++;
} }
@ -366,6 +367,60 @@ SpiceDisplayConn.prototype.process_channel_message = function(msg)
return true; return true;
} }
if (msg.type == SPICE_MSG_DISPLAY_DRAW_OPAQUE)
{
this.known_unimplemented(msg.type, "Display Draw Opaque");
return true;
}
if (msg.type == SPICE_MSG_DISPLAY_DRAW_BLEND)
{
this.known_unimplemented(msg.type, "Display Draw Blend");
return true;
}
if (msg.type == SPICE_MSG_DISPLAY_DRAW_BLACKNESS)
{
this.known_unimplemented(msg.type, "Display Draw Blackness");
return true;
}
if (msg.type == SPICE_MSG_DISPLAY_DRAW_WHITENESS)
{
this.known_unimplemented(msg.type, "Display Draw Whiteness");
return true;
}
if (msg.type == SPICE_MSG_DISPLAY_DRAW_INVERS)
{
this.known_unimplemented(msg.type, "Display Draw Invers");
return true;
}
if (msg.type == SPICE_MSG_DISPLAY_DRAW_ROP3)
{
this.known_unimplemented(msg.type, "Display Draw ROP3");
return true;
}
if (msg.type == SPICE_MSG_DISPLAY_DRAW_STROKE)
{
this.known_unimplemented(msg.type, "Display Draw Stroke");
return true;
}
if (msg.type == SPICE_MSG_DISPLAY_DRAW_TRANSPARENT)
{
this.known_unimplemented(msg.type, "Display Draw Transparent");
return true;
}
if (msg.type == SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND)
{
this.known_unimplemented(msg.type, "Display Draw Alpha Blend");
return true;
}
if (msg.type == SPICE_MSG_DISPLAY_COPY_BITS) if (msg.type == SPICE_MSG_DISPLAY_COPY_BITS)
{ {
var copy_bits = new SpiceMsgDisplayCopyBits(msg.data); var copy_bits = new SpiceMsgDisplayCopyBits(msg.data);
@ -402,6 +457,18 @@ SpiceDisplayConn.prototype.process_channel_message = function(msg)
return true; return true;
} }
if (msg.type == SPICE_MSG_DISPLAY_INVAL_ALL_PIXMAPS)
{
this.known_unimplemented(msg.type, "Display Inval All Pixmaps");
return true;
}
if (msg.type == SPICE_MSG_DISPLAY_INVAL_PALETTE)
{
this.known_unimplemented(msg.type, "Display Inval Palette");
return true;
}
if (msg.type == SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES) if (msg.type == SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES)
{ {
this.known_unimplemented(msg.type, "Inval All Palettes"); this.known_unimplemented(msg.type, "Inval All Palettes");
@ -414,9 +481,9 @@ SpiceDisplayConn.prototype.process_channel_message = function(msg)
this.surfaces = []; this.surfaces = [];
var m = new SpiceMsgSurfaceCreate(msg.data); var m = new SpiceMsgSurfaceCreate(msg.data);
DEBUG > 1 && console.log(this.type + ": MsgSurfaceCreate id " + m.surface.surface_id DEBUG > 1 && console.log(this.type + ": MsgSurfaceCreate id " + m.surface.surface_id
+ "; " + m.surface.width + "x" + m.surface.height + "; " + m.surface.width + "x" + m.surface.height
+ "; format " + m.surface.format + "; format " + m.surface.format
+ "; flags " + m.surface.flags); + "; flags " + m.surface.flags);
if (m.surface.format != SPICE_SURFACE_FMT_32_xRGB && if (m.surface.format != SPICE_SURFACE_FMT_32_xRGB &&
m.surface.format != SPICE_SURFACE_FMT_32_ARGB) m.surface.format != SPICE_SURFACE_FMT_32_ARGB)
@ -465,58 +532,105 @@ SpiceDisplayConn.prototype.process_channel_message = function(msg)
if (msg.type == SPICE_MSG_DISPLAY_STREAM_CREATE) if (msg.type == SPICE_MSG_DISPLAY_STREAM_CREATE)
{ {
var m = new SpiceMsgDisplayStreamCreate(msg.data); var m = new SpiceMsgDisplayStreamCreate(msg.data);
DEBUG > 1 && console.log(this.type + ": MsgStreamCreate id" + m.id); STREAM_DEBUG > 0 && console.log(this.type + ": MsgStreamCreate id" + m.id + "; type " + m.codec_type +
"; width " + m.stream_width + "; height " + m.stream_height +
"; left " + m.dest.left + "; top " + m.dest.top
);
if (!this.streams) if (!this.streams)
this.streams = new Array(); this.streams = new Array();
if (this.streams[m.id]) if (this.streams[m.id])
console.log("Stream already exists"); console.log("Stream " + m.id + " already exists");
else else
this.streams[m.id] = m; this.streams[m.id] = m;
if (m.codec_type != SPICE_VIDEO_CODEC_TYPE_MJPEG)
if (m.codec_type == SPICE_VIDEO_CODEC_TYPE_VP8)
{
var media = new MediaSource();
var v = document.createElement("video");
v.src = window.URL.createObjectURL(media);
v.setAttribute('autoplay', true);
v.setAttribute('width', m.stream_width);
v.setAttribute('height', m.stream_height);
var left = m.dest.left;
var top = m.dest.top;
if (this.surfaces[m.surface_id] !== undefined)
{
left += this.surfaces[m.surface_id].canvas.offsetLeft;
top += this.surfaces[m.surface_id].canvas.offsetTop;
}
document.getElementById(this.parent.screen_id).appendChild(v);
v.setAttribute('style', "position: absolute; top:" + top + "px; left:" + left + "px;");
media.addEventListener('sourceopen', handle_video_source_open, false);
media.addEventListener('sourceended', handle_video_source_ended, false);
media.addEventListener('sourceclosed', handle_video_source_closed, false);
var s = this.streams[m.id];
s.video = v;
s.media = media;
s.queue = new Array();
s.start_time = 0;
s.cluster_time = 0;
s.append_okay = false;
media.stream = s;
media.spiceconn = this;
v.spice_stream = s;
}
else if (m.codec_type == SPICE_VIDEO_CODEC_TYPE_MJPEG)
this.streams[m.id].frames_loading = 0;
else
console.log("Unhandled stream codec: "+m.codec_type); console.log("Unhandled stream codec: "+m.codec_type);
return true; 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]) if (!this.streams[m.base.id])
{ {
console.log("no stream for data"); console.log("no stream for data");
return false; return false;
} }
var time_until_due = m.base.multi_media_time - this.parent.relative_now();
if (this.streams[m.base.id].codec_type === SPICE_VIDEO_CODEC_TYPE_MJPEG) if (this.streams[m.base.id].codec_type === SPICE_VIDEO_CODEC_TYPE_MJPEG)
process_mjpeg_stream_data(this, m, time_until_due);
if (this.streams[m.base.id].codec_type === SPICE_VIDEO_CODEC_TYPE_VP8)
process_video_stream_data(this.streams[m.base.id], m);
return true;
}
if (msg.type == SPICE_MSG_DISPLAY_STREAM_ACTIVATE_REPORT)
{
var m = new SpiceMsgDisplayStreamActivateReport(msg.data);
var report = new SpiceMsgcDisplayStreamReport(m.stream_id, m.unique_id);
if (this.streams[m.stream_id])
{ {
var tmpstr = "data:image/jpeg,"; this.streams[m.stream_id].report = report;
var img = new Image; this.streams[m.stream_id].max_window_size = m.max_window_size;
var i; this.streams[m.stream_id].timeout_ms = m.timeout_ms
for (i = 0; i < m.data.length; i++)
{
tmpstr += '%';
if (m.data[i] < 16)
tmpstr += '0';
tmpstr += m.data[i].toString(16);
}
var strm_base = new SpiceMsgDisplayBase();
strm_base.surface_id = this.streams[m.base.id].surface_id;
strm_base.box = this.streams[m.base.id].dest;
strm_base.clip = this.streams[m.base.id].clip;
img.o =
{ base: strm_base,
tag: "mjpeg." + m.base.id,
descriptor: null,
sc : this,
};
img.onload = handle_draw_jpeg_onload;
img.src = tmpstr;
} }
return true; return true;
} }
if (msg.type == SPICE_MSG_DISPLAY_STREAM_CLIP) if (msg.type == SPICE_MSG_DISPLAY_STREAM_CLIP)
{ {
var m = new SpiceMsgDisplayStreamClip(msg.data); var m = new SpiceMsgDisplayStreamClip(msg.data);
DEBUG > 1 && console.log(this.type + ": MsgStreamClip id" + m.id); STREAM_DEBUG > 1 && console.log(this.type + ": MsgStreamClip id" + m.id);
this.streams[m.id].clip = m.clip; this.streams[m.id].clip = m.clip;
return true; return true;
} }
@ -524,10 +638,25 @@ SpiceDisplayConn.prototype.process_channel_message = function(msg)
if (msg.type == SPICE_MSG_DISPLAY_STREAM_DESTROY) if (msg.type == SPICE_MSG_DISPLAY_STREAM_DESTROY)
{ {
var m = new SpiceMsgDisplayStreamDestroy(msg.data); var m = new SpiceMsgDisplayStreamDestroy(msg.data);
DEBUG > 1 && console.log(this.type + ": MsgStreamDestroy id" + m.id); STREAM_DEBUG > 0 && console.log(this.type + ": MsgStreamDestroy id" + m.id);
if (this.streams[m.id].codec_type == SPICE_VIDEO_CODEC_TYPE_VP8)
{
document.getElementById(this.parent.screen_id).removeChild(this.streams[m.id].video);
this.streams[m.id].source_buffer = null;
this.streams[m.id].media = null;
this.streams[m.id].video = null;
}
this.streams[m.id] = undefined; this.streams[m.id] = undefined;
return true; return true;
} }
if (msg.type == SPICE_MSG_DISPLAY_STREAM_DESTROY_ALL)
{
this.known_unimplemented(msg.type, "Display Stream Destroy All");
return true;
}
if (msg.type == SPICE_MSG_DISPLAY_INVAL_LIST) if (msg.type == SPICE_MSG_DISPLAY_INVAL_LIST)
{ {
var m = new SpiceMsgDisplayInvalList(msg.data); var m = new SpiceMsgDisplayInvalList(msg.data);
@ -539,6 +668,18 @@ SpiceDisplayConn.prototype.process_channel_message = function(msg)
return true; return true;
} }
if (msg.type == SPICE_MSG_DISPLAY_MONITORS_CONFIG)
{
this.known_unimplemented(msg.type, "Display Monitors Config");
return true;
}
if (msg.type == SPICE_MSG_DISPLAY_DRAW_COMPOSITE)
{
this.known_unimplemented(msg.type, "Display Draw Composite");
return true;
}
return false; return false;
} }
@ -611,7 +752,7 @@ SpiceDisplayConn.prototype.draw_copy_helper = function(o)
SpiceDisplayConn.prototype.log_draw = function(prefix, draw) SpiceDisplayConn.prototype.log_draw = function(prefix, draw)
{ {
var str = prefix + "." + draw.base.surface_id + "." + this.surfaces[draw.base.surface_id].draw_count + ": "; var str = prefix + "." + draw.base.surface_id + "." + this.surfaces[draw.base.surface_id].draw_count + ": ";
str += "base.box " + draw.base.box.left + ", " + draw.base.box.top + " to " + str += "base.box " + draw.base.box.left + ", " + draw.base.box.top + " to " +
draw.base.box.right + ", " + draw.base.box.bottom; draw.base.box.right + ", " + draw.base.box.bottom;
str += "; clip.type " + draw.base.clip.type; str += "; clip.type " + draw.base.clip.type;
@ -629,12 +770,12 @@ SpiceDisplayConn.prototype.log_draw = function(prefix, draw)
if (draw.data.src_bitmap.surface_id !== undefined) if (draw.data.src_bitmap.surface_id !== undefined)
str += "; src_bitmap surface_id " + draw.data.src_bitmap.surface_id; str += "; src_bitmap surface_id " + draw.data.src_bitmap.surface_id;
if (draw.data.src_bitmap.quic) if (draw.data.src_bitmap.quic)
str += "; QUIC type " + draw.data.src_bitmap.quic.type + str += "; QUIC type " + draw.data.src_bitmap.quic.type +
"; width " + draw.data.src_bitmap.quic.width + "; width " + draw.data.src_bitmap.quic.width +
"; height " + draw.data.src_bitmap.quic.height ; "; height " + draw.data.src_bitmap.quic.height ;
if (draw.data.src_bitmap.lz_rgb) if (draw.data.src_bitmap.lz_rgb)
str += "; LZ_RGB length " + draw.data.src_bitmap.lz_rgb.length + str += "; LZ_RGB length " + draw.data.src_bitmap.lz_rgb.length +
"; magic " + draw.data.src_bitmap.lz_rgb.magic + "; magic " + draw.data.src_bitmap.lz_rgb.magic +
"; version 0x" + draw.data.src_bitmap.lz_rgb.version.toString(16) + "; version 0x" + draw.data.src_bitmap.lz_rgb.version.toString(16) +
"; type " + draw.data.src_bitmap.lz_rgb.type + "; type " + draw.data.src_bitmap.lz_rgb.type +
"; width " + draw.data.src_bitmap.lz_rgb.width + "; width " + draw.data.src_bitmap.lz_rgb.width +
@ -742,6 +883,9 @@ function handle_draw_jpeg_onload()
var temp_canvas = null; var temp_canvas = null;
var context; var context;
if (this.o.sc.streams[this.o.id])
this.o.sc.streams[this.o.id].frames_loading--;
/*------------------------------------------------------------ /*------------------------------------------------------------
** FIXME: ** FIXME:
** The helper should be extended to be able to handle actual HtmlImageElements ** The helper should be extended to be able to handle actual HtmlImageElements
@ -751,7 +895,7 @@ function handle_draw_jpeg_onload()
{ {
// This can happen; if the jpeg image loads after our surface // This can happen; if the jpeg image loads after our surface
// has been destroyed (e.g. open a menu, close it quickly), // has been destroyed (e.g. open a menu, close it quickly),
// we'll find we have no surface. // we'll find we have no surface.
DEBUG > 2 && this.o.sc.log_info("Discarding jpeg; presumed lost surface " + this.o.base.surface_id); DEBUG > 2 && this.o.sc.log_info("Discarding jpeg; presumed lost surface " + this.o.base.surface_id);
temp_canvas = document.createElement("canvas"); temp_canvas = document.createElement("canvas");
temp_canvas.setAttribute('width', this.o.base.box.right); temp_canvas.setAttribute('width', this.o.base.box.right);
@ -770,16 +914,16 @@ function handle_draw_jpeg_onload()
t.putImageData(this.alpha_img, 0, 0); t.putImageData(this.alpha_img, 0, 0);
t.globalCompositeOperation = 'source-in'; t.globalCompositeOperation = 'source-in';
t.drawImage(this, 0, 0); t.drawImage(this, 0, 0);
context.drawImage(c, this.o.base.box.left, this.o.base.box.top); context.drawImage(c, this.o.base.box.left, this.o.base.box.top);
if (this.o.descriptor && if (this.o.descriptor &&
(this.o.descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) (this.o.descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME))
{ {
if (! ("cache" in this.o.sc)) if (! ("cache" in this.o.sc))
this.o.sc.cache = {}; this.o.sc.cache = {};
this.o.sc.cache[this.o.descriptor.id] = this.o.sc.cache[this.o.descriptor.id] =
t.getImageData(0, 0, t.getImageData(0, 0,
this.alpha_img.width, this.alpha_img.width,
this.alpha_img.height); this.alpha_img.height);
@ -791,15 +935,16 @@ function handle_draw_jpeg_onload()
// Give the Garbage collector a clue to recycle this; avoids // Give the Garbage collector a clue to recycle this; avoids
// fairly massive memory leaks during video playback // fairly massive memory leaks during video playback
this.src = null; this.onload = undefined;
this.src = EMPTY_GIF_IMAGE;
if (this.o.descriptor && if (this.o.descriptor &&
(this.o.descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME)) (this.o.descriptor.flags & SPICE_IMAGE_FLAGS_CACHE_ME))
{ {
if (! ("cache" in this.o.sc)) if (! ("cache" in this.o.sc))
this.o.sc.cache = {}; this.o.sc.cache = {};
this.o.sc.cache[this.o.descriptor.id] = this.o.sc.cache[this.o.descriptor.id] =
context.getImageData(this.o.base.box.left, this.o.base.box.top, context.getImageData(this.o.base.box.left, this.o.base.box.top,
this.o.base.box.right - this.o.base.box.left, this.o.base.box.right - this.o.base.box.left,
this.o.base.box.bottom - this.o.base.box.top); this.o.base.box.bottom - this.o.base.box.top);
@ -820,4 +965,283 @@ function handle_draw_jpeg_onload()
this.o.sc.surfaces[this.o.base.surface_id].draw_count++; this.o.sc.surfaces[this.o.base.surface_id].draw_count++;
} }
if (this.o.sc.streams[this.o.id] && "report" in this.o.sc.streams[this.o.id])
process_stream_data_report(this.o.sc, this.o.id, this.o.msg_mmtime, this.o.msg_mmtime - this.o.sc.parent.relative_now());
}
function process_mjpeg_stream_data(sc, m, time_until_due)
{
/* If we are currently processing an mjpeg frame when a new one arrives,
and the new one is 'late', drop the new frame. This helps the browsers
keep up, and provides rate control feedback as well */
if (time_until_due < 0 && sc.streams[m.base.id].frames_loading > 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;
for (i = 0; i < m.data.length; i++)
{
tmpstr += '%';
if (m.data[i] < 16)
tmpstr += '0';
tmpstr += m.data[i].toString(16);
}
var strm_base = new SpiceMsgDisplayBase();
strm_base.surface_id = sc.streams[m.base.id].surface_id;
strm_base.box = m.dest || sc.streams[m.base.id].dest;
strm_base.clip = sc.streams[m.base.id].clip;
img.o =
{ base: strm_base,
tag: "mjpeg." + m.base.id,
descriptor: null,
sc : sc,
id : m.base.id,
msg_mmtime : m.base.multi_media_time,
};
img.onload = handle_draw_jpeg_onload;
img.src = tmpstr;
sc.streams[m.base.id].frames_loading++;
}
function process_stream_data_report(sc, id, msg_mmtime, time_until_due)
{
sc.streams[id].report.num_frames++;
if (sc.streams[id].report.start_frame_mm_time == 0)
sc.streams[id].report.start_frame_mm_time = msg_mmtime;
if (sc.streams[id].report.num_frames > sc.streams[id].max_window_size ||
(msg_mmtime - sc.streams[id].report.start_frame_mm_time) > sc.streams[id].timeout_ms)
{
sc.streams[id].report.end_frame_mm_time = msg_mmtime;
sc.streams[id].report.last_frame_delay = time_until_due;
var msg = new SpiceMiniData();
msg.build_msg(SPICE_MSGC_DISPLAY_STREAM_REPORT, sc.streams[id].report);
sc.send_msg(msg);
sc.streams[id].report.start_frame_mm_time = 0;
sc.streams[id].report.num_frames = 0;
sc.streams[id].report.num_drops = 0;
}
}
function handle_video_source_open(e)
{
var stream = this.stream;
var p = this.spiceconn;
if (stream.source_buffer)
return;
var s = this.addSourceBuffer(SPICE_VP8_CODEC);
if (! s)
{
p.log_err('Codec ' + SPICE_VP8_CODEC + ' not available.');
return;
}
stream.source_buffer = s;
s.spiceconn = p;
s.stream = stream;
listen_for_video_events(stream);
var h = new webm_Header();
var te = new webm_VideoTrackEntry(this.stream.stream_width, this.stream.stream_height);
var t = new webm_Tracks(te);
var mb = new ArrayBuffer(h.buffer_size() + t.buffer_size())
var b = h.to_buffer(mb);
t.to_buffer(mb, b);
s.addEventListener('error', handle_video_buffer_error, false);
s.addEventListener('updateend', handle_append_video_buffer_done, false);
append_video_buffer(s, mb);
}
function handle_video_source_ended(e)
{
var p = this.spiceconn;
p.log_err('Video source unexpectedly ended.');
}
function handle_video_source_closed(e)
{
var p = this.spiceconn;
p.log_err('Video source unexpectedly closed.');
}
function append_video_buffer(sb, mb)
{
try
{
sb.stream.append_okay = false;
sb.appendBuffer(mb);
}
catch (e)
{
var p = sb.spiceconn;
p.log_err("Error invoking appendBuffer: " + e.message);
}
}
function handle_append_video_buffer_done(e)
{
var stream = this.stream;
if (stream.current_frame && "report" in stream)
{
var sc = this.stream.media.spiceconn;
var t = this.stream.current_frame.msg_mmtime;
process_stream_data_report(sc, stream.id, t, t - sc.parent.relative_now());
}
if (stream.queue.length > 0)
{
stream.current_frame = stream.queue.shift();
append_video_buffer(stream.source_buffer, stream.current_frame.mb);
}
else
{
stream.append_okay = true;
}
if (!stream.video)
{
if (STREAM_DEBUG > 0)
console.log("Stream id " + stream.id + " received updateend after video is gone.");
return;
}
if (stream.video.buffered.length > 0 &&
stream.video.currentTime < stream.video.buffered.start(stream.video.buffered.length - 1))
{
console.log("Video appears to have fallen behind; advancing to " +
stream.video.buffered.start(stream.video.buffered.length - 1));
stream.video.currentTime = stream.video.buffered.start(stream.video.buffered.length - 1);
}
if (STREAM_DEBUG > 1)
console.log(stream.video.currentTime + ":id " + stream.id + " updateend " + dump_media_element(stream.video));
}
function handle_video_buffer_error(e)
{
var p = this.spiceconn;
p.log_err('source_buffer error ' + e.message);
}
function push_or_queue(stream, msg, mb)
{
var frame =
{
msg_mmtime : msg.base.multi_media_time,
};
if (stream.append_okay)
{
stream.current_frame = frame;
append_video_buffer(stream.source_buffer, mb);
}
else
{
frame.mb = mb;
stream.queue.push(frame);
}
}
function video_simple_block(stream, msg, keyframe)
{
var simple = new webm_SimpleBlock(msg.base.multi_media_time - stream.cluster_time, msg.data, keyframe);
var mb = new ArrayBuffer(simple.buffer_size());
simple.to_buffer(mb);
push_or_queue(stream, msg, mb);
}
function new_video_cluster(stream, msg)
{
stream.cluster_time = msg.base.multi_media_time;
var c = new webm_Cluster(stream.cluster_time - stream.start_time, msg.data);
var mb = new ArrayBuffer(c.buffer_size());
c.to_buffer(mb);
push_or_queue(stream, msg, mb);
video_simple_block(stream, msg, true);
}
function process_video_stream_data(stream, msg)
{
if (stream.start_time == 0)
{
stream.start_time = msg.base.multi_media_time;
new_video_cluster(stream, msg);
}
else if (msg.base.multi_media_time - stream.cluster_time >= MAX_CLUSTER_TIME)
new_video_cluster(stream, msg);
else
video_simple_block(stream, msg, false);
}
function video_handle_event_debug(e)
{
var s = this.spice_stream;
if (s.video)
{
if (STREAM_DEBUG > 0 || s.video.buffered.len > 1)
console.log(s.video.currentTime + ":id " + s.id + " event " + e.type +
dump_media_element(s.video));
}
if (STREAM_DEBUG > 1 && s.media)
console.log(" media_source " + dump_media_source(s.media));
if (STREAM_DEBUG > 1 && s.source_buffer)
console.log(" source_buffer " + dump_source_buffer(s.source_buffer));
if (STREAM_DEBUG > 1 || s.queue.length > 1)
console.log(' queue len ' + s.queue.length + '; append_okay: ' + s.append_okay);
}
function video_debug_listen_for_one_event(name)
{
this.addEventListener(name, video_handle_event_debug);
}
function listen_for_video_events(stream)
{
var video_0_events = [
"abort", "error"
];
var video_1_events = [
"loadstart", "suspend", "emptied", "stalled", "loadedmetadata", "loadeddata", "canplay",
"canplaythrough", "playing", "waiting", "seeking", "seeked", "ended", "durationchange",
"play", "pause", "ratechange"
];
var video_2_events = [
"timeupdate",
"progress",
"resize",
"volumechange"
];
video_0_events.forEach(video_debug_listen_for_one_event, stream.video);
if (STREAM_DEBUG > 0)
video_1_events.forEach(video_debug_listen_for_one_event, stream.video);
if (STREAM_DEBUG > 1)
video_2_events.forEach(video_debug_listen_for_one_event, stream.video);
} }

31
static/js/spice-html5/enums.js Normal file → Executable file
View file

@ -127,8 +127,13 @@ var SPICE_MSG_DISPLAY_DRAW_TRANSPARENT = 312;
var SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND = 313; var SPICE_MSG_DISPLAY_DRAW_ALPHA_BLEND = 313;
var SPICE_MSG_DISPLAY_SURFACE_CREATE = 314; var SPICE_MSG_DISPLAY_SURFACE_CREATE = 314;
var SPICE_MSG_DISPLAY_SURFACE_DESTROY = 315; var SPICE_MSG_DISPLAY_SURFACE_DESTROY = 315;
var SPICE_MSG_DISPLAY_STREAM_DATA_SIZED = 316;
var SPICE_MSG_DISPLAY_MONITORS_CONFIG = 317;
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_INIT = 101;
var SPICE_MSGC_DISPLAY_STREAM_REPORT = 102;
var SPICE_MSG_INPUTS_INIT = 101; var SPICE_MSG_INPUTS_INIT = 101;
var SPICE_MSG_INPUTS_KEY_MODIFIERS = 102; var SPICE_MSG_INPUTS_KEY_MODIFIERS = 102;
@ -161,6 +166,15 @@ var SPICE_MSG_PLAYBACK_VOLUME = 105;
var SPICE_MSG_PLAYBACK_MUTE = 106; var SPICE_MSG_PLAYBACK_MUTE = 106;
var SPICE_MSG_PLAYBACK_LATENCY = 107; var SPICE_MSG_PLAYBACK_LATENCY = 107;
var SPICE_MSG_SPICEVMC_DATA = 101;
var SPICE_MSG_PORT_INIT = 201;
var SPICE_MSG_PORT_EVENT = 202;
var SPICE_MSG_END_PORT = 203;
var SPICE_MSGC_SPICEVMC_DATA = 101;
var SPICE_MSGC_PORT_EVENT = 201;
var SPICE_MSGC_END_PORT = 202;
var SPICE_PLAYBACK_CAP_CELT_0_5_1 = 0; var SPICE_PLAYBACK_CAP_CELT_0_5_1 = 0;
var SPICE_PLAYBACK_CAP_VOLUME = 1; var SPICE_PLAYBACK_CAP_VOLUME = 1;
var SPICE_PLAYBACK_CAP_LATENCY = 2; var SPICE_PLAYBACK_CAP_LATENCY = 2;
@ -171,6 +185,18 @@ var SPICE_MAIN_CAP_NAME_AND_UUID = 1;
var SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS = 2; var SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS = 2;
var SPICE_MAIN_CAP_SEAMLESS_MIGRATE = 3; var SPICE_MAIN_CAP_SEAMLESS_MIGRATE = 3;
var SPICE_DISPLAY_CAP_SIZED_STREAM = 0;
var SPICE_DISPLAY_CAP_MONITORS_CONFIG = 1;
var SPICE_DISPLAY_CAP_COMPOSITE = 2;
var SPICE_DISPLAY_CAP_A8_SURFACE = 3;
var SPICE_DISPLAY_CAP_STREAM_REPORT = 4;
var SPICE_DISPLAY_CAP_LZ4_COMPRESSION = 5;
var SPICE_DISPLAY_CAP_PREF_COMPRESSION = 6;
var SPICE_DISPLAY_CAP_GL_SCANOUT = 7;
var SPICE_DISPLAY_CAP_MULTI_CODEC = 8;
var SPICE_DISPLAY_CAP_CODEC_MJPEG = 9;
var SPICE_DISPLAY_CAP_CODEC_VP8 = 10;
var SPICE_AUDIO_DATA_MODE_INVALID = 0; var SPICE_AUDIO_DATA_MODE_INVALID = 0;
var SPICE_AUDIO_DATA_MODE_RAW = 1; var SPICE_AUDIO_DATA_MODE_RAW = 1;
var SPICE_AUDIO_DATA_MODE_CELT_0_5_1 = 2; var SPICE_AUDIO_DATA_MODE_CELT_0_5_1 = 2;
@ -188,6 +214,8 @@ var SPICE_CHANNEL_RECORD = 6;
var SPICE_CHANNEL_TUNNEL = 7; var SPICE_CHANNEL_TUNNEL = 7;
var SPICE_CHANNEL_SMARTCARD = 8; var SPICE_CHANNEL_SMARTCARD = 8;
var SPICE_CHANNEL_USBREDIR = 9; var SPICE_CHANNEL_USBREDIR = 9;
var SPICE_CHANNEL_PORT = 10;
var SPICE_CHANNEL_WEBDAV = 11;
var SPICE_SURFACE_FLAGS_PRIMARY = (1 << 0); var SPICE_SURFACE_FLAGS_PRIMARY = (1 << 0);
@ -245,7 +273,7 @@ var SPICE_MOUSE_BUTTON_MASK_LEFT = (1 << 0),
SPICE_MOUSE_BUTTON_MASK_MIDDLE = (1 << 1), SPICE_MOUSE_BUTTON_MASK_MIDDLE = (1 << 1),
SPICE_MOUSE_BUTTON_MASK_RIGHT = (1 << 2), SPICE_MOUSE_BUTTON_MASK_RIGHT = (1 << 2),
SPICE_MOUSE_BUTTON_MASK_MASK = 0x7; SPICE_MOUSE_BUTTON_MASK_MASK = 0x7;
var SPICE_MOUSE_BUTTON_INVALID = 0; var SPICE_MOUSE_BUTTON_INVALID = 0;
var SPICE_MOUSE_BUTTON_LEFT = 1; var SPICE_MOUSE_BUTTON_LEFT = 1;
var SPICE_MOUSE_BUTTON_MIDDLE = 2; var SPICE_MOUSE_BUTTON_MIDDLE = 2;
@ -310,6 +338,7 @@ var SPICE_CURSOR_TYPE_ALPHA = 0,
SPICE_CURSOR_TYPE_COLOR32 = 6; SPICE_CURSOR_TYPE_COLOR32 = 6;
var SPICE_VIDEO_CODEC_TYPE_MJPEG = 1; var SPICE_VIDEO_CODEC_TYPE_MJPEG = 1;
var SPICE_VIDEO_CODEC_TYPE_VP8 = 2;
var VD_AGENT_PROTOCOL = 1; var VD_AGENT_PROTOCOL = 1;
var VD_AGENT_MAX_DATA_SIZE = 2048; var VD_AGENT_MAX_DATA_SIZE = 2048;

2
static/js/spice-html5/filexfer.js Normal file → Executable file
View file

@ -81,7 +81,7 @@ function handle_file_drop(e)
e.preventDefault(); e.preventDefault();
for (var i = files.length - 1; i >= 0; i--) for (var i = files.length - 1; i >= 0; i--)
{ {
if (files[i].type); // do not copy a directory if (files[i].type) // do not copy a directory
sc.file_xfer_start(files[i]); sc.file_xfer_start(files[i]);
} }

0
static/js/spice-html5/inputs.js Normal file → Executable file
View file

View file

@ -1,589 +0,0 @@
// Downloaded from http://www-cs-students.stanford.edu/~tjw/jsbn/ by Jeremy White on 6/1/2012
/*
* Copyright (c) 2003-2005 Tom Wu
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
*
* IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
* THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* In addition, the following condition applies:
*
* All redistributions must retain an intact copy of this copyright notice
* and disclaimer.
*/
// Basic JavaScript BN library - subset useful for RSA encryption.
// Bits per digit
var dbits;
// JavaScript engine analysis
var canary = 0xdeadbeefcafe;
var j_lm = ((canary&0xffffff)==0xefcafe);
// (public) Constructor
function BigInteger(a,b,c) {
if(a != null)
if("number" == typeof a) this.fromNumber(a,b,c);
else if(b == null && "string" != typeof a) this.fromString(a,256);
else this.fromString(a,b);
}
// return new, unset BigInteger
function nbi() { return new BigInteger(null); }
// am: Compute w_j += (x*this_i), propagate carries,
// c is initial carry, returns final carry.
// c < 3*dvalue, x < 2*dvalue, this_i < dvalue
// We need to select the fastest one that works in this environment.
// am1: use a single mult and divide to get the high bits,
// max digit bits should be 26 because
// max internal value = 2*dvalue^2-2*dvalue (< 2^53)
function am1(i,x,w,j,c,n) {
while(--n >= 0) {
var v = x*this[i++]+w[j]+c;
c = Math.floor(v/0x4000000);
w[j++] = v&0x3ffffff;
}
return c;
}
// am2 avoids a big mult-and-extract completely.
// Max digit bits should be <= 30 because we do bitwise ops
// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31)
function am2(i,x,w,j,c,n) {
var xl = x&0x7fff, xh = x>>15;
while(--n >= 0) {
var l = this[i]&0x7fff;
var h = this[i++]>>15;
var m = xh*l+h*xl;
l = xl*l+((m&0x7fff)<<15)+w[j]+(c&0x3fffffff);
c = (l>>>30)+(m>>>15)+xh*h+(c>>>30);
w[j++] = l&0x3fffffff;
}
return c;
}
// Alternately, set max digit bits to 28 since some
// browsers slow down when dealing with 32-bit numbers.
function am3(i,x,w,j,c,n) {
var xl = x&0x3fff, xh = x>>14;
while(--n >= 0) {
var l = this[i]&0x3fff;
var h = this[i++]>>14;
var m = xh*l+h*xl;
l = xl*l+((m&0x3fff)<<14)+w[j]+c;
c = (l>>28)+(m>>14)+xh*h;
w[j++] = l&0xfffffff;
}
return c;
}
if(j_lm && (navigator.appName == "Microsoft Internet Explorer")) {
BigInteger.prototype.am = am2;
dbits = 30;
}
else if(j_lm && (navigator.appName != "Netscape")) {
BigInteger.prototype.am = am1;
dbits = 26;
}
else { // Mozilla/Netscape seems to prefer am3
BigInteger.prototype.am = am3;
dbits = 28;
}
BigInteger.prototype.DB = dbits;
BigInteger.prototype.DM = ((1<<dbits)-1);
BigInteger.prototype.DV = (1<<dbits);
var BI_FP = 52;
BigInteger.prototype.FV = Math.pow(2,BI_FP);
BigInteger.prototype.F1 = BI_FP-dbits;
BigInteger.prototype.F2 = 2*dbits-BI_FP;
// Digit conversions
var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz";
var BI_RC = new Array();
var rr,vv;
rr = "0".charCodeAt(0);
for(vv = 0; vv <= 9; ++vv) BI_RC[rr++] = vv;
rr = "a".charCodeAt(0);
for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
rr = "A".charCodeAt(0);
for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
function int2char(n) { return BI_RM.charAt(n); }
function intAt(s,i) {
var c = BI_RC[s.charCodeAt(i)];
return (c==null)?-1:c;
}
// (protected) copy this to r
function bnpCopyTo(r) {
for(var i = this.t-1; i >= 0; --i) r[i] = this[i];
r.t = this.t;
r.s = this.s;
}
// (protected) set from integer value x, -DV <= x < DV
function bnpFromInt(x) {
this.t = 1;
this.s = (x<0)?-1:0;
if(x > 0) this[0] = x;
else if(x < -1) this[0] = x+DV;
else this.t = 0;
}
// return bigint initialized to value
function nbv(i) { var r = nbi(); r.fromInt(i); return r; }
// (protected) set from string and radix
function bnpFromString(s,b) {
var k;
if(b == 16) k = 4;
else if(b == 8) k = 3;
else if(b == 256) k = 8; // byte array
else if(b == 2) k = 1;
else if(b == 32) k = 5;
else if(b == 4) k = 2;
else { this.fromRadix(s,b); return; }
this.t = 0;
this.s = 0;
var i = s.length, mi = false, sh = 0;
while(--i >= 0) {
var x = (k==8)?s[i]&0xff:intAt(s,i);
if(x < 0) {
if(s.charAt(i) == "-") mi = true;
continue;
}
mi = false;
if(sh == 0)
this[this.t++] = x;
else if(sh+k > this.DB) {
this[this.t-1] |= (x&((1<<(this.DB-sh))-1))<<sh;
this[this.t++] = (x>>(this.DB-sh));
}
else
this[this.t-1] |= x<<sh;
sh += k;
if(sh >= this.DB) sh -= this.DB;
}
if(k == 8 && (s[0]&0x80) != 0) {
this.s = -1;
if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)<<sh;
}
this.clamp();
if(mi) BigInteger.ZERO.subTo(this,this);
}
// (protected) clamp off excess high words
function bnpClamp() {
var c = this.s&this.DM;
while(this.t > 0 && this[this.t-1] == c) --this.t;
}
// (public) return string representation in given radix
function bnToString(b) {
if(this.s < 0) return "-"+this.negate().toString(b);
var k;
if(b == 16) k = 4;
else if(b == 8) k = 3;
else if(b == 2) k = 1;
else if(b == 32) k = 5;
else if(b == 4) k = 2;
else return this.toRadix(b);
var km = (1<<k)-1, d, m = false, r = "", i = this.t;
var p = this.DB-(i*this.DB)%k;
if(i-- > 0) {
if(p < this.DB && (d = this[i]>>p) > 0) { m = true; r = int2char(d); }
while(i >= 0) {
if(p < k) {
d = (this[i]&((1<<p)-1))<<(k-p);
d |= this[--i]>>(p+=this.DB-k);
}
else {
d = (this[i]>>(p-=k))&km;
if(p <= 0) { p += this.DB; --i; }
}
if(d > 0) m = true;
if(m) r += int2char(d);
}
}
return m?r:"0";
}
// (public) -this
function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; }
// (public) |this|
function bnAbs() { return (this.s<0)?this.negate():this; }
// (public) return + if this > a, - if this < a, 0 if equal
function bnCompareTo(a) {
var r = this.s-a.s;
if(r != 0) return r;
var i = this.t;
r = i-a.t;
if(r != 0) return r;
while(--i >= 0) if((r=this[i]-a[i]) != 0) return r;
return 0;
}
// returns bit length of the integer x
function nbits(x) {
var r = 1, t;
if((t=x>>>16) != 0) { x = t; r += 16; }
if((t=x>>8) != 0) { x = t; r += 8; }
if((t=x>>4) != 0) { x = t; r += 4; }
if((t=x>>2) != 0) { x = t; r += 2; }
if((t=x>>1) != 0) { x = t; r += 1; }
return r;
}
// (public) return the number of bits in "this"
function bnBitLength() {
if(this.t <= 0) return 0;
return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM));
}
// (protected) r = this << n*DB
function bnpDLShiftTo(n,r) {
var i;
for(i = this.t-1; i >= 0; --i) r[i+n] = this[i];
for(i = n-1; i >= 0; --i) r[i] = 0;
r.t = this.t+n;
r.s = this.s;
}
// (protected) r = this >> n*DB
function bnpDRShiftTo(n,r) {
for(var i = n; i < this.t; ++i) r[i-n] = this[i];
r.t = Math.max(this.t-n,0);
r.s = this.s;
}
// (protected) r = this << n
function bnpLShiftTo(n,r) {
var bs = n%this.DB;
var cbs = this.DB-bs;
var bm = (1<<cbs)-1;
var ds = Math.floor(n/this.DB), c = (this.s<<bs)&this.DM, i;
for(i = this.t-1; i >= 0; --i) {
r[i+ds+1] = (this[i]>>cbs)|c;
c = (this[i]&bm)<<bs;
}
for(i = ds-1; i >= 0; --i) r[i] = 0;
r[ds] = c;
r.t = this.t+ds+1;
r.s = this.s;
r.clamp();
}
// (protected) r = this >> n
function bnpRShiftTo(n,r) {
r.s = this.s;
var ds = Math.floor(n/this.DB);
if(ds >= this.t) { r.t = 0; return; }
var bs = n%this.DB;
var cbs = this.DB-bs;
var bm = (1<<bs)-1;
r[0] = this[ds]>>bs;
for(var i = ds+1; i < this.t; ++i) {
r[i-ds-1] |= (this[i]&bm)<<cbs;
r[i-ds] = this[i]>>bs;
}
if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<<cbs;
r.t = this.t-ds;
r.clamp();
}
// (protected) r = this - a
function bnpSubTo(a,r) {
var i = 0, c = 0, m = Math.min(a.t,this.t);
while(i < m) {
c += this[i]-a[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
if(a.t < this.t) {
c -= a.s;
while(i < this.t) {
c += this[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
c += this.s;
}
else {
c += this.s;
while(i < a.t) {
c -= a[i];
r[i++] = c&this.DM;
c >>= this.DB;
}
c -= a.s;
}
r.s = (c<0)?-1:0;
if(c < -1) r[i++] = this.DV+c;
else if(c > 0) r[i++] = c;
r.t = i;
r.clamp();
}
// (protected) r = this * a, r != this,a (HAC 14.12)
// "this" should be the larger one if appropriate.
function bnpMultiplyTo(a,r) {
var x = this.abs(), y = a.abs();
var i = x.t;
r.t = i+y.t;
while(--i >= 0) r[i] = 0;
for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t);
r.s = 0;
r.clamp();
if(this.s != a.s) BigInteger.ZERO.subTo(r,r);
}
// (protected) r = this^2, r != this (HAC 14.16)
function bnpSquareTo(r) {
var x = this.abs();
var i = r.t = 2*x.t;
while(--i >= 0) r[i] = 0;
for(i = 0; i < x.t-1; ++i) {
var c = x.am(i,x[i],r,2*i,0,1);
if((r[i+x.t]+=x.am(i+1,2*x[i],r,2*i+1,c,x.t-i-1)) >= x.DV) {
r[i+x.t] -= x.DV;
r[i+x.t+1] = 1;
}
}
if(r.t > 0) r[r.t-1] += x.am(i,x[i],r,2*i,0,1);
r.s = 0;
r.clamp();
}
// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20)
// r != q, this != m. q or r may be null.
function bnpDivRemTo(m,q,r) {
var pm = m.abs();
if(pm.t <= 0) return;
var pt = this.abs();
if(pt.t < pm.t) {
if(q != null) q.fromInt(0);
if(r != null) this.copyTo(r);
return;
}
if(r == null) r = nbi();
var y = nbi(), ts = this.s, ms = m.s;
var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus
if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); }
else { pm.copyTo(y); pt.copyTo(r); }
var ys = y.t;
var y0 = y[ys-1];
if(y0 == 0) return;
var yt = y0*(1<<this.F1)+((ys>1)?y[ys-2]>>this.F2:0);
var d1 = this.FV/yt, d2 = (1<<this.F1)/yt, e = 1<<this.F2;
var i = r.t, j = i-ys, t = (q==null)?nbi():q;
y.dlShiftTo(j,t);
if(r.compareTo(t) >= 0) {
r[r.t++] = 1;
r.subTo(t,r);
}
BigInteger.ONE.dlShiftTo(ys,t);
t.subTo(y,y); // "negative" y so we can replace sub with am later
while(y.t < ys) y[y.t++] = 0;
while(--j >= 0) {
// Estimate quotient digit
var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2);
if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out
y.dlShiftTo(j,t);
r.subTo(t,r);
while(r[i] < --qd) r.subTo(t,r);
}
}
if(q != null) {
r.drShiftTo(ys,q);
if(ts != ms) BigInteger.ZERO.subTo(q,q);
}
r.t = ys;
r.clamp();
if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder
if(ts < 0) BigInteger.ZERO.subTo(r,r);
}
// (public) this mod a
function bnMod(a) {
var r = nbi();
this.abs().divRemTo(a,null,r);
if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r);
return r;
}
// Modular reduction using "classic" algorithm
function Classic(m) { this.m = m; }
function cConvert(x) {
if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m);
else return x;
}
function cRevert(x) { return x; }
function cReduce(x) { x.divRemTo(this.m,null,x); }
function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
Classic.prototype.convert = cConvert;
Classic.prototype.revert = cRevert;
Classic.prototype.reduce = cReduce;
Classic.prototype.mulTo = cMulTo;
Classic.prototype.sqrTo = cSqrTo;
// (protected) return "-1/this % 2^DB"; useful for Mont. reduction
// justification:
// xy == 1 (mod m)
// xy = 1+km
// xy(2-xy) = (1+km)(1-km)
// x[y(2-xy)] = 1-k^2m^2
// x[y(2-xy)] == 1 (mod m^2)
// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2
// should reduce x and y(2-xy) by m^2 at each step to keep size bounded.
// JS multiply "overflows" differently from C/C++, so care is needed here.
function bnpInvDigit() {
if(this.t < 1) return 0;
var x = this[0];
if((x&1) == 0) return 0;
var y = x&3; // y == 1/x mod 2^2
y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4
y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8
y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16
// last step - calculate inverse mod DV directly;
// assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints
y = (y*(2-x*y%this.DV))%this.DV; // y == 1/x mod 2^dbits
// we really want the negative inverse, and -DV < y < DV
return (y>0)?this.DV-y:-y;
}
// Montgomery reduction
function Montgomery(m) {
this.m = m;
this.mp = m.invDigit();
this.mpl = this.mp&0x7fff;
this.mph = this.mp>>15;
this.um = (1<<(m.DB-15))-1;
this.mt2 = 2*m.t;
}
// xR mod m
function montConvert(x) {
var r = nbi();
x.abs().dlShiftTo(this.m.t,r);
r.divRemTo(this.m,null,r);
if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r);
return r;
}
// x/R mod m
function montRevert(x) {
var r = nbi();
x.copyTo(r);
this.reduce(r);
return r;
}
// x = x/R mod m (HAC 14.32)
function montReduce(x) {
while(x.t <= this.mt2) // pad x so am has enough room later
x[x.t++] = 0;
for(var i = 0; i < this.m.t; ++i) {
// faster way of calculating u0 = x[i]*mp mod DV
var j = x[i]&0x7fff;
var u0 = (j*this.mpl+(((j*this.mph+(x[i]>>15)*this.mpl)&this.um)<<15))&x.DM;
// use am to combine the multiply-shift-add into one call
j = i+this.m.t;
x[j] += this.m.am(0,u0,x,i,0,this.m.t);
// propagate carry
while(x[j] >= x.DV) { x[j] -= x.DV; x[++j]++; }
}
x.clamp();
x.drShiftTo(this.m.t,x);
if(x.compareTo(this.m) >= 0) x.subTo(this.m,x);
}
// r = "x^2/R mod m"; x != r
function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); }
// r = "xy/R mod m"; x,y != r
function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
Montgomery.prototype.convert = montConvert;
Montgomery.prototype.revert = montRevert;
Montgomery.prototype.reduce = montReduce;
Montgomery.prototype.mulTo = montMulTo;
Montgomery.prototype.sqrTo = montSqrTo;
// (protected) true iff this is even
function bnpIsEven() { return ((this.t>0)?(this[0]&1):this.s) == 0; }
// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79)
function bnpExp(e,z) {
if(e > 0xffffffff || e < 1) return BigInteger.ONE;
var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1;
g.copyTo(r);
while(--i >= 0) {
z.sqrTo(r,r2);
if((e&(1<<i)) > 0) z.mulTo(r2,g,r);
else { var t = r; r = r2; r2 = t; }
}
return z.revert(r);
}
// (public) this^e % m, 0 <= e < 2^32
function bnModPowInt(e,m) {
var z;
if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m);
return this.exp(e,z);
}
// protected
BigInteger.prototype.copyTo = bnpCopyTo;
BigInteger.prototype.fromInt = bnpFromInt;
BigInteger.prototype.fromString = bnpFromString;
BigInteger.prototype.clamp = bnpClamp;
BigInteger.prototype.dlShiftTo = bnpDLShiftTo;
BigInteger.prototype.drShiftTo = bnpDRShiftTo;
BigInteger.prototype.lShiftTo = bnpLShiftTo;
BigInteger.prototype.rShiftTo = bnpRShiftTo;
BigInteger.prototype.subTo = bnpSubTo;
BigInteger.prototype.multiplyTo = bnpMultiplyTo;
BigInteger.prototype.squareTo = bnpSquareTo;
BigInteger.prototype.divRemTo = bnpDivRemTo;
BigInteger.prototype.invDigit = bnpInvDigit;
BigInteger.prototype.isEven = bnpIsEven;
BigInteger.prototype.exp = bnpExp;
// public
BigInteger.prototype.toString = bnToString;
BigInteger.prototype.negate = bnNegate;
BigInteger.prototype.abs = bnAbs;
BigInteger.prototype.compareTo = bnCompareTo;
BigInteger.prototype.bitLength = bnBitLength;
BigInteger.prototype.mod = bnMod;
BigInteger.prototype.modPowInt = bnModPowInt;
// "constants"
BigInteger.ZERO = nbv(0);
BigInteger.ONE = nbv(1);

16
static/js/spice-html5/lz.js Normal file → Executable file
View file

@ -141,6 +141,19 @@ function lz_rgb32_decompress(in_buf, at, out_buf, type, default_alpha)
return encoder - 1; return encoder - 1;
} }
function flip_image_data(img)
{
var wb = img.width * 4;
var h = img.height;
var temp_h = h;
var buff = new Uint8Array(img.width * img.height * 4);
while (temp_h--)
{
buff.set(img.data.subarray(temp_h * wb, (temp_h + 1) * wb), (h - temp_h - 1) * wb);
}
img.data.set(buff);
}
function convert_spice_lz_to_web(context, lz_image) function convert_spice_lz_to_web(context, lz_image)
{ {
var at; var at;
@ -150,6 +163,9 @@ function convert_spice_lz_to_web(context, lz_image)
var ret = context.createImageData(lz_image.width, lz_image.height); var ret = context.createImageData(lz_image.width, lz_image.height);
at = lz_rgb32_decompress(u8, 0, ret.data, LZ_IMAGE_TYPE_RGB32, lz_image.type != LZ_IMAGE_TYPE_RGBA); at = lz_rgb32_decompress(u8, 0, ret.data, LZ_IMAGE_TYPE_RGB32, lz_image.type != LZ_IMAGE_TYPE_RGBA);
if (!lz_image.top_down)
flip_image_data(ret);
if (lz_image.type == LZ_IMAGE_TYPE_RGBA) if (lz_image.type == LZ_IMAGE_TYPE_RGBA)
lz_rgb32_decompress(u8, at, ret.data, LZ_IMAGE_TYPE_RGBA, false); lz_rgb32_decompress(u8, at, ret.data, LZ_IMAGE_TYPE_RGBA, false);
} }

83
static/js/spice-html5/main.js Normal file → Executable file
View file

@ -22,7 +22,7 @@
** SpiceMainConn ** SpiceMainConn
** This is the master Javascript class for establishing and ** This is the master Javascript class for establishing and
** managing a connection to a Spice Server. ** managing a connection to a Spice Server.
** **
** Invocation: You must pass an object with properties as follows: ** Invocation: You must pass an object with properties as follows:
** uri (required) Uri of a WebSocket listener that is ** uri (required) Uri of a WebSocket listener that is
** connected to a spice server. ** connected to a spice server.
@ -59,11 +59,24 @@ function SpiceMainConn()
this.file_xfer_tasks = {}; this.file_xfer_tasks = {};
this.file_xfer_task_id = 0; this.file_xfer_task_id = 0;
this.file_xfer_read_queue = []; this.file_xfer_read_queue = [];
this.ports = [];
} }
SpiceMainConn.prototype = Object.create(SpiceConn.prototype); SpiceMainConn.prototype = Object.create(SpiceConn.prototype);
SpiceMainConn.prototype.process_channel_message = function(msg) SpiceMainConn.prototype.process_channel_message = function(msg)
{ {
if (msg.type == SPICE_MSG_MAIN_MIGRATE_BEGIN)
{
this.known_unimplemented(msg.type, "Main Migrate Begin");
return true;
}
if (msg.type == SPICE_MSG_MAIN_MIGRATE_CANCEL)
{
this.known_unimplemented(msg.type, "Main Migrate Cancel");
return true;
}
if (msg.type == SPICE_MSG_MAIN_INIT) if (msg.type == SPICE_MSG_MAIN_INIT)
{ {
this.log_info("Connected to " + this.ws.url); this.log_info("Connected to " + this.ws.url);
@ -86,6 +99,9 @@ SpiceMainConn.prototype.process_channel_message = function(msg)
" ; ram_hint " + this.main_init.ram_hint); " ; 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.handle_mouse_mode(this.main_init.current_mouse_mode,
this.main_init.supported_mouse_modes); this.main_init.supported_mouse_modes);
@ -107,6 +123,12 @@ SpiceMainConn.prototype.process_channel_message = function(msg)
return true; return true;
} }
if (msg.type == SPICE_MSG_MAIN_MULTI_MEDIA_TIME)
{
this.known_unimplemented(msg.type, "Main Multi Media Time");
return true;
}
if (msg.type == SPICE_MSG_MAIN_CHANNELS_LIST) if (msg.type == SPICE_MSG_MAIN_CHANNELS_LIST)
{ {
var i; var i;
@ -123,7 +145,12 @@ SpiceMainConn.prototype.process_channel_message = function(msg)
chan_id : chans.channels[i].id chan_id : chans.channels[i].id
}; };
if (chans.channels[i].type == SPICE_CHANNEL_DISPLAY) if (chans.channels[i].type == SPICE_CHANNEL_DISPLAY)
this.display = new SpiceDisplayConn(conn); {
if (this.display !== undefined)
this.log_warn("The spice-html5 client does not handle multiple heads.");
else
this.display = new SpiceDisplayConn(conn);
}
else if (chans.channels[i].type == SPICE_CHANNEL_INPUTS) else if (chans.channels[i].type == SPICE_CHANNEL_INPUTS)
{ {
this.inputs = new SpiceInputsConn(conn); this.inputs = new SpiceInputsConn(conn);
@ -133,12 +160,14 @@ SpiceMainConn.prototype.process_channel_message = function(msg)
this.cursor = new SpiceCursorConn(conn); this.cursor = new SpiceCursorConn(conn);
else if (chans.channels[i].type == SPICE_CHANNEL_PLAYBACK) else if (chans.channels[i].type == SPICE_CHANNEL_PLAYBACK)
this.cursor = new SpicePlaybackConn(conn); this.cursor = new SpicePlaybackConn(conn);
else if (chans.channels[i].type == SPICE_CHANNEL_PORT)
this.ports.push(new SpicePortConn(conn));
else else
{ {
this.log_err("Channel type " + chans.channels[i].type + " unknown.");
if (! ("extra_channels" in this)) if (! ("extra_channels" in this))
this.extra_channels = []; this.extra_channels = [];
this.extra_channels[i] = new SpiceConn(conn); this.extra_channels[i] = new SpiceConn(conn);
this.log_err("Channel type " + this.extra_channels[i].channel_type() + " not implemented");
} }
} }
@ -201,6 +230,48 @@ SpiceMainConn.prototype.process_channel_message = function(msg)
return false; return false;
} }
if (msg.type == SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST)
{
this.known_unimplemented(msg.type, "Main Migrate Switch Host");
return true;
}
if (msg.type == SPICE_MSG_MAIN_MIGRATE_END)
{
this.known_unimplemented(msg.type, "Main Migrate End");
return true;
}
if (msg.type == SPICE_MSG_MAIN_NAME)
{
this.known_unimplemented(msg.type, "Main Name");
return true;
}
if (msg.type == SPICE_MSG_MAIN_UUID)
{
this.known_unimplemented(msg.type, "Main UUID");
return true;
}
if (msg.type == SPICE_MSG_MAIN_MIGRATE_BEGIN_SEAMLESS)
{
this.known_unimplemented(msg.type, "Main Migrate Begin Seamless");
return true;
}
if (msg.type == SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK)
{
this.known_unimplemented(msg.type, "Main Migrate Dst Seamless ACK");
return true;
}
if (msg.type == SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_NACK)
{
this.known_unimplemented(msg.type, "Main Migrate Dst Seamless NACK");
return true;
}
return false; return false;
} }
@ -415,3 +486,9 @@ SpiceMainConn.prototype.handle_mouse_mode = function(current, supported)
this.inputs.mouse_mode = current; this.inputs.mouse_mode = current;
} }
/* Shift current time to attempt to get a time matching that of the server */
SpiceMainConn.prototype.relative_now = function()
{
var ret = (Date.now() - this.our_mm_time) + this.mm_time;
return ret;
}

196
static/js/spice-html5/playback.js Normal file → Executable file
View file

@ -29,8 +29,6 @@ function SpicePlaybackConn()
this.queue = new Array(); this.queue = new Array();
this.append_okay = false; this.append_okay = false;
this.start_time = 0; this.start_time = 0;
this.skip_until = 0;
this.gap_time = 0;
} }
SpicePlaybackConn.prototype = Object.create(SpiceConn.prototype); SpicePlaybackConn.prototype = Object.create(SpiceConn.prototype);
@ -46,7 +44,7 @@ SpicePlaybackConn.prototype.process_channel_message = function(msg)
{ {
var start = new SpiceMsgPlaybackStart(msg.data); var start = new SpiceMsgPlaybackStart(msg.data);
DEBUG > 0 && console.log("PlaybackStart; frequency " + start.frequency); PLAYBACK_DEBUG > 0 && console.log("PlaybackStart; frequency " + start.frequency);
if (start.frequency != OPUS_FREQUENCY) if (start.frequency != OPUS_FREQUENCY)
{ {
@ -72,6 +70,7 @@ SpicePlaybackConn.prototype.process_channel_message = function(msg)
this.media_source.spiceconn = this; this.media_source.spiceconn = this;
this.audio = document.createElement("audio"); this.audio = document.createElement("audio");
this.audio.spiceconn = this;
this.audio.setAttribute('autoplay', true); this.audio.setAttribute('autoplay', true);
this.audio.src = window.URL.createObjectURL(this.media_source); this.audio.src = window.URL.createObjectURL(this.media_source);
document.getElementById(this.parent.screen_id).appendChild(this.audio); document.getElementById(this.parent.screen_id).appendChild(this.audio);
@ -90,56 +89,59 @@ SpicePlaybackConn.prototype.process_channel_message = function(msg)
{ {
var data = new SpiceMsgPlaybackData(msg.data); var data = new SpiceMsgPlaybackData(msg.data);
// If this packet has the same time as the last, just bump up by one. if (! this.source_buffer)
if (this.last_data_time && data.time <= this.last_data_time) return true;
if (this.audio.readyState >= 3 && this.audio.buffered.length > 1 &&
this.audio.currentTime == this.audio.buffered.end(0) &&
this.audio.currentTime < this.audio.buffered.start(this.audio.buffered.length - 1))
{ {
// FIXME - this is arguably wrong. But delaying the transmission was worse, console.log("Audio underrun: we appear to have fallen behind; advancing to " +
// in initial testing. Could use more research. this.audio.buffered.start(this.audio.buffered.length - 1));
DEBUG > 1 && console.log("Hacking time of " + data.time + " to " + this.last_data_time + 1); this.audio.currentTime = this.audio.buffered.start(this.audio.buffered.length - 1);
data.time = this.last_data_time + 1;
} }
/* Gap detection: If there has been a delay since our last packet, then audio must /* Around version 45, Firefox started being very particular about the
have paused. Handling that gets tricky. In Chrome, you can seek forward, time stamps put into the Opus stream. The time stamps from the Spice server are
but you cannot in Firefox. And seeking forward in Chrome is nice, as it keeps somewhat irregular. They mostly arrive every 10 ms, but sometimes it is 11, or sometimes
Chrome from being overly cautious in it's buffer strategy. with two time stamps the same in a row. The previous logic resulted in fuzzy and/or
distorted audio streams in Firefox in a row.
So we do two things. First, we seek forward. Second, we compute how much of a gap In theory, the sequence mode should be appropriate for us, but as of 09/27/2016,
there would have been, and essentially eliminate it. I was unable to make sequence mode work with Firefox.
Thus, we end up with an inelegant hack. Essentially, we force every packet to have
a 10ms time delta, unless there is an obvious gap in time stream, in which case we
will resync.
*/ */
if (this.last_data_time && data.time >= (this.last_data_time + GAP_DETECTION_THRESHOLD))
if (this.start_time != 0 && data.time != (this.last_data_time + EXPECTED_PACKET_DURATION))
{ {
this.skip_until = data.time; if (Math.abs(data.time - (EXPECTED_PACKET_DURATION + this.last_data_time)) < MAX_CLUSTER_TIME)
this.gap_time = (data.time - this.start_time) - {
(this.source_buffer.buffered.end(this.source_buffer.buffered.end.length - 1) * 1000.0).toFixed(0); PLAYBACK_DEBUG > 1 && console.log("Hacking time of " + data.time + " to " +
(this.last_data_time + EXPECTED_PACKET_DURATION));
data.time = this.last_data_time + EXPECTED_PACKET_DURATION;
}
else
{
PLAYBACK_DEBUG > 1 && console.log("Apparent gap in audio time; now is " + data.time + " last was " + this.last_data_time);
}
} }
this.last_data_time = data.time; this.last_data_time = data.time;
PLAYBACK_DEBUG > 1 && console.log("PlaybackData; time " + data.time + "; length " + data.data.byteLength);
DEBUG > 1 && console.log("PlaybackData; time " + data.time + "; length " + data.data.byteLength);
if (! this.source_buffer)
return true;
if (this.start_time == 0) if (this.start_time == 0)
this.start_playback(data); this.start_playback(data);
else if (data.time - this.cluster_time >= MAX_CLUSTER_TIME || this.skip_until > 0) else if (data.time - this.cluster_time >= MAX_CLUSTER_TIME)
this.new_cluster(data); this.new_cluster(data);
else else
this.simple_block(data, false); this.simple_block(data, false);
if (this.skip_until > 0)
{
this.audio.currentTime = (this.skip_until - this.start_time - this.gap_time) / 1000.0;
this.skip_until = 0;
}
if (this.audio.paused)
this.audio.play();
return true; return true;
} }
@ -156,6 +158,39 @@ SpicePlaybackConn.prototype.process_channel_message = function(msg)
if (msg.type == SPICE_MSG_PLAYBACK_STOP) if (msg.type == SPICE_MSG_PLAYBACK_STOP)
{ {
PLAYBACK_DEBUG > 0 && console.log("PlaybackStop");
if (this.source_buffer)
{
document.getElementById(this.parent.screen_id).removeChild(this.audio);
window.URL.revokeObjectURL(this.audio.src);
delete this.source_buffer;
delete this.media_source;
delete this.audio;
this.append_okay = false;
this.queue = new Array();
this.start_time = 0;
return true;
}
}
if (msg.type == SPICE_MSG_PLAYBACK_VOLUME)
{
this.known_unimplemented(msg.type, "Playback Volume");
return true;
}
if (msg.type == SPICE_MSG_PLAYBACK_MUTE)
{
this.known_unimplemented(msg.type, "Playback Mute");
return true;
}
if (msg.type == SPICE_MSG_PLAYBACK_LATENCY)
{
this.known_unimplemented(msg.type, "Playback Latency");
return true; return true;
} }
@ -167,10 +202,13 @@ SpicePlaybackConn.prototype.start_playback = function(data)
this.start_time = data.time; this.start_time = data.time;
var h = new webm_Header(); var h = new webm_Header();
var te = new webm_AudioTrackEntry;
var t = new webm_Tracks(te);
var mb = new ArrayBuffer(h.buffer_size()) var mb = new ArrayBuffer(h.buffer_size() + t.buffer_size())
this.bytes_written = h.to_buffer(mb); this.bytes_written = h.to_buffer(mb);
this.bytes_written = t.to_buffer(mb, this.bytes_written);
this.source_buffer.addEventListener('error', handle_sourcebuffer_error, false); this.source_buffer.addEventListener('error', handle_sourcebuffer_error, false);
this.source_buffer.addEventListener('updateend', handle_append_buffer_done, false); this.source_buffer.addEventListener('updateend', handle_append_buffer_done, false);
@ -183,7 +221,7 @@ SpicePlaybackConn.prototype.new_cluster = function(data)
{ {
this.cluster_time = data.time; this.cluster_time = data.time;
var c = new webm_Cluster(data.time - this.start_time - this.gap_time); var c = new webm_Cluster(data.time - this.start_time);
var mb = new ArrayBuffer(c.buffer_size()); var mb = new ArrayBuffer(c.buffer_size());
this.bytes_written += c.to_buffer(mb); this.bytes_written += c.to_buffer(mb);
@ -222,6 +260,12 @@ function handle_source_open(e)
p.log_err('Codec ' + SPICE_PLAYBACK_CODEC + ' not available.'); p.log_err('Codec ' + SPICE_PLAYBACK_CODEC + ' not available.');
return; return;
} }
if (PLAYBACK_DEBUG > 0)
playback_handle_event_debug.call(this, e);
listen_for_audio_events(p);
p.source_buffer.spiceconn = p; p.source_buffer.spiceconn = p;
p.source_buffer.mode = "segments"; p.source_buffer.mode = "segments";
@ -245,12 +289,38 @@ function handle_source_closed(e)
p.log_err('Audio source unexpectedly closed.'); p.log_err('Audio source unexpectedly closed.');
} }
function handle_append_buffer_done(b) function condense_playback_queue(queue)
{
if (queue.length == 1)
return queue.shift();
var len = 0;
var i = 0;
for (i = 0; i < queue.length; i++)
len += queue[i].byteLength;
var mb = new ArrayBuffer(len);
var tmp = new Uint8Array(mb);
len = 0;
for (i = 0; i < queue.length; i++)
{
tmp.set(new Uint8Array(queue[i]), len);
len += queue[i].byteLength;
}
queue.length = 0;
return mb;
}
function handle_append_buffer_done(e)
{ {
var p = this.spiceconn; var p = this.spiceconn;
if (PLAYBACK_DEBUG > 1)
playback_handle_event_debug.call(this, e);
if (p.queue.length > 0) if (p.queue.length > 0)
{ {
var mb = p.queue.shift(); var mb = condense_playback_queue(p.queue);
playback_append_buffer(p, mb); playback_append_buffer(p, mb);
} }
else else
@ -276,3 +346,53 @@ function playback_append_buffer(p, b)
p.log_err("Error invoking appendBuffer: " + e.message); p.log_err("Error invoking appendBuffer: " + e.message);
} }
} }
function playback_handle_event_debug(e)
{
var p = this.spiceconn;
if (p.audio)
{
if (PLAYBACK_DEBUG > 0 || p.audio.buffered.len > 1)
console.log(p.audio.currentTime + ": event " + e.type +
dump_media_element(p.audio));
}
if (PLAYBACK_DEBUG > 1 && p.media_source)
console.log(" media_source " + dump_media_source(p.media_source));
if (PLAYBACK_DEBUG > 1 && p.source_buffer)
console.log(" source_buffer " + dump_source_buffer(p.source_buffer));
if (PLAYBACK_DEBUG > 0 || p.queue.length > 1)
console.log(' queue len ' + p.queue.length + '; append_okay: ' + p.append_okay);
}
function playback_debug_listen_for_one_event(name)
{
this.addEventListener(name, playback_handle_event_debug);
}
function listen_for_audio_events(spiceconn)
{
var audio_0_events = [
"abort", "error"
];
var audio_1_events = [
"loadstart", "suspend", "emptied", "stalled", "loadedmetadata", "loadeddata", "canplay",
"canplaythrough", "playing", "waiting", "seeking", "seeked", "ended", "durationchange",
"timeupdate", "play", "pause", "ratechange"
];
var audio_2_events = [
"progress",
"resize",
"volumechange"
];
audio_0_events.forEach(playback_debug_listen_for_one_event, spiceconn.audio);
if (PLAYBACK_DEBUG > 0)
audio_1_events.forEach(playback_debug_listen_for_one_event, spiceconn.audio);
if (PLAYBACK_DEBUG > 1)
audio_2_events.forEach(playback_debug_listen_for_one_event, spiceconn.audio);
}

0
static/js/spice-html5/png.js Normal file → Executable file
View file

85
static/js/spice-html5/port.js Executable file
View file

@ -0,0 +1,85 @@
"use strict";
/*
Copyright (C) 2016 by Oliver Gutierrez <ogutsua@gmail.com>
Miroslav Chodil <mchodil@redhat.com>
This file is part of spice-html5.
spice-html5 is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
spice-html5 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with spice-html5. If not, see <http://www.gnu.org/licenses/>.
*/
/*----------------------------------------------------------------------------
** SpicePortConn
** Drive the Spice Port Channel
**--------------------------------------------------------------------------*/
function SpicePortConn()
{
DEBUG > 0 && console.log('SPICE port: created SPICE port channel. Args:', arguments);
SpiceConn.apply(this, arguments);
this.port_name = null;
}
SpicePortConn.prototype = Object.create(SpiceConn.prototype);
SpicePortConn.prototype.process_channel_message = function(msg)
{
if (msg.type == SPICE_MSG_PORT_INIT)
{
if (this.port_name === null)
{
var m = new SpiceMsgPortInit(msg.data);
this.portName = arraybuffer_to_str(new Uint8Array(m.name));
this.portOpened = m.opened
DEBUG > 0 && console.log('SPICE port: Port', this.portName, 'initialized');
return true;
}
DEBUG > 0 && console.log('SPICE port: Port', this.port_name, 'is already initialized.');
}
else if (msg.type == SPICE_MSG_PORT_EVENT)
{
DEBUG > 0 && console.log('SPICE port: Port event received for', this.portName, msg);
var event = new CustomEvent('spice-port-event', {
detail: {
channel: this,
spiceEvent: new Uint8Array(msg.data)
},
bubbles: true,
cancelable: true
});
window.dispatchEvent(event);
return true;
}
else if (msg.type == SPICE_MSG_SPICEVMC_DATA)
{
DEBUG > 0 && console.log('SPICE port: Data received in port', this.portName, msg);
var event = new CustomEvent('spice-port-data', {
detail: {
channel: this,
data: msg.data
},
bubbles: true,
cancelable: true
});
window.dispatchEvent(event);
return true;
}
else
{
DEBUG > 0 && console.log('SPICE port: SPICE message type not recognized:', msg)
}
return false;
};

View file

@ -1,79 +0,0 @@
// Downloaded from http://www-cs-students.stanford.edu/~tjw/jsbn/ by Jeremy White on 6/1/2012
/*
* Copyright (c) 2003-2005 Tom Wu
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
*
* IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
* THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* In addition, the following condition applies:
*
* All redistributions must retain an intact copy of this copyright notice
* and disclaimer.
*/
// prng4.js - uses Arcfour as a PRNG
function Arcfour() {
this.i = 0;
this.j = 0;
this.S = new Array();
}
// Initialize arcfour context from key, an array of ints, each from [0..255]
function ARC4init(key) {
var i, j, t;
for(i = 0; i < 256; ++i)
this.S[i] = i;
j = 0;
for(i = 0; i < 256; ++i) {
j = (j + this.S[i] + key[i % key.length]) & 255;
t = this.S[i];
this.S[i] = this.S[j];
this.S[j] = t;
}
this.i = 0;
this.j = 0;
}
function ARC4next() {
var t;
this.i = (this.i + 1) & 255;
this.j = (this.j + this.S[this.i]) & 255;
t = this.S[this.i];
this.S[this.i] = this.S[this.j];
this.S[this.j] = t;
return this.S[(t + this.S[this.i]) & 255];
}
Arcfour.prototype.init = ARC4init;
Arcfour.prototype.next = ARC4next;
// Plug in your RNG constructor here
function prng_newstate() {
return new Arcfour();
}
// Pool size must be a multiple of 4 and greater than 32.
// An array of bytes the size of the pool will be passed to init()
var rng_psize = 256;

1
static/js/spice-html5/quic.js Normal file → Executable file
View file

@ -280,6 +280,7 @@ function QuicModel(bpc)
case 2: case 2:
case 4: case 4:
console.log("quic: findmodelparams(): evol value obsolete!!!\n"); console.log("quic: findmodelparams(): evol value obsolete!!!\n");
break;
default: default:
console.log("quic: findmodelparams(): evol out of range!!!\n"); console.log("quic: findmodelparams(): evol out of range!!!\n");
} }

20
static/js/spice-html5/resize.js Normal file → Executable file
View file

@ -33,17 +33,29 @@
function resize_helper(sc) function resize_helper(sc)
{ {
var w = document.getElementById(sc.screen_id).clientWidth; var w = document.getElementById(sc.screen_id).clientWidth;
var h = document.getElementById(sc.screen_id).clientHeight;
var m = document.getElementById(sc.message_id); var m = document.getElementById(sc.message_id);
/* Resize vertically; basically we leave a 20 pixel margin /* Resize vertically; basically we leave a 20 pixel margin
at the bottom, and use the position of the message window at the bottom, and use the position of the message window
to figure out how to resize */ to figure out how to resize */
var hd = window.innerHeight - m.offsetHeight - m.offsetTop - 20;
var h = window.innerHeight - 20;
/* Screen height based on debug console visibility */
if (window.getComputedStyle(m).getPropertyValue("display") == 'none')
{
/* Get console height from spice.css .spice-message */
var mh = parseInt(window.getComputedStyle(m).getPropertyValue("height"), 10);
h = h - mh;
}
else
{
/* Show both div elements - spice-area and message-div */
h = h - m.offsetHeight - m.clientHeight;
}
/* Xorg requires height be a multiple of 8; round up */ /* Xorg requires height be a multiple of 8; round up */
h = h + hd;
if (h % 8 > 0) if (h % 8 > 0)
h += (8 - (h % 8)); h += (8 - (h % 8));

View file

@ -1,102 +0,0 @@
// Downloaded from http://www-cs-students.stanford.edu/~tjw/jsbn/ by Jeremy White on 6/1/2012
/*
* Copyright (c) 2003-2005 Tom Wu
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
*
* IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
* THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* In addition, the following condition applies:
*
* All redistributions must retain an intact copy of this copyright notice
* and disclaimer.
*/
// Random number generator - requires a PRNG backend, e.g. prng4.js
// For best results, put code like
// <body onClick='rng_seed_time();' onKeyPress='rng_seed_time();'>
// in your main HTML document.
var rng_state;
var rng_pool;
var rng_pptr;
// Mix in a 32-bit integer into the pool
function rng_seed_int(x) {
rng_pool[rng_pptr++] ^= x & 255;
rng_pool[rng_pptr++] ^= (x >> 8) & 255;
rng_pool[rng_pptr++] ^= (x >> 16) & 255;
rng_pool[rng_pptr++] ^= (x >> 24) & 255;
if(rng_pptr >= rng_psize) rng_pptr -= rng_psize;
}
// Mix in the current time (w/milliseconds) into the pool
function rng_seed_time() {
rng_seed_int(new Date().getTime());
}
// Initialize the pool with junk if needed.
if(rng_pool == null) {
rng_pool = new Array();
rng_pptr = 0;
var t;
if(navigator.appName == "Netscape" && navigator.appVersion < "5" && window.crypto) {
// Extract entropy (256 bits) from NS4 RNG if available
var z = window.crypto.random(32);
for(t = 0; t < z.length; ++t)
rng_pool[rng_pptr++] = z.charCodeAt(t) & 255;
}
while(rng_pptr < rng_psize) { // extract some randomness from Math.random()
t = Math.floor(65536 * Math.random());
rng_pool[rng_pptr++] = t >>> 8;
rng_pool[rng_pptr++] = t & 255;
}
rng_pptr = 0;
rng_seed_time();
//rng_seed_int(window.screenX);
//rng_seed_int(window.screenY);
}
function rng_get_byte() {
if(rng_state == null) {
rng_seed_time();
rng_state = prng_newstate();
rng_state.init(rng_pool);
for(rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr)
rng_pool[rng_pptr] = 0;
rng_pptr = 0;
//rng_pool = null;
}
// TODO: allow reseeding after first request
return rng_state.next();
}
function rng_get_bytes(ba) {
var i;
for(i = 0; i < ba.length; ++i) ba[i] = rng_get_byte();
}
function SecureRandom() {}
SecureRandom.prototype.nextBytes = rng_get_bytes;

View file

@ -1,146 +0,0 @@
// Downloaded from http://www-cs-students.stanford.edu/~tjw/jsbn/ by Jeremy White on 6/1/2012
/*
* Copyright (c) 2003-2005 Tom Wu
* All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
*
* IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
* THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* In addition, the following condition applies:
*
* All redistributions must retain an intact copy of this copyright notice
* and disclaimer.
*/
// Depends on jsbn.js and rng.js
// Version 1.1: support utf-8 encoding in pkcs1pad2
// convert a (hex) string to a bignum object
function parseBigInt(str,r) {
return new BigInteger(str,r);
}
function linebrk(s,n) {
var ret = "";
var i = 0;
while(i + n < s.length) {
ret += s.substring(i,i+n) + "\n";
i += n;
}
return ret + s.substring(i,s.length);
}
function byte2Hex(b) {
if(b < 0x10)
return "0" + b.toString(16);
else
return b.toString(16);
}
// PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint
function pkcs1pad2(s,n) {
if(n < s.length + 11) { // TODO: fix for utf-8
alert("Message too long for RSA");
return null;
}
var ba = new Array();
var i = s.length - 1;
while(i >= 0 && n > 0) {
var c = s.charCodeAt(i--);
if(c < 128) { // encode using utf-8
ba[--n] = c;
}
else if((c > 127) && (c < 2048)) {
ba[--n] = (c & 63) | 128;
ba[--n] = (c >> 6) | 192;
}
else {
ba[--n] = (c & 63) | 128;
ba[--n] = ((c >> 6) & 63) | 128;
ba[--n] = (c >> 12) | 224;
}
}
ba[--n] = 0;
var rng = new SecureRandom();
var x = new Array();
while(n > 2) { // random non-zero pad
x[0] = 0;
while(x[0] == 0) rng.nextBytes(x);
ba[--n] = x[0];
}
ba[--n] = 2;
ba[--n] = 0;
return new BigInteger(ba);
}
// "empty" RSA key constructor
function RSAKey() {
this.n = null;
this.e = 0;
this.d = null;
this.p = null;
this.q = null;
this.dmp1 = null;
this.dmq1 = null;
this.coeff = null;
}
// Set the public key fields N and e from hex strings
function RSASetPublic(N,E) {
if(N != null && E != null && N.length > 0 && E.length > 0) {
this.n = parseBigInt(N,16);
this.e = parseInt(E,16);
}
else
alert("Invalid RSA public key");
}
// Perform raw public operation on "x": return x^e (mod n)
function RSADoPublic(x) {
return x.modPowInt(this.e, this.n);
}
// Return the PKCS#1 RSA encryption of "text" as an even-length hex string
function RSAEncrypt(text) {
var m = pkcs1pad2(text,(this.n.bitLength()+7)>>3);
if(m == null) return null;
var c = this.doPublic(m);
if(c == null) return null;
var h = c.toString(16);
if((h.length & 1) == 0) return h; else return "0" + h;
}
// Return the PKCS#1 RSA encryption of "text" as a Base64-encoded string
//function RSAEncryptB64(text) {
// var h = this.encrypt(text);
// if(h) return hex2b64(h); else return null;
//}
// protected
RSAKey.prototype.doPublic = RSADoPublic;
// public
RSAKey.prototype.setPublic = RSASetPublic;
RSAKey.prototype.encrypt = RSAEncrypt;
//RSAKey.prototype.encrypt_b64 = RSAEncryptB64;

View file

@ -1,346 +0,0 @@
/*
* A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
* in FIPS 180-1
* Version 2.2 Copyright Paul Johnston 2000 - 2009.
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
* Distributed under the BSD License
* See http://pajhome.org.uk/crypt/md5 for details.
*/
/* Downloaded 6/1/2012 from the above address by Jeremy White.
License reproduce here for completeness:
Copyright (c) 1998 - 2009, Paul Johnston & Contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Configurable variables. You may need to tweak these to be compatible with
* the server-side, but the defaults work in most cases.
*/
var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
/*
* These are the functions you'll usually want to call
* They take string arguments and return either hex or base-64 encoded strings
*/
function hex_sha1(s) { return rstr2hex(rstr_sha1(str2rstr_utf8(s))); }
function b64_sha1(s) { return rstr2b64(rstr_sha1(str2rstr_utf8(s))); }
function any_sha1(s, e) { return rstr2any(rstr_sha1(str2rstr_utf8(s)), e); }
function hex_hmac_sha1(k, d)
{ return rstr2hex(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d))); }
function b64_hmac_sha1(k, d)
{ return rstr2b64(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d))); }
function any_hmac_sha1(k, d, e)
{ return rstr2any(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d)), e); }
/*
* Perform a simple self-test to see if the VM is working
*/
function sha1_vm_test()
{
return hex_sha1("abc").toLowerCase() == "a9993e364706816aba3e25717850c26c9cd0d89d";
}
/*
* Calculate the SHA1 of a raw string
*/
function rstr_sha1(s)
{
return binb2rstr(binb_sha1(rstr2binb(s), s.length * 8));
}
/*
* Calculate the HMAC-SHA1 of a key and some data (raw strings)
*/
function rstr_hmac_sha1(key, data)
{
var bkey = rstr2binb(key);
if(bkey.length > 16) bkey = binb_sha1(bkey, key.length * 8);
var ipad = Array(16), opad = Array(16);
for(var i = 0; i < 16; i++)
{
ipad[i] = bkey[i] ^ 0x36363636;
opad[i] = bkey[i] ^ 0x5C5C5C5C;
}
var hash = binb_sha1(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
return binb2rstr(binb_sha1(opad.concat(hash), 512 + 160));
}
/*
* Convert a raw string to a hex string
*/
function rstr2hex(input)
{
try { hexcase } catch(e) { hexcase=0; }
var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
var output = "";
var x;
for(var i = 0; i < input.length; i++)
{
x = input.charCodeAt(i);
output += hex_tab.charAt((x >>> 4) & 0x0F)
+ hex_tab.charAt( x & 0x0F);
}
return output;
}
/*
* Convert a raw string to a base-64 string
*/
function rstr2b64(input)
{
try { b64pad } catch(e) { b64pad=''; }
var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var output = "";
var len = input.length;
for(var i = 0; i < len; i += 3)
{
var triplet = (input.charCodeAt(i) << 16)
| (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
| (i + 2 < len ? input.charCodeAt(i+2) : 0);
for(var j = 0; j < 4; j++)
{
if(i * 8 + j * 6 > input.length * 8) output += b64pad;
else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
}
}
return output;
}
/*
* Convert a raw string to an arbitrary string encoding
*/
function rstr2any(input, encoding)
{
var divisor = encoding.length;
var remainders = Array();
var i, q, x, quotient;
/* Convert to an array of 16-bit big-endian values, forming the dividend */
var dividend = Array(Math.ceil(input.length / 2));
for(i = 0; i < dividend.length; i++)
{
dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
}
/*
* Repeatedly perform a long division. The binary array forms the dividend,
* the length of the encoding is the divisor. Once computed, the quotient
* forms the dividend for the next step. We stop when the dividend is zero.
* All remainders are stored for later use.
*/
while(dividend.length > 0)
{
quotient = Array();
x = 0;
for(i = 0; i < dividend.length; i++)
{
x = (x << 16) + dividend[i];
q = Math.floor(x / divisor);
x -= q * divisor;
if(quotient.length > 0 || q > 0)
quotient[quotient.length] = q;
}
remainders[remainders.length] = x;
dividend = quotient;
}
/* Convert the remainders to the output string */
var output = "";
for(i = remainders.length - 1; i >= 0; i--)
output += encoding.charAt(remainders[i]);
/* Append leading zero equivalents */
var full_length = Math.ceil(input.length * 8 /
(Math.log(encoding.length) / Math.log(2)))
for(i = output.length; i < full_length; i++)
output = encoding[0] + output;
return output;
}
/*
* Encode a string as utf-8.
* For efficiency, this assumes the input is valid utf-16.
*/
function str2rstr_utf8(input)
{
var output = "";
var i = -1;
var x, y;
while(++i < input.length)
{
/* Decode utf-16 surrogate pairs */
x = input.charCodeAt(i);
y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF)
{
x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
i++;
}
/* Encode output as utf-8 */
if(x <= 0x7F)
output += String.fromCharCode(x);
else if(x <= 0x7FF)
output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
0x80 | ( x & 0x3F));
else if(x <= 0xFFFF)
output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
0x80 | ((x >>> 6 ) & 0x3F),
0x80 | ( x & 0x3F));
else if(x <= 0x1FFFFF)
output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
0x80 | ((x >>> 12) & 0x3F),
0x80 | ((x >>> 6 ) & 0x3F),
0x80 | ( x & 0x3F));
}
return output;
}
/*
* Encode a string as utf-16
*/
function str2rstr_utf16le(input)
{
var output = "";
for(var i = 0; i < input.length; i++)
output += String.fromCharCode( input.charCodeAt(i) & 0xFF,
(input.charCodeAt(i) >>> 8) & 0xFF);
return output;
}
function str2rstr_utf16be(input)
{
var output = "";
for(var i = 0; i < input.length; i++)
output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
input.charCodeAt(i) & 0xFF);
return output;
}
/*
* Convert a raw string to an array of big-endian words
* Characters >255 have their high-byte silently ignored.
*/
function rstr2binb(input)
{
var output = Array(input.length >> 2);
for(var i = 0; i < output.length; i++)
output[i] = 0;
for(var i = 0; i < input.length * 8; i += 8)
output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
return output;
}
/*
* Convert an array of big-endian words to a string
*/
function binb2rstr(input)
{
var output = "";
for(var i = 0; i < input.length * 32; i += 8)
output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF);
return output;
}
/*
* Calculate the SHA-1 of an array of big-endian words, and a bit length
*/
function binb_sha1(x, len)
{
/* append padding */
x[len >> 5] |= 0x80 << (24 - len % 32);
x[((len + 64 >> 9) << 4) + 15] = len;
var w = Array(80);
var a = 1732584193;
var b = -271733879;
var c = -1732584194;
var d = 271733878;
var e = -1009589776;
for(var i = 0; i < x.length; i += 16)
{
var olda = a;
var oldb = b;
var oldc = c;
var oldd = d;
var olde = e;
for(var j = 0; j < 80; j++)
{
if(j < 16) w[j] = x[i + j];
else w[j] = bit_rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
var t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)),
safe_add(safe_add(e, w[j]), sha1_kt(j)));
e = d;
d = c;
c = bit_rol(b, 30);
b = a;
a = t;
}
a = safe_add(a, olda);
b = safe_add(b, oldb);
c = safe_add(c, oldc);
d = safe_add(d, oldd);
e = safe_add(e, olde);
}
return Array(a, b, c, d, e);
}
/*
* Perform the appropriate triplet combination function for the current
* iteration
*/
function sha1_ft(t, b, c, d)
{
if(t < 20) return (b & c) | ((~b) & d);
if(t < 40) return b ^ c ^ d;
if(t < 60) return (b & c) | (b & d) | (c & d);
return b ^ c ^ d;
}
/*
* Determine the appropriate additive constant for the current iteration
*/
function sha1_kt(t)
{
return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
(t < 60) ? -1894007588 : -899497514;
}
/*
* Add integers, wrapping at 2^32. This uses 16-bit operations internally
* to work around bugs in some JS interpreters.
*/
function safe_add(x, y)
{
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
return (msw << 16) | (lsw & 0xFFFF);
}
/*
* Bitwise rotate a 32-bit number to the left.
*/
function bit_rol(num, cnt)
{
return (num << cnt) | (num >>> (32 - cnt));
}

6
static/js/spice-html5/simulatecursor.js Normal file → Executable file
View file

@ -71,7 +71,7 @@ simulate_cursor: function (spicecursor, cursor, screen, pngstr)
if (window.getComputedStyle(screen, null).cursor == 'auto') if (window.getComputedStyle(screen, null).cursor == 'auto')
{ {
SpiceSimulateCursor.unknown_cursor(cursor_sha, SpiceSimulateCursor.unknown_cursor(cursor_sha,
SpiceSimulateCursor.create_icondir(cursor.header.width, cursor.header.height, SpiceSimulateCursor.create_icondir(cursor.header.width, cursor.header.height,
cursor.data.byteLength, cursor.header.hot_spot_x, cursor.header.hot_spot_y) + pngstr); cursor.data.byteLength, cursor.header.hot_spot_x, cursor.header.hot_spot_y) + pngstr);
@ -99,7 +99,7 @@ simulate_cursor: function (spicecursor, cursor, screen, pngstr)
spicecursor.spice_simulated_cursor.style.pointerEvents = "none"; spicecursor.spice_simulated_cursor.style.pointerEvents = "none";
} }
else else
{ {
if (spicecursor.spice_simulated_cursor) if (spicecursor.spice_simulated_cursor)
{ {
spicecursor.spice_simulated_cursor.spice_screen.removeChild(spicecursor.spice_simulated_cursor); spicecursor.spice_simulated_cursor.spice_screen.removeChild(spicecursor.spice_simulated_cursor);
@ -162,7 +162,7 @@ create_icondir: function (width, height, bytes, hot_x, hot_y)
}; };
SpiceSimulateCursor.ICONDIR.prototype = SpiceSimulateCursor.ICONDIR.prototype =
{ {
to_buffer: function(a, at) to_buffer: function(a, at)
{ {

117
static/js/spice-html5/spice.css Executable file
View file

@ -0,0 +1,117 @@
body
{
background-color: #999999;
color: #000000; margin: 0; padding: 0;
font-family: "Lucida Grande", "Lucida Sans Unicode", "Helvetica Neue", Helvetica, Arial, Verdana, sans-serif;
font-size: 12pt;
line-height: 1.5em;
}
* { margin: 0; }
#login
{
width: 95%;
margin-left: auto;
margin-right: auto;
border: 1px solid #999999;
background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#24414e));
background: -moz-linear-gradient(top, #fff, #24414e);
background-color: #24414e;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
}
#login span.logo
{
display: inline-block;
margin-right: 5px;
padding: 2px 10px 2px 20px;
border-right: 1px solid #999999;
font-size: 20px;
font-weight: bolder;
text-shadow: #efefef 1px 1px 0px;
}
#login label { color: #ffffff; text-shadow: 1px 1px 0px rgba(175, 210, 220, 0.8); }
#login input
{
padding: 5px;
background-color: #fAfAfA;
border: 1px inset #999999;
outline: none;
-moz-border-radius: 3px; -webkit-border-radius: 3px; border-radius: 3px;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
}
#login input#host { width: 200px; }
#login input#port { width: 75px; }
#login input#password { width: 100px; }
#login button
{
padding: 5px 10px 5px 10px;
margin-left: 5px;
text-shadow: #efefef 1px 1px 0px;
border: 1px outset #999999;
cursor: pointer;
-moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px;
}
#login button:hover
{
background-color: #666666;
color: #ffffff;
}
#spice-area
{
height: 100%;
width: 95%;
padding: 0;
margin-left: auto;
margin-right: auto;
border: solid #222222 1px;
-webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
-moz-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
}
.spice-screen
{
min-height: 600px;
height: 100%;
margin: 10px;
padding: 0;
background-color: #333333;
}
.spice-message {
width: 700px;
height: 50px;
overflow: auto;
margin-top: 5px;
margin-left: auto;
margin-right: auto;
padding: 10px;
background-color: #efefef;
border: solid #c3c3c3 2px;
font-size: 8pt;
line-height: 1.1em;
font-family: 'Andale Mono', monospace;
-moz-border-radius: 10px;
-webkit-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
-moz-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.2);
}
.spice-message p {
margin-bottom: 0em;
margin-top: 0em;
}
.spice-message-warning {
color: orange;
}
.spice-message-error {
color: red;
}

0
static/js/spice-html5/spicearraybuffer.js Normal file → Executable file
View file

55
static/js/spice-html5/spiceconn.js Normal file → Executable file
View file

@ -23,7 +23,7 @@
** This is the base Javascript class for establishing and ** This is the base Javascript class for establishing and
** managing a connection to a Spice Server. ** managing a connection to a Spice Server.
** It is used to provide core functionality to the Spice main, ** It is used to provide core functionality to the Spice main,
** display, inputs, and cursor channels. See main.js for ** display, inputs, and cursor channels. See main.js for
** usage. ** usage.
**--------------------------------------------------------------------------*/ **--------------------------------------------------------------------------*/
function SpiceConn(o) function SpiceConn(o)
@ -119,20 +119,36 @@ SpiceConn.prototype =
msg.connection_id = this.connection_id; msg.connection_id = this.connection_id;
msg.channel_type = this.type; msg.channel_type = this.type;
// FIXME - we're not setting a channel_id... msg.channel_id = this.chan_id;
msg.common_caps.push( msg.common_caps.push(
(1 << SPICE_COMMON_CAP_PROTOCOL_AUTH_SELECTION) | (1 << SPICE_COMMON_CAP_PROTOCOL_AUTH_SELECTION) |
(1 << SPICE_COMMON_CAP_MINI_HEADER) (1 << SPICE_COMMON_CAP_MINI_HEADER)
); );
if (msg.channel_type == SPICE_CHANNEL_PLAYBACK) if (msg.channel_type == SPICE_CHANNEL_PLAYBACK)
msg.channel_caps.push( {
(1 << SPICE_PLAYBACK_CAP_OPUS) var caps = 0;
); if ('MediaSource' in window && MediaSource.isTypeSupported(SPICE_PLAYBACK_CODEC))
caps |= (1 << SPICE_PLAYBACK_CAP_OPUS);
msg.channel_caps.push(caps);
}
else if (msg.channel_type == SPICE_CHANNEL_MAIN) else if (msg.channel_type == SPICE_CHANNEL_MAIN)
{
msg.channel_caps.push( msg.channel_caps.push(
(1 << SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS) (1 << SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS)
); );
}
else if (msg.channel_type == SPICE_CHANNEL_DISPLAY)
{
var caps = (1 << SPICE_DISPLAY_CAP_SIZED_STREAM) |
(1 << SPICE_DISPLAY_CAP_STREAM_REPORT) |
(1 << SPICE_DISPLAY_CAP_MULTI_CODEC) |
(1 << SPICE_DISPLAY_CAP_CODEC_MJPEG);
if ('MediaSource' in window && MediaSource.isTypeSupported(SPICE_VP8_CODEC))
caps |= (1 << SPICE_DISPLAY_CAP_CODEC_VP8);
msg.channel_caps.push(caps);
}
hdr.size = msg.buffer_size(); hdr.size = msg.buffer_size();
@ -180,8 +196,11 @@ SpiceConn.prototype =
if (msg.type > 500) if (msg.type > 500)
{ {
alert("Something has gone very wrong; we think we have message of type " + msg.type); if (DEBUG > 0)
debugger; {
alert("Something has gone very wrong; we think we have message of type " + msg.type);
debugger;
}
} }
if (msg.size == 0) if (msg.size == 0)
@ -329,6 +348,7 @@ SpiceConn.prototype =
process_message: function(msg) process_message: function(msg)
{ {
var rc; var rc;
var start = Date.now();
DEBUG > 0 && console.log("<< hdr " + this.channel_type() + " type " + msg.type + " size " + (msg.data && msg.data.byteLength)); DEBUG > 0 && console.log("<< hdr " + this.channel_type() + " type " + msg.type + " size " + (msg.data && msg.data.byteLength));
rc = this.process_common_messages(msg); rc = this.process_common_messages(msg);
if (! rc) if (! rc)
@ -337,10 +357,10 @@ SpiceConn.prototype =
{ {
rc = this.process_channel_message(msg); rc = this.process_channel_message(msg);
if (! rc) if (! rc)
this.log_warn(this.type + ": Unknown message type " + msg.type + "!"); this.log_warn(this.channel_type() + ": Unknown message type " + msg.type + "!");
} }
else else
this.log_err(this.type + ": No message handlers for this channel; message " + msg.type); this.log_err(this.channel_type() + ": No message handlers for this channel; message " + msg.type);
} }
if (this.msgs_until_ack !== undefined && this.ack_window) if (this.msgs_until_ack !== undefined && this.ack_window)
@ -356,6 +376,9 @@ SpiceConn.prototype =
} }
} }
var delta = Date.now() - start;
if (DEBUG > 0 || delta > GAP_DETECTION_THRESHOLD)
console.log("delta " + this.channel_type() + ":" + msg.type + " " + delta);
return rc; return rc;
}, },
@ -369,6 +392,20 @@ SpiceConn.prototype =
return "inputs"; return "inputs";
else if (this.type == SPICE_CHANNEL_CURSOR) else if (this.type == SPICE_CHANNEL_CURSOR)
return "cursor"; return "cursor";
else if (this.type == SPICE_CHANNEL_PLAYBACK)
return "playback";
else if (this.type == SPICE_CHANNEL_RECORD)
return "record";
else if (this.type == SPICE_CHANNEL_TUNNEL)
return "tunnel";
else if (this.type == SPICE_CHANNEL_SMARTCARD)
return "smartcard";
else if (this.type == SPICE_CHANNEL_USBREDIR)
return "usbredir";
else if (this.type == SPICE_CHANNEL_PORT)
return "port";
else if (this.type == SPICE_CHANNEL_WEBDAV)
return "webdav";
return "unknown-" + this.type; return "unknown-" + this.type;
}, },

8
static/js/spice-html5/spicedataview.js Normal file → Executable file
View file

@ -20,10 +20,10 @@
/*---------------------------------------------------------------------------- /*----------------------------------------------------------------------------
** SpiceDataView ** SpiceDataView
** FIXME FIXME ** FIXME FIXME
** This is used because Firefox does not have DataView yet. ** This is used because Firefox does not have DataView yet.
** We should use DataView if we have it, because it *has* to ** We should use DataView if we have it, because it *has* to
** be faster than this code ** be faster than this code
**--------------------------------------------------------------------------*/ **--------------------------------------------------------------------------*/
function SpiceDataView(buffer, byteOffset, byteLength) function SpiceDataView(buffer, byteOffset, byteLength)
{ {
@ -63,7 +63,7 @@ SpiceDataView.prototype = {
high = 2; high = 2;
} }
return (this.getUint16(byteOffset + high, littleEndian) << 16) | return (this.getUint16(byteOffset + high, littleEndian) << 16) |
this.getUint16(byteOffset + low, littleEndian); this.getUint16(byteOffset + low, littleEndian);
}, },
getUint64: function (byteOffset, littleEndian) getUint64: function (byteOffset, littleEndian)

101
static/js/spice-html5/spicemsg.js Normal file → Executable file
View file

@ -21,7 +21,7 @@
/*---------------------------------------------------------------------------- /*----------------------------------------------------------------------------
** Spice messages ** Spice messages
** This file contains classes for passing messages to and from ** This file contains classes for passing messages to and from
** a spice server. This file should arguably be generated from ** a spice server. This file should arguably be generated from
** spice.proto, but it was instead put together by hand. ** spice.proto, but it was instead put together by hand.
**--------------------------------------------------------------------------*/ **--------------------------------------------------------------------------*/
function SpiceLinkHeader(a, at) function SpiceLinkHeader(a, at)
@ -63,7 +63,7 @@ SpiceLinkHeader.prototype =
dv.setUint32(at, this.size, true); at += 4; dv.setUint32(at, this.size, true); at += 4;
}, },
buffer_size: function() buffer_size: function()
{ {
return 16; return 16;
}, },
} }
@ -938,7 +938,7 @@ function SpiceMsgcMousePosition(sc, e)
this.x = e.clientX - sc.display.surfaces[sc.display.primary_surface].canvas.offsetLeft + scrollLeft; this.x = e.clientX - sc.display.surfaces[sc.display.primary_surface].canvas.offsetLeft + scrollLeft;
this.y = e.clientY - sc.display.surfaces[sc.display.primary_surface].canvas.offsetTop + scrollTop; this.y = e.clientY - sc.display.surfaces[sc.display.primary_surface].canvas.offsetTop + scrollTop;
sc.mousex = this.x; sc.mousex = this.x;
sc.mousey = this.y; sc.mousey = this.y;
} }
else else
{ {
@ -1146,6 +1146,29 @@ SpiceMsgDisplayStreamData.prototype =
}, },
} }
function SpiceMsgDisplayStreamDataSized(a, at)
{
this.from_buffer(a, at);
}
SpiceMsgDisplayStreamDataSized.prototype =
{
from_buffer: function(a, at)
{
at = at || 0;
var dv = new SpiceDataView(a);
this.base = new SpiceStreamDataHeader;
at = this.base.from_dv(dv, at, a);
this.width = dv.getUint32(at, true); at += 4;
this.height = dv.getUint32(at, true); at += 4;
this.dest = new SpiceRect;
at = this.dest.from_dv(dv, at, a);
this.data_size = dv.getUint32(at, true); at += 4;
this.data = dv.u8.subarray(at, at + this.data_size);
},
}
function SpiceMsgDisplayStreamClip(a, at) function SpiceMsgDisplayStreamClip(a, at)
{ {
this.from_buffer(a, at); this.from_buffer(a, at);
@ -1178,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) function SpiceMsgDisplayInvalList(a, at)
{ {
this.count = 0; this.count = 0;
@ -1201,3 +1278,21 @@ SpiceMsgDisplayInvalList.prototype =
} }
}, },
} }
function SpiceMsgPortInit(a, at)
{
this.from_buffer(a,at);
};
SpiceMsgPortInit.prototype =
{
from_buffer: function (a, at)
{
at = at || 0;
var dv = new SpiceDataView(a);
var namesize = dv.getUint32(at, true); at += 4;
var offset = dv.getUint32(at, true); at += 4;
this.opened = dv.getUint8(at, true); at += 1;
this.name = a.slice(offset, offset + namesize - 1);
}
}

2
static/js/spice-html5/spicetype.js Normal file → Executable file
View file

@ -469,5 +469,5 @@ SpiceSurface.prototype =
}, },
} }
/* FIXME - SpiceImage types lz_plt, jpeg, zlib_glz, and jpeg_alpha are /* FIXME - SpiceImage types lz_plt, jpeg, zlib_glz, and jpeg_alpha are
completely unimplemented */ completely unimplemented */

6
static/js/spice-html5/thirdparty/jsbn.js vendored Normal file → Executable file
View file

@ -15,9 +15,9 @@
* The above copyright notice and this permission notice shall be * The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software. * included in all copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
* *
* IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER

6
static/js/spice-html5/thirdparty/prng4.js vendored Normal file → Executable file
View file

@ -15,9 +15,9 @@
* The above copyright notice and this permission notice shall be * The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software. * included in all copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
* *
* IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER

8
static/js/spice-html5/thirdparty/rng.js vendored Normal file → Executable file
View file

@ -15,9 +15,9 @@
* The above copyright notice and this permission notice shall be * The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software. * included in all copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
* *
* IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
@ -66,7 +66,7 @@ if(rng_pool == null) {
var z = window.crypto.random(32); var z = window.crypto.random(32);
for(t = 0; t < z.length; ++t) for(t = 0; t < z.length; ++t)
rng_pool[rng_pptr++] = z.charCodeAt(t) & 255; rng_pool[rng_pptr++] = z.charCodeAt(t) & 255;
} }
while(rng_pptr < rng_psize) { // extract some randomness from Math.random() while(rng_pptr < rng_psize) { // extract some randomness from Math.random()
t = Math.floor(65536 * Math.random()); t = Math.floor(65536 * Math.random());
rng_pool[rng_pptr++] = t >>> 8; rng_pool[rng_pptr++] = t >>> 8;

6
static/js/spice-html5/thirdparty/rsa.js vendored Normal file → Executable file
View file

@ -15,9 +15,9 @@
* The above copyright notice and this permission notice shall be * The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software. * included in all copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
* EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
* WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
* *
* IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER

0
static/js/spice-html5/thirdparty/sha1.js vendored Normal file → Executable file
View file

0
static/js/spice-html5/ticket.js Normal file → Executable file
View file

71
static/js/spice-html5/utils.js Normal file → Executable file
View file

@ -22,9 +22,16 @@
** Utility settings and functions for Spice ** Utility settings and functions for Spice
**--------------------------------------------------------------------------*/ **--------------------------------------------------------------------------*/
var DEBUG = 0; var DEBUG = 0;
var PLAYBACK_DEBUG = 0;
var STREAM_DEBUG = 0;
var DUMP_DRAWS = false; var DUMP_DRAWS = false;
var DUMP_CANVASES = false; var DUMP_CANVASES = false;
/*----------------------------------------------------------------------------
** We use an Image temporarily, and the image/src does not get garbage
** collected as quickly as we might like. This blank image helps with that.
**--------------------------------------------------------------------------*/
var EMPTY_GIF_IMAGE = "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=";
/*---------------------------------------------------------------------------- /*----------------------------------------------------------------------------
** combine_array_buffers ** combine_array_buffers
@ -97,10 +104,17 @@ function hexdump_buffer(a)
} }
} }
/*----------------------------------------------------------------------------
** Convert arraybuffer to string
**--------------------------------------------------------------------------*/
function arraybuffer_to_str(buf) {
return String.fromCharCode.apply(null, new Uint16Array(buf));
}
/*---------------------------------------------------------------------------- /*----------------------------------------------------------------------------
** Converting keycodes to AT scancodes is very hard. ** Converting keycodes to AT scancodes is very hard.
** luckly there are some resources on the web and in the Xorg driver that help ** luckly there are some resources on the web and in the Xorg driver that help
** us figure out what browser depenend keycodes match to what scancodes. ** us figure out what browser dependent keycodes match to what scancodes.
** **
** This will most likely not work for non US keyboard and browsers other than ** This will most likely not work for non US keyboard and browsers other than
** modern Chrome and FireFox. ** modern Chrome and FireFox.
@ -155,7 +169,7 @@ common_scanmap[121] = KEY_F10;
common_scanmap[122] = KEY_F11; common_scanmap[122] = KEY_F11;
common_scanmap[123] = KEY_F12; common_scanmap[123] = KEY_F12;
/* These externded scancodes do not line up with values from atKeynames */ /* These extended scancodes do not line up with values from atKeynames */
common_scanmap[42] = 99; common_scanmap[42] = 99;
common_scanmap[19] = 101; // Break common_scanmap[19] = 101; // Break
common_scanmap[111] = 0xE035; // KP_Divide common_scanmap[111] = 0xE035; // KP_Divide
@ -263,3 +277,56 @@ function keycode_to_end_scan(code)
return 0x80e0 | ((scancode - 0x100) << 8); return 0x80e0 | ((scancode - 0x100) << 8);
} }
} }
function dump_media_element(m)
{
var ret =
"[networkState " + m.networkState +
"|readyState " + m.readyState +
"|error " + m.error +
"|seeking " + m.seeking +
"|duration " + m.duration +
"|paused " + m.paused +
"|ended " + m.error +
"|buffered " + dump_timerange(m.buffered) +
"]";
return ret;
}
function dump_media_source(ms)
{
var ret =
"[duration " + ms.duration +
"|readyState " + ms.readyState + "]";
return ret;
}
function dump_source_buffer(sb)
{
var ret =
"[appendWindowStart " + sb.appendWindowStart +
"|appendWindowEnd " + sb.appendWindowEnd +
"|buffered " + dump_timerange(sb.buffered) +
"|timeStampOffset " + sb.timeStampOffset +
"|updating " + sb.updating +
"]";
return ret;
}
function dump_timerange(tr)
{
var ret;
if (tr)
{
var i = tr.length;
ret = "{len " + i;
if (i > 0)
ret += "; start " + tr.start(0) + "; end " + tr.end(i - 1);
ret += "}";
}
else
ret = "N/A";
return ret;
}

110
static/js/spice-html5/webm.js Normal file → Executable file
View file

@ -61,6 +61,10 @@ var WEBM_CODEC_DELAY = [ 0x56, 0xAA ];
var WEBM_CODEC_PRIVATE = [ 0x63, 0xA2 ]; var WEBM_CODEC_PRIVATE = [ 0x63, 0xA2 ];
var WEBM_CODEC_ID = [ 0x86 ]; var WEBM_CODEC_ID = [ 0x86 ];
var WEBM_VIDEO = [ 0xE0 ] ;
var WEBM_PIXEL_WIDTH = [ 0xB0 ] ;
var WEBM_PIXEL_HEIGHT = [ 0xBA ] ;
var WEBM_AUDIO = [ 0xE1 ] ; var WEBM_AUDIO = [ 0xE1 ] ;
var WEBM_SAMPLING_FREQUENCY = [ 0xB5 ] ; var WEBM_SAMPLING_FREQUENCY = [ 0xB5 ] ;
var WEBM_CHANNELS = [ 0x9F ] ; var WEBM_CHANNELS = [ 0x9F ] ;
@ -80,8 +84,11 @@ var OPUS_CHANNELS = 2;
var SPICE_PLAYBACK_CODEC = 'audio/webm; codecs="opus"'; var SPICE_PLAYBACK_CODEC = 'audio/webm; codecs="opus"';
var MAX_CLUSTER_TIME = 1000; var MAX_CLUSTER_TIME = 1000;
var EXPECTED_PACKET_DURATION = 10;
var GAP_DETECTION_THRESHOLD = 50; var GAP_DETECTION_THRESHOLD = 50;
var SPICE_VP8_CODEC = 'video/webm; codecs="vp8"';
/*---------------------------------------------------------------------------- /*----------------------------------------------------------------------------
** EBML utility functions ** EBML utility functions
** These classes can create the binary representation of a webm file ** These classes can create the binary representation of a webm file
@ -291,6 +298,34 @@ webm_Audio.prototype =
}, },
} }
function webm_Video(width, height)
{
this.id = WEBM_VIDEO;
this.width = width;
this.height = height;
}
webm_Video.prototype =
{
to_buffer: function(a, at)
{
at = at || 0;
var dv = new DataView(a);
at = EBML_write_array(this.id, dv, at);
at = EBML_write_u64_data_len(this.buffer_size() - 8 - this.id.length, dv, at);
at = EBML_write_u16_value(WEBM_PIXEL_WIDTH, this.width, dv, at)
at = EBML_write_u16_value(WEBM_PIXEL_HEIGHT, this.height, dv, at)
return at;
},
buffer_size: function()
{
return this.id.length + 8 +
WEBM_PIXEL_WIDTH.length + 1 + 2 +
WEBM_PIXEL_HEIGHT.length + 1 + 2;
},
}
/* --------------------------- /* ---------------------------
SeekHead not currently used. Hopefully not needed. SeekHead not currently used. Hopefully not needed.
@ -356,7 +391,7 @@ webm_SeekHead.prototype =
End of Seek Head End of Seek Head
*/ */
function webm_TrackEntry() function webm_AudioTrackEntry()
{ {
this.id = WEBM_TRACK_ENTRY; this.id = WEBM_TRACK_ENTRY;
this.number = 1; this.number = 1;
@ -385,7 +420,7 @@ function webm_TrackEntry()
]; ];
} }
webm_TrackEntry.prototype = webm_AudioTrackEntry.prototype =
{ {
to_buffer: function(a, at) to_buffer: function(a, at)
{ {
@ -431,6 +466,70 @@ webm_TrackEntry.prototype =
this.audio.buffer_size(); this.audio.buffer_size();
}, },
} }
function webm_VideoTrackEntry(width, height)
{
this.id = WEBM_TRACK_ENTRY;
this.number = 1;
this.uid = 1;
this.type = 1; // Video
this.flag_enabled = 1;
this.flag_default = 1;
this.flag_forced = 1;
this.flag_lacing = 0;
this.min_cache = 0; // fixme - check
this.max_block_addition_id = 0;
this.codec_decode_all = 0; // fixme - check
this.seek_pre_roll = 0; // 80000000; // fixme - check
this.codec_delay = 80000000; // Must match codec_private.preskip
this.codec_id = "V_VP8";
this.video = new webm_Video(width, height);
}
webm_VideoTrackEntry.prototype =
{
to_buffer: function(a, at)
{
at = at || 0;
var dv = new DataView(a);
at = EBML_write_array(this.id, dv, at);
at = EBML_write_u64_data_len(this.buffer_size() - 8 - this.id.length, dv, at);
at = EBML_write_u8_value(WEBM_TRACK_NUMBER, this.number, dv, at);
at = EBML_write_u8_value(WEBM_TRACK_UID, this.uid, dv, at);
at = EBML_write_u8_value(WEBM_FLAG_ENABLED, this.flag_enabled, dv, at);
at = EBML_write_u8_value(WEBM_FLAG_DEFAULT, this.flag_default, dv, at);
at = EBML_write_u8_value(WEBM_FLAG_FORCED, this.flag_forced, dv, at);
at = EBML_write_u8_value(WEBM_FLAG_LACING, this.flag_lacing, dv, at);
at = EBML_write_data(WEBM_CODEC_ID, this.codec_id, dv, at);
at = EBML_write_u8_value(WEBM_MIN_CACHE, this.min_cache, dv, at);
at = EBML_write_u8_value(WEBM_MAX_BLOCK_ADDITION_ID, this.max_block_addition_id, dv, at);
at = EBML_write_u8_value(WEBM_CODEC_DECODE_ALL, this.codec_decode_all, dv, at);
at = EBML_write_u32_value(WEBM_CODEC_DELAY, this.codec_delay, dv, at);
at = EBML_write_u32_value(WEBM_SEEK_PRE_ROLL, this.seek_pre_roll, dv, at);
at = EBML_write_u8_value(WEBM_TRACK_TYPE, this.type, dv, at);
at = this.video.to_buffer(a, at);
return at;
},
buffer_size: function()
{
return this.id.length + 8 +
WEBM_TRACK_NUMBER.length + 1 + 1 +
WEBM_TRACK_UID.length + 1 + 1 +
WEBM_FLAG_ENABLED.length + 1 + 1 +
WEBM_FLAG_DEFAULT.length + 1 + 1 +
WEBM_FLAG_FORCED.length + 1 + 1 +
WEBM_FLAG_LACING.length + 1 + 1 +
WEBM_CODEC_ID.length + this.codec_id.length + 1 +
WEBM_MIN_CACHE.length + 1 + 1 +
WEBM_MAX_BLOCK_ADDITION_ID.length + 1 + 1 +
WEBM_CODEC_DECODE_ALL.length + 1 + 1 +
WEBM_CODEC_DELAY.length + 1 + 4 +
WEBM_SEEK_PRE_ROLL.length + 1 + 4 +
WEBM_TRACK_TYPE.length + 1 + 1 +
this.video.buffer_size();
},
}
function webm_Tracks(entry) function webm_Tracks(entry)
{ {
this.id = WEBM_TRACKS; this.id = WEBM_TRACKS;
@ -526,9 +625,6 @@ function webm_Header()
this.info = new webm_SegmentInformation; this.info = new webm_SegmentInformation;
this.seek_head.track.pos = this.seek_head.info.pos + this.info.buffer_size(); this.seek_head.track.pos = this.seek_head.info.pos + this.info.buffer_size();
this.track_entry = new webm_TrackEntry;
this.tracks = new webm_Tracks(this.track_entry);
} }
webm_Header.prototype = webm_Header.prototype =
@ -539,7 +635,6 @@ webm_Header.prototype =
at = this.ebml.to_buffer(a, at); at = this.ebml.to_buffer(a, at);
at = this.segment.to_buffer(a, at); at = this.segment.to_buffer(a, at);
at = this.info.to_buffer(a, at); at = this.info.to_buffer(a, at);
at = this.tracks.to_buffer(a, at);
return at; return at;
}, },
@ -547,7 +642,6 @@ webm_Header.prototype =
{ {
return this.ebml.buffer_size() + return this.ebml.buffer_size() +
this.segment.buffer_size() + this.segment.buffer_size() +
this.info.buffer_size() + this.info.buffer_size();
this.tracks.buffer_size();
}, },
} }

2
static/js/spice-html5/wire.js Normal file → Executable file
View file

@ -96,7 +96,7 @@ SpiceWireReader.prototype =
this.callback.call(this.sc, mb, this.callback.call(this.sc, mb,
this.saved_msg_header || undefined); this.saved_msg_header || undefined);
} }
}, },
request: function(n) request: function(n)