blob: 6aa4ec8af16e88797a66eb2db173bdef35ecaa7a [file] [log] [blame]
Tomohito Esakif709d222018-01-24 17:08:02 +09001/*
2 * Copyright © 2018 Renesas Electronics Corp.
3 *
4 * Based on vaapi-recorder by:
5 * Copyright (c) 2012 Intel Corporation. All Rights Reserved.
6 * Copyright © 2013 Intel Corporation
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining
9 * a copy of this software and associated documentation files (the
10 * "Software"), to deal in the Software without restriction, including
11 * without limitation the rights to use, copy, modify, merge, publish,
12 * distribute, sublicense, and/or sell copies of the Software, and to
13 * permit persons to whom the Software is furnished to do so, subject to
14 * the following conditions:
15 *
16 * The above copyright notice and this permission notice (including the
17 * next paragraph) shall be included in all copies or substantial
18 * portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
24 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
25 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
26 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 * SOFTWARE.
28 *
29 * Authors: IGEL Co., Ltd.
30 */
31
32#include "config.h"
33
34#include <stdint.h>
35#include <string.h>
36#include <unistd.h>
37#include <sys/types.h>
38#include <fcntl.h>
Tomohito Esakif709d222018-01-24 17:08:02 +090039
40#include <gst/gst.h>
41#include <gst/allocators/gstdmabuf.h>
42#include <gst/app/gstappsrc.h>
43#include <gst/video/gstvideometa.h>
44
Marius Vladc4076ef2020-08-28 16:59:36 +030045#include <libweston/remoting-plugin.h>
Pekka Paalanen75710272019-03-29 16:39:12 +020046#include <libweston/backend-drm.h>
Tomohito Esakif709d222018-01-24 17:08:02 +090047#include "shared/helpers.h"
48#include "shared/timespec-util.h"
Pekka Paalanen4b301fe2021-02-04 17:39:45 +020049#include "shared/weston-drm-fourcc.h"
Marius Vlade41c1bf2019-07-16 23:11:25 +030050#include "backend.h"
Marius Vlad56f3a682019-07-10 14:48:39 +030051#include "libweston-internal.h"
Tomohito Esakif709d222018-01-24 17:08:02 +090052
53#define MAX_RETRY_COUNT 3
54
55struct weston_remoting {
56 struct weston_compositor *compositor;
57 struct wl_list output_list;
58 struct wl_listener destroy_listener;
59 const struct weston_drm_virtual_output_api *virtual_output_api;
60
61 GstAllocator *allocator;
62};
63
64struct remoted_gstpipe {
65 int readfd;
66 int writefd;
67 struct wl_event_source *source;
68};
69
70/* supported gbm format list */
71struct remoted_output_support_gbm_format {
Daniel Stone4b8b60e2019-11-11 09:40:27 +000072 /* GBM_FORMAT_* tokens are strictly aliased with DRM_FORMAT_*, so we
73 * use the latter to avoid a dependency on GBM */
Tomohito Esakif709d222018-01-24 17:08:02 +090074 uint32_t gbm_format;
75 const char *gst_format_string;
76 GstVideoFormat gst_video_format;
77};
78
79static const struct remoted_output_support_gbm_format supported_formats[] = {
80 {
Daniel Stone4b8b60e2019-11-11 09:40:27 +000081 .gbm_format = DRM_FORMAT_XRGB8888,
Tomohito Esakif709d222018-01-24 17:08:02 +090082 .gst_format_string = "BGRx",
83 .gst_video_format = GST_VIDEO_FORMAT_BGRx,
84 }, {
Daniel Stone4b8b60e2019-11-11 09:40:27 +000085 .gbm_format = DRM_FORMAT_RGB565,
Tomohito Esakif709d222018-01-24 17:08:02 +090086 .gst_format_string = "RGB16",
87 .gst_video_format = GST_VIDEO_FORMAT_RGB16,
88 }, {
Daniel Stone4b8b60e2019-11-11 09:40:27 +000089 .gbm_format = DRM_FORMAT_XRGB2101010,
Tomohito Esakif709d222018-01-24 17:08:02 +090090 .gst_format_string = "r210",
91 .gst_video_format = GST_VIDEO_FORMAT_r210,
92 }
93};
94
95struct remoted_output {
96 struct weston_output *output;
97 void (*saved_destroy)(struct weston_output *output);
98 int (*saved_enable)(struct weston_output *output);
99 int (*saved_disable)(struct weston_output *output);
Antonio Borneoc90fccc2019-06-30 15:51:10 +0200100 int (*saved_start_repaint_loop)(struct weston_output *output);
Tomohito Esakif709d222018-01-24 17:08:02 +0900101
102 char *host;
103 int port;
Tomohito Esaki3ea03bb2019-05-22 12:21:32 +0900104 char *gst_pipeline;
Tomohito Esakif709d222018-01-24 17:08:02 +0900105 const struct remoted_output_support_gbm_format *format;
106
107 struct weston_head *head;
108
109 struct weston_remoting *remoting;
110 struct wl_event_source *finish_frame_timer;
111 struct wl_list link;
112 bool submitted_frame;
113 int fence_sync_fd;
114 struct wl_event_source *fence_sync_event_source;
115
116 GstElement *pipeline;
117 GstAppSrc *appsrc;
118 GstBus *bus;
119 struct remoted_gstpipe gstpipe;
120 GstClockTime start_time;
121 int retry_count;
Marius Vlade0b937a2020-08-27 14:14:27 +0300122 enum dpms_enum dpms;
Tomohito Esakif709d222018-01-24 17:08:02 +0900123};
124
125struct mem_free_cb_data {
126 struct remoted_output *output;
127 struct drm_fb *output_buffer;
128};
129
130struct gst_frame_buffer_data {
131 struct remoted_output *output;
132 GstBuffer *buffer;
133};
134
135/* message type for pipe */
136#define GSTPIPE_MSG_BUS_SYNC 1
137#define GSTPIPE_MSG_BUFFER_RELEASE 2
138
139struct gstpipe_msg_data {
140 int type;
141 void *data;
142};
143
144static int
145remoting_gst_init(struct weston_remoting *remoting)
146{
147 GError *err = NULL;
148
149 if (!gst_init_check(NULL, NULL, &err)) {
150 weston_log("GStreamer initialization error: %s\n",
151 err->message);
152 g_error_free(err);
153 return -1;
154 }
155
156 remoting->allocator = gst_dmabuf_allocator_new();
157
158 return 0;
159}
160
161static void
162remoting_gst_deinit(struct weston_remoting *remoting)
163{
164 gst_object_unref(remoting->allocator);
165}
166
167static GstBusSyncReply
168remoting_gst_bus_sync_handler(GstBus *bus, GstMessage *message,
169 gpointer user_data)
170{
171 struct remoted_gstpipe *pipe = user_data;
172 struct gstpipe_msg_data msg = {
173 .type = GSTPIPE_MSG_BUS_SYNC,
174 .data = NULL
175 };
176 ssize_t ret;
177
178 ret = write(pipe->writefd, &msg, sizeof(msg));
179 if (ret != sizeof(msg))
180 weston_log("ERROR: failed to write, ret=%zd, errno=%d\n",
181 ret, errno);
182
183 return GST_BUS_PASS;
184}
185
186static int
187remoting_gst_pipeline_init(struct remoted_output *output)
188{
Tomohito Esakif709d222018-01-24 17:08:02 +0900189 GstCaps *caps;
190 GError *err = NULL;
191 GstStateChangeReturn ret;
192 struct weston_mode *mode = output->output->current_mode;
193
Tomohito Esaki3ea03bb2019-05-22 12:21:32 +0900194 if (!output->gst_pipeline) {
195 char pipeline_str[1024];
196 /* TODO: use encodebin instead of jpegenc */
197 snprintf(pipeline_str, sizeof(pipeline_str),
198 "rtpbin name=rtpbin "
199 "appsrc name=src ! videoconvert ! "
200 "video/x-raw,format=I420 ! jpegenc ! rtpjpegpay ! "
201 "rtpbin.send_rtp_sink_0 "
202 "rtpbin.send_rtp_src_0 ! "
203 "udpsink name=sink host=%s port=%d "
204 "rtpbin.send_rtcp_src_0 ! "
205 "udpsink host=%s port=%d sync=false async=false "
206 "udpsrc port=%d ! rtpbin.recv_rtcp_sink_0",
207 output->host, output->port, output->host,
208 output->port + 1, output->port + 2);
209 output->gst_pipeline = strdup(pipeline_str);
210 }
211 weston_log("GST pipeline: %s\n", output->gst_pipeline);
Tomohito Esakif709d222018-01-24 17:08:02 +0900212
Tomohito Esaki3ea03bb2019-05-22 12:21:32 +0900213 output->pipeline = gst_parse_launch(output->gst_pipeline, &err);
Tomohito Esakif709d222018-01-24 17:08:02 +0900214 if (!output->pipeline) {
215 weston_log("Could not create gstreamer pipeline. Error: %s\n",
216 err->message);
217 g_error_free(err);
218 return -1;
219 }
220
221 output->appsrc = (GstAppSrc*)
222 gst_bin_get_by_name(GST_BIN(output->pipeline), "src");
223 if (!output->appsrc) {
224 weston_log("Could not get appsrc from gstreamer pipeline\n");
225 goto err;
226 }
227
Tomohito Esaki3ea03bb2019-05-22 12:21:32 +0900228 /* check sink */
229 if (!gst_bin_get_by_name(GST_BIN(output->pipeline), "sink")) {
230 weston_log("Could not get sink from gstreamer pipeline\n");
231 goto err;
232 }
233
Tomohito Esakif709d222018-01-24 17:08:02 +0900234 caps = gst_caps_new_simple("video/x-raw",
235 "format", G_TYPE_STRING,
236 output->format->gst_format_string,
237 "width", G_TYPE_INT, mode->width,
238 "height", G_TYPE_INT, mode->height,
239 "framerate", GST_TYPE_FRACTION,
240 mode->refresh, 1000,
241 NULL);
242 if (!caps) {
243 weston_log("Could not create gstreamer caps.\n");
244 goto err;
245 }
246 g_object_set(G_OBJECT(output->appsrc),
247 "caps", caps,
248 "stream-type", 0,
249 "format", GST_FORMAT_TIME,
250 "is-live", TRUE,
251 NULL);
252 gst_caps_unref(caps);
253
254 output->bus = gst_pipeline_get_bus(GST_PIPELINE(output->pipeline));
255 if (!output->bus) {
256 weston_log("Could not get bus from gstreamer pipeline\n");
257 goto err;
258 }
259 gst_bus_set_sync_handler(output->bus, remoting_gst_bus_sync_handler,
260 &output->gstpipe, NULL);
261
262 output->start_time = 0;
263 ret = gst_element_set_state(output->pipeline, GST_STATE_PLAYING);
264 if (ret == GST_STATE_CHANGE_FAILURE) {
265 weston_log("Couldn't set GST_STATE_PLAYING to pipeline\n");
266 goto err;
267 }
268
269 return 0;
270
271err:
272 gst_object_unref(GST_OBJECT(output->pipeline));
273 output->pipeline = NULL;
274 return -1;
275}
276
277static void
278remoting_gst_pipeline_deinit(struct remoted_output *output)
279{
280 if (!output->pipeline)
281 return;
282
283 gst_element_set_state(output->pipeline, GST_STATE_NULL);
284 if (output->bus)
285 gst_object_unref(GST_OBJECT(output->bus));
286 gst_object_unref(GST_OBJECT(output->pipeline));
287 output->pipeline = NULL;
288}
289
290static int
291remoting_output_disable(struct weston_output *output);
292
293static void
294remoting_gst_restart(void *data)
295{
296 struct remoted_output *output = data;
297
298 if (remoting_gst_pipeline_init(output) < 0) {
299 weston_log("gst: Could not restart pipeline!!\n");
300 remoting_output_disable(output->output);
301 }
302}
303
304static void
305remoting_gst_schedule_restart(struct remoted_output *output)
306{
307 struct wl_event_loop *loop;
308 struct weston_compositor *c = output->remoting->compositor;
309
310 loop = wl_display_get_event_loop(c->wl_display);
311 wl_event_loop_add_idle(loop, remoting_gst_restart, output);
312}
313
314static void
315remoting_gst_bus_message_handler(struct remoted_output *output)
316{
317 GstMessage *message;
318 GError *error;
319 gchar *debug;
320
321 /* get message from bus queue */
322 message = gst_bus_pop(output->bus);
323 if (!message)
324 return;
325
326 switch (GST_MESSAGE_TYPE(message)) {
327 case GST_MESSAGE_STATE_CHANGED: {
328 GstState new_state;
329 gst_message_parse_state_changed(message, NULL, &new_state,
330 NULL);
331 if (!strcmp(GST_OBJECT_NAME(message->src), "sink") &&
332 new_state == GST_STATE_PLAYING)
333 output->retry_count = 0;
334 break;
335 }
336 case GST_MESSAGE_WARNING:
337 gst_message_parse_warning(message, &error, &debug);
338 weston_log("gst: Warning: %s: %s\n",
339 GST_OBJECT_NAME(message->src), error->message);
340 break;
341 case GST_MESSAGE_ERROR:
342 gst_message_parse_error(message, &error, &debug);
343 weston_log("gst: Error: %s: %s\n",
344 GST_OBJECT_NAME(message->src), error->message);
345 if (output->retry_count < MAX_RETRY_COUNT) {
346 output->retry_count++;
347 remoting_gst_pipeline_deinit(output);
348 remoting_gst_schedule_restart(output);
349 } else {
350 remoting_output_disable(output->output);
351 }
352 break;
353 default:
354 break;
355 }
356}
357
358static void
359remoting_output_buffer_release(struct remoted_output *output, void *buffer)
360{
361 const struct weston_drm_virtual_output_api *api
362 = output->remoting->virtual_output_api;
363
364 api->buffer_released(buffer);
365}
366
367static int
368remoting_gstpipe_handler(int fd, uint32_t mask, void *data)
369{
370 ssize_t ret;
371 struct gstpipe_msg_data msg;
372 struct remoted_output *output = data;
373
Emmanuel Gil Peyrot426c2462019-02-20 16:33:32 +0100374 /* receive message */
Tomohito Esakif709d222018-01-24 17:08:02 +0900375 ret = read(fd, &msg, sizeof(msg));
376 if (ret != sizeof(msg)) {
377 weston_log("ERROR: failed to read, ret=%zd, errno=%d\n",
378 ret, errno);
379 remoting_output_disable(output->output);
380 return 0;
381 }
382
383 switch (msg.type) {
384 case GSTPIPE_MSG_BUS_SYNC:
385 remoting_gst_bus_message_handler(output);
386 break;
387 case GSTPIPE_MSG_BUFFER_RELEASE:
388 remoting_output_buffer_release(output, msg.data);
389 break;
390 default:
Emmanuel Gil Peyrot426c2462019-02-20 16:33:32 +0100391 weston_log("Received unknown message! msg=%d\n", msg.type);
Tomohito Esakif709d222018-01-24 17:08:02 +0900392 }
393 return 1;
394}
395
396static int
397remoting_gstpipe_init(struct weston_compositor *c,
398 struct remoted_output *output)
399{
400 struct wl_event_loop *loop;
401 int fd[2];
402
403 if (pipe2(fd, O_CLOEXEC) == -1)
404 return -1;
405
406 output->gstpipe.readfd = fd[0];
407 output->gstpipe.writefd = fd[1];
408 loop = wl_display_get_event_loop(c->wl_display);
409 output->gstpipe.source =
410 wl_event_loop_add_fd(loop, output->gstpipe.readfd,
411 WL_EVENT_READABLE,
412 remoting_gstpipe_handler, output);
413 if (!output->gstpipe.source) {
414 close(fd[0]);
415 close(fd[1]);
416 return -1;
417 }
418
419 return 0;
420}
421
422static void
423remoting_gstpipe_release(struct remoted_gstpipe *pipe)
424{
425 wl_event_source_remove(pipe->source);
426 close(pipe->readfd);
427 close(pipe->writefd);
428}
429
430static void
431remoting_output_destroy(struct weston_output *output);
432
433static void
434weston_remoting_destroy(struct wl_listener *l, void *data)
435{
436 struct weston_remoting *remoting =
437 container_of(l, struct weston_remoting, destroy_listener);
438 struct remoted_output *output, *next;
439
440 wl_list_for_each_safe(output, next, &remoting->output_list, link)
441 remoting_output_destroy(output->output);
442
443 /* Finalize gstreamer */
444 remoting_gst_deinit(remoting);
445
446 wl_list_remove(&remoting->destroy_listener.link);
447 free(remoting);
448}
449
450static struct weston_remoting *
451weston_remoting_get(struct weston_compositor *compositor)
452{
453 struct wl_listener *listener;
454 struct weston_remoting *remoting;
455
456 listener = wl_signal_get(&compositor->destroy_signal,
457 weston_remoting_destroy);
458 if (!listener)
459 return NULL;
460
461 remoting = wl_container_of(listener, remoting, destroy_listener);
462 return remoting;
463}
464
465static int
466remoting_output_finish_frame_handler(void *data)
467{
468 struct remoted_output *output = data;
469 const struct weston_drm_virtual_output_api *api
470 = output->remoting->virtual_output_api;
471 struct timespec now;
472 int64_t msec;
473
474 if (output->submitted_frame) {
475 struct weston_compositor *c = output->remoting->compositor;
476 output->submitted_frame = false;
477 weston_compositor_read_presentation_clock(c, &now);
478 api->finish_frame(output->output, &now, 0);
479 }
480
Marius Vlade0b937a2020-08-27 14:14:27 +0300481 if (output->dpms == WESTON_DPMS_ON) {
482 msec = millihz_to_nsec(output->output->current_mode->refresh) / 1000000;
483 wl_event_source_timer_update(output->finish_frame_timer, msec);
484 } else {
485 wl_event_source_timer_update(output->finish_frame_timer, 0);
486 }
Tomohito Esakif709d222018-01-24 17:08:02 +0900487 return 0;
488}
489
490static void
491remoting_gst_mem_free_cb(struct mem_free_cb_data *cb_data, GstMiniObject *obj)
492{
493 struct remoted_output *output = cb_data->output;
494 struct remoted_gstpipe *pipe = &output->gstpipe;
495 struct gstpipe_msg_data msg = {
496 .type = GSTPIPE_MSG_BUFFER_RELEASE,
497 .data = cb_data->output_buffer
498 };
499 ssize_t ret;
500
501 ret = write(pipe->writefd, &msg, sizeof(msg));
502 if (ret != sizeof(msg))
503 weston_log("ERROR: failed to write, ret=%zd, errno=%d\n", ret,
504 errno);
505 free(cb_data);
506}
507
508static struct remoted_output *
509lookup_remoted_output(struct weston_output *output)
510{
511 struct weston_compositor *c = output->compositor;
512 struct weston_remoting *remoting = weston_remoting_get(c);
513 struct remoted_output *remoted_output;
514
515 wl_list_for_each(remoted_output, &remoting->output_list, link) {
516 if (remoted_output->output == output)
517 return remoted_output;
518 }
519
520 weston_log("%s: %s: could not find output\n", __FILE__, __func__);
521 return NULL;
522}
523
524static void
525remoting_output_gst_push_buffer(struct remoted_output *output,
526 GstBuffer *buffer)
527{
528 struct timespec current_frame_ts;
529 GstClockTime ts, current_frame_time;
530
531 weston_compositor_read_presentation_clock(output->remoting->compositor,
532 &current_frame_ts);
533 current_frame_time = GST_TIMESPEC_TO_TIME(current_frame_ts);
534 if (output->start_time == 0)
535 output->start_time = current_frame_time;
536 ts = current_frame_time - output->start_time;
537
538 if (GST_CLOCK_TIME_IS_VALID(ts))
539 GST_BUFFER_PTS(buffer) = ts;
540 else
541 GST_BUFFER_PTS(buffer) = GST_CLOCK_TIME_NONE;
542 GST_BUFFER_DURATION(buffer) = GST_CLOCK_TIME_NONE;
543
544 gst_app_src_push_buffer(output->appsrc, buffer);
545 output->submitted_frame = true;
546}
547
548static int
549remoting_output_fence_sync_handler(int fd, uint32_t mask, void *data)
550{
551 struct gst_frame_buffer_data *frame_data = data;
552 struct remoted_output *output = frame_data->output;
553
554 remoting_output_gst_push_buffer(output, frame_data->buffer);
555
556 wl_event_source_remove(output->fence_sync_event_source);
557 close(output->fence_sync_fd);
558 free(frame_data);
559
560 return 0;
561}
562
563static int
564remoting_output_frame(struct weston_output *output_base, int fd, int stride,
565 struct drm_fb *output_buffer)
566{
567 struct remoted_output *output = lookup_remoted_output(output_base);
568 struct weston_remoting *remoting = output->remoting;
569 struct weston_mode *mode;
570 const struct weston_drm_virtual_output_api *api
571 = output->remoting->virtual_output_api;
572 struct wl_event_loop *loop;
573 GstBuffer *buf;
574 GstMemory *mem;
575 gsize offset = 0;
576 struct mem_free_cb_data *cb_data;
577 struct gst_frame_buffer_data *frame_data;
578
579 if (!output)
580 return -1;
581
582 cb_data = zalloc(sizeof *cb_data);
583 if (!cb_data)
584 return -1;
585
586 mode = output->output->current_mode;
587 buf = gst_buffer_new();
588 mem = gst_dmabuf_allocator_alloc(remoting->allocator, fd,
589 stride * mode->height);
590 gst_buffer_append_memory(buf, mem);
591 gst_buffer_add_video_meta_full(buf,
592 GST_VIDEO_FRAME_FLAG_NONE,
593 output->format->gst_video_format,
594 mode->width,
595 mode->height,
596 1,
597 &offset,
598 &stride);
599
600 cb_data->output = output;
601 cb_data->output_buffer = output_buffer;
602 gst_mini_object_weak_ref(GST_MINI_OBJECT(mem),
603 (GstMiniObjectNotify)remoting_gst_mem_free_cb,
604 cb_data);
605
606 output->fence_sync_fd = api->get_fence_sync_fd(output->output);
607 /* Push buffer to gstreamer immediately on get_fence_sync_fd failure */
608 if (output->fence_sync_fd == -1) {
609 remoting_output_gst_push_buffer(output, buf);
610 return 0;
611 }
612
613 frame_data = zalloc(sizeof *frame_data);
614 if (!frame_data) {
615 close(output->fence_sync_fd);
616 remoting_output_gst_push_buffer(output, buf);
617 return 0;
618 }
619
620 frame_data->output = output;
621 frame_data->buffer = buf;
622 loop = wl_display_get_event_loop(remoting->compositor->wl_display);
623 output->fence_sync_event_source =
624 wl_event_loop_add_fd(loop, output->fence_sync_fd,
625 WL_EVENT_READABLE,
626 remoting_output_fence_sync_handler,
627 frame_data);
628
629 return 0;
630}
631
632static void
633remoting_output_destroy(struct weston_output *output)
634{
635 struct remoted_output *remoted_output = lookup_remoted_output(output);
636 struct weston_mode *mode, *next;
637
638 wl_list_for_each_safe(mode, next, &output->mode_list, link) {
639 wl_list_remove(&mode->link);
640 free(mode);
641 }
642
643 remoted_output->saved_destroy(output);
644
645 remoting_gst_pipeline_deinit(remoted_output);
646 remoting_gstpipe_release(&remoted_output->gstpipe);
647
648 if (remoted_output->host)
649 free(remoted_output->host);
Tomohito Esaki3ea03bb2019-05-22 12:21:32 +0900650 if (remoted_output->gst_pipeline)
651 free(remoted_output->gst_pipeline);
Tomohito Esakif709d222018-01-24 17:08:02 +0900652
653 wl_list_remove(&remoted_output->link);
654 weston_head_release(remoted_output->head);
655 free(remoted_output->head);
656 free(remoted_output);
657}
658
Antonio Borneoc90fccc2019-06-30 15:51:10 +0200659static int
Tomohito Esakif709d222018-01-24 17:08:02 +0900660remoting_output_start_repaint_loop(struct weston_output *output)
661{
662 struct remoted_output *remoted_output = lookup_remoted_output(output);
663 int64_t msec;
664
665 remoted_output->saved_start_repaint_loop(output);
666
667 msec = millihz_to_nsec(remoted_output->output->current_mode->refresh)
668 / 1000000;
669 wl_event_source_timer_update(remoted_output->finish_frame_timer, msec);
Antonio Borneoc90fccc2019-06-30 15:51:10 +0200670
671 return 0;
Tomohito Esakif709d222018-01-24 17:08:02 +0900672}
673
Marius Vlade0b937a2020-08-27 14:14:27 +0300674static void
675remoting_output_set_dpms(struct weston_output *base_output, enum dpms_enum level)
676{
677 struct remoted_output *output = lookup_remoted_output(base_output);
678
679 if (output->dpms == level)
680 return;
681
682 output->dpms = level;
683 remoting_output_finish_frame_handler(output);
684}
685
Tomohito Esakif709d222018-01-24 17:08:02 +0900686static int
687remoting_output_enable(struct weston_output *output)
688{
689 struct remoted_output *remoted_output = lookup_remoted_output(output);
690 struct weston_compositor *c = output->compositor;
691 const struct weston_drm_virtual_output_api *api
692 = remoted_output->remoting->virtual_output_api;
693 struct wl_event_loop *loop;
694 int ret;
695
696 api->set_submit_frame_cb(output, remoting_output_frame);
697
698 ret = remoted_output->saved_enable(output);
699 if (ret < 0)
700 return ret;
701
702 remoted_output->saved_start_repaint_loop = output->start_repaint_loop;
703 output->start_repaint_loop = remoting_output_start_repaint_loop;
Marius Vlade0b937a2020-08-27 14:14:27 +0300704 output->set_dpms = remoting_output_set_dpms;
Tomohito Esakif709d222018-01-24 17:08:02 +0900705
706 ret = remoting_gst_pipeline_init(remoted_output);
707 if (ret < 0) {
708 remoted_output->saved_disable(output);
709 return ret;
710 }
711
712 loop = wl_display_get_event_loop(c->wl_display);
713 remoted_output->finish_frame_timer =
714 wl_event_loop_add_timer(loop,
715 remoting_output_finish_frame_handler,
716 remoted_output);
717
Marius Vlade0b937a2020-08-27 14:14:27 +0300718 remoted_output->dpms = WESTON_DPMS_ON;
Tomohito Esakif709d222018-01-24 17:08:02 +0900719 return 0;
720}
721
722static int
723remoting_output_disable(struct weston_output *output)
724{
725 struct remoted_output *remoted_output = lookup_remoted_output(output);
726
727 wl_event_source_remove(remoted_output->finish_frame_timer);
728 remoting_gst_pipeline_deinit(remoted_output);
729
730 return remoted_output->saved_disable(output);
731}
732
733static struct weston_output *
734remoting_output_create(struct weston_compositor *c, char *name)
735{
736 struct weston_remoting *remoting = weston_remoting_get(c);
737 struct remoted_output *output;
738 struct weston_head *head;
739 const struct weston_drm_virtual_output_api *api;
740 const char *make = "Renesas";
741 const char *model = "Virtual Display";
742 const char *serial_number = "unknown";
743 const char *connector_name = "remoting";
Marius Vladbb7ed372020-11-05 19:30:51 +0200744 char *remoting_name;
Tomohito Esakif709d222018-01-24 17:08:02 +0900745
746 if (!name || !strlen(name))
747 return NULL;
748
749 api = remoting->virtual_output_api;
750
751 output = zalloc(sizeof *output);
752 if (!output)
753 return NULL;
754
755 head = zalloc(sizeof *head);
756 if (!head)
757 goto err;
758
759 if (remoting_gstpipe_init(c, output) < 0) {
760 weston_log("Can not create pipe for gstreamer\n");
761 goto err;
762 }
763
764 output->output = api->create_output(c, name);
765 if (!output->output) {
766 weston_log("Can not create virtual output\n");
767 goto err;
768 }
769
770 output->saved_destroy = output->output->destroy;
771 output->output->destroy = remoting_output_destroy;
772 output->saved_enable = output->output->enable;
773 output->output->enable = remoting_output_enable;
774 output->saved_disable = output->output->disable;
775 output->output->disable = remoting_output_disable;
776 output->remoting = remoting;
777 wl_list_insert(remoting->output_list.prev, &output->link);
778
Marius Vladbb7ed372020-11-05 19:30:51 +0200779 asprintf(&remoting_name, "%s-%s", connector_name, name);
780 weston_head_init(head, remoting_name);
Tomohito Esakif709d222018-01-24 17:08:02 +0900781 weston_head_set_subpixel(head, WL_OUTPUT_SUBPIXEL_NONE);
782 weston_head_set_monitor_strings(head, make, model, serial_number);
783 head->compositor = c;
784
785 weston_output_attach_head(output->output, head);
786 output->head = head;
787
788 /* set XRGB8888 format */
789 output->format = &supported_formats[0];
Marius Vladbb7ed372020-11-05 19:30:51 +0200790 free(remoting_name);
Tomohito Esakif709d222018-01-24 17:08:02 +0900791
792 return output->output;
793
794err:
795 if (output->gstpipe.source)
796 remoting_gstpipe_release(&output->gstpipe);
797 if (head)
798 free(head);
799 free(output);
800 return NULL;
801}
802
803static bool
804remoting_output_is_remoted(struct weston_output *output)
805{
806 struct remoted_output *remoted_output = lookup_remoted_output(output);
807
808 if (remoted_output)
809 return true;
810
811 return false;
812}
813
814static int
815remoting_output_set_mode(struct weston_output *output, const char *modeline)
816{
817 struct weston_mode *mode;
818 int n, width, height, refresh = 0;
819
820 if (!remoting_output_is_remoted(output)) {
821 weston_log("Output is not remoted.\n");
822 return -1;
823 }
824
825 if (!modeline)
826 return -1;
827
828 n = sscanf(modeline, "%dx%d@%d", &width, &height, &refresh);
829 if (n != 2 && n != 3)
830 return -1;
831
832 mode = zalloc(sizeof *mode);
833 if (!mode)
834 return -1;
835
836 mode->flags = WL_OUTPUT_MODE_CURRENT;
837 mode->width = width;
838 mode->height = height;
839 mode->refresh = (refresh ? refresh : 60) * 1000LL;
840
841 wl_list_insert(output->mode_list.prev, &mode->link);
842
843 output->current_mode = mode;
844
845 return 0;
846}
847
848static void
849remoting_output_set_gbm_format(struct weston_output *output,
850 const char *gbm_format)
851{
852 struct remoted_output *remoted_output = lookup_remoted_output(output);
853 const struct weston_drm_virtual_output_api *api;
854 uint32_t format, i;
855
856 if (!remoted_output)
857 return;
858
859 api = remoted_output->remoting->virtual_output_api;
860 format = api->set_gbm_format(output, gbm_format);
861
862 for (i = 0; i < ARRAY_LENGTH(supported_formats); i++) {
863 if (format == supported_formats[i].gbm_format) {
864 remoted_output->format = &supported_formats[i];
865 return;
866 }
867 }
868}
869
870static void
871remoting_output_set_seat(struct weston_output *output, const char *seat)
872{
873 /* for now, nothing todo */
874}
875
876static void
877remoting_output_set_host(struct weston_output *output, char *host)
878{
879 struct remoted_output *remoted_output = lookup_remoted_output(output);
880
881 if (!remoted_output)
882 return;
883
884 if (remoted_output->host)
885 free(remoted_output->host);
886 remoted_output->host = strdup(host);
887}
888
889static void
890remoting_output_set_port(struct weston_output *output, int port)
891{
892 struct remoted_output *remoted_output = lookup_remoted_output(output);
893
894 if (remoted_output)
895 remoted_output->port = port;
896}
897
Tomohito Esaki3ea03bb2019-05-22 12:21:32 +0900898static void
899remoting_output_set_gst_pipeline(struct weston_output *output,
900 char *gst_pipeline)
901{
902 struct remoted_output *remoted_output = lookup_remoted_output(output);
903
904 if (!remoted_output)
905 return;
906
907 if (remoted_output->gst_pipeline)
908 free(remoted_output->gst_pipeline);
909 remoted_output->gst_pipeline = strdup(gst_pipeline);
910}
911
Tomohito Esakif709d222018-01-24 17:08:02 +0900912static const struct weston_remoting_api remoting_api = {
913 remoting_output_create,
914 remoting_output_is_remoted,
915 remoting_output_set_mode,
916 remoting_output_set_gbm_format,
917 remoting_output_set_seat,
918 remoting_output_set_host,
919 remoting_output_set_port,
Tomohito Esaki3ea03bb2019-05-22 12:21:32 +0900920 remoting_output_set_gst_pipeline,
Tomohito Esakif709d222018-01-24 17:08:02 +0900921};
922
923WL_EXPORT int
924weston_module_init(struct weston_compositor *compositor)
925{
926 int ret;
927 struct weston_remoting *remoting;
928 const struct weston_drm_virtual_output_api *api =
929 weston_drm_virtual_output_get_api(compositor);
930
931 if (!api)
932 return -1;
933
934 remoting = zalloc(sizeof *remoting);
935 if (!remoting)
936 return -1;
937
Pekka Paalanen6ffbba32019-11-06 12:59:32 +0200938 if (!weston_compositor_add_destroy_listener_once(compositor,
939 &remoting->destroy_listener,
940 weston_remoting_destroy)) {
941 free(remoting);
942 return 0;
943 }
944
Tomohito Esakif709d222018-01-24 17:08:02 +0900945 remoting->virtual_output_api = api;
946 remoting->compositor = compositor;
947 wl_list_init(&remoting->output_list);
948
949 ret = weston_plugin_api_register(compositor, WESTON_REMOTING_API_NAME,
950 &remoting_api, sizeof(remoting_api));
951
952 if (ret < 0) {
953 weston_log("Failed to register remoting API.\n");
954 goto failed;
955 }
956
957 /* Initialize gstreamer */
958 ret = remoting_gst_init(remoting);
959 if (ret < 0) {
960 weston_log("Failed to initialize gstreamer.\n");
961 goto failed;
962 }
963
Tomohito Esakif709d222018-01-24 17:08:02 +0900964 return 0;
965
966failed:
Pekka Paalanen6ffbba32019-11-06 12:59:32 +0200967 wl_list_remove(&remoting->destroy_listener.link);
Tomohito Esakif709d222018-01-24 17:08:02 +0900968 free(remoting);
969 return -1;
970}