[Spice-devel] [RFC PATCH spice-server v5 09/22] stream-device: Handle streaming data from device to channel
Frediano Ziglio
fziglio at redhat.com
Wed Aug 30 15:28:26 UTC 2017
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 at redhat.com>
Acked-by: Jonathon Jongsma <jjongsma at redhat.com>
---
server/stream-channel.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++++
server/stream-channel.h | 8 ++++
server/stream-device.c | 4 +-
3 files changed, 123 insertions(+), 2 deletions(-)
diff --git a/server/stream-channel.c b/server/stream-channel.c
index 2a454c2f..27655991 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
@@ -81,6 +105,7 @@ stream_channel_client_class_init(StreamChannelClientClass *klass)
static void
stream_channel_client_init(StreamChannelClient *client)
{
+ client->stream_id = -1;
}
static void
@@ -122,6 +147,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: {
@@ -147,6 +173,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");
}
@@ -255,4 +307,65 @@ stream_channel_class_init(StreamChannelClass *klass)
static void
stream_channel_init(StreamChannel *channel)
{
+ channel->stream_id = -1;
+}
+
+static RedPipeItem *
+pipe_item_new_ref(G_GNUC_UNUSED RedChannelClient *rcc, void *data, G_GNUC_UNUSED int num)
+{
+ RedPipeItem *item = data;
+ red_pipe_item_ref(item);
+ return item;
+}
+
+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 = spice_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_new_add(red_channel, pipe_item_new_ref, item);
+ red_pipe_item_unref(&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 = spice_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_new_add(red_channel, pipe_item_new_ref, item);
+ red_pipe_item_unref(&item->base);
}
diff --git a/server/stream-channel.h b/server/stream-channel.h
index e50e17e9..156a75d3 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 0c9173ae..6e78b1a9 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;
}
--
2.13.5
More information about the Spice-devel
mailing list