virtbuilder_pel7x64builder0
6 years ago
21 changed files with 2649 additions and 86 deletions
@ -0,0 +1,32 @@
@@ -0,0 +1,32 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
||||
From: Frediano Ziglio <fziglio@redhat.com> |
||||
Date: Fri, 6 Oct 2017 09:38:31 +0100 |
||||
Subject: [spice-server] inputs-channel: Check message size handling migration |
||||
data |
||||
|
||||
Prevent possible buffer reading overflow. |
||||
Note that message pointer must be valid and data are checked |
||||
value by value so even on overflow you just get an error. |
||||
|
||||
Signed-off-by: Frediano Ziglio <fziglio@redhat.com> |
||||
Acked-by: Christophe Fergeau <cfergeau@redhat.com> |
||||
--- |
||||
server/inputs-channel.c | 5 +++++ |
||||
1 file changed, 5 insertions(+) |
||||
|
||||
diff --git a/server/inputs-channel.c b/server/inputs-channel.c |
||||
index 8e17cc724..11a338a26 100644 |
||||
--- a/server/inputs-channel.c |
||||
+++ b/server/inputs-channel.c |
||||
@@ -505,6 +505,11 @@ static bool inputs_channel_handle_migrate_data(RedChannelClient *rcc, |
||||
SpiceMigrateDataHeader *header; |
||||
SpiceMigrateDataInputs *mig_data; |
||||
|
||||
+ if (size < sizeof(SpiceMigrateDataHeader) + sizeof(SpiceMigrateDataInputs)) { |
||||
+ spice_warning("bad message size %u", size); |
||||
+ return FALSE; |
||||
+ } |
||||
+ |
||||
header = (SpiceMigrateDataHeader *)message; |
||||
mig_data = (SpiceMigrateDataInputs *)(header + 1); |
||||
|
@ -0,0 +1,66 @@
@@ -0,0 +1,66 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
||||
From: Frediano Ziglio <fziglio@redhat.com> |
||||
Date: Fri, 6 Oct 2017 08:57:42 +0100 |
||||
Subject: [spice-server] red-channel: Remove |
||||
red_channel_init_outgoing_messages_window |
||||
|
||||
This function does not make much sense anymore. |
||||
Is called by RedVmcChannel which doesn't use RedChannelClient ACKs |
||||
so the variable changed are not used. |
||||
Moreover, at red_vmc_channel_constructed() time, there will be no |
||||
clients yet, so red_channel_init_outgoing_messages() will be a no-op. |
||||
|
||||
Signed-off-by: Frediano Ziglio <fziglio@redhat.com> |
||||
Acked-by: Christophe Fergeau <cfergeau@redhat.com> |
||||
--- |
||||
server/red-channel.c | 8 -------- |
||||
server/red-channel.h | 3 --- |
||||
server/spicevmc.c | 2 -- |
||||
3 files changed, 13 deletions(-) |
||||
|
||||
diff --git a/server/red-channel.c b/server/red-channel.c |
||||
index b5094829e..8c507f202 100644 |
||||
--- a/server/red-channel.c |
||||
+++ b/server/red-channel.c |
||||
@@ -421,14 +421,6 @@ void red_channel_push(RedChannel *channel) |
||||
g_list_foreach(channel->priv->clients, (GFunc)red_channel_client_push, NULL); |
||||
} |
||||
|
||||
-// TODO: this function doesn't make sense because the window should be client (WAN/LAN) |
||||
-// specific |
||||
-void red_channel_init_outgoing_messages_window(RedChannel *channel) |
||||
-{ |
||||
- g_list_foreach(channel->priv->clients, |
||||
- (GFunc)red_channel_client_init_outgoing_messages_window, NULL); |
||||
-} |
||||
- |
||||
void red_channel_pipes_add(RedChannel *channel, RedPipeItem *item) |
||||
{ |
||||
RedChannelClient *rcc; |
||||
diff --git a/server/red-channel.h b/server/red-channel.h |
||||
index e0fe94fec..281ed0c9e 100644 |
||||
--- a/server/red-channel.h |
||||
+++ b/server/red-channel.h |
||||
@@ -157,9 +157,6 @@ void red_channel_destroy(RedChannel *channel); |
||||
/* return true if all the channel clients support the cap */ |
||||
bool red_channel_test_remote_cap(RedChannel *channel, uint32_t cap); |
||||
|
||||
-/* should be called when a new channel is ready to send messages */ |
||||
-void red_channel_init_outgoing_messages_window(RedChannel *channel); |
||||
- |
||||
// helper to push a new item to all channels |
||||
typedef RedPipeItem *(*new_pipe_item_t)(RedChannelClient *rcc, void *data, int num); |
||||
int red_channel_pipes_new_add(RedChannel *channel, new_pipe_item_t creator, void *data); |
||||
diff --git a/server/spicevmc.c b/server/spicevmc.c |
||||
index 6b9b96fc8..a1685483d 100644 |
||||
--- a/server/spicevmc.c |
||||
+++ b/server/spicevmc.c |
||||
@@ -246,8 +246,6 @@ red_vmc_channel_constructed(GObject *object) |
||||
red_channel_set_cap(RED_CHANNEL(self), SPICE_SPICEVMC_CAP_DATA_COMPRESS_LZ4); |
||||
#endif |
||||
|
||||
- red_channel_init_outgoing_messages_window(RED_CHANNEL(self)); |
||||
- |
||||
reds_register_channel(reds, RED_CHANNEL(self)); |
||||
} |
||||
|
@ -0,0 +1,40 @@
@@ -0,0 +1,40 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
||||
From: Frediano Ziglio <fziglio@redhat.com> |
||||
Date: Tue, 19 Sep 2017 08:28:02 +0100 |
||||
Subject: [spice-server] reds: Remove leak allocating migration state |
||||
|
||||
Signed-off-by: Frediano Ziglio <fziglio@redhat.com> |
||||
Acked-by: Christophe Fergeau <cfergeau@redhat.com> |
||||
--- |
||||
server/reds.c | 2 +- |
||||
server/reds.h | 4 ---- |
||||
2 files changed, 1 insertion(+), 5 deletions(-) |
||||
|
||||
diff --git a/server/reds.c b/server/reds.c |
||||
index 12c33570a..1b1ab94ea 100644 |
||||
--- a/server/reds.c |
||||
+++ b/server/reds.c |
||||
@@ -3378,7 +3378,7 @@ SPICE_GNUC_VISIBLE int spice_server_add_interface(SpiceServer *reds, |
||||
return -1; |
||||
} |
||||
reds->migration_interface = SPICE_CONTAINEROF(sin, SpiceMigrateInstance, base); |
||||
- reds->migration_interface->st = spice_new0(SpiceMigrateState, 1); |
||||
+ reds->migration_interface->st = (SpiceMigrateState *)(intptr_t)1; // dummy pointer |
||||
} |
||||
|
||||
return 0; |
||||
diff --git a/server/reds.h b/server/reds.h |
||||
index 4f5fc28c3..cea002c51 100644 |
||||
--- a/server/reds.h |
||||
+++ b/server/reds.h |
||||
@@ -35,10 +35,6 @@ static inline QXLInterface * qxl_get_interface(QXLInstance *qxl) |
||||
return SPICE_CONTAINEROF(qxl->base.sif, QXLInterface, base); |
||||
} |
||||
|
||||
-struct SpiceMigrateState { |
||||
- int dummy; |
||||
-}; |
||||
- |
||||
/* main thread only */ |
||||
void reds_handle_channel_event(RedsState *reds, int event, SpiceChannelEventInfo *info); |
||||
|
@ -0,0 +1,64 @@
@@ -0,0 +1,64 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
||||
From: Frediano Ziglio <fziglio@redhat.com> |
||||
Date: Tue, 19 Sep 2017 08:27:38 +0100 |
||||
Subject: [spice-server] tests: Check leaks registering migration interface |
||||
|
||||
Signed-off-by: Frediano Ziglio <fziglio@redhat.com> |
||||
Acked-by: Christophe Fergeau <cfergeau@redhat.com> |
||||
--- |
||||
server/tests/test-leaks.c | 36 ++++++++++++++++++++++++++++++++++++ |
||||
1 file changed, 36 insertions(+) |
||||
|
||||
diff --git a/server/tests/test-leaks.c b/server/tests/test-leaks.c |
||||
index 04dcac4f4..7032000aa 100644 |
||||
--- a/server/tests/test-leaks.c |
||||
+++ b/server/tests/test-leaks.c |
||||
@@ -143,12 +143,48 @@ static void vmc_leaks(void) |
||||
basic_event_loop_destroy(); |
||||
} |
||||
|
||||
+static void migrate_cb(SpiceMigrateInstance *sin) |
||||
+{ |
||||
+} |
||||
+ |
||||
+static const SpiceMigrateInterface migrate_interface = { |
||||
+ .base = { |
||||
+ .type = SPICE_INTERFACE_MIGRATION, |
||||
+ .description = "migration", |
||||
+ .major_version = SPICE_INTERFACE_MIGRATION_MAJOR, |
||||
+ .minor_version = SPICE_INTERFACE_MIGRATION_MINOR, |
||||
+ }, |
||||
+ .migrate_connect_complete = migrate_cb, |
||||
+ .migrate_end_complete = migrate_cb, |
||||
+}; |
||||
+ |
||||
+static void migration_leaks(void) |
||||
+{ |
||||
+ SpiceCoreInterface *core; |
||||
+ SpiceServer *server = spice_server_new(); |
||||
+ SpiceMigrateInstance migrate; |
||||
+ |
||||
+ g_assert_nonnull(server); |
||||
+ |
||||
+ core = basic_event_loop_init(); |
||||
+ g_assert_nonnull(core); |
||||
+ |
||||
+ g_assert_cmpint(spice_server_init(server, core), ==, 0); |
||||
+ |
||||
+ migrate.base.sif = &migrate_interface.base; |
||||
+ spice_server_add_interface(server, &migrate.base); |
||||
+ |
||||
+ spice_server_destroy(server); |
||||
+ basic_event_loop_destroy(); |
||||
+} |
||||
+ |
||||
int main(int argc, char *argv[]) |
||||
{ |
||||
g_test_init(&argc, &argv, NULL); |
||||
|
||||
g_test_add_func("/server/server leaks", server_leaks); |
||||
g_test_add_func("/server/vmc leaks", vmc_leaks); |
||||
+ g_test_add_func("/server/migration leaks", migration_leaks); |
||||
|
||||
return g_test_run(); |
||||
} |
@ -0,0 +1,179 @@
@@ -0,0 +1,179 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
||||
From: Frediano Ziglio <fziglio@redhat.com> |
||||
Date: Thu, 24 Aug 2017 23:37:25 +0100 |
||||
Subject: [spice-server] Notify client of the creation of new channels |
||||
dynamically |
||||
|
||||
This allows the server to add channels after the client is connected. |
||||
|
||||
Signed-off-by: Frediano Ziglio <fziglio@redhat.com> |
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com> |
||||
--- |
||||
server/main-channel-client.c | 51 ++++++++++++++++++++++++++++++++++++++++++++ |
||||
server/main-channel-client.h | 3 +++ |
||||
server/main-channel.c | 6 ++++++ |
||||
server/main-channel.h | 3 +++ |
||||
server/reds.c | 2 ++ |
||||
5 files changed, 65 insertions(+) |
||||
|
||||
diff --git a/server/main-channel-client.c b/server/main-channel-client.c |
||||
index b7b60eddb..61a2830fa 100644 |
||||
--- a/server/main-channel-client.c |
||||
+++ b/server/main-channel-client.c |
||||
@@ -62,6 +62,7 @@ struct MainChannelClientPrivate { |
||||
int mig_wait_prev_try_seamless; |
||||
int init_sent; |
||||
int seamless_mig_dst; |
||||
+ bool initial_channels_list_sent; |
||||
uint8_t recv_buf[MAIN_CHANNEL_RECEIVE_BUF_SIZE]; |
||||
}; |
||||
|
||||
@@ -119,6 +120,12 @@ typedef struct RedMultiMediaTimePipeItem { |
||||
uint32_t time; |
||||
} RedMultiMediaTimePipeItem; |
||||
|
||||
+typedef struct RedRegisteredChannelPipeItem { |
||||
+ RedPipeItem base; |
||||
+ uint32_t channel_type; |
||||
+ uint32_t channel_id; |
||||
+} RedRegisteredChannelPipeItem; |
||||
+ |
||||
#define ZERO_BUF_SIZE 4096 |
||||
|
||||
static const uint8_t zero_page[ZERO_BUF_SIZE] = {0}; |
||||
@@ -446,6 +453,20 @@ RedPipeItem *main_multi_media_time_item_new(uint32_t mm_time) |
||||
return &item->base; |
||||
} |
||||
|
||||
+RedPipeItem *registered_channel_item_new(RedChannel *channel) |
||||
+{ |
||||
+ RedRegisteredChannelPipeItem *item; |
||||
+ |
||||
+ item = g_new0(RedRegisteredChannelPipeItem, 1); |
||||
+ red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_MAIN_REGISTERED_CHANNEL); |
||||
+ |
||||
+ uint32_t type, id; |
||||
+ g_object_get(channel, "channel-type", &type, "id", &id, NULL); |
||||
+ item->channel_type = type; |
||||
+ item->channel_id = id; |
||||
+ return &item->base; |
||||
+} |
||||
+ |
||||
void main_channel_client_handle_migrate_connected(MainChannelClient *mcc, |
||||
int success, |
||||
int seamless) |
||||
@@ -927,6 +948,25 @@ static void main_channel_marshall_agent_connected(SpiceMarshaller *m, |
||||
spice_marshall_msg_main_agent_connected_tokens(m, &connected); |
||||
} |
||||
|
||||
+static void main_channel_marshall_registered_channel(RedChannelClient *rcc, |
||||
+ SpiceMarshaller *m, |
||||
+ RedRegisteredChannelPipeItem *item) |
||||
+{ |
||||
+ struct { |
||||
+ SpiceMsgChannels info; |
||||
+ SpiceChannelId ids[1]; |
||||
+ } channels_info_buffer; |
||||
+ SpiceMsgChannels* channels_info = &channels_info_buffer.info; |
||||
+ |
||||
+ red_channel_client_init_send_data(rcc, SPICE_MSG_MAIN_CHANNELS_LIST); |
||||
+ |
||||
+ channels_info->channels[0].type = item->channel_type; |
||||
+ channels_info->channels[0].id = item->channel_id; |
||||
+ channels_info->num_of_channels = 1; |
||||
+ |
||||
+ spice_marshall_msg_main_channels_list(m, channels_info); |
||||
+} |
||||
+ |
||||
void main_channel_client_send_item(RedChannelClient *rcc, RedPipeItem *base) |
||||
{ |
||||
MainChannelClient *mcc = MAIN_CHANNEL_CLIENT(rcc); |
||||
@@ -947,6 +987,7 @@ void main_channel_client_send_item(RedChannelClient *rcc, RedPipeItem *base) |
||||
switch (base->type) { |
||||
case RED_PIPE_ITEM_TYPE_MAIN_CHANNELS_LIST: |
||||
main_channel_marshall_channels(rcc, m, base); |
||||
+ mcc->priv->initial_channels_list_sent = true; |
||||
break; |
||||
case RED_PIPE_ITEM_TYPE_MAIN_PING: |
||||
main_channel_marshall_ping(rcc, m, |
||||
@@ -1003,6 +1044,16 @@ void main_channel_client_send_item(RedChannelClient *rcc, RedPipeItem *base) |
||||
case RED_PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS: |
||||
main_channel_marshall_agent_connected(m, rcc, base); |
||||
break; |
||||
+ case RED_PIPE_ITEM_TYPE_MAIN_REGISTERED_CHANNEL: |
||||
+ /* The spice protocol requires that the server receive a ATTACH_CHANNELS |
||||
+ * message from the client before sending any CHANNEL_LIST message. If |
||||
+ * we've already sent our initial CHANNELS_LIST message, then it should be |
||||
+ * safe to send new ones for newly-registered channels. */ |
||||
+ if (mcc->priv->initial_channels_list_sent) { |
||||
+ main_channel_marshall_registered_channel(rcc, m, |
||||
+ SPICE_UPCAST(RedRegisteredChannelPipeItem, base)); |
||||
+ } |
||||
+ break; |
||||
default: |
||||
break; |
||||
}; |
||||
diff --git a/server/main-channel-client.h b/server/main-channel-client.h |
||||
index 26b7e20b8..2cf2e3424 100644 |
||||
--- a/server/main-channel-client.h |
||||
+++ b/server/main-channel-client.h |
||||
@@ -122,12 +122,15 @@ enum { |
||||
RED_PIPE_ITEM_TYPE_MAIN_NAME, |
||||
RED_PIPE_ITEM_TYPE_MAIN_UUID, |
||||
RED_PIPE_ITEM_TYPE_MAIN_AGENT_CONNECTED_TOKENS, |
||||
+ RED_PIPE_ITEM_TYPE_MAIN_REGISTERED_CHANNEL, |
||||
}; |
||||
|
||||
RedPipeItem *main_mouse_mode_item_new(SpiceMouseMode current_mode, int is_client_mouse_allowed); |
||||
|
||||
RedPipeItem *main_multi_media_time_item_new(uint32_t mm_time); |
||||
|
||||
+RedPipeItem *registered_channel_item_new(RedChannel *channel); |
||||
+ |
||||
G_END_DECLS |
||||
|
||||
#endif /* MAIN_CHANNEL_CLIENT_H_ */ |
||||
diff --git a/server/main-channel.c b/server/main-channel.c |
||||
index eca857f6b..4dc130e40 100644 |
||||
--- a/server/main-channel.c |
||||
+++ b/server/main-channel.c |
||||
@@ -149,6 +149,12 @@ static void main_channel_fill_mig_target(MainChannel *main_channel, RedsMigSpice |
||||
main_channel->mig_target.sport = mig_target->sport; |
||||
} |
||||
|
||||
+void |
||||
+main_channel_registered_new_channel(MainChannel *main_chan, RedChannel *channel) |
||||
+{ |
||||
+ red_channel_pipes_add(RED_CHANNEL(main_chan), registered_channel_item_new(channel)); |
||||
+} |
||||
+ |
||||
void main_channel_migrate_switch(MainChannel *main_chan, RedsMigSpice *mig_target) |
||||
{ |
||||
main_channel_fill_mig_target(main_chan, mig_target); |
||||
diff --git a/server/main-channel.h b/server/main-channel.h |
||||
index eb3bcec3a..0cb5be728 100644 |
||||
--- a/server/main-channel.h |
||||
+++ b/server/main-channel.h |
||||
@@ -66,6 +66,9 @@ void main_channel_push_mouse_mode(MainChannel *main_chan, SpiceMouseMode current |
||||
void main_channel_push_agent_connected(MainChannel *main_chan); |
||||
void main_channel_push_agent_disconnected(MainChannel *main_chan); |
||||
void main_channel_push_multi_media_time(MainChannel *main_chan, uint32_t time); |
||||
+/* tell MainChannel we have a new channel ready */ |
||||
+void main_channel_registered_new_channel(MainChannel *main_chan, |
||||
+ RedChannel *channel); |
||||
|
||||
int main_channel_is_connected(MainChannel *main_chan); |
||||
|
||||
diff --git a/server/reds.c b/server/reds.c |
||||
index 1b1ab94ea..99b1fd76b 100644 |
||||
--- a/server/reds.c |
||||
+++ b/server/reds.c |
||||
@@ -387,6 +387,8 @@ void reds_register_channel(RedsState *reds, RedChannel *channel) |
||||
{ |
||||
spice_assert(reds); |
||||
reds->channels = g_list_prepend(reds->channels, channel); |
||||
+ // create new channel in the client if possible |
||||
+ main_channel_registered_new_channel(reds->main_channel, channel); |
||||
} |
||||
|
||||
void reds_unregister_channel(RedsState *reds, RedChannel *channel) |
@ -0,0 +1,194 @@
@@ -0,0 +1,194 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
||||
From: Frediano Ziglio <fziglio@redhat.com> |
||||
Date: Sat, 21 Jan 2017 11:24:58 +0000 |
||||
Subject: [spice-server] stream-device: Add device to handle streaming |
||||
|
||||
Add a stub device in guest. |
||||
The aim of this device is to make it possible for the guest to send a |
||||
stream through a DisplayChannel (in the sense of protocol channel). |
||||
This stub allows the guest to send some data and you can see some debug |
||||
lines of data arrived on host logs. |
||||
|
||||
Signed-off-by: Frediano Ziglio <fziglio@redhat.com> |
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com> |
||||
--- |
||||
server/Makefile.am | 1 + |
||||
server/char-device.h | 1 + |
||||
server/reds.c | 2 + |
||||
server/stream-device.c | 130 +++++++++++++++++++++++++++++++++++++++++++++++++ |
||||
4 files changed, 134 insertions(+) |
||||
create mode 100644 server/stream-device.c |
||||
|
||||
diff --git a/server/Makefile.am b/server/Makefile.am |
||||
index 5d5590af9..f08ddf883 100644 |
||||
--- a/server/Makefile.am |
||||
+++ b/server/Makefile.am |
||||
@@ -166,6 +166,7 @@ libserver_la_SOURCES = \ |
||||
stat.h \ |
||||
stream.c \ |
||||
stream.h \ |
||||
+ stream-device.c \ |
||||
sw-canvas.c \ |
||||
tree.c \ |
||||
tree.h \ |
||||
diff --git a/server/char-device.h b/server/char-device.h |
||||
index dccd576da..54a1ef939 100644 |
||||
--- a/server/char-device.h |
||||
+++ b/server/char-device.h |
||||
@@ -236,6 +236,7 @@ RedCharDevice *spicevmc_device_connect(RedsState *reds, |
||||
uint8_t channel_type); |
||||
void spicevmc_device_disconnect(RedsState *reds, |
||||
SpiceCharDeviceInstance *char_device); |
||||
+RedCharDevice *stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin); |
||||
|
||||
SpiceCharDeviceInterface *spice_char_device_get_interface(SpiceCharDeviceInstance *instance); |
||||
|
||||
diff --git a/server/reds.c b/server/reds.c |
||||
index 99b1fd76b..b24f61ab2 100644 |
||||
--- a/server/reds.c |
||||
+++ b/server/reds.c |
||||
@@ -3223,6 +3223,8 @@ static int spice_server_char_device_add_interface(SpiceServer *reds, |
||||
else if (strcmp(char_device->subtype, SUBTYPE_PORT) == 0) { |
||||
if (strcmp(char_device->portname, "org.spice-space.webdav.0") == 0) { |
||||
dev_state = spicevmc_device_connect(reds, char_device, SPICE_CHANNEL_WEBDAV); |
||||
+ } else if (strcmp(char_device->portname, "com.redhat.stream.0") == 0) { |
||||
+ dev_state = stream_device_connect(reds, char_device); |
||||
} else { |
||||
dev_state = spicevmc_device_connect(reds, char_device, SPICE_CHANNEL_PORT); |
||||
} |
||||
diff --git a/server/stream-device.c b/server/stream-device.c |
||||
new file mode 100644 |
||||
index 000000000..f3a147b80 |
||||
--- /dev/null |
||||
+++ b/server/stream-device.c |
||||
@@ -0,0 +1,130 @@ |
||||
+/* spice-server character device to handle a video stream |
||||
+ |
||||
+ Copyright (C) 2017 Red Hat, Inc. |
||||
+ |
||||
+ This library 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 2.1 of the License, or (at your option) any later version. |
||||
+ |
||||
+ This library 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 this library; if not, see <http://www.gnu.org/licenses/>. |
||||
+*/ |
||||
+#ifdef HAVE_CONFIG_H |
||||
+#include <config.h> |
||||
+#endif |
||||
+ |
||||
+#include "char-device.h" |
||||
+ |
||||
+#define TYPE_STREAM_DEVICE stream_device_get_type() |
||||
+ |
||||
+#define STREAM_DEVICE(obj) \ |
||||
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_STREAM_DEVICE, StreamDevice)) |
||||
+#define STREAM_DEVICE_CLASS(klass) \ |
||||
+ (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_STREAM_DEVICE, StreamDeviceClass)) |
||||
+#define STREAM_DEVICE_GET_CLASS(obj) \ |
||||
+ (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_STREAM_DEVICE, StreamDeviceClass)) |
||||
+ |
||||
+typedef struct StreamDevice StreamDevice; |
||||
+typedef struct StreamDeviceClass StreamDeviceClass; |
||||
+ |
||||
+struct StreamDevice { |
||||
+ RedCharDevice parent; |
||||
+}; |
||||
+ |
||||
+struct StreamDeviceClass { |
||||
+ RedCharDeviceClass parent_class; |
||||
+}; |
||||
+ |
||||
+static GType stream_device_get_type(void) G_GNUC_CONST; |
||||
+static StreamDevice *stream_device_new(SpiceCharDeviceInstance *sin, RedsState *reds); |
||||
+ |
||||
+G_DEFINE_TYPE(StreamDevice, stream_device, RED_TYPE_CHAR_DEVICE) |
||||
+ |
||||
+static RedPipeItem * |
||||
+stream_device_read_msg_from_dev(RedCharDevice *self, SpiceCharDeviceInstance *sin) |
||||
+{ |
||||
+ SpiceCharDeviceInterface *sif; |
||||
+ int n; |
||||
+ |
||||
+ sif = spice_char_device_get_interface(sin); |
||||
+ |
||||
+ do { |
||||
+ uint8_t buf[256]; |
||||
+ n = sif->read(sin, buf, sizeof(buf)); |
||||
+ spice_debug("read %d bytes from device", n); |
||||
+ } while (n > 0); |
||||
+ |
||||
+ return NULL; |
||||
+} |
||||
+ |
||||
+static void |
||||
+stream_device_send_msg_to_client(RedCharDevice *self, RedPipeItem *msg, RedClient *client) |
||||
+{ |
||||
+} |
||||
+ |
||||
+static void |
||||
+stream_device_send_tokens_to_client(RedCharDevice *self, RedClient *client, uint32_t tokens) |
||||
+{ |
||||
+ spice_printerr("Not implemented!"); |
||||
+} |
||||
+ |
||||
+static void |
||||
+stream_device_remove_client(RedCharDevice *self, RedClient *client) |
||||
+{ |
||||
+} |
||||
+ |
||||
+RedCharDevice * |
||||
+stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin) |
||||
+{ |
||||
+ SpiceCharDeviceInterface *sif; |
||||
+ |
||||
+ StreamDevice *dev = stream_device_new(sin, reds); |
||||
+ |
||||
+ sif = spice_char_device_get_interface(sin); |
||||
+ if (sif->state) { |
||||
+ sif->state(sin, 1); |
||||
+ } |
||||
+ |
||||
+ return RED_CHAR_DEVICE(dev); |
||||
+} |
||||
+ |
||||
+static void |
||||
+stream_device_dispose(GObject *object) |
||||
+{ |
||||
+} |
||||
+ |
||||
+static void |
||||
+stream_device_class_init(StreamDeviceClass *klass) |
||||
+{ |
||||
+ GObjectClass *object_class = G_OBJECT_CLASS(klass); |
||||
+ RedCharDeviceClass *char_dev_class = RED_CHAR_DEVICE_CLASS(klass); |
||||
+ |
||||
+ object_class->dispose = stream_device_dispose; |
||||
+ |
||||
+ char_dev_class->read_one_msg_from_device = stream_device_read_msg_from_dev; |
||||
+ char_dev_class->send_msg_to_client = stream_device_send_msg_to_client; |
||||
+ char_dev_class->send_tokens_to_client = stream_device_send_tokens_to_client; |
||||
+ char_dev_class->remove_client = stream_device_remove_client; |
||||
+} |
||||
+ |
||||
+static void |
||||
+stream_device_init(StreamDevice *self) |
||||
+{ |
||||
+} |
||||
+ |
||||
+static StreamDevice * |
||||
+stream_device_new(SpiceCharDeviceInstance *sin, RedsState *reds) |
||||
+{ |
||||
+ return g_object_new(TYPE_STREAM_DEVICE, |
||||
+ "sin", sin, |
||||
+ "spice-server", reds, |
||||
+ "client-tokens-interval", 0ULL, |
||||
+ "self-tokens", ~0ULL, |
||||
+ NULL); |
||||
+} |
@ -0,0 +1,178 @@
@@ -0,0 +1,178 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
||||
From: Frediano Ziglio <fziglio@redhat.com> |
||||
Date: Fri, 13 Jan 2017 18:20:43 +0000 |
||||
Subject: [spice-server] stream-device: Start parsing new protocol from guest |
||||
|
||||
Parse the data sent from the guest to the streaming device. |
||||
At the moment, the data is simply discarded after it is parsed. |
||||
|
||||
Signed-off-by: Frediano Ziglio <fziglio@redhat.com> |
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com> |
||||
--- |
||||
server/stream-device.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++-- |
||||
1 file changed, 124 insertions(+), 4 deletions(-) |
||||
|
||||
diff --git a/server/stream-device.c b/server/stream-device.c |
||||
index f3a147b80..58edb3d11 100644 |
||||
--- a/server/stream-device.c |
||||
+++ b/server/stream-device.c |
||||
@@ -19,6 +19,8 @@ |
||||
#include <config.h> |
||||
#endif |
||||
|
||||
+#include <spice/stream-device.h> |
||||
+ |
||||
#include "char-device.h" |
||||
|
||||
#define TYPE_STREAM_DEVICE stream_device_get_type() |
||||
@@ -35,6 +37,9 @@ typedef struct StreamDeviceClass StreamDeviceClass; |
||||
|
||||
struct StreamDevice { |
||||
RedCharDevice parent; |
||||
+ StreamDevHeader hdr; |
||||
+ uint8_t hdr_pos; |
||||
+ bool has_error; |
||||
}; |
||||
|
||||
struct StreamDeviceClass { |
||||
@@ -46,21 +51,136 @@ static StreamDevice *stream_device_new(SpiceCharDeviceInstance *sin, RedsState * |
||||
|
||||
G_DEFINE_TYPE(StreamDevice, stream_device, RED_TYPE_CHAR_DEVICE) |
||||
|
||||
+typedef bool StreamMsgHandler(StreamDevice *dev, SpiceCharDeviceInstance *sin) |
||||
+ SPICE_GNUC_WARN_UNUSED_RESULT; |
||||
+ |
||||
+static StreamMsgHandler handle_msg_format, handle_msg_data; |
||||
+ |
||||
+static bool handle_msg_invalid(StreamDevice *dev, SpiceCharDeviceInstance *sin, |
||||
+ const char *error_msg) SPICE_GNUC_WARN_UNUSED_RESULT; |
||||
+ |
||||
static RedPipeItem * |
||||
stream_device_read_msg_from_dev(RedCharDevice *self, SpiceCharDeviceInstance *sin) |
||||
{ |
||||
+ StreamDevice *dev = STREAM_DEVICE(self); |
||||
SpiceCharDeviceInterface *sif; |
||||
int n; |
||||
+ bool handled = false; |
||||
+ |
||||
+ if (dev->has_error) { |
||||
+ return NULL; |
||||
+ } |
||||
|
||||
sif = spice_char_device_get_interface(sin); |
||||
|
||||
- do { |
||||
- uint8_t buf[256]; |
||||
+ /* read header */ |
||||
+ while (dev->hdr_pos < sizeof(dev->hdr)) { |
||||
+ n = sif->read(sin, (uint8_t *) &dev->hdr, sizeof(dev->hdr) - dev->hdr_pos); |
||||
+ if (n <= 0) { |
||||
+ return NULL; |
||||
+ } |
||||
+ dev->hdr_pos += n; |
||||
+ if (dev->hdr_pos >= sizeof(dev->hdr)) { |
||||
+ dev->hdr.type = GUINT16_FROM_LE(dev->hdr.type); |
||||
+ dev->hdr.size = GUINT32_FROM_LE(dev->hdr.size); |
||||
+ } |
||||
+ } |
||||
+ |
||||
+ switch ((StreamMsgType) dev->hdr.type) { |
||||
+ case STREAM_TYPE_FORMAT: |
||||
+ if (dev->hdr.size != sizeof(StreamMsgFormat)) { |
||||
+ handled = handle_msg_invalid(dev, sin, "Wrong size for StreamMsgFormat"); |
||||
+ } else { |
||||
+ handled = handle_msg_format(dev, sin); |
||||
+ } |
||||
+ break; |
||||
+ case STREAM_TYPE_DATA: |
||||
+ handled = handle_msg_data(dev, sin); |
||||
+ break; |
||||
+ case STREAM_TYPE_CAPABILITIES: |
||||
+ /* FIXME */ |
||||
+ default: |
||||
+ handled = handle_msg_invalid(dev, sin, "Invalid message type"); |
||||
+ break; |
||||
+ } |
||||
+ |
||||
+ /* current message has been handled, so reset state and get ready to parse |
||||
+ * the next message */ |
||||
+ if (handled) { |
||||
+ dev->hdr_pos = 0; |
||||
+ } |
||||
+ |
||||
+ return NULL; |
||||
+} |
||||
+ |
||||
+static bool |
||||
+handle_msg_invalid(StreamDevice *dev, SpiceCharDeviceInstance *sin, const char *error_msg) |
||||
+{ |
||||
+ static const char default_error_msg[] = "Protocol error"; |
||||
+ |
||||
+ if (!error_msg) { |
||||
+ error_msg = default_error_msg; |
||||
+ } |
||||
+ |
||||
+ int msg_size = sizeof(StreamMsgNotifyError) + strlen(error_msg) + 1; |
||||
+ int total_size = sizeof(StreamDevHeader) + msg_size; |
||||
+ |
||||
+ RedCharDevice *char_dev = RED_CHAR_DEVICE(dev); |
||||
+ RedCharDeviceWriteBuffer *buf = |
||||
+ red_char_device_write_buffer_get_server_no_token(char_dev, total_size); |
||||
+ buf->buf_used = total_size; |
||||
+ |
||||
+ StreamDevHeader *const hdr = (StreamDevHeader *)buf->buf; |
||||
+ hdr->protocol_version = STREAM_DEVICE_PROTOCOL; |
||||
+ hdr->padding = 0; |
||||
+ hdr->type = GUINT16_TO_LE(STREAM_TYPE_NOTIFY_ERROR); |
||||
+ hdr->size = GUINT32_TO_LE(msg_size); |
||||
+ |
||||
+ StreamMsgNotifyError *const error = (StreamMsgNotifyError *)(hdr+1); |
||||
+ error->error_code = GUINT32_TO_LE(0); |
||||
+ strcpy((char *) error->msg, error_msg); |
||||
+ |
||||
+ red_char_device_write_buffer_add(char_dev, buf); |
||||
+ |
||||
+ dev->has_error = true; |
||||
+ return false; |
||||
+} |
||||
+ |
||||
+static bool |
||||
+handle_msg_format(StreamDevice *dev, SpiceCharDeviceInstance *sin) |
||||
+{ |
||||
+ StreamMsgFormat fmt; |
||||
+ SpiceCharDeviceInterface *sif = spice_char_device_get_interface(sin); |
||||
+ int n = sif->read(sin, (uint8_t *) &fmt, sizeof(fmt)); |
||||
+ if (n == 0) { |
||||
+ return false; |
||||
+ } |
||||
+ if (n != sizeof(fmt)) { |
||||
+ return handle_msg_invalid(dev, sin, NULL); |
||||
+ } |
||||
+ fmt.width = GUINT32_FROM_LE(fmt.width); |
||||
+ fmt.height = GUINT32_FROM_LE(fmt.height); |
||||
+ |
||||
+ return true; |
||||
+} |
||||
+ |
||||
+static bool |
||||
+handle_msg_data(StreamDevice *dev, SpiceCharDeviceInstance *sin) |
||||
+{ |
||||
+ SpiceCharDeviceInterface *sif = spice_char_device_get_interface(sin); |
||||
+ int n; |
||||
+ while (1) { |
||||
+ uint8_t buf[16 * 1024]; |
||||
n = sif->read(sin, buf, sizeof(buf)); |
||||
+ /* TODO */ |
||||
spice_debug("read %d bytes from device", n); |
||||
- } while (n > 0); |
||||
+ if (n <= 0) { |
||||
+ break; |
||||
+ } |
||||
+ dev->hdr.size -= n; |
||||
+ } |
||||
|
||||
- return NULL; |
||||
+ return dev->hdr.size == 0; |
||||
} |
||||
|
||||
static void |
@ -0,0 +1,271 @@
@@ -0,0 +1,271 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
||||
From: Frediano Ziglio <fziglio@redhat.com> |
||||
Date: Sat, 21 Jan 2017 15:29:18 +0000 |
||||
Subject: [spice-server] stream-channel: Write a base channel to implement the |
||||
streaming |
||||
|
||||
Currently only compile, not used and not much sense |
||||
|
||||
Signed-off-by: Frediano Ziglio <fziglio@redhat.com> |
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com> |
||||
--- |
||||
server/Makefile.am | 2 + |
||||
server/stream-channel.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++ |
||||
server/stream-channel.h | 53 +++++++++++++++ |
||||
3 files changed, 230 insertions(+) |
||||
create mode 100644 server/stream-channel.c |
||||
create mode 100644 server/stream-channel.h |
||||
|
||||
diff --git a/server/Makefile.am b/server/Makefile.am |
||||
index f08ddf883..e2e3ce861 100644 |
||||
--- a/server/Makefile.am |
||||
+++ b/server/Makefile.am |
||||
@@ -166,6 +166,8 @@ libserver_la_SOURCES = \ |
||||
stat.h \ |
||||
stream.c \ |
||||
stream.h \ |
||||
+ stream-channel.c \ |
||||
+ stream-channel.h \ |
||||
stream-device.c \ |
||||
sw-canvas.c \ |
||||
tree.c \ |
||||
diff --git a/server/stream-channel.c b/server/stream-channel.c |
||||
new file mode 100644 |
||||
index 000000000..fd735f562 |
||||
--- /dev/null |
||||
+++ b/server/stream-channel.c |
||||
@@ -0,0 +1,175 @@ |
||||
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ |
||||
+/* |
||||
+ Copyright (C) 2017 Red Hat, Inc. |
||||
+ |
||||
+ This library 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 2.1 of the License, or (at your option) any later version. |
||||
+ |
||||
+ This library 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 this library; if not, see <http://www.gnu.org/licenses/>. |
||||
+*/ |
||||
+#ifdef HAVE_CONFIG_H |
||||
+#include <config.h> |
||||
+#endif |
||||
+ |
||||
+#include "red-channel-client.h" |
||||
+#include "stream-channel.h" |
||||
+#include "reds.h" |
||||
+#include "common-graphics-channel.h" |
||||
+ |
||||
+#define TYPE_STREAM_CHANNEL_CLIENT stream_channel_client_get_type() |
||||
+ |
||||
+#define STREAM_CHANNEL_CLIENT(obj) \ |
||||
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_STREAM_CHANNEL_CLIENT, StreamChannelClient)) |
||||
+#define STREAM_CHANNEL_CLIENT_CLASS(klass) \ |
||||
+ (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_STREAM_CHANNEL_CLIENT, StreamChannelClientClass)) |
||||
+#define IS_STREAM_CHANNEL_CLIENT(obj) \ |
||||
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_STREAM_CHANNEL_CLIENT)) |
||||
+#define IS_STREAM_CHANNEL_CLIENT_CLASS(klass) \ |
||||
+ (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_STREAM_CHANNEL_CLIENT)) |
||||
+#define STREAM_CHANNEL_CLIENT_GET_CLASS(obj) \ |
||||
+ (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_STREAM_CHANNEL_CLIENT, StreamChannelClientClass)) |
||||
+ |
||||
+typedef struct StreamChannelClient StreamChannelClient; |
||||
+typedef struct StreamChannelClientClass StreamChannelClientClass; |
||||
+ |
||||
+/* we need to inherit from CommonGraphicsChannelClient |
||||
+ * to get buffer handling */ |
||||
+struct StreamChannelClient { |
||||
+ CommonGraphicsChannelClient parent; |
||||
+}; |
||||
+ |
||||
+struct StreamChannelClientClass { |
||||
+ CommonGraphicsChannelClientClass parent_class; |
||||
+}; |
||||
+ |
||||
+GType stream_channel_client_get_type(void) G_GNUC_CONST; |
||||
+ |
||||
+G_DEFINE_TYPE(StreamChannelClient, stream_channel_client, TYPE_COMMON_GRAPHICS_CHANNEL_CLIENT) |
||||
+ |
||||
+struct StreamChannel { |
||||
+ RedChannel parent; |
||||
+}; |
||||
+ |
||||
+struct StreamChannelClass { |
||||
+ RedChannelClass parent_class; |
||||
+}; |
||||
+ |
||||
+G_DEFINE_TYPE(StreamChannel, stream_channel, RED_TYPE_CHANNEL) |
||||
+ |
||||
+static void stream_channel_client_on_disconnect(RedChannelClient *rcc); |
||||
+ |
||||
+static void |
||||
+stream_channel_client_class_init(StreamChannelClientClass *klass) |
||||
+{ |
||||
+ RedChannelClientClass *channel_class = RED_CHANNEL_CLIENT_CLASS(klass); |
||||
+ |
||||
+ channel_class->on_disconnect = stream_channel_client_on_disconnect; |
||||
+} |
||||
+ |
||||
+static void |
||||
+stream_channel_client_init(StreamChannelClient *client) |
||||
+{ |
||||
+} |
||||
+ |
||||
+static void |
||||
+stream_channel_client_on_disconnect(RedChannelClient *rcc) |
||||
+{ |
||||
+} |
||||
+ |
||||
+static StreamChannelClient* |
||||
+stream_channel_client_new(StreamChannel *channel, RedClient *client, RedsStream *stream, |
||||
+ int mig_target, RedChannelCapabilities *caps) |
||||
+{ |
||||
+ StreamChannelClient *rcc; |
||||
+ |
||||
+ rcc = g_initable_new(TYPE_STREAM_CHANNEL_CLIENT, |
||||
+ NULL, NULL, |
||||
+ "channel", channel, |
||||
+ "client", client, |
||||
+ "stream", stream, |
||||
+ "monitor-latency", FALSE, |
||||
+ "caps", caps, |
||||
+ NULL); |
||||
+ |
||||
+ return rcc; |
||||
+} |
||||
+ |
||||
+static void |
||||
+stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item) |
||||
+{ |
||||
+ switch (pipe_item->type) { |
||||
+ default: |
||||
+ spice_error("invalid pipe item type"); |
||||
+ } |
||||
+ |
||||
+ red_channel_client_begin_send_message(rcc); |
||||
+} |
||||
+ |
||||
+StreamChannel* |
||||
+stream_channel_new(RedsState *server, uint32_t id) |
||||
+{ |
||||
+ return g_object_new(TYPE_STREAM_CHANNEL, |
||||
+ "spice-server", server, |
||||
+ "core-interface", reds_get_core_interface(server), |
||||
+ "channel-type", SPICE_CHANNEL_DISPLAY, |
||||
+ // TODO this id should be after all qxl devices |
||||
+ "id", id, |
||||
+ "migration-flags", 0, |
||||
+ "handle-acks", TRUE, // TODO sure ?? |
||||
+ NULL); |
||||
+} |
||||
+ |
||||
+static void |
||||
+stream_channel_connect(RedChannel *red_channel, RedClient *red_client, RedsStream *stream, |
||||
+ int migration, RedChannelCapabilities *caps) |
||||
+{ |
||||
+ StreamChannel *channel = STREAM_CHANNEL(red_channel); |
||||
+ StreamChannelClient *client; |
||||
+ |
||||
+ spice_return_if_fail(stream != NULL); |
||||
+ |
||||
+ client = stream_channel_client_new(channel, red_client, stream, migration, caps); |
||||
+ spice_return_if_fail(client != NULL); |
||||
+} |
||||
+ |
||||
+static void |
||||
+stream_channel_constructed(GObject *object) |
||||
+{ |
||||
+ ClientCbs client_cbs = { NULL, }; |
||||
+ RedChannel *red_channel = RED_CHANNEL(object); |
||||
+ RedsState *reds = red_channel_get_server(red_channel); |
||||
+ |
||||
+ G_OBJECT_CLASS(stream_channel_parent_class)->constructed(object); |
||||
+ |
||||
+ client_cbs.connect = stream_channel_connect; |
||||
+ red_channel_register_client_cbs(red_channel, &client_cbs, NULL); |
||||
+ |
||||
+ reds_register_channel(reds, red_channel); |
||||
+} |
||||
+ |
||||
+static void |
||||
+stream_channel_class_init(StreamChannelClass *klass) |
||||
+{ |
||||
+ GObjectClass *object_class = G_OBJECT_CLASS(klass); |
||||
+ RedChannelClass *channel_class = RED_CHANNEL_CLASS(klass); |
||||
+ |
||||
+ object_class->constructed = stream_channel_constructed; |
||||
+ |
||||
+ channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_DISPLAY, NULL); |
||||
+ channel_class->handle_message = red_channel_client_handle_message; |
||||
+ |
||||
+ channel_class->send_item = stream_channel_send_item; |
||||
+} |
||||
+ |
||||
+static void |
||||
+stream_channel_init(StreamChannel *channel) |
||||
+{ |
||||
+} |
||||
diff --git a/server/stream-channel.h b/server/stream-channel.h |
||||
new file mode 100644 |
||||
index 000000000..e50e17e9e |
||||
--- /dev/null |
||||
+++ b/server/stream-channel.h |
||||
@@ -0,0 +1,53 @@ |
||||
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */ |
||||
+/* |
||||
+ Copyright (C) 2017 Red Hat, Inc. |
||||
+ |
||||
+ This library 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 2.1 of the License, or (at your option) any later version. |
||||
+ |
||||
+ This library 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 this library; if not, see <http://www.gnu.org/licenses/>. |
||||
+*/ |
||||
+ |
||||
+#ifndef STREAM_CHANNEL_H_ |
||||
+#define STREAM_CHANNEL_H_ |
||||
+ |
||||
+#include "red-channel.h" |
||||
+ |
||||
+G_BEGIN_DECLS |
||||
+ |
||||
+/** |
||||
+ * This type it's a RedChannel class which implement display |
||||
+ * channel with input only by stream. |
||||
+ * A pointer to StreamChannel can be converted to a RedChannel. |
||||
+ */ |
||||
+typedef struct StreamChannel StreamChannel; |
||||
+typedef struct StreamChannelClass StreamChannelClass; |
||||
+ |
||||
+#define TYPE_STREAM_CHANNEL stream_channel_get_type() |
||||
+ |
||||
+#define STREAM_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_STREAM_CHANNEL, StreamChannel)) |
||||
+#define STREAM_CHANNEL_CLASS(klass) \ |
||||
+ (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_STREAM_CHANNEL, StreamChannelClass)) |
||||
+#define IS_STREAM_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_STREAM_CHANNEL)) |
||||
+#define IS_STREAM_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_STREAM_CHANNEL)) |
||||
+#define STREAM_CHANNEL_GET_CLASS(obj) \ |
||||
+ (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_STREAM_CHANNEL, StreamChannelClass)) |
||||
+ |
||||
+GType stream_channel_get_type(void) G_GNUC_CONST; |
||||
+ |
||||
+/** |
||||
+ * Create StreamChannel. |
||||
+ */ |
||||
+StreamChannel* stream_channel_new(RedsState *server, uint32_t id); |
||||
+ |
||||
+G_END_DECLS |
||||
+ |
||||
+#endif /* STREAM_CHANNEL_H_ */ |
@ -0,0 +1,88 @@
@@ -0,0 +1,88 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
||||
From: Frediano Ziglio <fziglio@redhat.com> |
||||
Date: Tue, 14 Mar 2017 12:12:22 +0000 |
||||
Subject: [spice-server] stream-channel: Start implementing DisplayChannel |
||||
properly |
||||
|
||||
Handle messages from clients. |
||||
Send some messages to clients. |
||||
|
||||
Signed-off-by: Frediano Ziglio <fziglio@redhat.com> |
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com> |
||||
--- |
||||
server/stream-channel.c | 41 ++++++++++++++++++++++++++++++++++++++++- |
||||
1 file changed, 40 insertions(+), 1 deletion(-) |
||||
|
||||
diff --git a/server/stream-channel.c b/server/stream-channel.c |
||||
index fd735f562..df6936513 100644 |
||||
--- a/server/stream-channel.c |
||||
+++ b/server/stream-channel.c |
||||
@@ -113,6 +113,25 @@ stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item) |
||||
red_channel_client_begin_send_message(rcc); |
||||
} |
||||
|
||||
+static bool |
||||
+handle_message(RedChannelClient *rcc, uint16_t type, uint32_t size, void *msg) |
||||
+{ |
||||
+ switch (type) { |
||||
+ case SPICE_MSGC_DISPLAY_INIT: |
||||
+ case SPICE_MSGC_DISPLAY_PREFERRED_COMPRESSION: |
||||
+ return true; |
||||
+ case SPICE_MSGC_DISPLAY_STREAM_REPORT: |
||||
+ /* TODO these will help tune the streaming reducing/increasing quality */ |
||||
+ return true; |
||||
+ case SPICE_MSGC_DISPLAY_GL_DRAW_DONE: |
||||
+ /* client should not send this message */ |
||||
+ return false; |
||||
+ default: |
||||
+ return red_channel_client_handle_message(rcc, type, size, msg); |
||||
+ } |
||||
+} |
||||
+ |
||||
+ |
||||
StreamChannel* |
||||
stream_channel_new(RedsState *server, uint32_t id) |
||||
{ |
||||
@@ -138,6 +157,22 @@ stream_channel_connect(RedChannel *red_channel, RedClient *red_client, RedsStrea |
||||
|
||||
client = stream_channel_client_new(channel, red_client, stream, migration, caps); |
||||
spice_return_if_fail(client != NULL); |
||||
+ |
||||
+ // TODO set capabilities like SPICE_DISPLAY_CAP_MONITORS_CONFIG |
||||
+ // see guest_set_client_capabilities |
||||
+ RedChannelClient *rcc = RED_CHANNEL_CLIENT(client); |
||||
+ red_channel_client_push_set_ack(rcc); |
||||
+ |
||||
+ // TODO what should happen on migration, dcc return if on migration wait ?? |
||||
+ red_channel_client_ack_zero_messages_window(rcc); |
||||
+ |
||||
+ // "emulate" dcc_start |
||||
+ // TODO only if "surface" |
||||
+ red_channel_client_pipe_add_empty_msg(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES); |
||||
+ // TODO red_surface_create_item_new |
||||
+ // TODO surface data ?? |
||||
+ // TODO monitor configs ?? |
||||
+ red_channel_client_pipe_add_empty_msg(rcc, SPICE_MSG_DISPLAY_MARK); |
||||
} |
||||
|
||||
static void |
||||
@@ -152,6 +187,10 @@ stream_channel_constructed(GObject *object) |
||||
client_cbs.connect = stream_channel_connect; |
||||
red_channel_register_client_cbs(red_channel, &client_cbs, NULL); |
||||
|
||||
+ // TODO, send monitor to support multiple monitors in the future |
||||
+// red_channel_set_cap(red_channel, SPICE_DISPLAY_CAP_MONITORS_CONFIG); |
||||
+ red_channel_set_cap(red_channel, SPICE_DISPLAY_CAP_STREAM_REPORT); |
||||
+ |
||||
reds_register_channel(reds, red_channel); |
||||
} |
||||
|
||||
@@ -164,7 +203,7 @@ stream_channel_class_init(StreamChannelClass *klass) |
||||
object_class->constructed = stream_channel_constructed; |
||||
|
||||
channel_class->parser = spice_get_client_channel_parser(SPICE_CHANNEL_DISPLAY, NULL); |
||||
- channel_class->handle_message = red_channel_client_handle_message; |
||||
+ channel_class->handle_message = handle_message; |
||||
|
||||
channel_class->send_item = stream_channel_send_item; |
||||
} |
@ -0,0 +1,151 @@
@@ -0,0 +1,151 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
||||
From: Frediano Ziglio <fziglio@redhat.com> |
||||
Date: Sat, 21 Jan 2017 14:02:29 +0000 |
||||
Subject: [spice-server] stream-device: Create channel for stream device |
||||
|
||||
So can be used by the device to communicate with the clients. |
||||
|
||||
Signed-off-by: Frediano Ziglio <fziglio@redhat.com> |
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com> |
||||
--- |
||||
server/stream-channel.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++-- |
||||
server/stream-device.c | 14 +++++++++++++ |
||||
2 files changed, 64 insertions(+), 2 deletions(-) |
||||
|
||||
diff --git a/server/stream-channel.c b/server/stream-channel.c |
||||
index df6936513..baf3d58a6 100644 |
||||
--- a/server/stream-channel.c |
||||
+++ b/server/stream-channel.c |
||||
@@ -19,6 +19,8 @@ |
||||
#include <config.h> |
||||
#endif |
||||
|
||||
+#include <common/generated_server_marshallers.h> |
||||
+ |
||||
#include "red-channel-client.h" |
||||
#include "stream-channel.h" |
||||
#include "reds.h" |
||||
@@ -64,6 +66,13 @@ struct StreamChannelClass { |
||||
|
||||
G_DEFINE_TYPE(StreamChannel, stream_channel, RED_TYPE_CHANNEL) |
||||
|
||||
+enum { |
||||
+ RED_PIPE_ITEM_TYPE_SURFACE_CREATE = RED_PIPE_ITEM_TYPE_COMMON_LAST, |
||||
+ RED_PIPE_ITEM_TYPE_FILL_SURFACE, |
||||
+}; |
||||
+ |
||||
+#define PRIMARY_SURFACE_ID 0 |
||||
+ |
||||
static void stream_channel_client_on_disconnect(RedChannelClient *rcc); |
||||
|
||||
static void |
||||
@@ -103,9 +112,46 @@ stream_channel_client_new(StreamChannel *channel, RedClient *client, RedsStream |
||||
} |
||||
|
||||
static void |
||||
+fill_base(SpiceMarshaller *m) |
||||
+{ |
||||
+ SpiceMsgDisplayBase base; |
||||
+ |
||||
+ base.surface_id = PRIMARY_SURFACE_ID; |
||||
+ base.box = (SpiceRect) { 0, 0, 1024, 768 }; |
||||
+ base.clip = (SpiceClip) { SPICE_CLIP_TYPE_NONE, NULL }; |
||||
+ |
||||
+ spice_marshall_DisplayBase(m, &base); |
||||
+} |
||||
+ |
||||
+static void |
||||
stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item) |
||||
{ |
||||
+ SpiceMarshaller *m = red_channel_client_get_marshaller(rcc); |
||||
+ |
||||
switch (pipe_item->type) { |
||||
+ case RED_PIPE_ITEM_TYPE_SURFACE_CREATE: { |
||||
+ red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_SURFACE_CREATE); |
||||
+ SpiceMsgSurfaceCreate surface_create = { |
||||
+ PRIMARY_SURFACE_ID, |
||||
+ 1024, 768, |
||||
+ SPICE_SURFACE_FMT_32_xRGB, SPICE_SURFACE_FLAGS_PRIMARY |
||||
+ }; |
||||
+ spice_marshall_msg_display_surface_create(m, &surface_create); |
||||
+ break; |
||||
+ } |
||||
+ case RED_PIPE_ITEM_TYPE_FILL_SURFACE: { |
||||
+ red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_FILL); |
||||
+ |
||||
+ fill_base(m); |
||||
+ |
||||
+ SpiceFill fill; |
||||
+ fill.brush = (SpiceBrush) { SPICE_BRUSH_TYPE_SOLID, { .color = 0 } }; |
||||
+ fill.rop_descriptor = SPICE_ROPD_OP_PUT; |
||||
+ fill.mask = (SpiceQMask) { 0, { 0, 0 }, NULL }; |
||||
+ SpiceMarshaller *brush_pat_out, *mask_bitmap_out; |
||||
+ spice_marshall_Fill(m, &fill, &brush_pat_out, &mask_bitmap_out); |
||||
+ break; |
||||
+ } |
||||
default: |
||||
spice_error("invalid pipe item type"); |
||||
} |
||||
@@ -169,8 +215,10 @@ stream_channel_connect(RedChannel *red_channel, RedClient *red_client, RedsStrea |
||||
// "emulate" dcc_start |
||||
// TODO only if "surface" |
||||
red_channel_client_pipe_add_empty_msg(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES); |
||||
- // TODO red_surface_create_item_new |
||||
- // TODO surface data ?? |
||||
+ // TODO pass proper data |
||||
+ red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_SURFACE_CREATE); |
||||
+ // surface data |
||||
+ red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_FILL_SURFACE); |
||||
// TODO monitor configs ?? |
||||
red_channel_client_pipe_add_empty_msg(rcc, SPICE_MSG_DISPLAY_MARK); |
||||
} |
||||
diff --git a/server/stream-device.c b/server/stream-device.c |
||||
index 58edb3d11..0c9173ae0 100644 |
||||
--- a/server/stream-device.c |
||||
+++ b/server/stream-device.c |
||||
@@ -22,6 +22,8 @@ |
||||
#include <spice/stream-device.h> |
||||
|
||||
#include "char-device.h" |
||||
+#include "stream-channel.h" |
||||
+#include "reds.h" |
||||
|
||||
#define TYPE_STREAM_DEVICE stream_device_get_type() |
||||
|
||||
@@ -37,9 +39,11 @@ typedef struct StreamDeviceClass StreamDeviceClass; |
||||
|
||||
struct StreamDevice { |
||||
RedCharDevice parent; |
||||
+ |
||||
StreamDevHeader hdr; |
||||
uint8_t hdr_pos; |
||||
bool has_error; |
||||
+ StreamChannel *stream_channel; |
||||
}; |
||||
|
||||
struct StreamDeviceClass { |
||||
@@ -204,7 +208,10 @@ stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin) |
||||
{ |
||||
SpiceCharDeviceInterface *sif; |
||||
|
||||
+ StreamChannel *stream_channel = stream_channel_new(reds, 1); // TODO id |
||||
+ |
||||
StreamDevice *dev = stream_device_new(sin, reds); |
||||
+ dev->stream_channel = stream_channel; |
||||
|
||||
sif = spice_char_device_get_interface(sin); |
||||
if (sif->state) { |
||||
@@ -217,6 +224,13 @@ stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin) |
||||
static void |
||||
stream_device_dispose(GObject *object) |
||||
{ |
||||
+ StreamDevice *dev = STREAM_DEVICE(object); |
||||
+ |
||||
+ if (dev->stream_channel) { |
||||
+ // close all current connections and drop the reference |
||||
+ red_channel_destroy(RED_CHANNEL(dev->stream_channel)); |
||||
+ dev->stream_channel = NULL; |
||||
+ } |
||||
} |
||||
|
||||
static void |
@ -0,0 +1,231 @@
@@ -0,0 +1,231 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
||||
From: Frediano Ziglio <fziglio@redhat.com> |
||||
Date: Sat, 21 Jan 2017 10:47:40 +0000 |
||||
Subject: [spice-server] stream-device: Handle streaming data from device to |
||||
channel |
||||
|
||||
Handle stream data from device sending to the channel. |
||||
The StreamChannel will forward the data to the clients using standard |
||||
DisplayChannel messages, and will create and destroy streams as |
||||
necessary. |
||||
|
||||
Signed-off-by: Frediano Ziglio <fziglio@redhat.com> |
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com> |
||||
--- |
||||
server/stream-channel.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++ |
||||
server/stream-channel.h | 8 ++++ |
||||
server/stream-device.c | 4 +- |
||||
3 files changed, 113 insertions(+), 2 deletions(-) |
||||
|
||||
diff --git a/server/stream-channel.c b/server/stream-channel.c |
||||
index baf3d58a6..8931d8794 100644 |
||||
--- a/server/stream-channel.c |
||||
+++ b/server/stream-channel.c |
||||
@@ -20,11 +20,13 @@ |
||||
#endif |
||||
|
||||
#include <common/generated_server_marshallers.h> |
||||
+#include <spice/stream-device.h> |
||||
|
||||
#include "red-channel-client.h" |
||||
#include "stream-channel.h" |
||||
#include "reds.h" |
||||
#include "common-graphics-channel.h" |
||||
+#include "display-limits.h" |
||||
|
||||
#define TYPE_STREAM_CHANNEL_CLIENT stream_channel_client_get_type() |
||||
|
||||
@@ -46,6 +48,10 @@ typedef struct StreamChannelClientClass StreamChannelClientClass; |
||||
* to get buffer handling */ |
||||
struct StreamChannelClient { |
||||
CommonGraphicsChannelClient parent; |
||||
+ |
||||
+ /* current video stream id, <0 if not initialized or |
||||
+ * we are not sending a stream */ |
||||
+ int stream_id; |
||||
}; |
||||
|
||||
struct StreamChannelClientClass { |
||||
@@ -58,6 +64,10 @@ G_DEFINE_TYPE(StreamChannelClient, stream_channel_client, TYPE_COMMON_GRAPHICS_C |
||||
|
||||
struct StreamChannel { |
||||
RedChannel parent; |
||||
+ |
||||
+ /* current video stream id, <0 if not initialized or |
||||
+ * we are not sending a stream */ |
||||
+ int stream_id; |
||||
}; |
||||
|
||||
struct StreamChannelClass { |
||||
@@ -69,8 +79,22 @@ G_DEFINE_TYPE(StreamChannel, stream_channel, RED_TYPE_CHANNEL) |
||||
enum { |
||||
RED_PIPE_ITEM_TYPE_SURFACE_CREATE = RED_PIPE_ITEM_TYPE_COMMON_LAST, |
||||
RED_PIPE_ITEM_TYPE_FILL_SURFACE, |
||||
+ RED_PIPE_ITEM_TYPE_STREAM_CREATE, |
||||
+ RED_PIPE_ITEM_TYPE_STREAM_DATA, |
||||
+ RED_PIPE_ITEM_TYPE_STREAM_DESTROY, |
||||
}; |
||||
|
||||
+typedef struct StreamCreateItem { |
||||
+ RedPipeItem base; |
||||
+ SpiceMsgDisplayStreamCreate stream_create; |
||||
+} StreamCreateItem; |
||||
+ |
||||
+typedef struct StreamDataItem { |
||||
+ RedPipeItem base; |
||||
+ // NOTE: this must be the last field in the structure |
||||
+ SpiceMsgDisplayStreamData data; |
||||
+} StreamDataItem; |
||||
+ |
||||
#define PRIMARY_SURFACE_ID 0 |
||||
|
||||
static void stream_channel_client_on_disconnect(RedChannelClient *rcc); |
||||
@@ -86,6 +110,7 @@ stream_channel_client_class_init(StreamChannelClientClass *klass) |
||||
static void |
||||
stream_channel_client_init(StreamChannelClient *client) |
||||
{ |
||||
+ client->stream_id = -1; |
||||
} |
||||
|
||||
static void |
||||
@@ -127,6 +152,7 @@ static void |
||||
stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item) |
||||
{ |
||||
SpiceMarshaller *m = red_channel_client_get_marshaller(rcc); |
||||
+ StreamChannelClient *client = STREAM_CHANNEL_CLIENT(rcc); |
||||
|
||||
switch (pipe_item->type) { |
||||
case RED_PIPE_ITEM_TYPE_SURFACE_CREATE: { |
||||
@@ -152,6 +178,32 @@ stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item) |
||||
spice_marshall_Fill(m, &fill, &brush_pat_out, &mask_bitmap_out); |
||||
break; |
||||
} |
||||
+ case RED_PIPE_ITEM_TYPE_STREAM_CREATE: { |
||||
+ StreamCreateItem *item = SPICE_UPCAST(StreamCreateItem, pipe_item); |
||||
+ client->stream_id = item->stream_create.id; |
||||
+ red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_CREATE); |
||||
+ spice_marshall_msg_display_stream_create(m, &item->stream_create); |
||||
+ break; |
||||
+ } |
||||
+ case RED_PIPE_ITEM_TYPE_STREAM_DATA: { |
||||
+ StreamDataItem *item = SPICE_UPCAST(StreamDataItem, pipe_item); |
||||
+ red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DATA); |
||||
+ spice_marshall_msg_display_stream_data(m, &item->data); |
||||
+ red_pipe_item_ref(pipe_item); |
||||
+ spice_marshaller_add_by_ref_full(m, item->data.data, item->data.data_size, |
||||
+ marshaller_unref_pipe_item, pipe_item); |
||||
+ break; |
||||
+ } |
||||
+ case RED_PIPE_ITEM_TYPE_STREAM_DESTROY: { |
||||
+ if (client->stream_id < 0) { |
||||
+ return; |
||||
+ } |
||||
+ SpiceMsgDisplayStreamDestroy stream_destroy = { client->stream_id }; |
||||
+ red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DESTROY); |
||||
+ spice_marshall_msg_display_stream_destroy(m, &stream_destroy); |
||||
+ client->stream_id = -1; |
||||
+ break; |
||||
+ } |
||||
default: |
||||
spice_error("invalid pipe item type"); |
||||
} |
||||
@@ -259,4 +311,55 @@ stream_channel_class_init(StreamChannelClass *klass) |
||||
static void |
||||
stream_channel_init(StreamChannel *channel) |
||||
{ |
||||
+ channel->stream_id = -1; |
||||
+} |
||||
+ |
||||
+void |
||||
+stream_channel_change_format(StreamChannel *channel, const StreamMsgFormat *fmt) |
||||
+{ |
||||
+ RedChannel *red_channel = RED_CHANNEL(channel); |
||||
+ |
||||
+ // send destroy old stream |
||||
+ red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_STREAM_DESTROY); |
||||
+ |
||||
+ // TODO send new create surface if required |
||||
+ |
||||
+ // allocate a new stream id |
||||
+ channel->stream_id = (channel->stream_id + 1) % NUM_STREAMS; |
||||
+ |
||||
+ // send create stream |
||||
+ StreamCreateItem *item = g_new0(StreamCreateItem, 1); |
||||
+ red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_STREAM_CREATE); |
||||
+ item->stream_create.id = channel->stream_id; |
||||
+ item->stream_create.flags = SPICE_STREAM_FLAGS_TOP_DOWN; |
||||
+ item->stream_create.codec_type = fmt->codec; |
||||
+ item->stream_create.stream_width = fmt->width; |
||||
+ item->stream_create.stream_height = fmt->height; |
||||
+ item->stream_create.src_width = fmt->width; |
||||
+ item->stream_create.src_height = fmt->height; |
||||
+ item->stream_create.dest = (SpiceRect) { 0, 0, fmt->width, fmt->height }; |
||||
+ item->stream_create.clip = (SpiceClip) { SPICE_CLIP_TYPE_NONE, NULL }; |
||||
+ red_channel_pipes_add(red_channel, &item->base); |
||||
+} |
||||
+ |
||||
+void |
||||
+stream_channel_send_data(StreamChannel *channel, const void *data, size_t size, uint32_t mm_time) |
||||
+{ |
||||
+ if (channel->stream_id < 0) { |
||||
+ // this condition can happen if the guest didn't handle |
||||
+ // the format stop that we send so think the stream is still |
||||
+ // started |
||||
+ return; |
||||
+ } |
||||
+ |
||||
+ RedChannel *red_channel = RED_CHANNEL(channel); |
||||
+ |
||||
+ StreamDataItem *item = g_malloc(sizeof(*item) + size); |
||||
+ red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_STREAM_DATA); |
||||
+ item->data.base.id = channel->stream_id; |
||||
+ item->data.base.multi_media_time = mm_time; |
||||
+ item->data.data_size = size; |
||||
+ // TODO try to optimize avoiding the copy |
||||
+ memcpy(item->data.data, data, size); |
||||
+ red_channel_pipes_add(red_channel, &item->base); |
||||
} |
||||
diff --git a/server/stream-channel.h b/server/stream-channel.h |
||||
index e50e17e9e..156a75d31 100644 |
||||
--- a/server/stream-channel.h |
||||
+++ b/server/stream-channel.h |
||||
@@ -48,6 +48,14 @@ GType stream_channel_get_type(void) G_GNUC_CONST; |
||||
*/ |
||||
StreamChannel* stream_channel_new(RedsState *server, uint32_t id); |
||||
|
||||
+struct StreamMsgFormat; |
||||
+ |
||||
+void stream_channel_change_format(StreamChannel *channel, |
||||
+ const struct StreamMsgFormat *fmt); |
||||
+void stream_channel_send_data(StreamChannel *channel, |
||||
+ const void *data, size_t size, |
||||
+ uint32_t mm_time); |
||||
+ |
||||
G_END_DECLS |
||||
|
||||
#endif /* STREAM_CHANNEL_H_ */ |
||||
diff --git a/server/stream-device.c b/server/stream-device.c |
||||
index 0c9173ae0..6e78b1a99 100644 |
||||
--- a/server/stream-device.c |
||||
+++ b/server/stream-device.c |
||||
@@ -164,6 +164,7 @@ handle_msg_format(StreamDevice *dev, SpiceCharDeviceInstance *sin) |
||||
} |
||||
fmt.width = GUINT32_FROM_LE(fmt.width); |
||||
fmt.height = GUINT32_FROM_LE(fmt.height); |
||||
+ stream_channel_change_format(dev->stream_channel, &fmt); |
||||
|
||||
return true; |
||||
} |
||||
@@ -176,11 +177,10 @@ handle_msg_data(StreamDevice *dev, SpiceCharDeviceInstance *sin) |
||||
while (1) { |
||||
uint8_t buf[16 * 1024]; |
||||
n = sif->read(sin, buf, sizeof(buf)); |
||||
- /* TODO */ |
||||
- spice_debug("read %d bytes from device", n); |
||||
if (n <= 0) { |
||||
break; |
||||
} |
||||
+ stream_channel_send_data(dev->stream_channel, buf, n, reds_get_mm_time()); |
||||
dev->hdr.size -= n; |
||||
} |
||||
|
@ -0,0 +1,115 @@
@@ -0,0 +1,115 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
||||
From: Frediano Ziglio <fziglio@redhat.com> |
||||
Date: Sat, 21 Jan 2017 19:03:11 +0000 |
||||
Subject: [spice-server] stream-channel: Allows not fixed size |
||||
|
||||
Remove the fixed size stream and support any display size. |
||||
|
||||
Signed-off-by: Frediano Ziglio <fziglio@redhat.com> |
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com> |
||||
--- |
||||
server/stream-channel.c | 31 +++++++++++++++++++++++++------ |
||||
1 file changed, 25 insertions(+), 6 deletions(-) |
||||
|
||||
diff --git a/server/stream-channel.c b/server/stream-channel.c |
||||
index 8931d8794..59f566f50 100644 |
||||
--- a/server/stream-channel.c |
||||
+++ b/server/stream-channel.c |
||||
@@ -68,6 +68,8 @@ struct StreamChannel { |
||||
/* current video stream id, <0 if not initialized or |
||||
* we are not sending a stream */ |
||||
int stream_id; |
||||
+ /* size of the current video stream */ |
||||
+ unsigned width, height; |
||||
}; |
||||
|
||||
struct StreamChannelClass { |
||||
@@ -78,6 +80,7 @@ G_DEFINE_TYPE(StreamChannel, stream_channel, RED_TYPE_CHANNEL) |
||||
|
||||
enum { |
||||
RED_PIPE_ITEM_TYPE_SURFACE_CREATE = RED_PIPE_ITEM_TYPE_COMMON_LAST, |
||||
+ RED_PIPE_ITEM_TYPE_SURFACE_DESTROY, |
||||
RED_PIPE_ITEM_TYPE_FILL_SURFACE, |
||||
RED_PIPE_ITEM_TYPE_STREAM_CREATE, |
||||
RED_PIPE_ITEM_TYPE_STREAM_DATA, |
||||
@@ -137,12 +140,12 @@ stream_channel_client_new(StreamChannel *channel, RedClient *client, RedsStream |
||||
} |
||||
|
||||
static void |
||||
-fill_base(SpiceMarshaller *m) |
||||
+fill_base(SpiceMarshaller *m, const StreamChannel *channel) |
||||
{ |
||||
SpiceMsgDisplayBase base; |
||||
|
||||
base.surface_id = PRIMARY_SURFACE_ID; |
||||
- base.box = (SpiceRect) { 0, 0, 1024, 768 }; |
||||
+ base.box = (SpiceRect) { 0, 0, channel->width, channel->height }; |
||||
base.clip = (SpiceClip) { SPICE_CLIP_TYPE_NONE, NULL }; |
||||
|
||||
spice_marshall_DisplayBase(m, &base); |
||||
@@ -153,22 +156,29 @@ stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item) |
||||
{ |
||||
SpiceMarshaller *m = red_channel_client_get_marshaller(rcc); |
||||
StreamChannelClient *client = STREAM_CHANNEL_CLIENT(rcc); |
||||
+ StreamChannel *channel = STREAM_CHANNEL(red_channel_client_get_channel(rcc)); |
||||
|
||||
switch (pipe_item->type) { |
||||
case RED_PIPE_ITEM_TYPE_SURFACE_CREATE: { |
||||
red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_SURFACE_CREATE); |
||||
SpiceMsgSurfaceCreate surface_create = { |
||||
PRIMARY_SURFACE_ID, |
||||
- 1024, 768, |
||||
+ channel->width, channel->height, |
||||
SPICE_SURFACE_FMT_32_xRGB, SPICE_SURFACE_FLAGS_PRIMARY |
||||
}; |
||||
spice_marshall_msg_display_surface_create(m, &surface_create); |
||||
break; |
||||
} |
||||
+ case RED_PIPE_ITEM_TYPE_SURFACE_DESTROY: { |
||||
+ red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_SURFACE_DESTROY); |
||||
+ SpiceMsgSurfaceDestroy surface_destroy = { PRIMARY_SURFACE_ID }; |
||||
+ spice_marshall_msg_display_surface_destroy(m, &surface_destroy); |
||||
+ break; |
||||
+ } |
||||
case RED_PIPE_ITEM_TYPE_FILL_SURFACE: { |
||||
red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_DRAW_FILL); |
||||
|
||||
- fill_base(m); |
||||
+ fill_base(m, channel); |
||||
|
||||
SpiceFill fill; |
||||
fill.brush = (SpiceBrush) { SPICE_BRUSH_TYPE_SOLID, { .color = 0 } }; |
||||
@@ -267,7 +277,7 @@ stream_channel_connect(RedChannel *red_channel, RedClient *red_client, RedsStrea |
||||
// "emulate" dcc_start |
||||
// TODO only if "surface" |
||||
red_channel_client_pipe_add_empty_msg(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES); |
||||
- // TODO pass proper data |
||||
+ // pass proper data |
||||
red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_SURFACE_CREATE); |
||||
// surface data |
||||
red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_FILL_SURFACE); |
||||
@@ -312,6 +322,8 @@ static void |
||||
stream_channel_init(StreamChannel *channel) |
||||
{ |
||||
channel->stream_id = -1; |
||||
+ channel->width = 1024; |
||||
+ channel->height = 768; |
||||
} |
||||
|
||||
void |
||||
@@ -322,7 +334,14 @@ stream_channel_change_format(StreamChannel *channel, const StreamMsgFormat *fmt) |
||||
// send destroy old stream |
||||
red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_STREAM_DESTROY); |
||||
|
||||
- // TODO send new create surface if required |
||||
+ // send new create surface if required |
||||
+ if (channel->width != fmt->width || channel->height != fmt->height) { |
||||
+ channel->width = fmt->width; |
||||
+ channel->height = fmt->height; |
||||
+ red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_SURFACE_DESTROY); |
||||
+ red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_SURFACE_CREATE); |
||||
+ // TODO monitors config ?? |
||||
+ } |
||||
|
||||
// allocate a new stream id |
||||
channel->stream_id = (channel->stream_id + 1) % NUM_STREAMS; |
@ -0,0 +1,67 @@
@@ -0,0 +1,67 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
||||
From: Frediano Ziglio <fziglio@redhat.com> |
||||
Date: Sat, 21 Jan 2017 19:08:12 +0000 |
||||
Subject: [spice-server] stream-channel: Allows to register callback to get new |
||||
stream request |
||||
|
||||
The channel needs to communicate when it receive a new |
||||
stream request from the guest. |
||||
|
||||
Signed-off-by: Frediano Ziglio <fziglio@redhat.com> |
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com> |
||||
--- |
||||
server/stream-channel.c | 12 ++++++++++++ |
||||
server/stream-channel.h | 6 ++++++ |
||||
2 files changed, 18 insertions(+) |
||||
|
||||
diff --git a/server/stream-channel.c b/server/stream-channel.c |
||||
index 59f566f50..be6f7c7d1 100644 |
||||
--- a/server/stream-channel.c |
||||
+++ b/server/stream-channel.c |
||||
@@ -70,6 +70,10 @@ struct StreamChannel { |
||||
int stream_id; |
||||
/* size of the current video stream */ |
||||
unsigned width, height; |
||||
+ |
||||
+ /* callback to notify when a stream should be started or stopped */ |
||||
+ stream_channel_start_proc start_cb; |
||||
+ void *start_opaque; |
||||
}; |
||||
|
||||
struct StreamChannelClass { |
||||
@@ -382,3 +386,11 @@ stream_channel_send_data(StreamChannel *channel, const void *data, size_t size, |
||||
memcpy(item->data.data, data, size); |
||||
red_channel_pipes_add(red_channel, &item->base); |
||||
} |
||||
+ |
||||
+void |
||||
+stream_channel_register_start_cb(StreamChannel *channel, |
||||
+ stream_channel_start_proc cb, void *opaque) |
||||
+{ |
||||
+ channel->start_cb = cb; |
||||
+ channel->start_opaque = opaque; |
||||
+} |
||||
diff --git a/server/stream-channel.h b/server/stream-channel.h |
||||
index 156a75d31..ba098df49 100644 |
||||
--- a/server/stream-channel.h |
||||
+++ b/server/stream-channel.h |
||||
@@ -49,6 +49,7 @@ GType stream_channel_get_type(void) G_GNUC_CONST; |
||||
StreamChannel* stream_channel_new(RedsState *server, uint32_t id); |
||||
|
||||
struct StreamMsgFormat; |
||||
+struct StreamMsgStartStop; |
||||
|
||||
void stream_channel_change_format(StreamChannel *channel, |
||||
const struct StreamMsgFormat *fmt); |
||||
@@ -56,6 +57,11 @@ void stream_channel_send_data(StreamChannel *channel, |
||||
const void *data, size_t size, |
||||
uint32_t mm_time); |
||||
|
||||
+typedef void (*stream_channel_start_proc)(void *opaque, struct StreamMsgStartStop *start, |
||||
+ StreamChannel *channel); |
||||
+void stream_channel_register_start_cb(StreamChannel *channel, |
||||
+ stream_channel_start_proc cb, void *opaque); |
||||
+ |
||||
G_END_DECLS |
||||
|
||||
#endif /* STREAM_CHANNEL_H_ */ |
@ -0,0 +1,131 @@
@@ -0,0 +1,131 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
||||
From: Frediano Ziglio <fziglio@redhat.com> |
||||
Date: Sat, 21 Jan 2017 19:03:19 +0000 |
||||
Subject: [spice-server] stream-channel: Support client |
||||
connection/disconnection |
||||
|
||||
When a new client is connected we must restart the stream so new |
||||
clients can receive correct data without having to wait for the |
||||
next full screen (which on idle screen could take ages). |
||||
On disconnection we should tell the guest to stop streaming |
||||
not wasting resources to stream not needed data. |
||||
|
||||
Signed-off-by: Frediano Ziglio <fziglio@redhat.com> |
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com> |
||||
--- |
||||
server/stream-channel.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++ |
||||
1 file changed, 81 insertions(+) |
||||
|
||||
diff --git a/server/stream-channel.c b/server/stream-channel.c |
||||
index be6f7c7d1..2ad9ebae3 100644 |
||||
--- a/server/stream-channel.c |
||||
+++ b/server/stream-channel.c |
||||
@@ -121,8 +121,32 @@ stream_channel_client_init(StreamChannelClient *client) |
||||
} |
||||
|
||||
static void |
||||
+request_new_stream(StreamChannel *channel, StreamMsgStartStop *start) |
||||
+{ |
||||
+ if (channel->start_cb) { |
||||
+ channel->start_cb(channel->start_opaque, start, channel); |
||||
+ } |
||||
+} |
||||
+ |
||||
+static void |
||||
stream_channel_client_on_disconnect(RedChannelClient *rcc) |
||||
{ |
||||
+ RedChannel *red_channel = red_channel_client_get_channel(rcc); |
||||
+ |
||||
+ // if there are still some client connected keep streaming |
||||
+ // TODO, maybe would be worth sending new codecs if they are better |
||||
+ if (red_channel_is_connected(red_channel)) { |
||||
+ return; |
||||
+ } |
||||
+ |
||||
+ StreamChannel *channel = STREAM_CHANNEL(red_channel); |
||||
+ channel->stream_id = -1; |
||||
+ channel->width = 0; |
||||
+ channel->height = 0; |
||||
+ |
||||
+ // send stream stop to device |
||||
+ StreamMsgStartStop stop = { 0, }; |
||||
+ request_new_stream(channel, &stop); |
||||
} |
||||
|
||||
static StreamChannelClient* |
||||
@@ -258,18 +282,75 @@ stream_channel_new(RedsState *server, uint32_t id) |
||||
NULL); |
||||
} |
||||
|
||||
+#define MAX_SUPPORTED_CODECS SPICE_VIDEO_CODEC_TYPE_ENUM_END |
||||
+ |
||||
+// find common codecs supported by all clients |
||||
+static uint8_t |
||||
+stream_channel_get_supported_codecs(StreamChannel *channel, uint8_t *out_codecs) |
||||
+{ |
||||
+ RedChannelClient *rcc; |
||||
+ int codec; |
||||
+ |
||||
+ static const uint16_t codec2cap[] = { |
||||
+ 0, // invalid |
||||
+ SPICE_DISPLAY_CAP_CODEC_MJPEG, |
||||
+ SPICE_DISPLAY_CAP_CODEC_VP8, |
||||
+ SPICE_DISPLAY_CAP_CODEC_H264, |
||||
+ SPICE_DISPLAY_CAP_CODEC_VP9, |
||||
+ }; |
||||
+ |
||||
+ bool supported[SPICE_N_ELEMENTS(codec2cap)]; |
||||
+ |
||||
+ for (codec = 0; codec < SPICE_N_ELEMENTS(codec2cap); ++codec) { |
||||
+ supported[codec] = true; |
||||
+ } |
||||
+ |
||||
+ FOREACH_CLIENT(channel, rcc) { |
||||
+ for (codec = 1; codec < SPICE_N_ELEMENTS(codec2cap); ++codec) { |
||||
+ // if do not support codec delete from list |
||||
+ if (!red_channel_client_test_remote_cap(rcc, codec2cap[codec])) { |
||||
+ supported[codec] = false; |
||||
+ } |
||||
+ } |
||||
+ } |
||||
+ |
||||
+ // surely mjpeg is supported |
||||
+ supported[SPICE_VIDEO_CODEC_TYPE_MJPEG] = true; |
||||
+ |
||||
+ int num = 0; |
||||
+ for (codec = 1; codec < SPICE_N_ELEMENTS(codec2cap); ++codec) { |
||||
+ if (supported[codec]) { |
||||
+ out_codecs[num++] = codec; |
||||
+ } |
||||
+ } |
||||
+ |
||||
+ return num; |
||||
+} |
||||
+ |
||||
static void |
||||
stream_channel_connect(RedChannel *red_channel, RedClient *red_client, RedsStream *stream, |
||||
int migration, RedChannelCapabilities *caps) |
||||
{ |
||||
StreamChannel *channel = STREAM_CHANNEL(red_channel); |
||||
StreamChannelClient *client; |
||||
+ struct { |
||||
+ StreamMsgStartStop base; |
||||
+ uint8_t codecs_buffer[MAX_SUPPORTED_CODECS]; |
||||
+ } start_msg; |
||||
+ StreamMsgStartStop *const start = &start_msg.base; |
||||
|
||||
spice_return_if_fail(stream != NULL); |
||||
|
||||
client = stream_channel_client_new(channel, red_client, stream, migration, caps); |
||||
spice_return_if_fail(client != NULL); |
||||
|
||||
+ // request new stream |
||||
+ start->num_codecs = stream_channel_get_supported_codecs(channel, start->codecs); |
||||
+ // send in any case, even if list is not changed |
||||
+ // notify device about changes |
||||
+ request_new_stream(channel, start); |
||||
+ |
||||
+ |
||||
// TODO set capabilities like SPICE_DISPLAY_CAP_MONITORS_CONFIG |
||||
// see guest_set_client_capabilities |
||||
RedChannelClient *rcc = RED_CHANNEL_CLIENT(client); |
@ -0,0 +1,64 @@
@@ -0,0 +1,64 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
||||
From: Frediano Ziglio <fziglio@redhat.com> |
||||
Date: Sat, 21 Jan 2017 09:01:18 +0000 |
||||
Subject: [spice-server] stream-channel: Do not show an empty blank screen on |
||||
start |
||||
|
||||
Start showing something when we have a surface and stream |
||||
instead of showing a blank screen which is now not useful. |
||||
Was useful for debugging purposes to understand that the |
||||
new channel was sending messages correctly to client and |
||||
client could handle them. |
||||
|
||||
Signed-off-by: Frediano Ziglio <fziglio@redhat.com> |
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com> |
||||
--- |
||||
server/stream-channel.c | 16 ++++++++++++---- |
||||
1 file changed, 12 insertions(+), 4 deletions(-) |
||||
|
||||
diff --git a/server/stream-channel.c b/server/stream-channel.c |
||||
index 2ad9ebae3..e89563b82 100644 |
||||
--- a/server/stream-channel.c |
||||
+++ b/server/stream-channel.c |
||||
@@ -360,8 +360,13 @@ stream_channel_connect(RedChannel *red_channel, RedClient *red_client, RedsStrea |
||||
red_channel_client_ack_zero_messages_window(rcc); |
||||
|
||||
// "emulate" dcc_start |
||||
- // TODO only if "surface" |
||||
red_channel_client_pipe_add_empty_msg(rcc, SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES); |
||||
+ |
||||
+ // only if "surface" |
||||
+ if (channel->width == 0 || channel->height == 0) { |
||||
+ return; |
||||
+ } |
||||
+ |
||||
// pass proper data |
||||
red_channel_client_pipe_add_type(rcc, RED_PIPE_ITEM_TYPE_SURFACE_CREATE); |
||||
// surface data |
||||
@@ -407,8 +412,8 @@ static void |
||||
stream_channel_init(StreamChannel *channel) |
||||
{ |
||||
channel->stream_id = -1; |
||||
- channel->width = 1024; |
||||
- channel->height = 768; |
||||
+ channel->width = 0; |
||||
+ channel->height = 0; |
||||
} |
||||
|
||||
void |
||||
@@ -421,11 +426,14 @@ stream_channel_change_format(StreamChannel *channel, const StreamMsgFormat *fmt) |
||||
|
||||
// send new create surface if required |
||||
if (channel->width != fmt->width || channel->height != fmt->height) { |
||||
+ if (channel->width != 0 && channel->height != 0) { |
||||
+ red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_SURFACE_DESTROY); |
||||
+ } |
||||
channel->width = fmt->width; |
||||
channel->height = fmt->height; |
||||
- red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_SURFACE_DESTROY); |
||||
red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_SURFACE_CREATE); |
||||
// TODO monitors config ?? |
||||
+ red_channel_pipes_add_empty_msg(red_channel, SPICE_MSG_DISPLAY_MARK); |
||||
} |
||||
|
||||
// allocate a new stream id |
@ -0,0 +1,67 @@
@@ -0,0 +1,67 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
||||
From: Frediano Ziglio <fziglio@redhat.com> |
||||
Date: Tue, 24 Jan 2017 11:36:27 +0000 |
||||
Subject: [spice-server] char-device: Do not stop and clear interface on reset |
||||
|
||||
Currently, red_char_device_reset() stops the device, clears all pending |
||||
messages, and clears its device instance. After this function is called, |
||||
the char device will not work again until it is assigned a new device |
||||
instance and restarted. This is fine for the vdagent char device, which |
||||
is currently the only user of this function. But for the stream device, |
||||
we want to be able to reset the char device to a working state (e.g. |
||||
clear all pending messages, etc) without stopping or disabling the char |
||||
device. So this function will now only reset the char device to a clean |
||||
working state, and the _stop() and _reset_dev_instance() calls will be |
||||
moved up to the caller. |
||||
|
||||
Signed-off-by: Frediano Ziglio <fziglio@redhat.com> |
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com> |
||||
--- |
||||
server/char-device.c | 2 -- |
||||
server/reds.c | 6 +++++- |
||||
2 files changed, 5 insertions(+), 3 deletions(-) |
||||
|
||||
diff --git a/server/char-device.c b/server/char-device.c |
||||
index 658f9f364..f8a098bd8 100644 |
||||
--- a/server/char-device.c |
||||
+++ b/server/char-device.c |
||||
@@ -823,7 +823,6 @@ void red_char_device_reset(RedCharDevice *dev) |
||||
GList *client_item; |
||||
RedCharDeviceWriteBuffer *buf; |
||||
|
||||
- red_char_device_stop(dev); |
||||
dev->priv->wait_for_migrate_data = FALSE; |
||||
spice_debug("char device %p", dev); |
||||
while ((buf = g_queue_pop_tail(&dev->priv->write_queue))) { |
||||
@@ -845,7 +844,6 @@ void red_char_device_reset(RedCharDevice *dev) |
||||
dev_client->num_client_tokens += dev_client->num_client_tokens_free; |
||||
dev_client->num_client_tokens_free = 0; |
||||
} |
||||
- red_char_device_reset_dev_instance(dev, NULL); |
||||
} |
||||
|
||||
void red_char_device_wakeup(RedCharDevice *dev) |
||||
diff --git a/server/reds.c b/server/reds.c |
||||
index b24f61ab2..401d242fb 100644 |
||||
--- a/server/reds.c |
||||
+++ b/server/reds.c |
||||
@@ -470,6 +470,7 @@ static void reds_reset_vdp(RedsState *reds) |
||||
{ |
||||
RedCharDeviceVDIPort *dev = reds->agent_dev; |
||||
SpiceCharDeviceInterface *sif; |
||||
+ RedCharDevice *char_dev; |
||||
|
||||
dev->priv->read_state = VDI_PORT_READ_STATE_READ_HEADER; |
||||
dev->priv->receive_pos = (uint8_t *)&dev->priv->vdi_chunk_header; |
||||
@@ -502,7 +503,10 @@ static void reds_reset_vdp(RedsState *reds) |
||||
* The tokens are also reset to avoid mismatch in upon agent reconnection. |
||||
*/ |
||||
dev->priv->agent_attached = FALSE; |
||||
- red_char_device_reset(RED_CHAR_DEVICE(dev)); |
||||
+ char_dev = RED_CHAR_DEVICE(dev); |
||||
+ red_char_device_stop(char_dev); |
||||
+ red_char_device_reset(char_dev); |
||||
+ red_char_device_reset_dev_instance(char_dev, NULL); |
||||
|
||||
sif = spice_char_device_get_interface(reds->vdagent); |
||||
if (sif->state) { |
@ -0,0 +1,166 @@
@@ -0,0 +1,166 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
||||
From: Frediano Ziglio <fziglio@redhat.com> |
||||
Date: Wed, 25 Jan 2017 22:42:00 +0000 |
||||
Subject: [spice-server] stream-device: Start supporting resetting device when |
||||
close/open on guest |
||||
|
||||
When guest close the device the host device has to be reset too. |
||||
This make easier to restart the guest device which can happen in case |
||||
of reboot, agent issues or if we want to update the agent. |
||||
|
||||
Signed-off-by: Frediano Ziglio <fziglio@redhat.com> |
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com> |
||||
--- |
||||
server/stream-channel.c | 34 ++++++++++++++++++++++++++++++++++ |
||||
server/stream-channel.h | 7 ++++++- |
||||
server/stream-device.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ |
||||
3 files changed, 89 insertions(+), 1 deletion(-) |
||||
|
||||
diff --git a/server/stream-channel.c b/server/stream-channel.c |
||||
index e89563b82..51b8badf9 100644 |
||||
--- a/server/stream-channel.c |
||||
+++ b/server/stream-channel.c |
||||
@@ -483,3 +483,37 @@ stream_channel_register_start_cb(StreamChannel *channel, |
||||
channel->start_cb = cb; |
||||
channel->start_opaque = opaque; |
||||
} |
||||
+ |
||||
+void |
||||
+stream_channel_reset(StreamChannel *channel) |
||||
+{ |
||||
+ struct { |
||||
+ StreamMsgStartStop base; |
||||
+ uint8_t codecs_buffer[MAX_SUPPORTED_CODECS]; |
||||
+ } start_msg; |
||||
+ StreamMsgStartStop *const start = &start_msg.base; |
||||
+ RedChannel *red_channel = RED_CHANNEL(channel); |
||||
+ |
||||
+ // send destroy old stream |
||||
+ red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_STREAM_DESTROY); |
||||
+ |
||||
+ // destroy display surface |
||||
+ if (channel->width != 0 && channel->height != 0) { |
||||
+ red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_SURFACE_DESTROY); |
||||
+ } |
||||
+ |
||||
+ channel->stream_id = -1; |
||||
+ channel->width = 0; |
||||
+ channel->height = 0; |
||||
+ |
||||
+ if (!red_channel_is_connected(red_channel)) { |
||||
+ return; |
||||
+ } |
||||
+ |
||||
+ // try to request a new stream, this should start a new stream |
||||
+ // if the guest is connected to the device and a client is already connected |
||||
+ start->num_codecs = stream_channel_get_supported_codecs(channel, start->codecs); |
||||
+ // send in any case, even if list is not changed |
||||
+ // notify device about changes |
||||
+ request_new_stream(channel, start); |
||||
+} |
||||
diff --git a/server/stream-channel.h b/server/stream-channel.h |
||||
index ba098df49..bd075a951 100644 |
||||
--- a/server/stream-channel.h |
||||
+++ b/server/stream-channel.h |
||||
@@ -48,7 +48,12 @@ GType stream_channel_get_type(void) G_GNUC_CONST; |
||||
*/ |
||||
StreamChannel* stream_channel_new(RedsState *server, uint32_t id); |
||||
|
||||
-struct StreamMsgFormat; |
||||
+/** |
||||
+ * Reset channel at initial state |
||||
+ */ |
||||
+void stream_channel_reset(StreamChannel *channel); |
||||
+ |
||||
+struct StreamMsgStreamFormat; |
||||
struct StreamMsgStartStop; |
||||
|
||||
void stream_channel_change_format(StreamChannel *channel, |
||||
diff --git a/server/stream-device.c b/server/stream-device.c |
||||
index 6e78b1a99..9e401f8ed 100644 |
||||
--- a/server/stream-device.c |
||||
+++ b/server/stream-device.c |
||||
@@ -43,6 +43,7 @@ struct StreamDevice { |
||||
StreamDevHeader hdr; |
||||
uint8_t hdr_pos; |
||||
bool has_error; |
||||
+ bool opened; |
||||
StreamChannel *stream_channel; |
||||
}; |
||||
|
||||
@@ -203,6 +204,35 @@ stream_device_remove_client(RedCharDevice *self, RedClient *client) |
||||
{ |
||||
} |
||||
|
||||
+static void |
||||
+stream_device_stream_start(void *opaque, StreamMsgStartStop *start, |
||||
+ StreamChannel *stream_channel G_GNUC_UNUSED) |
||||
+{ |
||||
+ StreamDevice *dev = (StreamDevice *) opaque; |
||||
+ |
||||
+ if (!dev->opened) { |
||||
+ return; |
||||
+ } |
||||
+ |
||||
+ int msg_size = sizeof(*start) + sizeof(start->codecs[0]) * start->num_codecs; |
||||
+ int total_size = sizeof(StreamDevHeader) + msg_size; |
||||
+ |
||||
+ RedCharDevice *char_dev = RED_CHAR_DEVICE(dev); |
||||
+ RedCharDeviceWriteBuffer *buf = |
||||
+ red_char_device_write_buffer_get_server_no_token(char_dev, total_size); |
||||
+ buf->buf_used = total_size; |
||||
+ |
||||
+ StreamDevHeader *hdr = (StreamDevHeader *)buf->buf; |
||||
+ hdr->protocol_version = STREAM_DEVICE_PROTOCOL; |
||||
+ hdr->padding = 0; |
||||
+ hdr->type = GUINT16_TO_LE(STREAM_TYPE_START_STOP); |
||||
+ hdr->size = GUINT32_TO_LE(msg_size); |
||||
+ |
||||
+ memcpy(&hdr[1], start, msg_size); |
||||
+ |
||||
+ red_char_device_write_buffer_add(char_dev, buf); |
||||
+} |
||||
+ |
||||
RedCharDevice * |
||||
stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin) |
||||
{ |
||||
@@ -212,6 +242,7 @@ stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin) |
||||
|
||||
StreamDevice *dev = stream_device_new(sin, reds); |
||||
dev->stream_channel = stream_channel; |
||||
+ stream_channel_register_start_cb(stream_channel, stream_device_stream_start, dev); |
||||
|
||||
sif = spice_char_device_get_interface(sin); |
||||
if (sif->state) { |
||||
@@ -234,6 +265,23 @@ stream_device_dispose(GObject *object) |
||||
} |
||||
|
||||
static void |
||||
+stream_device_port_event(RedCharDevice *char_dev, uint8_t event) |
||||
+{ |
||||
+ if (event != SPICE_PORT_EVENT_OPENED && event != SPICE_PORT_EVENT_CLOSED) { |
||||
+ return; |
||||
+ } |
||||
+ |
||||
+ StreamDevice *dev = STREAM_DEVICE(char_dev); |
||||
+ |
||||
+ // reset device and channel on close/open |
||||
+ dev->opened = (event == SPICE_PORT_EVENT_OPENED); |
||||
+ dev->hdr_pos = 0; |
||||
+ dev->has_error = false; |
||||
+ red_char_device_reset(char_dev); |
||||
+ stream_channel_reset(dev->stream_channel); |
||||
+} |
||||
+ |
||||
+static void |
||||
stream_device_class_init(StreamDeviceClass *klass) |
||||
{ |
||||
GObjectClass *object_class = G_OBJECT_CLASS(klass); |
||||
@@ -245,6 +293,7 @@ stream_device_class_init(StreamDeviceClass *klass) |
||||
char_dev_class->send_msg_to_client = stream_device_send_msg_to_client; |
||||
char_dev_class->send_tokens_to_client = stream_device_send_tokens_to_client; |
||||
char_dev_class->remove_client = stream_device_remove_client; |
||||
+ char_dev_class->port_event = stream_device_port_event; |
||||
} |
||||
|
||||
static void |
@ -0,0 +1,89 @@
@@ -0,0 +1,89 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
||||
From: Frediano Ziglio <fziglio@redhat.com> |
||||
Date: Fri, 25 Aug 2017 00:02:54 +0100 |
||||
Subject: [spice-server] stream-device: Create channel when needed |
||||
|
||||
This allows a better id allocation as devices are created after |
||||
fixed ones. |
||||
Also will allow to support more easily multiple monitor. |
||||
|
||||
Signed-off-by: Frediano Ziglio <fziglio@redhat.com> |
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com> |
||||
--- |
||||
server/stream-device.c | 38 ++++++++++++++++++++++++++++++++------ |
||||
1 file changed, 32 insertions(+), 6 deletions(-) |
||||
|
||||
diff --git a/server/stream-device.c b/server/stream-device.c |
||||
index 9e401f8ed..ae108788b 100644 |
||||
--- a/server/stream-device.c |
||||
+++ b/server/stream-device.c |
||||
@@ -72,7 +72,7 @@ stream_device_read_msg_from_dev(RedCharDevice *self, SpiceCharDeviceInstance *si |
||||
int n; |
||||
bool handled = false; |
||||
|
||||
- if (dev->has_error) { |
||||
+ if (dev->has_error || !dev->stream_channel) { |
||||
return NULL; |
||||
} |
||||
|
||||
@@ -238,11 +238,7 @@ stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin) |
||||
{ |
||||
SpiceCharDeviceInterface *sif; |
||||
|
||||
- StreamChannel *stream_channel = stream_channel_new(reds, 1); // TODO id |
||||
- |
||||
StreamDevice *dev = stream_device_new(sin, reds); |
||||
- dev->stream_channel = stream_channel; |
||||
- stream_channel_register_start_cb(stream_channel, stream_device_stream_start, dev); |
||||
|
||||
sif = spice_char_device_get_interface(sin); |
||||
if (sif->state) { |
||||
@@ -265,6 +261,33 @@ stream_device_dispose(GObject *object) |
||||
} |
||||
|
||||
static void |
||||
+allocate_channels(StreamDevice *dev) |
||||
+{ |
||||
+ if (dev->stream_channel) { |
||||
+ return; |
||||
+ } |
||||
+ |
||||
+ SpiceServer* reds = red_char_device_get_server(RED_CHAR_DEVICE(dev)); |
||||
+ |
||||
+ int id = reds_get_free_channel_id(reds, SPICE_CHANNEL_DISPLAY); |
||||
+ g_return_if_fail(id >= 0); |
||||
+ |
||||
+ StreamChannel *stream_channel = stream_channel_new(reds, id); |
||||
+ |
||||
+ dev->stream_channel = stream_channel; |
||||
+ |
||||
+ stream_channel_register_start_cb(stream_channel, stream_device_stream_start, dev); |
||||
+} |
||||
+ |
||||
+static void |
||||
+reset_channels(StreamDevice *dev) |
||||
+{ |
||||
+ if (dev->stream_channel) { |
||||
+ stream_channel_reset(dev->stream_channel); |
||||
+ } |
||||
+} |
||||
+ |
||||
+static void |
||||
stream_device_port_event(RedCharDevice *char_dev, uint8_t event) |
||||
{ |
||||
if (event != SPICE_PORT_EVENT_OPENED && event != SPICE_PORT_EVENT_CLOSED) { |
||||
@@ -275,10 +298,13 @@ stream_device_port_event(RedCharDevice *char_dev, uint8_t event) |
||||
|
||||
// reset device and channel on close/open |
||||
dev->opened = (event == SPICE_PORT_EVENT_OPENED); |
||||
+ if (dev->opened) { |
||||
+ allocate_channels(dev); |
||||
+ } |
||||
dev->hdr_pos = 0; |
||||
dev->has_error = false; |
||||
red_char_device_reset(char_dev); |
||||
- stream_channel_reset(dev->stream_channel); |
||||
+ reset_channels(dev); |
||||
} |
||||
|
||||
static void |
@ -0,0 +1,206 @@
@@ -0,0 +1,206 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
||||
From: Frediano Ziglio <fziglio@redhat.com> |
||||
Date: Wed, 19 Apr 2017 16:24:54 +0100 |
||||
Subject: [spice-server] stream-device: Limit sending queue from guest to |
||||
server |
||||
|
||||
Do not allow the guest to fill host memory. |
||||
Also having a huge queue mainly cause to have a higher video |
||||
latency. |
||||
|
||||
Signed-off-by: Frediano Ziglio <fziglio@redhat.com> |
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com> |
||||
--- |
||||
server/stream-channel.c | 41 ++++++++++++++++++++++++++++++++++++++++- |
||||
server/stream-channel.h | 10 ++++++++++ |
||||
server/stream-device.c | 35 ++++++++++++++++++++++++++++++++++- |
||||
3 files changed, 84 insertions(+), 2 deletions(-) |
||||
|
||||
diff --git a/server/stream-channel.c b/server/stream-channel.c |
||||
index 51b8badf9..ec4bf021d 100644 |
||||
--- a/server/stream-channel.c |
||||
+++ b/server/stream-channel.c |
||||
@@ -71,9 +71,15 @@ struct StreamChannel { |
||||
/* size of the current video stream */ |
||||
unsigned width, height; |
||||
|
||||
+ StreamQueueStat queue_stat; |
||||
+ |
||||
/* callback to notify when a stream should be started or stopped */ |
||||
stream_channel_start_proc start_cb; |
||||
void *start_opaque; |
||||
+ |
||||
+ /* callback to notify when queue statistics changes */ |
||||
+ stream_channel_queue_stat_proc queue_cb; |
||||
+ void *queue_opaque; |
||||
}; |
||||
|
||||
struct StreamChannelClass { |
||||
@@ -98,6 +104,7 @@ typedef struct StreamCreateItem { |
||||
|
||||
typedef struct StreamDataItem { |
||||
RedPipeItem base; |
||||
+ StreamChannel *channel; |
||||
// NOTE: this must be the last field in the structure |
||||
SpiceMsgDisplayStreamData data; |
||||
} StreamDataItem; |
||||
@@ -454,6 +461,27 @@ stream_channel_change_format(StreamChannel *channel, const StreamMsgFormat *fmt) |
||||
red_channel_pipes_add(red_channel, &item->base); |
||||
} |
||||
|
||||
+static inline void |
||||
+stream_channel_update_queue_stat(StreamChannel *channel, |
||||
+ int32_t num_diff, int32_t size_diff) |
||||
+{ |
||||
+ channel->queue_stat.num_items += num_diff; |
||||
+ channel->queue_stat.size += size_diff; |
||||
+ if (channel->queue_cb) { |
||||
+ channel->queue_cb(channel->queue_opaque, &channel->queue_stat, channel); |
||||
+ } |
||||
+} |
||||
+ |
||||
+static void |
||||
+data_item_free(RedPipeItem *base) |
||||
+{ |
||||
+ StreamDataItem *pipe_item = SPICE_UPCAST(StreamDataItem, base); |
||||
+ |
||||
+ stream_channel_update_queue_stat(pipe_item->channel, -1, -pipe_item->data.data_size); |
||||
+ |
||||
+ g_free(pipe_item); |
||||
+} |
||||
+ |
||||
void |
||||
stream_channel_send_data(StreamChannel *channel, const void *data, size_t size, uint32_t mm_time) |
||||
{ |
||||
@@ -467,10 +495,13 @@ stream_channel_send_data(StreamChannel *channel, const void *data, size_t size, |
||||
RedChannel *red_channel = RED_CHANNEL(channel); |
||||
|
||||
StreamDataItem *item = g_malloc(sizeof(*item) + size); |
||||
- red_pipe_item_init(&item->base, RED_PIPE_ITEM_TYPE_STREAM_DATA); |
||||
+ red_pipe_item_init_full(&item->base, RED_PIPE_ITEM_TYPE_STREAM_DATA, |
||||
+ data_item_free); |
||||
item->data.base.id = channel->stream_id; |
||||
item->data.base.multi_media_time = mm_time; |
||||
item->data.data_size = size; |
||||
+ item->channel = channel; |
||||
+ stream_channel_update_queue_stat(channel, 1, size); |
||||
// TODO try to optimize avoiding the copy |
||||
memcpy(item->data.data, data, size); |
||||
red_channel_pipes_add(red_channel, &item->base); |
||||
@@ -485,6 +516,14 @@ stream_channel_register_start_cb(StreamChannel *channel, |
||||
} |
||||
|
||||
void |
||||
+stream_channel_register_queue_stat_cb(StreamChannel *channel, |
||||
+ stream_channel_queue_stat_proc cb, void *opaque) |
||||
+{ |
||||
+ channel->queue_cb = cb; |
||||
+ channel->queue_opaque = opaque; |
||||
+} |
||||
+ |
||||
+void |
||||
stream_channel_reset(StreamChannel *channel) |
||||
{ |
||||
struct { |
||||
diff --git a/server/stream-channel.h b/server/stream-channel.h |
||||
index bd075a951..f961d7157 100644 |
||||
--- a/server/stream-channel.h |
||||
+++ b/server/stream-channel.h |
||||
@@ -67,6 +67,16 @@ typedef void (*stream_channel_start_proc)(void *opaque, struct StreamMsgStartSto |
||||
void stream_channel_register_start_cb(StreamChannel *channel, |
||||
stream_channel_start_proc cb, void *opaque); |
||||
|
||||
+typedef struct StreamQueueStat { |
||||
+ uint32_t num_items; |
||||
+ uint32_t size; |
||||
+} StreamQueueStat; |
||||
+ |
||||
+typedef void (*stream_channel_queue_stat_proc)(void *opaque, const StreamQueueStat *stats, |
||||
+ StreamChannel *channel); |
||||
+void stream_channel_register_queue_stat_cb(StreamChannel *channel, |
||||
+ stream_channel_queue_stat_proc cb, void *opaque); |
||||
+ |
||||
G_END_DECLS |
||||
|
||||
#endif /* STREAM_CHANNEL_H_ */ |
||||
diff --git a/server/stream-device.c b/server/stream-device.c |
||||
index ae108788b..f87538d49 100644 |
||||
--- a/server/stream-device.c |
||||
+++ b/server/stream-device.c |
||||
@@ -44,6 +44,7 @@ struct StreamDevice { |
||||
uint8_t hdr_pos; |
||||
bool has_error; |
||||
bool opened; |
||||
+ bool flow_stopped; |
||||
StreamChannel *stream_channel; |
||||
}; |
||||
|
||||
@@ -72,7 +73,7 @@ stream_device_read_msg_from_dev(RedCharDevice *self, SpiceCharDeviceInstance *si |
||||
int n; |
||||
bool handled = false; |
||||
|
||||
- if (dev->has_error || !dev->stream_channel) { |
||||
+ if (dev->has_error || dev->flow_stopped || !dev->stream_channel) { |
||||
return NULL; |
||||
} |
||||
|
||||
@@ -181,6 +182,9 @@ handle_msg_data(StreamDevice *dev, SpiceCharDeviceInstance *sin) |
||||
if (n <= 0) { |
||||
break; |
||||
} |
||||
+ // TODO collect all message ?? |
||||
+ // up: we send a single frame together |
||||
+ // down: guest can cause a crash |
||||
stream_channel_send_data(dev->stream_channel, buf, n, reds_get_mm_time()); |
||||
dev->hdr.size -= n; |
||||
} |
||||
@@ -233,6 +237,33 @@ stream_device_stream_start(void *opaque, StreamMsgStartStop *start, |
||||
red_char_device_write_buffer_add(char_dev, buf); |
||||
} |
||||
|
||||
+static void |
||||
+stream_device_stream_queue_stat(void *opaque, const StreamQueueStat *stats G_GNUC_UNUSED, |
||||
+ StreamChannel *stream_channel G_GNUC_UNUSED) |
||||
+{ |
||||
+ StreamDevice *dev = (StreamDevice *) opaque; |
||||
+ |
||||
+ if (!dev->opened) { |
||||
+ return; |
||||
+ } |
||||
+ |
||||
+ // very easy control flow... if any data stop |
||||
+ // this seems a very small queue but as we use tcp |
||||
+ // there's already that queue |
||||
+ if (stats->num_items) { |
||||
+ dev->flow_stopped = true; |
||||
+ return; |
||||
+ } |
||||
+ |
||||
+ if (dev->flow_stopped) { |
||||
+ dev->flow_stopped = false; |
||||
+ // TODO resume flow... |
||||
+ // avoid recursion if we need to call get data from data handling from |
||||
+ // data handling |
||||
+ red_char_device_wakeup(&dev->parent); |
||||
+ } |
||||
+} |
||||
+ |
||||
RedCharDevice * |
||||
stream_device_connect(RedsState *reds, SpiceCharDeviceInstance *sin) |
||||
{ |
||||
@@ -277,6 +308,7 @@ allocate_channels(StreamDevice *dev) |
||||
dev->stream_channel = stream_channel; |
||||
|
||||
stream_channel_register_start_cb(stream_channel, stream_device_stream_start, dev); |
||||
+ stream_channel_register_queue_stat_cb(stream_channel, stream_device_stream_queue_stat, dev); |
||||
} |
||||
|
||||
static void |
||||
@@ -303,6 +335,7 @@ stream_device_port_event(RedCharDevice *char_dev, uint8_t event) |
||||
} |
||||
dev->hdr_pos = 0; |
||||
dev->has_error = false; |
||||
+ dev->flow_stopped = false; |
||||
red_char_device_reset(char_dev); |
||||
reset_channels(dev); |
||||
} |
@ -0,0 +1,65 @@
@@ -0,0 +1,65 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
||||
From: Frediano Ziglio <fziglio@redhat.com> |
||||
Date: Wed, 7 Jun 2017 13:13:21 +0100 |
||||
Subject: [spice-server] stream-channel: Activate streaming report from client |
||||
|
||||
Setting the capability is not enough, each stream must be enabled |
||||
so do so if client support them. |
||||
|
||||
Signed-off-by: Frediano Ziglio <fziglio@redhat.com> |
||||
Acked-by: Jonathon Jongsma <jjongsma@redhat.com> |
||||
--- |
||||
server/stream-channel.c | 19 +++++++++++++++++++ |
||||
1 file changed, 19 insertions(+) |
||||
|
||||
diff --git a/server/stream-channel.c b/server/stream-channel.c |
||||
index ec4bf021d..7e15dd363 100644 |
||||
--- a/server/stream-channel.c |
||||
+++ b/server/stream-channel.c |
||||
@@ -27,6 +27,7 @@ |
||||
#include "reds.h" |
||||
#include "common-graphics-channel.h" |
||||
#include "display-limits.h" |
||||
+#include "stream.h" // TODO remove, put common stuff |
||||
|
||||
#define TYPE_STREAM_CHANNEL_CLIENT stream_channel_client_get_type() |
||||
|
||||
@@ -95,6 +96,7 @@ enum { |
||||
RED_PIPE_ITEM_TYPE_STREAM_CREATE, |
||||
RED_PIPE_ITEM_TYPE_STREAM_DATA, |
||||
RED_PIPE_ITEM_TYPE_STREAM_DESTROY, |
||||
+ RED_PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT, |
||||
}; |
||||
|
||||
typedef struct StreamCreateItem { |
||||
@@ -230,6 +232,20 @@ stream_channel_send_item(RedChannelClient *rcc, RedPipeItem *pipe_item) |
||||
spice_marshall_msg_display_stream_create(m, &item->stream_create); |
||||
break; |
||||
} |
||||
+ case RED_PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT: { |
||||
+ if (client->stream_id < 0 |
||||
+ || !red_channel_client_test_remote_cap(rcc, SPICE_DISPLAY_CAP_STREAM_REPORT)) { |
||||
+ return; |
||||
+ } |
||||
+ SpiceMsgDisplayStreamActivateReport msg; |
||||
+ msg.stream_id = client->stream_id; |
||||
+ msg.unique_id = 1; // TODO useful ? |
||||
+ msg.max_window_size = RED_STREAM_CLIENT_REPORT_WINDOW; |
||||
+ msg.timeout_ms = RED_STREAM_CLIENT_REPORT_TIMEOUT; |
||||
+ red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_ACTIVATE_REPORT); |
||||
+ spice_marshall_msg_display_stream_activate_report(m, &msg); |
||||
+ break; |
||||
+ } |
||||
case RED_PIPE_ITEM_TYPE_STREAM_DATA: { |
||||
StreamDataItem *item = SPICE_UPCAST(StreamDataItem, pipe_item); |
||||
red_channel_client_init_send_data(rcc, SPICE_MSG_DISPLAY_STREAM_DATA); |
||||
@@ -459,6 +475,9 @@ stream_channel_change_format(StreamChannel *channel, const StreamMsgFormat *fmt) |
||||
item->stream_create.dest = (SpiceRect) { 0, 0, fmt->width, fmt->height }; |
||||
item->stream_create.clip = (SpiceClip) { SPICE_CLIP_TYPE_NONE, NULL }; |
||||
red_channel_pipes_add(red_channel, &item->base); |
||||
+ |
||||
+ // activate stream report if possible |
||||
+ red_channel_pipes_add_type(red_channel, RED_PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT); |
||||
} |
||||
|
||||
static inline void |
Loading…
Reference in new issue