1 /* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3    Copyright (C) 2017 Red Hat, Inc.
4 
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    This library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 */
18 #include <config.h>
19 
20 #include <common/generated_server_marshallers.h>
21 #include <common/recorder.h>
22 #include <spice/stream-device.h>
23 
24 #include "red-channel-client.h"
25 #include "stream-channel.h"
26 #include "reds.h"
27 #include "common-graphics-channel.h"
28 #include "display-limits.h"
29 #include "video-stream.h" // TODO remove, put common stuff
30 
31 /* we need to inherit from CommonGraphicsChannelClient
32  * to get buffer handling */
33 class StreamChannelClient final: public CommonGraphicsChannelClient
34 {
35 protected:
36     ~StreamChannelClient() override;
37 public:
38     using CommonGraphicsChannelClient::CommonGraphicsChannelClient;
39 
40     /* current video stream id, <0 if not initialized or
41      * we are not sending a stream */
42     int stream_id = -1;
43 private:
get_channel()44     StreamChannel* get_channel()
45     {
46         return static_cast<StreamChannel*>(CommonGraphicsChannelClient::get_channel());
47     }
48     /* Array with SPICE_VIDEO_CODEC_TYPE_ENUM_END elements, with the client
49      * preference order (index) as value */
50     GArray *client_preferred_video_codecs;
51     bool handle_preferred_video_codec_type(SpiceMsgcDisplayPreferredVideoCodecType *msg);
52     void marshall_monitors_config(StreamChannel *channel, SpiceMarshaller *m);
53     void fill_base(SpiceMarshaller *m, const StreamChannel *channel);
54     void on_disconnect() override;
55     bool handle_message(uint16_t type, uint32_t size, void *msg) override;
56     void send_item(RedPipeItem *pipe_item) override;
57 };
58 
59 enum {
60     RED_PIPE_ITEM_TYPE_SURFACE_CREATE = RED_PIPE_ITEM_TYPE_COMMON_LAST,
61     RED_PIPE_ITEM_TYPE_SURFACE_DESTROY,
62     RED_PIPE_ITEM_TYPE_FILL_SURFACE,
63     RED_PIPE_ITEM_TYPE_STREAM_CREATE,
64     RED_PIPE_ITEM_TYPE_STREAM_DATA,
65     RED_PIPE_ITEM_TYPE_STREAM_DESTROY,
66     RED_PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT,
67     RED_PIPE_ITEM_TYPE_MONITORS_CONFIG,
68 };
69 
70 struct StreamCreateItem: public RedPipeItemNum<RED_PIPE_ITEM_TYPE_STREAM_CREATE> {
71     SpiceMsgDisplayStreamCreate stream_create;
72 };
73 
74 struct StreamDataItem: public RedPipeItemNum<RED_PIPE_ITEM_TYPE_STREAM_DATA> {
75     ~StreamDataItem() override;
76 
77     StreamChannel *channel;
78     // NOTE: this must be the last field in the structure
79     SpiceMsgDisplayStreamData data;
80 };
81 
82 #define PRIMARY_SURFACE_ID 0
83 
84 RECORDER(stream_channel_data, 32, "Stream channel data packet");
85 
~StreamChannelClient()86 StreamChannelClient::~StreamChannelClient()
87 {
88     g_clear_pointer(&client_preferred_video_codecs, g_array_unref);
89 }
90 
request_new_stream(StreamMsgStartStop * start)91 void StreamChannel::request_new_stream(StreamMsgStartStop *start)
92 {
93     if (start_cb) {
94         start_cb(start_opaque, start, this);
95     }
96 }
97 
98 void
on_disconnect()99 StreamChannelClient::on_disconnect()
100 {
101     StreamChannel *channel = get_channel();
102 
103     // if there are still some client connected keep streaming
104     // TODO, maybe would be worth sending new codecs if they are better
105     if (channel->is_connected()) {
106         return;
107     }
108 
109     channel->stream_id = -1;
110     channel->width = 0;
111     channel->height = 0;
112 
113     // send stream stop to device
114     StreamMsgStartStop stop = { 0, };
115     get_channel()->request_new_stream(&stop);
116 }
117 
118 static StreamChannelClient*
stream_channel_client_new(StreamChannel * channel,RedClient * client,RedStream * stream,int mig_target,RedChannelCapabilities * caps)119 stream_channel_client_new(StreamChannel *channel, RedClient *client, RedStream *stream,
120                           int mig_target, RedChannelCapabilities *caps)
121 {
122     auto rcc =
123         red::make_shared<StreamChannelClient>(channel, client, stream, caps);
124     if (!rcc->init()) {
125         return nullptr;
126     }
127     return rcc.get();
128 }
129 
130 void
fill_base(SpiceMarshaller * m,const StreamChannel * channel)131 StreamChannelClient::fill_base(SpiceMarshaller *m, const StreamChannel *channel)
132 {
133     SpiceMsgDisplayBase base;
134 
135     base.surface_id = PRIMARY_SURFACE_ID;
136     base.box = (SpiceRect) { 0, 0, channel->width, channel->height };
137     base.clip = (SpiceClip) { SPICE_CLIP_TYPE_NONE, nullptr };
138 
139     spice_marshall_DisplayBase(m, &base);
140 }
141 
142 void
marshall_monitors_config(StreamChannel * channel,SpiceMarshaller * m)143 StreamChannelClient::marshall_monitors_config(StreamChannel *channel, SpiceMarshaller *m)
144 {
145     struct {
146         SpiceMsgDisplayMonitorsConfig config;
147         SpiceHead head;
148     } msg = {
149         { 1, 1, },
150         {
151             // monitor ID. These IDs are allocated per channel starting from 0
152             0,
153             PRIMARY_SURFACE_ID,
154             channel->width, channel->height,
155             0, 0,
156             0 // flags
157         }
158     };
159 
160     init_send_data(SPICE_MSG_DISPLAY_MONITORS_CONFIG);
161     spice_marshall_msg_display_monitors_config(m, &msg.config);
162 }
163 
send_item(RedPipeItem * pipe_item)164 void StreamChannelClient::send_item(RedPipeItem *pipe_item)
165 {
166     SpiceMarshaller *m = get_marshaller();
167     StreamChannel *channel = get_channel();
168 
169     switch (pipe_item->type) {
170     case RED_PIPE_ITEM_TYPE_SURFACE_CREATE: {
171         init_send_data(SPICE_MSG_DISPLAY_SURFACE_CREATE);
172         SpiceMsgSurfaceCreate surface_create = {
173             PRIMARY_SURFACE_ID,
174             channel->width, channel->height,
175             SPICE_SURFACE_FMT_32_xRGB, SPICE_SURFACE_FLAGS_PRIMARY
176         };
177 
178         // give an hint to client that we are sending just streaming
179         // see spice.proto for capability check here
180         if (test_remote_cap(SPICE_DISPLAY_CAP_MULTI_CODEC)) {
181             surface_create.flags |= SPICE_SURFACE_FLAGS_STREAMING_MODE;
182         }
183 
184         spice_marshall_msg_display_surface_create(m, &surface_create);
185         break;
186     }
187     case RED_PIPE_ITEM_TYPE_MONITORS_CONFIG:
188         if (!test_remote_cap(SPICE_DISPLAY_CAP_MONITORS_CONFIG)) {
189             return;
190         }
191         marshall_monitors_config(channel, m);
192         break;
193     case RED_PIPE_ITEM_TYPE_SURFACE_DESTROY: {
194         init_send_data(SPICE_MSG_DISPLAY_SURFACE_DESTROY);
195         SpiceMsgSurfaceDestroy surface_destroy = { PRIMARY_SURFACE_ID };
196         spice_marshall_msg_display_surface_destroy(m, &surface_destroy);
197         break;
198     }
199     case RED_PIPE_ITEM_TYPE_FILL_SURFACE: {
200         init_send_data(SPICE_MSG_DISPLAY_DRAW_FILL);
201 
202         fill_base(m, channel);
203 
204         SpiceFill fill;
205         fill.brush = (SpiceBrush) { SPICE_BRUSH_TYPE_SOLID, { .color = 0 } };
206         fill.rop_descriptor = SPICE_ROPD_OP_PUT;
207         fill.mask = (SpiceQMask) { 0, { 0, 0 }, nullptr };
208         SpiceMarshaller *brush_pat_out, *mask_bitmap_out;
209         spice_marshall_Fill(m, &fill, &brush_pat_out, &mask_bitmap_out);
210         break;
211     }
212     case RED_PIPE_ITEM_TYPE_STREAM_CREATE: {
213         auto item = static_cast<StreamCreateItem*>(pipe_item);
214         stream_id = item->stream_create.id;
215         init_send_data(SPICE_MSG_DISPLAY_STREAM_CREATE);
216         spice_marshall_msg_display_stream_create(m, &item->stream_create);
217         break;
218     }
219     case RED_PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT: {
220         if (stream_id < 0
221             || !test_remote_cap(SPICE_DISPLAY_CAP_STREAM_REPORT)) {
222             return;
223         }
224         SpiceMsgDisplayStreamActivateReport msg;
225         msg.stream_id = stream_id;
226         msg.unique_id = 1; // TODO useful ?
227         msg.max_window_size = RED_STREAM_CLIENT_REPORT_WINDOW;
228         msg.timeout_ms = RED_STREAM_CLIENT_REPORT_TIMEOUT;
229         init_send_data(SPICE_MSG_DISPLAY_STREAM_ACTIVATE_REPORT);
230         spice_marshall_msg_display_stream_activate_report(m, &msg);
231         break;
232     }
233     case RED_PIPE_ITEM_TYPE_STREAM_DATA: {
234         auto item = static_cast<StreamDataItem*>(pipe_item);
235         init_send_data(SPICE_MSG_DISPLAY_STREAM_DATA);
236         spice_marshall_msg_display_stream_data(m, &item->data);
237         pipe_item->add_to_marshaller(m, item->data.data, item->data.data_size);
238         record(stream_channel_data, "Stream data packet size %u mm_time %u",
239                item->data.data_size, item->data.base.multi_media_time);
240         break;
241     }
242     case RED_PIPE_ITEM_TYPE_STREAM_DESTROY: {
243         if (stream_id < 0) {
244             return;
245         }
246         SpiceMsgDisplayStreamDestroy stream_destroy = { stream_id };
247         init_send_data(SPICE_MSG_DISPLAY_STREAM_DESTROY);
248         spice_marshall_msg_display_stream_destroy(m, &stream_destroy);
249         stream_id = -1;
250         break;
251     }
252     default:
253         spice_error("invalid pipe item type");
254     }
255 
256     begin_send_message();
257 }
258 
handle_message(uint16_t type,uint32_t size,void * msg)259 bool StreamChannelClient::handle_message(uint16_t type, uint32_t size, void *msg)
260 {
261     switch (type) {
262     case SPICE_MSGC_DISPLAY_INIT:
263     case SPICE_MSGC_DISPLAY_PREFERRED_COMPRESSION:
264         return true;
265     case SPICE_MSGC_DISPLAY_STREAM_REPORT:
266         /* TODO these will help tune the streaming reducing/increasing quality */
267         return true;
268     case SPICE_MSGC_DISPLAY_GL_DRAW_DONE:
269         /* client should not send this message */
270         return false;
271     case SPICE_MSGC_DISPLAY_PREFERRED_VIDEO_CODEC_TYPE:
272         return handle_preferred_video_codec_type(
273             (SpiceMsgcDisplayPreferredVideoCodecType *)msg);
274     default:
275         return CommonGraphicsChannelClient::handle_message(type, size, msg);
276     }
277 }
278 
279 
280 red::shared_ptr<StreamChannel>
stream_channel_new(RedsState * server,uint32_t id)281 stream_channel_new(RedsState *server, uint32_t id)
282 {
283     // TODO this id should be after all qxl devices
284     return red::make_shared<StreamChannel>(server, id);
285 }
286 
287 #define MAX_SUPPORTED_CODECS SPICE_VIDEO_CODEC_TYPE_ENUM_END
288 
289 // find common codecs supported by all clients
290 static uint8_t
stream_channel_get_supported_codecs(StreamChannel * channel,uint8_t * out_codecs)291 stream_channel_get_supported_codecs(StreamChannel *channel, uint8_t *out_codecs)
292 {
293     RedChannelClient *rcc;
294     int codec;
295 
296     static const uint16_t codec2cap[] = {
297         0, // invalid
298         SPICE_DISPLAY_CAP_CODEC_MJPEG,
299         SPICE_DISPLAY_CAP_CODEC_VP8,
300         SPICE_DISPLAY_CAP_CODEC_H264,
301         SPICE_DISPLAY_CAP_CODEC_VP9,
302         SPICE_DISPLAY_CAP_CODEC_H265,
303     };
304 
305     bool supported[SPICE_N_ELEMENTS(codec2cap)];
306 
307     for (codec = 0; codec < SPICE_N_ELEMENTS(codec2cap); ++codec) {
308         supported[codec] = true;
309     }
310 
311     FOREACH_CLIENT(channel, rcc) {
312         for (codec = 1; codec < SPICE_N_ELEMENTS(codec2cap); ++codec) {
313             // if do not support codec delete from list
314             if (!rcc->test_remote_cap(codec2cap[codec])) {
315                 supported[codec] = false;
316             }
317         }
318     }
319 
320     // surely mjpeg is supported
321     supported[SPICE_VIDEO_CODEC_TYPE_MJPEG] = true;
322 
323     int num = 0;
324     for (codec = 1; codec < SPICE_N_ELEMENTS(codec2cap); ++codec) {
325         if (supported[codec]) {
326             out_codecs[num++] = codec;
327         }
328     }
329 
330     return num;
331 }
332 
333 bool
handle_preferred_video_codec_type(SpiceMsgcDisplayPreferredVideoCodecType * msg)334 StreamChannelClient::handle_preferred_video_codec_type(SpiceMsgcDisplayPreferredVideoCodecType *msg)
335 {
336     if (msg->num_of_codecs == 0) {
337         return true;
338     }
339 
340     g_clear_pointer(&client_preferred_video_codecs, g_array_unref);
341     client_preferred_video_codecs = video_stream_parse_preferred_codecs(msg);
342 
343     return true;
344 }
345 
on_connect(RedClient * red_client,RedStream * stream,int migration,RedChannelCapabilities * caps)346 void StreamChannel::on_connect(RedClient *red_client, RedStream *stream,
347                                int migration, RedChannelCapabilities *caps)
348 {
349     StreamChannelClient *client;
350     struct {
351         StreamMsgStartStop base;
352         uint8_t codecs_buffer[MAX_SUPPORTED_CODECS];
353     } start_msg;
354     StreamMsgStartStop *const start = &start_msg.base;
355 
356     spice_return_if_fail(stream != nullptr);
357 
358     client = stream_channel_client_new(this, red_client, stream, migration, caps);
359     if (client == nullptr) {
360         return;
361     }
362 
363     // request new stream
364     start->num_codecs = stream_channel_get_supported_codecs(this, start->codecs);
365     // send in any case, even if list is not changed
366     // notify device about changes
367     request_new_stream(start);
368 
369 
370     // see guest_set_client_capabilities
371     RedChannelClient *rcc = client;
372     rcc->push_set_ack();
373 
374     // TODO what should happen on migration, dcc return if on migration wait ??
375     rcc->ack_zero_messages_window();
376 
377     // "emulate" dcc_start
378     rcc->pipe_add_empty_msg(SPICE_MSG_DISPLAY_INVAL_ALL_PALETTES);
379 
380     // only if "surface"
381     if (width == 0 || height == 0) {
382         return;
383     }
384 
385     // pass proper data
386     rcc->pipe_add_type(RED_PIPE_ITEM_TYPE_SURFACE_CREATE);
387     rcc->pipe_add_type(RED_PIPE_ITEM_TYPE_MONITORS_CONFIG);
388     // surface data
389     rcc->pipe_add_type(RED_PIPE_ITEM_TYPE_FILL_SURFACE);
390     // TODO monitor configs ??
391     rcc->pipe_add_empty_msg(SPICE_MSG_DISPLAY_MARK);
392 }
393 
StreamChannel(RedsState * reds,uint32_t id)394 StreamChannel::StreamChannel(RedsState *reds, uint32_t id):
395     RedChannel(reds, SPICE_CHANNEL_DISPLAY, id, RedChannel::HandleAcks)
396 {
397     set_cap(SPICE_DISPLAY_CAP_MONITORS_CONFIG);
398     set_cap(SPICE_DISPLAY_CAP_STREAM_REPORT);
399     set_cap(SPICE_DISPLAY_CAP_PREF_VIDEO_CODEC_TYPE);
400 
401     reds_register_channel(reds, this);
402 }
403 
404 void
change_format(const StreamMsgFormat * fmt)405 StreamChannel::change_format(const StreamMsgFormat *fmt)
406 {
407     // send destroy old stream
408     pipes_add_type(RED_PIPE_ITEM_TYPE_STREAM_DESTROY);
409 
410     // send new create surface if required
411     if (width != fmt->width || height != fmt->height) {
412         if (width != 0 && height != 0) {
413             pipes_add_type(RED_PIPE_ITEM_TYPE_SURFACE_DESTROY);
414         }
415         width = fmt->width;
416         height = fmt->height;
417         pipes_add_type(RED_PIPE_ITEM_TYPE_SURFACE_CREATE);
418         pipes_add_type(RED_PIPE_ITEM_TYPE_MONITORS_CONFIG);
419         // TODO monitors config ??
420         pipes_add_empty_msg(SPICE_MSG_DISPLAY_MARK);
421     }
422 
423     // allocate a new stream id
424     stream_id = (stream_id + 1) % NUM_STREAMS;
425 
426     // send create stream
427     auto item = red::make_shared<StreamCreateItem>();
428     item->stream_create.id = stream_id;
429     item->stream_create.flags = SPICE_STREAM_FLAGS_TOP_DOWN;
430     item->stream_create.codec_type = fmt->codec;
431     item->stream_create.stream_width = fmt->width;
432     item->stream_create.stream_height = fmt->height;
433     item->stream_create.src_width = fmt->width;
434     item->stream_create.src_height = fmt->height;
435     item->stream_create.dest = (SpiceRect) { 0, 0, fmt->width, fmt->height };
436     item->stream_create.clip = (SpiceClip) { SPICE_CLIP_TYPE_NONE, nullptr };
437     pipes_add(item);
438 
439     // activate stream report if possible
440     pipes_add_type(RED_PIPE_ITEM_TYPE_STREAM_ACTIVATE_REPORT);
441 }
442 
443 inline void
update_queue_stat(int32_t num_diff,int32_t size_diff)444 StreamChannel::update_queue_stat(int32_t num_diff, int32_t size_diff)
445 {
446     queue_stat.num_items += num_diff;
447     queue_stat.size += size_diff;
448     if (queue_cb) {
449         queue_cb(queue_opaque, &queue_stat, this);
450     }
451 }
452 
~StreamDataItem()453 StreamDataItem::~StreamDataItem()
454 {
455     channel->update_queue_stat(-1, -data.data_size);
456 }
457 
458 void
send_data(const void * data,size_t size,uint32_t mm_time)459 StreamChannel::send_data(const void *data, size_t size, uint32_t mm_time)
460 {
461     if (stream_id < 0) {
462         // this condition can happen if the guest didn't handle
463         // the format stop that we send so think the stream is still
464         // started
465         return;
466     }
467 
468     auto item = new (size) StreamDataItem();
469     item->data.base.id = stream_id;
470     item->data.base.multi_media_time = mm_time;
471     item->data.data_size = size;
472     item->channel = this;
473     update_queue_stat(1, size);
474     // TODO try to optimize avoiding the copy
475     memcpy(item->data.data, data, size);
476     pipes_add(red::shared_ptr<StreamDataItem>(item));
477 }
478 
479 void
register_start_cb(stream_channel_start_proc cb,void * opaque)480 StreamChannel::register_start_cb(stream_channel_start_proc cb, void *opaque)
481 {
482     start_cb = cb;
483     start_opaque = opaque;
484 }
485 
486 void
register_queue_stat_cb(stream_channel_queue_stat_proc cb,void * opaque)487 StreamChannel::register_queue_stat_cb(stream_channel_queue_stat_proc cb, void *opaque)
488 {
489     queue_cb = cb;
490     queue_opaque = opaque;
491 }
492 
493 void
reset()494 StreamChannel::reset()
495 {
496     struct {
497         StreamMsgStartStop base;
498         uint8_t codecs_buffer[MAX_SUPPORTED_CODECS];
499     } start_msg;
500     StreamMsgStartStop *const start = &start_msg.base;
501 
502     // send destroy old stream
503     pipes_add_type(RED_PIPE_ITEM_TYPE_STREAM_DESTROY);
504 
505     // destroy display surface
506     if (width != 0 && height != 0) {
507         pipes_add_type(RED_PIPE_ITEM_TYPE_SURFACE_DESTROY);
508     }
509 
510     stream_id = -1;
511     width = 0;
512     height = 0;
513 
514     if (!is_connected()) {
515         return;
516     }
517 
518     // try to request a new stream, this should start a new stream
519     // if the guest is connected to the device and a client is already connected
520     start->num_codecs = stream_channel_get_supported_codecs(this, start->codecs);
521     // send in any case, even if list is not changed
522     // notify device about changes
523     request_new_stream(start);
524 }
525