spice server: heuristic for distinguishing between "real" videos and textual streams

This commit is contained in:
Yonit Halperin 2009-11-29 15:23:03 +02:00 committed by Yaniv Kamay
parent 6c5966d8ed
commit 00d5bf8b95
4 changed files with 161 additions and 26 deletions

View File

@ -88,6 +88,13 @@ typedef enum {
IMAGE_COMPRESS_OFF,
} image_compression_t;
enum {
STREAM_VIDEO_INVALID,
STREAM_VIDEO_OFF,
STREAM_VIDEO_ALL,
STREAM_VIDEO_FILTER
};
static inline uint64_t get_time_stamp()
{
struct timespec time_space;

View File

@ -345,7 +345,8 @@ void red_dispatcher_set_mm_time(uint32_t mm_time)
static inline int calc_compression_level()
{
if (streaming_video || (image_compression != IMAGE_COMPRESS_QUIC)) {
ASSERT(streaming_video != STREAM_VIDEO_INVALID);
if ((streaming_video != STREAM_VIDEO_OFF) || (image_compression != IMAGE_COMPRESS_QUIC)) {
return 0;
} else {
return 1;

View File

@ -74,7 +74,9 @@
#define RED_STREAM_DETACTION_MAX_DELTA ((1000 * 1000 * 1000) / 5) // 1/5 sec
#define RED_STREAM_CONTINUS_MAX_DELTA ((1000 * 1000 * 1000) / 2) // 1/2 sec
#define RED_STREAM_TIMOUT (1000 * 1000 * 1000)
#define RED_STREAM_START_CONDITION 20
#define RED_STREAM_FRAMES_START_CONDITION 20
#define RED_STREAM_GRADUAL_FRAMES_START_CONDITION 0.2
#define RED_STREAM_FRAMES_RESET_CONDITION 100
#define FPS_TEST_INTERVAL 1
#define MAX_FPS 30
@ -763,6 +765,13 @@ typedef struct DrawItem {
Shadow *shadow;
} DrawItem;
typedef enum {
BITMAP_GRADUAL_INVALID,
BITMAP_GRADUAL_NOT_AVAIL,
BITMAP_GRADUAL_TRUE,
BITMAP_GRADUAL_FALSE,
} BitmapGradualType;
struct Drawable {
uint8_t refs;
RingItem list_link;
@ -777,10 +786,13 @@ struct Drawable {
red_time_t creation_time;
int frames_count;
int gradual_frames_count;
int last_gradual_frame;
Stream *stream;
#ifdef STREAM_TRACE
int streamable;
#endif
BitmapGradualType copy_bitmap_graduality;
};
typedef struct _Drawable _Drawable;
@ -846,6 +858,8 @@ typedef struct DrawContext {
typedef struct ItemTrace {
red_time_t time;
int frames_count;
int gradual_frames_count;
int last_gradual_frame;
int width;
int height;
Rect dest_area;
@ -987,6 +1001,8 @@ static void red_display_release_stream_clip(DisplayChannel* channel, StreamClipI
static int red_display_free_some_independent_glz_drawables(DisplayChannel *channel);
static void red_display_free_glz_drawable(DisplayChannel *channel, RedGlzDrawable *drawable);
static void reset_rate(StreamAgent *stream_agent);
static int _bitmap_is_gradual(RedWorker *worker, Bitmap *bitmap);
static inline int _stride_is_extra(Bitmap *bitmap);
#ifdef DUMP_BITMAP
static void dump_bitmap(RedWorker *worker, Bitmap *bitmap);
@ -1514,6 +1530,8 @@ static inline void red_add_item_trace(RedWorker *worker, Drawable *item)
trace = &worker->items_trace[worker->next_item_trace++ & ITEMS_TRACE_MASK];
trace->time = item->creation_time;
trace->frames_count = item->frames_count;
trace->gradual_frames_count = item->gradual_frames_count;
trace->last_gradual_frame = item->last_gradual_frame;
Rect* src_area = &item->qxl_drawable->u.copy.src_area;
trace->width = src_area->right - src_area->left;
trace->height = src_area->bottom - src_area->top;
@ -2789,6 +2807,70 @@ static inline void pre_stream_item_swap(RedWorker *worker, Stream *stream)
agent->drops = 0;
}
static inline void red_update_copy_graduality(RedWorker* worker, Drawable *drawable)
{
QXLImage *qxl_image;
ASSERT(drawable->qxl_drawable->type == QXL_DRAW_COPY);
if (worker->streaming_video != STREAM_VIDEO_FILTER) {
drawable->copy_bitmap_graduality = BITMAP_GRADUAL_INVALID;
return;
}
if (drawable->copy_bitmap_graduality != BITMAP_GRADUAL_INVALID) {
return; // already set
}
qxl_image = (QXLImage *)get_virt(worker, drawable->qxl_drawable->u.copy.src_bitmap,
sizeof(QXLImage));
if (!BITMAP_FMT_IS_RGB[qxl_image->bitmap.format] || _stride_is_extra(&qxl_image->bitmap) ||
(qxl_image->bitmap.flags & QXL_BITMAP_UNSTABLE)) {
drawable->copy_bitmap_graduality = BITMAP_GRADUAL_NOT_AVAIL;
} else {
if (_bitmap_is_gradual(worker, &qxl_image->bitmap)) {
drawable->copy_bitmap_graduality = BITMAP_GRADUAL_TRUE;
} else {
drawable->copy_bitmap_graduality = BITMAP_GRADUAL_FALSE;
}
}
}
static inline int red_is_stream_start(Drawable *drawable)
{
return ((drawable->frames_count >= RED_STREAM_FRAMES_START_CONDITION) &&
(drawable->gradual_frames_count >=
(RED_STREAM_GRADUAL_FRAMES_START_CONDITION * drawable->frames_count)));
}
static void red_stream_add_frame(RedWorker* worker, Drawable *frame_drawable,
int frames_count,
int gradual_frames_count,
int last_gradual_frame)
{
red_update_copy_graduality(worker, frame_drawable);
frame_drawable->frames_count = frames_count + 1;
frame_drawable->gradual_frames_count = gradual_frames_count;
if (frame_drawable->copy_bitmap_graduality != BITMAP_GRADUAL_FALSE) {
if ((frame_drawable->frames_count - last_gradual_frame) >
RED_STREAM_FRAMES_RESET_CONDITION) {
frame_drawable->frames_count = 1;
frame_drawable->gradual_frames_count = 1;
} else {
frame_drawable->gradual_frames_count++;
}
frame_drawable->last_gradual_frame = frame_drawable->frames_count;
} else {
frame_drawable->last_gradual_frame = last_gradual_frame;
}
if (red_is_stream_start(frame_drawable)) {
red_create_stream(worker, frame_drawable);
}
}
static inline void red_stream_maintenance(RedWorker *worker, Drawable *candidate, Drawable *prev)
{
Stream *stream;
@ -2802,7 +2884,8 @@ static inline void red_stream_maintenance(RedWorker *worker, Drawable *candidate
return;
}
#else
if (!worker->streaming_video || !red_is_next_stream_frame(worker, candidate, prev) {
if ((worker->streaming_video == STREAM_VIDEO_OFF) ||
!red_is_next_stream_frame(worker, candidate, prev) {
return;
}
#endif
@ -2831,9 +2914,11 @@ static inline void red_stream_maintenance(RedWorker *worker, Drawable *candidate
}
}
#endif
} else if ((candidate->frames_count = prev->frames_count + 1) ==
RED_STREAM_START_CONDITION) {
red_create_stream(worker, candidate);
} else {
red_stream_add_frame(worker, candidate,
prev->frames_count,
prev->gradual_frames_count,
prev->last_gradual_frame);
}
}
@ -2937,10 +3022,10 @@ static inline void red_use_stream_trace(RedWorker *worker, Drawable *drawable)
for (; trace < trace_end; trace++) {
if (__red_is_next_stream_frame(worker, drawable, trace->width, trace->height,
&trace->dest_area, trace->time, NULL)) {
if ((drawable->frames_count = trace->frames_count + 1) == RED_STREAM_START_CONDITION) {
red_create_stream(worker, drawable);
}
return;
red_stream_add_frame(worker, drawable,
trace->frames_count,
trace->gradual_frames_count,
trace->last_gradual_frame);
}
}
}
@ -3289,7 +3374,7 @@ static inline void red_update_streamable(RedWorker *worker, Drawable *drawable,
{
QXLImage *qxl_image;
if (!worker->streaming_video) {
if (worker->streaming_video == STREAM_VIDEO_OFF) {
return;
}
@ -5610,8 +5695,12 @@ static inline int red_compress_image(DisplayChannel *display_channel,
if ((src->x < MIN_DIMENSION_TO_QUIC) || (src->y < MIN_DIMENSION_TO_QUIC)) {
quic_compress = FALSE;
} else {
quic_compress = BITMAP_FMT_IS_RGB[src->format] &&
_bitmap_is_gradual(display_channel->base.worker, src);
if (drawable->copy_bitmap_graduality == BITMAP_GRADUAL_INVALID) {
quic_compress = BITMAP_FMT_IS_RGB[src->format] &&
_bitmap_is_gradual(display_channel->base.worker, src);
} else {
quic_compress = (drawable->copy_bitmap_graduality == BITMAP_GRADUAL_TRUE);
}
}
} else {
quic_compress = FALSE;
@ -8484,7 +8573,20 @@ static void handle_dev_input(EventListener *listener, uint32_t events)
break;
case RED_WORKER_MESSAGE_SET_STREAMING_VIDEO:
receive_data(worker->channel, &worker->streaming_video, sizeof(uint32_t));
red_printf("sv %u", worker->streaming_video);
ASSERT(worker->streaming_video != STREAM_VIDEO_INVALID);
switch(worker->streaming_video) {
case STREAM_VIDEO_ALL:
red_printf("sv all");
break;
case STREAM_VIDEO_FILTER:
red_printf("sv filter");
break;
case STREAM_VIDEO_OFF:
red_printf("sv off");
break;
default:
red_printf("sv invalid");
}
break;
case RED_WORKER_MESSAGE_SET_MOUSE_MODE:
receive_data(worker->channel, &worker->mouse_mode, sizeof(uint32_t));

View File

@ -81,7 +81,7 @@ static struct in_addr spice_addr = {INADDR_ANY};
static int ticketing_enabled = 1; //Ticketing is enabled by default
static pthread_mutex_t *lock_cs;
static long *lock_count;
uint32_t streaming_video = TRUE;
uint32_t streaming_video = STREAM_VIDEO_FILTER;
image_compression_t image_compression = IMAGE_COMPRESS_AUTO_GLZ;
void *red_tunnel = NULL;
int agent_mouse = TRUE;
@ -3494,7 +3494,21 @@ static void reds_do_info_spice()
core->term_printf(core, " ic=invalid");
}
core->term_printf(core, " sv=%s", streaming_video ? "on" : "off");
switch (streaming_video) {
case STREAM_VIDEO_ALL:
core->term_printf(core, " sv=all");
break;
case STREAM_VIDEO_FILTER:
core->term_printf(core, " sv=filter");
break;
case STREAM_VIDEO_OFF:
core->term_printf(core, " sv=off");
break;
case STREAM_VIDEO_INVALID:
default:
core->term_printf(core, " sv=invalid");
}
core->term_printf(core, " playback-compression=%s\n",
snd_get_playback_compression() ? "on" : "off");
}
@ -3536,17 +3550,29 @@ static void reds_do_set_image_compression(const char *val)
set_image_compression(real_val);
}
static int reds_get_streaming_video(const char *val)
{
if (strcmp(val, "on") == 0) {
return STREAM_VIDEO_FILTER;
} else if (strcmp(val, "filter") == 0) {
return STREAM_VIDEO_FILTER;
} else if (strcmp(val, "all") == 0) {
return STREAM_VIDEO_ALL;
} else if (strcmp(val, "off") == 0){
return STREAM_VIDEO_OFF;
} else {
return STREAM_VIDEO_INVALID;
}
}
static void reds_do_set_streaming_video(const char *val)
{
uint32_t new_val;
if (strcmp(val, "on") == 0) {
new_val = TRUE;
} else if (strcmp(val, "off") == 0) {
new_val = FALSE;
} else {
uint32_t new_val = reds_get_streaming_video(val);
if (new_val == STREAM_VIDEO_INVALID) {
core->term_printf(core, "bad streaming video arg\n");
return;
}
if (new_val == streaming_video) {
return;
}
@ -3884,9 +3910,8 @@ int __attribute__ ((visibility ("default"))) spice_parse_args(const char *in_arg
if (!val) {
goto error;
}
if (strcmp(val, "off") == 0) {
streaming_video = FALSE;
} else if (strcmp(val, "on") != 0) {
streaming_video = reds_get_streaming_video(val);
if (streaming_video == STREAM_VIDEO_INVALID) {
goto error;
}
break;
@ -4583,7 +4608,7 @@ static void add_monitor_action_commands(QTermInterface *mon)
mon->add_action_command_handler(mon, "spice", "set_streaming_video", "s",
reds_do_set_streaming_video,
"",
"<on|off>");
"<on|filter|all|off>");
mon->add_action_command_handler(mon, "spice", "set_playback_compression", "s",
reds_do_set_playback_compression,
"",