Skip to content

Commit

Permalink
#1232: server-side support for scrolling
Browse files Browse the repository at this point in the history
git-svn-id: https://xpra.org/svn/Xpra/trunk@12912 3bb7dfac-3a0b-4e04-842a-767bc560f471
  • Loading branch information
totaam committed Jun 26, 2016
1 parent 26510ed commit bd292e6
Show file tree
Hide file tree
Showing 6 changed files with 317 additions and 28 deletions.
16 changes: 16 additions & 0 deletions src/xpra/codecs/image_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,22 @@ def clone_pixel_data(self):
#could be a race since this can run threaded
self.free()

def get_sub_image(self, x, y, w, h):
#raise NotImplementedError("no sub-images for %s" % type(self))
assert w>0 and h>0, "invalid sub-image size: %ix%i" % (w, h)
if x+w>self.width:
raise Exception("invalid sub-image width: %i+%i greater than image width %i" % (x, w, self.width))
if y+h>self.height:
raise Exception("invalid sub-image height: %i+%i greater than image height %i" % (y, h, self.height))
assert self.planes==0, "cannot sub-divide planar images!"
lines = []
pixels = self.pixels
stride = self.rowstride
for i in range(h):
pos = (y+i)*stride+x*4
lines.append(pixels[pos:pos+w*4])
return ImageWrapper(self.x+x, self.y+y, w, h, b"".join(lines), w*4)

def __del__(self):
#print("ImageWrapper.__del__() calling %s" % self.free)
self.free()
Expand Down
1 change: 1 addition & 0 deletions src/xpra/log.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ def enable_color(to=sys.stdout, formatter=NOPREFIX_FORMAT):
("encoding" , "Server side encoding selection and compression"),
("scaling" , "Picture scaling"),
("delta" , "Delta pre-compression"),
("scroll" , "Scrolling detection and compression"),
("xor" , "XOR delta pre-compression"),
("subregion" , "Video subregion processing"),
("regiondetect" , "Video region detection"),
Expand Down
116 changes: 116 additions & 0 deletions src/xpra/server/window/motion.pyx
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# coding=utf8
# This file is part of Xpra.
# Copyright (C) 2016 Antoine Martin <antoine@devloop.org.uk>
# Xpra is released under the terms of the GNU GPL v2, or, at your option, any
# later version. See the file COPYING for details.

#!python
#cython: boundscheck=False, wraparound=False, cdivision=True

import time
from zlib import crc32

cdef extern from "math.h":
double log(double x)

from libc.stdint cimport int32_t, uint8_t

cdef extern from "stdlib.h":
int abs(int number)

cdef extern from "string.h":
void free(void * ptr) nogil
void *memset(void * ptr, int value, size_t num) nogil
int memcmp(const void *a1, const void *a2, size_t size)

cdef extern from "../../buffers/memalign.h":
void *xmemalign(size_t size) nogil

cdef extern from "../../buffers/buffers.h":
int object_as_buffer(object obj, const void ** buffer, Py_ssize_t * buffer_len)


def CRC_Image(pixels, unsigned int width, unsigned int height, unsigned int rowstride, unsigned char bpp=4):
cdef uint8_t *buf = NULL
cdef Py_ssize_t buf_len = 0
assert object_as_buffer(pixels, <const void**> &buf, &buf_len)==0
crcs = []
cdef unsigned int i
for i in range(height):
crcs.append(crc32(buf[:width*bpp]))
buf += rowstride
return crcs


def calculate_distances(array1, array2, int min_score=0, int max_distance=1000):
#print("calculate_distances(..)")
assert len(array1)==len(array2)
cdef int l = len(array1)
cdef int i, y1, y2, miny, maxy, d
#we want fast array access,
#so cache both arrays in C arrays:
assert sizeof(int32_t)==32//8, "uint64_t is not 64-bit: %i!" % sizeof(int32_t)
cdef size_t asize = l*(sizeof(int32_t))
cdef int32_t *a1 = NULL
cdef int32_t *a2 = NULL
cdef int32_t *distances = NULL
#print("calculate_distances(%s, %s, %i, %i)" % (array1, array2, elen, min_score))
try:
a1 = <int32_t*> xmemalign(asize)
a2 = <int32_t*> xmemalign(asize)
distances = <int32_t*> xmemalign(2*l*sizeof(int32_t))
memset(<void*> distances, 0, 2*l*sizeof(int32_t))
assert a1!=NULL and a2!=NULL and distances!=NULL
for i in range(l):
a1[i] = array1[i]
a2[i] = array2[i]
#now compare all the values
for y1 in range(l):
miny = max(0, y1-max_distance)
maxy = min(l, y1+max_distance)
for y2 in range(miny, maxy):
if a1[y1]==a2[y2]:
d = y1-y2
distances[l+d] += 1
r = {}
for i in range(2*l):
d = distances[i]
if min_score<=0 or abs(d)>=min_score:
r[i-l] = d
return r
finally:
if a1!=NULL:
free(a1)
if a2!=NULL:
free(a2)
if distances!=NULL:
free(distances)

