blob: 7c9bbb114258b57fdf0571133acc191bbcb7657f [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>
39#include <gbm.h>
40
41#include <gst/gst.h>
42#include <gst/allocators/gstdmabuf.h>
43#include <gst/app/gstappsrc.h>
44#include <gst/video/gstvideometa.h>
45
46#include "remoting-plugin.h"
Pekka Paalanen75710272019-03-29 16:39:12 +020047#include <libweston/backend-drm.h>
Tomohito Esakif709d222018-01-24 17:08:02 +090048#include "shared/helpers.h"
49#include "shared/timespec-util.h"
50
51#define MAX_RETRY_COUNT 3
52
53struct weston_remoting {
54 struct weston_compositor *compositor;
55 struct wl_list output_list;
56 struct wl_listener destroy_listener;
57 const struct weston_drm_virtual_output_api *virtual_output_api;
58
59 GstAllocator *allocator;
60};
61
62struct remoted_gstpipe {
63 int readfd;
64 int writefd;
65 struct wl_event_source *source;
66};
67
68/* supported gbm format list */
69struct remoted_output_support_gbm_format {
70 uint32_t gbm_format;
71 const char *gst_format_string;
72 GstVideoFormat gst_video_format;
73};
74
75static const struct remoted_output_support_gbm_format supported_formats[] = {
76 {
77 .gbm_format = GBM_FORMAT_XRGB8888,
78 .gst_format_string = "BGRx",
79 .gst_video_format = GST_VIDEO_FORMAT_BGRx,
80 }, {
81 .gbm_format = GBM_FORMAT_RGB565,
82 .gst_format_string = "RGB16",
83 .gst_video_format = GST_VIDEO_FORMAT_RGB16,
84 }, {
85 .gbm_format = GBM_FORMAT_XRGB2101010,
86 .gst_format_string = "r210",
87 .gst_video_format = GST_VIDEO_FORMAT_r210,
88 }
89};
90
91struct remoted_output {
92 struct weston_output *output;
93 void (*saved_destroy)(struct weston_output *output);
94 int (*saved_enable)(struct weston_output *output);
95 int (*saved_disable)(struct weston_output *output);
96 void (*saved_start_repaint_loop)(struct weston_output *output);
97
98 char *host;
99 int port;
Tomohito Esaki3ea03bb2019-05-22 12:21:32 +0900100 char *gst_pipeline;
Tomohito Esakif709d222018-01-24 17:08:02 +0900101 const struct remoted_output_support_gbm_format *format;
102
103 struct weston_head *head;
104
105 struct weston_remoting *remoting;
106 struct wl_event_source *finish_frame_timer;
107 struct wl_list link;
108 bool submitted_frame;
109 int fence_sync_fd;
110 struct wl_event_source *fence_sync_event_source;
111
112 GstElement *pipeline;
113 GstAppSrc *appsrc;
114 GstBus *bus;
115 struct remoted_gstpipe gstpipe;
116 GstClockTime start_time;
117 int retry_count;
118};
119
120struct mem_free_cb_data {
121 struct remoted_output *output;
122 struct drm_fb *output_buffer;
123};
124
125struct gst_frame_buffer_data {
126 struct remoted_output *output;
127 GstBuffer *buffer;
128};
129
130/* message type for pipe */
131#define GSTPIPE_MSG_BUS_SYNC 1
132#define GSTPIPE_MSG_BUFFER_RELEASE 2
133
134struct gstpipe_msg_data {
135 int type;
136 void *data;
137};
138
139static int
140remoting_gst_init(struct weston_remoting *remoting)
141{
142 GError *err = NULL;
143
144 if (!gst_init_check(NULL, NULL, &err)) {
145 weston_log("GStreamer initialization error: %s\n",
146 err->message);
147 g_error_free(err);
148 return -1;
149 }
150
151 remoting->allocator = gst_dmabuf_allocator_new();
152
153 return 0;
154}
155
156static void
157remoting_gst_deinit(struct weston_remoting *remoting)
158{
159 gst_object_unref(remoting->allocator);
160}
161
162static GstBusSyncReply
163remoting_gst_bus_sync_handler(GstBus *bus, GstMessage *message,
164 gpointer user_data)
165{
166 struct remoted_gstpipe *pipe = user_data;
167 struct gstpipe_msg_data msg = {
168 .type = GSTPIPE_MSG_BUS_SYNC,
169 .data = NULL
170 };
171 ssize_t ret;
172
173 ret = write(pipe->writefd, &msg, sizeof(msg));
174 if (ret != sizeof(msg))
175 weston_log("ERROR: failed to write, ret=%zd, errno=%d\n",
176 ret, errno);
177
178 return GST_BUS_PASS;
179}
180
181static int
182remoting_gst_pipeline_init(struct remoted_output *output)
183{
Tomohito Esakif709d222018-01-24 17:08:02 +0900184 GstCaps *caps;
185 GError *err = NULL;
186 GstStateChangeReturn ret;
187 struct weston_mode *mode = output->output->current_mode;
188
Tomohito Esaki3ea03bb2019-05-22 12:21:32 +0900189 if (!output->gst_pipeline) {
190 char pipeline_str[1024];
191 /* TODO: use encodebin instead of jpegenc */
192 snprintf(pipeline_str, sizeof(pipeline_str),
193 "rtpbin name=rtpbin "
194 "appsrc name=src ! videoconvert ! "
195 "video/x-raw,format=I420 ! jpegenc ! rtpjpegpay ! "
196 "rtpbin.send_rtp_sink_0 "
197 "rtpbin.send_rtp_src_0 ! "
198 "udpsink name=sink host=%s port=%d "
199 "rtpbin.send_rtcp_src_0 ! "
200 "udpsink host=%s port=%d sync=false async=false "
201 "udpsrc port=%d ! rtpbin.recv_rtcp_sink_0",
202 output->host, output->port, output->host,
203 output->port + 1, output->port + 2);
204 output->gst_pipeline = strdup(pipeline_str);
205 }
206 weston_log("GST pipeline: %s\n", output->gst_pipeline);
Tomohito Esakif709d222018-01-24 17:08:02 +0900207
Tomohito Esaki3ea03bb2019-05-22 12:21:32 +0900208 output->pipeline = gst_parse_launch(output->gst_pipeline, &err);
Tomohito Esakif709d222018-01-24 17:08:02 +0900209 if (!output->pipeline) {
210 weston_log("Could not create gstreamer pipeline. Error: %s\n",
211 err->message);
212 g_error_free(err);
213 return -1;
214 }
215
216 output->appsrc = (GstAppSrc*)
217 gst_bin_get_by_name(GST_BIN(output->pipeline), "src");
218 if (!output->appsrc) {
219 weston_log("Could not get appsrc from gstreamer pipeline\n");
220 goto err;
221 }
222
Tomohito Esaki3ea03bb2019-05-22 12:21:32 +0900223 /* check sink */
224 if (!gst_bin_get_by_name(GST_BIN(output->pipeline), "sink")) {
225 weston_log("Could not get sink from gstreamer pipeline\n");
226 goto err;
227 }
228
Tomohito Esakif709d222018-01-24 17:08:02 +0900229 caps = gst_caps_new_simple("video/x-raw",
230 "format", G_TYPE_STRING,
231 output->format->gst_format_string,
232 "width", G_TYPE_INT, mode->width,
233 "height", G_TYPE_INT, mode->height,
234 "framerate", GST_TYPE_FRACTION,
235 mode->refresh, 1000,
236 NULL);
237 if (!caps) {
238 weston_log("Could not create gstreamer caps.\n");
239 goto err;
240 }
241 g_object_set(G_OBJECT(output->appsrc),
242 "caps", caps,
243 "stream-type", 0,
244 "format", GST_FORMAT_TIME,
245 "is-live", TRUE,
246 NULL);
247 gst_caps_unref(caps);
248
249 output->bus = gst_pipeline_get_bus(GST_PIPELINE(output->pipeline));
250 if (!output->bus) {
251 weston_log("Could not get bus from gstreamer pipeline\n");
252 goto err;
253 }
254 gst_bus_set_sync_handler(output->bus, remoting_gst_bus_sync_handler,
255 &output->gstpipe, NULL);
256
257 output->start_time = 0;
258 ret = gst_element_set_state(output->pipeline, GST_STATE_PLAYING);
259 if (ret == GST_STATE_CHANGE_FAILURE) {
260 weston_log("Couldn't set GST_STATE_PLAYING to pipeline\n");
261 goto err;
262 }
263
264 return 0;
265
266err:
267 gst_object_unref(GST_OBJECT(output->pipeline));
268 output->pipeline = NULL;
269 return -1;
270}
271
272static void
273remoting_gst_pipeline_deinit(struct remoted_output *output)
274{
275 if (!output->pipeline)
276 return;
277
278 gst_element_set_state(output->pipeline, GST_STATE_NULL);
279 if (output->bus)
280 gst_object_unref(GST_OBJECT(output->bus));
281 gst_object_unref(GST_OBJECT(output->pipeline));
282 output->pipeline = NULL;
283}
284
285static int
286remoting_output_disable(struct weston_output *output);
287
288static void
289remoting_gst_restart(void *data)
290{
291 struct remoted_output *output = data;
292
293 if (remoting_gst_pipeline_init(output) < 0) {
294 weston_log("gst: Could not restart pipeline!!\n");
295 remoting_output_disable(output->output);
296 }
297}
298
299static void
300remoting_gst_schedule_restart(struct remoted_output *output)
301{
302 struct wl_event_loop *loop;
303 struct weston_compositor *c = output->remoting->compositor;
304
305 loop = wl_display_get_event_loop(c->wl_display);
306 wl_event_loop_add_idle(loop, remoting_gst_restart, output);
307}
308
309static void
310remoting_gst_bus_message_handler(struct remoted_output *output)
311{
312 GstMessage *message;
313 GError *error;
314 gchar *debug;
315
316 /* get message from bus queue */
317 message = gst_bus_pop(output->bus);
318 if (!message)
319 return;
320
321 switch (GST_MESSAGE_TYPE(message)) {
322 case GST_MESSAGE_STATE_CHANGED: {
323 GstState new_state;
324 gst_message_parse_state_changed(message, NULL, &new_state,
325 NULL);
326 if (!strcmp(GST_OBJECT_NAME(message->src), "sink") &&
327 new_state == GST_STATE_PLAYING)
328 output->retry_count = 0;
329 break;
330 }
331 case GST_MESSAGE_WARNING:
332 gst_message_parse_warning(message, &error, &debug);
333 weston_log("gst: Warning: %s: %s\n",
334 GST_OBJECT_NAME(message->src), error->message);
335 break;
336 case GST_MESSAGE_ERROR:
337 gst_message_parse_error(message, &error, &debug);
338 weston_log("gst: Error: %s: %s\n",
339 GST_OBJECT_NAME(message->src), error->message);
340 if (output->retry_count < MAX_RETRY_COUNT) {
341 output->retry_count++;
342 remoting_gst_pipeline_deinit(output);
343 remoting_gst_schedule_restart(output);
344 } else {
345 remoting_output_disable(output->output);
346 }
347 break;
348 default:
349 break;
350 }
351}
352
353static void
354remoting_output_buffer_release(struct remoted_output *output, void *buffer)
355{
356 const struct weston_drm_virtual_output_api *api
357 = output->remoting->virtual_output_api;
358
359 api->buffer_released(buffer);
360}
361
362static int
363remoting_gstpipe_handler(int fd, uint32_t mask, void *data)
364{
365 ssize_t ret;
366 struct gstpipe_msg_data msg;
367 struct remoted_output *output = data;
368
Emmanuel Gil Peyrot426c2462019-02-20 16:33:32 +0100369 /* receive message */
Tomohito Esakif709d222018-01-24 17:08:02 +0900370 ret = read(fd, &msg, sizeof(msg));
371 if (ret != sizeof(msg)) {
372 weston_log("ERROR: failed to read, ret=%zd, errno=%d\n",
373 ret, errno);
374 remoting_output_disable(output->output);
375 return 0;
376 }
377
378 switch (msg.type) {
379 case GSTPIPE_MSG_BUS_SYNC:
380 remoting_gst_bus_message_handler(output);
381 break;
382 case GSTPIPE_MSG_BUFFER_RELEASE:
383 remoting_output_buffer_release(output, msg.data);
384 break;
385 default:
Emmanuel Gil Peyrot426c2462019-02-20 16:33:32 +0100386 weston_log("Received unknown message! msg=%d\n", msg.type);
Tomohito Esakif709d222018-01-24 17:08:02 +0900387 }
388 return 1;
389}
390
391static int
392remoting_gstpipe_init(struct weston_compositor *c,
393 struct remoted_output *output)
394{
395 struct wl_event_loop *loop;
396 int fd[2];
397
398 if (pipe2(fd, O_CLOEXEC) == -1)
399 return -1;
400
401 output->gstpipe.readfd = fd[0];
402 output->gstpipe.writefd = fd[1];
403 loop = wl_display_get_event_loop(c->wl_display);
404 output->gstpipe.source =
405 wl_event_loop_add_fd(loop, output->gstpipe.readfd,
406 WL_EVENT_READABLE,
407 remoting_gstpipe_handler, output);
408 if (!output->gstpipe.source) {
409 close(fd[0]);
410 close(fd[1]);
411 return -1;
412 }
413
414 return 0;
415}
416
417static void
418remoting_gstpipe_release(struct remoted_gstpipe *pipe)
419{
420 wl_event_source_remove(pipe->source);
421 close(pipe->readfd);
422 close(pipe->writefd);
423}
424
425static void
426remoting_output_destroy(struct weston_output *output);
427
428static void
429weston_remoting_destroy(struct wl_listener *l, void *data)
430{
431 struct weston_remoting *remoting =
432 container_of(l, struct weston_remoting, destroy_listener);
433 struct remoted_output *output, *next;
434
435 wl_list_for_each_safe(output, next, &remoting->output_list, link)
436 remoting_output_destroy(output->output);
437
438 /* Finalize gstreamer */
439 remoting_gst_deinit(remoting);
440
441 wl_list_remove(&remoting->destroy_listener.link);
442 free(remoting);
443}
444
445static struct weston_remoting *
446weston_remoting_get(struct weston_compositor *compositor)
447{
448 struct wl_listener *listener;
449 struct weston_remoting *remoting;
450
451 listener = wl_signal_get(&compositor->destroy_signal,
452 weston_remoting_destroy);
453 if (!listener)
454 return NULL;
455
456 remoting = wl_container_of(listener, remoting, destroy_listener);
457 return remoting;
458}
459
460static int
461remoting_output_finish_frame_handler(void *data)
462{
463 struct remoted_output *output = data;
464 const struct weston_drm_virtual_output_api *api
465 = output->remoting->virtual_output_api;
466 struct timespec now;
467 int64_t msec;
468
469 if (output->submitted_frame) {
470 struct weston_compositor *c = output->remoting->compositor;
471 output->submitted_frame = false;
472 weston_compositor_read_presentation_clock(c, &now);
473 api->finish_frame(output->output, &now, 0);
474 }
475
476 msec = millihz_to_nsec(output->output->current_mode->refresh) / 1000000;
477 wl_event_source_timer_update(output->finish_frame_timer, msec);
478 return 0;
479}
480
481static void
482remoting_gst_mem_free_cb(struct mem_free_cb_data *cb_data, GstMiniObject *obj)
483{
484 struct remoted_output *output = cb_data->output;
485 struct remoted_gstpipe *pipe = &output->gstpipe;
486 struct gstpipe_msg_data msg = {
487 .type = GSTPIPE_MSG_BUFFER_RELEASE,
488 .data = cb_data->output_buffer
489 };
490 ssize_t ret;
491
492 ret = write(pipe->writefd, &msg, sizeof(msg));
493 if (ret != sizeof(msg))
494 weston_log("ERROR: failed to write, ret=%zd, errno=%d\n", ret,
495 errno);
496 free(cb_data);
497}
498
499static struct remoted_output *
500lookup_remoted_output(struct weston_output *output)
501{
502 struct weston_compositor *c = output->compositor;
503 struct weston_remoting *remoting = weston_remoting_get(c);
504 struct remoted_output *remoted_output;
505
506 wl_list_for_each(remoted_output, &remoting->output_list, link) {
507 if (remoted_output->output == output)
508 return remoted_output;
509 }
510
511 weston_log("%s: %s: could not find output\n", __FILE__, __func__);
512 return NULL;
513}
514
515static void
516remoting_output_gst_push_buffer(struct remoted_output *output,
517 GstBuffer *buffer)
518{
519 struct timespec current_frame_ts;
520 GstClockTime ts, current_frame_time;
521
522 weston_compositor_read_presentation_clock(output->remoting->compositor,
523 &current_frame_ts);
524 current_frame_time = GST_TIMESPEC_TO_TIME(current_frame_ts);
525 if (output->start_time == 0)
526 output->start_time = current_frame_time;
527 ts = current_frame_time - output->start_time;
528
529 if (GST_CLOCK_TIME_IS_VALID(ts))
530 GST_BUFFER_PTS(buffer) = ts;
531 else
532 GST_BUFFER_PTS(buffer) = GST_CLOCK_TIME_NONE;
533 GST_BUFFER_DURATION(buffer) = GST_CLOCK_TIME_NONE;
534
535 gst_app_src_push_buffer(output->appsrc, buffer);
536 output->submitted_frame = true;
537}
538
539static int
540remoting_output_fence_sync_handler(int fd, uint32_t mask, void *data)
541{
542 struct gst_frame_buffer_data *frame_data = data;
543 struct remoted_output *output = frame_data->output;
544
545 remoting_output_gst_push_buffer(output, frame_data->buffer);
546
547 wl_event_source_remove(output->fence_sync_event_source);
548 close(output->fence_sync_fd);
549 free(frame_data);
550
551 return 0;
552}
553
554static int
555remoting_output_frame(struct weston_output *output_base, int fd, int stride,
556 struct drm_fb *output_buffer)
557{
558 struct remoted_output *output = lookup_remoted_output(output_base);
559 struct weston_remoting *remoting = output->remoting;
560 struct weston_mode *mode;
561 const struct weston_drm_virtual_output_api *api
562 = output->remoting->virtual_output_api;
563 struct wl_event_loop *loop;
564 GstBuffer *buf;
565 GstMemory *mem;
566 gsize offset = 0;
567 struct mem_free_cb_data *cb_data;
568 struct gst_frame_buffer_data *frame_data;
569
570 if (!output)
571 return -1;
572
573 cb_data = zalloc(sizeof *cb_data);
574 if (!cb_data)
575 return -1;
576
577 mode = output->output->current_mode;
578 buf = gst_buffer_new();
579 mem = gst_dmabuf_allocator_alloc(remoting->allocator, fd,
580 stride * mode->height);
581 gst_buffer_append_memory(buf, mem);
582 gst_buffer_add_video_meta_full(buf,
583 GST_VIDEO_FRAME_FLAG_NONE,
584 output->format->gst_video_format,
585 mode->width,
586 mode->height,
587 1,
588 &offset,
589 &stride);
590
591 cb_data->output = output;
592 cb_data->output_buffer = output_buffer;
593 gst_mini_object_weak_ref(GST_MINI_OBJECT(mem),
594 (GstMiniObjectNotify)remoting_gst_mem_free_cb,
595 cb_data);
596
597 output->fence_sync_fd = api->get_fence_sync_fd(output->output);
598 /* Push buffer to gstreamer immediately on get_fence_sync_fd failure */
599 if (output->fence_sync_fd == -1) {
600 remoting_output_gst_push_buffer(output, buf);
601 return 0;
602 }
603
604 frame_data = zalloc(sizeof *frame_data);
605 if (!frame_data) {
606 close(output->fence_sync_fd);
607 remoting_output_gst_push_buffer(output, buf);
608 return 0;
609 }
610
611 frame_data->output = output;
612 frame_data->buffer = buf;
613 loop = wl_display_get_event_loop(remoting->compositor->wl_display);
614 output->fence_sync_event_source =
615 wl_event_loop_add_fd(loop, output->fence_sync_fd,
616 WL_EVENT_READABLE,
617 remoting_output_fence_sync_handler,
618 frame_data);
619
620 return 0;
621}
622
623static void
624remoting_output_destroy(struct weston_output *output)
625{
626 struct remoted_output *remoted_output = lookup_remoted_output(output);
627 struct weston_mode *mode, *next;
628
629 wl_list_for_each_safe(mode, next, &output->mode_list, link) {
630 wl_list_remove(&mode->link);
631 free(mode);
632 }
633
634 remoted_output->saved_destroy(output);
635
636 remoting_gst_pipeline_deinit(remoted_output);
637 remoting_gstpipe_release(&remoted_output->gstpipe);
638
639 if (remoted_output->host)
640 free(remoted_output->host);
Tomohito Esaki3ea03bb2019-05-22 12:21:32 +0900641 if (remoted_output->gst_pipeline)
642 free(remoted_output->gst_pipeline);
Tomohito Esakif709d222018-01-24 17:08:02 +0900643
644 wl_list_remove(&remoted_output->link);
645 weston_head_release(remoted_output->head);
646 free(remoted_output->head);
647 free(remoted_output);
648}
649
650static void
651remoting_output_start_repaint_loop(struct weston_output *output)
652{
653 struct remoted_output *remoted_output = lookup_remoted_output(output);
654 int64_t msec;
655
656 remoted_output->saved_start_repaint_loop(output);
657
658 msec = millihz_to_nsec(remoted_output->output->current_mode->refresh)
659 / 1000000;
660 wl_event_source_timer_update(remoted_output->finish_frame_timer, msec);
661}
662
663static int
664remoting_output_enable(struct weston_output *output)
665{
666 struct remoted_output *remoted_output = lookup_remoted_output(output);
667 struct weston_compositor *c = output->compositor;
668 const struct weston_drm_virtual_output_api *api
669 = remoted_output->remoting->virtual_output_api;
670 struct wl_event_loop *loop;
671 int ret;
672
673 api->set_submit_frame_cb(output, remoting_output_frame);
674
675 ret = remoted_output->saved_enable(output);
676 if (ret < 0)
677 return ret;
678
679 remoted_output->saved_start_repaint_loop = output->start_repaint_loop;
680 output->start_repaint_loop = remoting_output_start_repaint_loop;
681
682 ret = remoting_gst_pipeline_init(remoted_output);
683 if (ret < 0) {
684 remoted_output->saved_disable(output);
685 return ret;
686 }
687
688 loop = wl_display_get_event_loop(c->wl_display);
689 remoted_output->finish_frame_timer =
690 wl_event_loop_add_timer(loop,
691 remoting_output_finish_frame_handler,
692 remoted_output);
693
694 return 0;
695}
696
697static int
698remoting_output_disable(struct weston_output *output)
699{
700 struct remoted_output *remoted_output = lookup_remoted_output(output);
701
702 wl_event_source_remove(remoted_output->finish_frame_timer);
703 remoting_gst_pipeline_deinit(remoted_output);
704
705 return remoted_output->saved_disable(output);
706}
707
708static struct weston_output *
709remoting_output_create(struct weston_compositor *c, char *name)
710{
711 struct weston_remoting *remoting = weston_remoting_get(c);
712 struct remoted_output *output;
713 struct weston_head *head;
714 const struct weston_drm_virtual_output_api *api;
715 const char *make = "Renesas";
716 const char *model = "Virtual Display";
717 const char *serial_number = "unknown";
718 const char *connector_name = "remoting";
719
720 if (!name || !strlen(name))
721 return NULL;
722
723 api = remoting->virtual_output_api;
724
725 output = zalloc(sizeof *output);
726 if (!output)
727 return NULL;
728
729 head = zalloc(sizeof *head);
730 if (!head)
731 goto err;
732
733 if (remoting_gstpipe_init(c, output) < 0) {
734 weston_log("Can not create pipe for gstreamer\n");
735 goto err;
736 }
737
738 output->output = api->create_output(c, name);
739 if (!output->output) {
740 weston_log("Can not create virtual output\n");
741 goto err;
742 }
743
744 output->saved_destroy = output->output->destroy;
745 output->output->destroy = remoting_output_destroy;
746 output->saved_enable = output->output->enable;
747 output->output->enable = remoting_output_enable;
748 output->saved_disable = output->output->disable;
749 output->output->disable = remoting_output_disable;
750 output->remoting = remoting;
751 wl_list_insert(remoting->output_list.prev, &output->link);
752
753 weston_head_init(head, connector_name);
754 weston_head_set_subpixel(head, WL_OUTPUT_SUBPIXEL_NONE);
755 weston_head_set_monitor_strings(head, make, model, serial_number);
756 head->compositor = c;
757
758 weston_output_attach_head(output->output, head);
759 output->head = head;
760
761 /* set XRGB8888 format */
762 output->format = &supported_formats[0];
763
764 return output->output;
765
766err:
767 if (output->gstpipe.source)
768 remoting_gstpipe_release(&output->gstpipe);
769 if (head)
770 free(head);
771 free(output);
772 return NULL;
773}
774
775static bool
776remoting_output_is_remoted(struct weston_output *output)
777{
778 struct remoted_output *remoted_output = lookup_remoted_output(output);
779
780 if (remoted_output)
781 return true;
782
783 return false;
784}
785
786static int
787remoting_output_set_mode(struct weston_output *output, const char *modeline)
788{
789 struct weston_mode *mode;
790 int n, width, height, refresh = 0;
791
792 if (!remoting_output_is_remoted(output)) {
793 weston_log("Output is not remoted.\n");
794 return -1;
795 }
796
797 if (!modeline)
798 return -1;
799
800 n = sscanf(modeline, "%dx%d@%d", &width, &height, &refresh);
801 if (n != 2 && n != 3)
802 return -1;
803
804 mode = zalloc(sizeof *mode);
805 if (!mode)
806 return -1;
807
808 mode->flags = WL_OUTPUT_MODE_CURRENT;
809 mode->width = width;
810 mode->height = height;
811 mode->refresh = (refresh ? refresh : 60) * 1000LL;
812
813 wl_list_insert(output->mode_list.prev, &mode->link);
814
815 output->current_mode = mode;
816
817 return 0;
818}
819
820static void
821remoting_output_set_gbm_format(struct weston_output *output,
822 const char *gbm_format)
823{
824 struct remoted_output *remoted_output = lookup_remoted_output(output);
825 const struct weston_drm_virtual_output_api *api;
826 uint32_t format, i;
827
828 if (!remoted_output)
829 return;
830
831 api = remoted_output->remoting->virtual_output_api;
832 format = api->set_gbm_format(output, gbm_format);
833
834 for (i = 0; i < ARRAY_LENGTH(supported_formats); i++) {
835 if (format == supported_formats[i].gbm_format) {
836 remoted_output->format = &supported_formats[i];
837 return;
838 }
839 }
840}
841
842static void
843remoting_output_set_seat(struct weston_output *output, const char *seat)
844{
845 /* for now, nothing todo */
846}
847
848static void
849remoting_output_set_host(struct weston_output *output, char *host)
850{
851 struct remoted_output *remoted_output = lookup_remoted_output(output);
852
853 if (!remoted_output)
854 return;
855
856 if (remoted_output->host)
857 free(remoted_output->host);
858 remoted_output->host = strdup(host);
859}
860
861static void
862remoting_output_set_port(struct weston_output *output, int port)
863{
864 struct remoted_output *remoted_output = lookup_remoted_output(output);
865
866 if (remoted_output)
867 remoted_output->port = port;
868}
869
Tomohito Esaki3ea03bb2019-05-22 12:21:32 +0900870static void
871remoting_output_set_gst_pipeline(struct weston_output *output,
872 char *gst_pipeline)
873{
874 struct remoted_output *remoted_output = lookup_remoted_output(output);
875
876 if (!remoted_output)
877 return;
878
879 if (remoted_output->gst_pipeline)
880 free(remoted_output->gst_pipeline);
881 remoted_output->gst_pipeline = strdup(gst_pipeline);
882}
883
Tomohito Esakif709d222018-01-24 17:08:02 +0900884static const struct weston_remoting_api remoting_api = {
885 remoting_output_create,
886 remoting_output_is_remoted,
887 remoting_output_set_mode,
888 remoting_output_set_gbm_format,
889 remoting_output_set_seat,
890 remoting_output_set_host,
891 remoting_output_set_port,
Tomohito Esaki3ea03bb2019-05-22 12:21:32 +0900892 remoting_output_set_gst_pipeline,
Tomohito Esakif709d222018-01-24 17:08:02 +0900893};
894
895WL_EXPORT int
896weston_module_init(struct weston_compositor *compositor)
897{
898 int ret;
899 struct weston_remoting *remoting;
900 const struct weston_drm_virtual_output_api *api =
901 weston_drm_virtual_output_get_api(compositor);
902
903 if (!api)
904 return -1;
905
906 remoting = zalloc(sizeof *remoting);
907 if (!remoting)
908 return -1;
909
910 remoting->virtual_output_api = api;
911 remoting->compositor = compositor;
912 wl_list_init(&remoting->output_list);
913
914 ret = weston_plugin_api_register(compositor, WESTON_REMOTING_API_NAME,
915 &remoting_api, sizeof(remoting_api));
916
917 if (ret < 0) {
918 weston_log("Failed to register remoting API.\n");
919 goto failed;
920 }
921
922 /* Initialize gstreamer */
923 ret = remoting_gst_init(remoting);
924 if (ret < 0) {
925 weston_log("Failed to initialize gstreamer.\n");
926 goto failed;
927 }
928
929 remoting->destroy_listener.notify = weston_remoting_destroy;
930 wl_signal_add(&compositor->destroy_signal, &remoting->destroy_listener);
931 return 0;
932
933failed:
934 free(remoting);
935 return -1;
936}