virtbuilder_pel7x64builder0
6 years ago
21 changed files with 2649 additions and 86 deletions
@ -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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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 @@ |
|||||||
|
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