def match_distance(array1, array2, int distance):
assert len(array1)==len(array2)
l = len(array1)
if distance>=0:
return [i for i,v in enumerate(array1) if (i+distance)<l and array2[i+distance]==v]
distance = abs(distance)
return [i+distance for i,v in enumerate(array2) if (i+distance)<l and array1[i+distance]==v]

def consecutive_lines(line_numbers):
#print("line_numbers_to_rectangles(%s)" % (line_numbers, ))
#aggregates consecutive lines:
#[1,2,3,4,8,9] -> [(1,3), (8,1)]
assert len(line_numbers)>0
if len(line_numbers)==1:
return [(line_numbers[0], 1)]
cdef int start = line_numbers[0]
cdef int last = start
cdef int line = 0
r = []
for line in line_numbers[1:]:
if line!=last+1:
#new rectangle
r.append((start, last-start+1))
start = line
last = line
if last!=line_numbers[0]:
r.append((start, last-start+1))
return r
39 changes: 22 additions & 17 deletions src/xpra/server/window/window_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -1391,28 +1391,33 @@ def process_damage_region(self, damage_time, x, y, w, h, coding, options, flush=
self.pixel_format = image.get_pixel_format()

now = time.time()
log("process_damage_regions: wid=%i, adding pixel data to encode queue (%ix%i - %s), elapsed time: %.1f ms, request time: %.1f ms",
self.wid, w, h, coding, 1000*(now-damage_time), 1000*(now-rgb_request_time))
item = (w, h, damage_time, now, image, coding, sequence, options, flush)
av_sync = options.get("av-sync", False)
if not av_sync:
self.call_in_encode_thread(True, self.make_data_packet_cb, *item)
else:
#schedule encode via queue, after freezing the pixels:
if not image.freeze():
if self.must_freeze(coding, options):
newstride = image.get_width()*4
if not image.restride(newstride):
avsynclog("Warning: failed to freeze image pixels for:")
avsynclog(" %s", image)
self.call_in_encode_thread(True, self.make_data_packet_cb, *item)
return
av_delay = self.get_frame_encode_delay(options)
log("process_damage_regions: wid=%i, adding pixel data to encode queue (%ix%i - %s), elapsed time: %.1f ms, request time: %.1f ms, frame delay=%ims",
self.wid, w, h, coding, 1000*(now-damage_time), 1000*(now-rgb_request_time), av_delay)
if av_delay<0:
self.call_in_encode_thread(True, self.make_data_packet_cb, *item)
else:
self.encode_queue.append(item)
l = len(self.encode_queue)
if l>=self.encode_queue_max_size:
av_delay = 0 #we must free some space!
else:
av_delay = self.av_sync_delay*int(av_sync)
avsynclog("scheduling encode queue iteration in %ims, encode queue size=%i (max=%i)", av_delay, l, self.encode_queue_max_size)
self.timeout_add(av_delay, self.call_in_encode_thread, True, self.encode_from_queue)

def must_freeze(self, coding, options):
return options.get("av-sync", False)

def get_frame_encode_delay(self, options):
if options.get("av-sync", False):
return -1
l = len(self.encode_queue)
if l>=self.encode_queue_max_size:
#we must free some space!
return 0
return self.av_sync_delay

def encode_from_queue(self):
#note: we use a queue here to ensure we preserve the order
#(so we encode frames in the same order they were grabbed)
Expand Down Expand Up @@ -1882,7 +1887,7 @@ def make_data_packet(self, damage_time, process_damage_time, image, coding, sequ
if self.supports_flush and flush is not None:
client_options["flush"] = flush
end = time.time()
compresslog.info("compress: %5.1fms for %4ix%-4i pixels for wid=%-5i using %5s with ratio %5.1f%% (%5iKB to %5iKB), client_options=%s",
compresslog("compress: %5.1fms for %4ix%-4i pixels for wid=%-5i using %5s with ratio %5.1f%% (%5iKB to %5iKB), client_options=%s",
(end-start)*1000.0, outw, outh, self.wid, coding, 100.0*csize/psize, psize/1024, csize/1024, client_options)
self.statistics.encoding_stats.append((end, coding, w*h, bpp, len(data), end-start))
return self.make_draw_packet(x, y, outw, outh, coding, data, outstride, client_options)
Expand Down
Loading

0 comments on commit bd292e6

Please sign in to comment.