summaryrefslogtreecommitdiff
path: root/remoting
diff options
context:
space:
mode:
authorTomohito Esaki <etom@igel.co.jp>2018-01-24 17:08:02 +0900
committerTomohito Esaki <etom@igel.co.jp>2018-10-30 17:09:01 +0900
commitf709d220388f1c57a699412f6df13ab5342b9453 (patch)
tree5ee24a4d6f3eba0334bdca4034221a676c651608 /remoting
parentf59dc1112be50467b7c0f8aeba68f3aa10d36725 (diff)
downloadweston-f709d220388f1c57a699412f6df13ab5342b9453.tar.gz
Add remoting plugin for output streaming
Remoting plugin support streaming image of virtual output on drm-backend to remote output. By appending remote-output section in weston.ini, weston loads remoting plugin module and creates virtual outputs via remoting plugin. The mode, host, and port properties are configurable in remote-output section. This plugin send motion jpeg images to client via RTP using gstreamer. Client can receive by using following pipeline of gst-launch. gst-launch-1.0 rtpbin name=rtpbin \ udpsrc caps="application/x-rtp,media=(string)video,clock-rate=(int)90000, encoding-name=JPEG,payload=26" port=[PORTNUMBER] ! rtpbin.recv_rtp_sink_0 \ rtpbin. ! rtpjpegdepay ! jpegdec ! autovideosink \ udpsrc port=[PORTNUMBER+1] ! rtpbin.recv_rtcp_sink_0 \ rtpbin.send_rtcp_src_0 ! udpsink port=[PORTNUMBER+2] sync=false async=false where, PORTNUMBER is specified in weston.ini. Signed-off-by: Tomohito Esaki <etom@igel.co.jp>
Diffstat (limited to 'remoting')
-rw-r--r--remoting/README28
-rw-r--r--remoting/remoting-plugin.c907
-rw-r--r--remoting/remoting-plugin.h74
3 files changed, 1009 insertions, 0 deletions
diff --git a/remoting/README b/remoting/README
new file mode 100644
index 00000000..2166d30e
--- /dev/null
+++ b/remoting/README
@@ -0,0 +1,28 @@
+ Remoting plugin for Weston
+
+
+The Remoting plugin creates a streaming image of a virtual output and transmits
+it to a remote host. It is currently only supported on the drm-backend. Virtual
+outputs are created and configured by adding a remote-output section to
+weston.ini. See man weston-drm(7) for configuration details. This plugin is
+loaded automatically if any remote-output sections are present.
+
+This plugin sends motion jpeg images to a client via RTP using gstreamer, and
+so requires gstreamer-1.0. This plugin starts sending images immediately when
+weston is run, and keeps sending them until weston shuts down. The image stream
+can be received by any appropriately configured RTP client, but a sample
+gstreamer RTP client script can be found at doc/remoting-client-receive.bash.
+
+Script usage:
+ remoting-client-receive.bash <PORT NUMBER>
+
+
+How to compile
+---------------
+Set --enable-remoting=true when configuring weston. The remoting-plugin.so
+module is created and installed in the libweston path.
+
+
+How to configure weston.ini
+----------------------------
+See man weston-drm(7).
diff --git a/remoting/remoting-plugin.c b/remoting/remoting-plugin.c
new file mode 100644
index 00000000..3715b22b
--- /dev/null
+++ b/remoting/remoting-plugin.c
@@ -0,0 +1,907 @@
+/*
+ * Copyright © 2018 Renesas Electronics Corp.
+ *
+ * Based on vaapi-recorder by:
+ * Copyright (c) 2012 Intel Corporation. All Rights Reserved.
+ * Copyright © 2013 Intel Corporation
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors: IGEL Co., Ltd.
+ */
+
+#include "config.h"
+
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <gbm.h>
+
+#include <gst/gst.h>
+#include <gst/allocators/gstdmabuf.h>
+#include <gst/app/gstappsrc.h>
+#include <gst/video/gstvideometa.h>
+
+#include "remoting-plugin.h"
+#include "compositor-drm.h"
+#include "shared/helpers.h"
+#include "shared/timespec-util.h"
+
+#define MAX_RETRY_COUNT 3
+
+struct weston_remoting {
+ struct weston_compositor *compositor;
+ struct wl_list output_list;
+ struct wl_listener destroy_listener;
+ const struct weston_drm_virtual_output_api *virtual_output_api;
+
+ GstAllocator *allocator;
+};
+
+struct remoted_gstpipe {
+ int readfd;
+ int writefd;
+ struct wl_event_source *source;
+};
+
+/* supported gbm format list */
+struct remoted_output_support_gbm_format {
+ uint32_t gbm_format;
+ const char *gst_format_string;
+ GstVideoFormat gst_video_format;
+};
+
+static const struct remoted_output_support_gbm_format supported_formats[] = {
+ {
+ .gbm_format = GBM_FORMAT_XRGB8888,
+ .gst_format_string = "BGRx",
+ .gst_video_format = GST_VIDEO_FORMAT_BGRx,
+ }, {
+ .gbm_format = GBM_FORMAT_RGB565,
+ .gst_format_string = "RGB16",
+ .gst_video_format = GST_VIDEO_FORMAT_RGB16,
+ }, {
+ .gbm_format = GBM_FORMAT_XRGB2101010,
+ .gst_format_string = "r210",
+ .gst_video_format = GST_VIDEO_FORMAT_r210,
+ }
+};
+
+struct remoted_output {
+ struct weston_output *output;
+ void (*saved_destroy)(struct weston_output *output);
+ int (*saved_enable)(struct weston_output *output);
+ int (*saved_disable)(struct weston_output *output);
+ void (*saved_start_repaint_loop)(struct weston_output *output);
+
+ char *host;
+ int port;
+ const struct remoted_output_support_gbm_format *format;
+
+ struct weston_head *head;
+
+ struct weston_remoting *remoting;
+ struct wl_event_source *finish_frame_timer;
+ struct wl_list link;
+ bool submitted_frame;
+ int fence_sync_fd;
+ struct wl_event_source *fence_sync_event_source;
+
+ GstElement *pipeline;
+ GstAppSrc *appsrc;
+ GstBus *bus;
+ struct remoted_gstpipe gstpipe;
+ GstClockTime start_time;
+ int retry_count;
+};
+
+struct mem_free_cb_data {
+ struct remoted_output *output;
+ struct drm_fb *output_buffer;
+};
+
+struct gst_frame_buffer_data {
+ struct remoted_output *output;
+ GstBuffer *buffer;
+};
+
+/* message type for pipe */
+#define GSTPIPE_MSG_BUS_SYNC 1
+#define GSTPIPE_MSG_BUFFER_RELEASE 2
+
+struct gstpipe_msg_data {
+ int type;
+ void *data;
+};
+
+static int
+remoting_gst_init(struct weston_remoting *remoting)
+{
+ GError *err = NULL;
+
+ if (!gst_init_check(NULL, NULL, &err)) {
+ weston_log("GStreamer initialization error: %s\n",
+ err->message);
+ g_error_free(err);
+ return -1;
+ }
+
+ remoting->allocator = gst_dmabuf_allocator_new();
+
+ return 0;
+}
+
+static void
+remoting_gst_deinit(struct weston_remoting *remoting)
+{
+ gst_object_unref(remoting->allocator);
+}
+
+static GstBusSyncReply
+remoting_gst_bus_sync_handler(GstBus *bus, GstMessage *message,
+ gpointer user_data)
+{
+ struct remoted_gstpipe *pipe = user_data;
+ struct gstpipe_msg_data msg = {
+ .type = GSTPIPE_MSG_BUS_SYNC,
+ .data = NULL
+ };
+ ssize_t ret;
+
+ ret = write(pipe->writefd, &msg, sizeof(msg));
+ if (ret != sizeof(msg))
+ weston_log("ERROR: failed to write, ret=%zd, errno=%d\n",
+ ret, errno);
+
+ return GST_BUS_PASS;
+}
+
+static int
+remoting_gst_pipeline_init(struct remoted_output *output)
+{
+ char pipeline_str[1024];
+ GstCaps *caps;
+ GError *err = NULL;
+ GstStateChangeReturn ret;
+ struct weston_mode *mode = output->output->current_mode;
+
+ /* TODO: use encodebin instead of jpegenc */
+ snprintf(pipeline_str, sizeof(pipeline_str),
+ "rtpbin name=rtpbin "
+ "appsrc name=src ! videoconvert ! video/x-raw,format=I420 ! "
+ "jpegenc ! rtpjpegpay ! rtpbin.send_rtp_sink_0 "
+ "rtpbin.send_rtp_src_0 ! udpsink name=sink host=%s port=%d "
+ "rtpbin.send_rtcp_src_0 ! "
+ "udpsink host=%s port=%d sync=false async=false "
+ "udpsrc port=%d ! rtpbin.recv_rtcp_sink_0",
+ output->host, output->port, output->host, output->port + 1,
+ output->port + 2);
+ weston_log("GST pipeline: %s\n", pipeline_str);
+
+ output->pipeline = gst_parse_launch(pipeline_str, &err);
+ if (!output->pipeline) {
+ weston_log("Could not create gstreamer pipeline. Error: %s\n",
+ err->message);
+ g_error_free(err);
+ return -1;
+ }
+
+ output->appsrc = (GstAppSrc*)
+ gst_bin_get_by_name(GST_BIN(output->pipeline), "src");
+ if (!output->appsrc) {
+ weston_log("Could not get appsrc from gstreamer pipeline\n");
+ goto err;
+ }
+
+ caps = gst_caps_new_simple("video/x-raw",
+ "format", G_TYPE_STRING,
+ output->format->gst_format_string,
+ "width", G_TYPE_INT, mode->width,
+ "height", G_TYPE_INT, mode->height,
+ "framerate", GST_TYPE_FRACTION,
+ mode->refresh, 1000,
+ NULL);
+ if (!caps) {
+ weston_log("Could not create gstreamer caps.\n");
+ goto err;
+ }
+ g_object_set(G_OBJECT(output->appsrc),
+ "caps", caps,
+ "stream-type", 0,
+ "format", GST_FORMAT_TIME,
+ "is-live", TRUE,
+ NULL);
+ gst_caps_unref(caps);
+
+ output->bus = gst_pipeline_get_bus(GST_PIPELINE(output->pipeline));
+ if (!output->bus) {
+ weston_log("Could not get bus from gstreamer pipeline\n");
+ goto err;
+ }
+ gst_bus_set_sync_handler(output->bus, remoting_gst_bus_sync_handler,
+ &output->gstpipe, NULL);
+
+ output->start_time = 0;
+ ret = gst_element_set_state(output->pipeline, GST_STATE_PLAYING);
+ if (ret == GST_STATE_CHANGE_FAILURE) {
+ weston_log("Couldn't set GST_STATE_PLAYING to pipeline\n");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ gst_object_unref(GST_OBJECT(output->pipeline));
+ output->pipeline = NULL;
+ return -1;
+}
+
+static void
+remoting_gst_pipeline_deinit(struct remoted_output *output)
+{
+ if (!output->pipeline)
+ return;
+
+ gst_element_set_state(output->pipeline, GST_STATE_NULL);
+ if (output->bus)
+ gst_object_unref(GST_OBJECT(output->bus));
+ gst_object_unref(GST_OBJECT(output->pipeline));
+ output->pipeline = NULL;
+}
+
+static int
+remoting_output_disable(struct weston_output *output);
+
+static void
+remoting_gst_restart(void *data)
+{
+ struct remoted_output *output = data;
+
+ if (remoting_gst_pipeline_init(output) < 0) {
+ weston_log("gst: Could not restart pipeline!!\n");
+ remoting_output_disable(output->output);
+ }
+}
+
+static void
+remoting_gst_schedule_restart(struct remoted_output *output)
+{
+ struct wl_event_loop *loop;
+ struct weston_compositor *c = output->remoting->compositor;
+
+ loop = wl_display_get_event_loop(c->wl_display);
+ wl_event_loop_add_idle(loop, remoting_gst_restart, output);
+}
+
+static void
+remoting_gst_bus_message_handler(struct remoted_output *output)
+{
+ GstMessage *message;
+ GError *error;
+ gchar *debug;
+
+ /* get message from bus queue */
+ message = gst_bus_pop(output->bus);
+ if (!message)
+ return;
+
+ switch (GST_MESSAGE_TYPE(message)) {
+ case GST_MESSAGE_STATE_CHANGED: {
+ GstState new_state;
+ gst_message_parse_state_changed(message, NULL, &new_state,
+ NULL);
+ if (!strcmp(GST_OBJECT_NAME(message->src), "sink") &&
+ new_state == GST_STATE_PLAYING)
+ output->retry_count = 0;
+ break;
+ }
+ case GST_MESSAGE_WARNING:
+ gst_message_parse_warning(message, &error, &debug);
+ weston_log("gst: Warning: %s: %s\n",
+ GST_OBJECT_NAME(message->src), error->message);
+ break;
+ case GST_MESSAGE_ERROR:
+ gst_message_parse_error(message, &error, &debug);
+ weston_log("gst: Error: %s: %s\n",
+ GST_OBJECT_NAME(message->src), error->message);
+ if (output->retry_count < MAX_RETRY_COUNT) {
+ output->retry_count++;
+ remoting_gst_pipeline_deinit(output);
+ remoting_gst_schedule_restart(output);
+ } else {
+ remoting_output_disable(output->output);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+remoting_output_buffer_release(struct remoted_output *output, void *buffer)
+{
+ const struct weston_drm_virtual_output_api *api
+ = output->remoting->virtual_output_api;
+
+ api->buffer_released(buffer);
+}
+
+static int
+remoting_gstpipe_handler(int fd, uint32_t mask, void *data)
+{
+ ssize_t ret;
+ struct gstpipe_msg_data msg;
+ struct remoted_output *output = data;
+
+ /* recieve message */
+ ret = read(fd, &msg, sizeof(msg));
+ if (ret != sizeof(msg)) {
+ weston_log("ERROR: failed to read, ret=%zd, errno=%d\n",
+ ret, errno);
+ remoting_output_disable(output->output);
+ return 0;
+ }
+
+ switch (msg.type) {
+ case GSTPIPE_MSG_BUS_SYNC:
+ remoting_gst_bus_message_handler(output);
+ break;
+ case GSTPIPE_MSG_BUFFER_RELEASE:
+ remoting_output_buffer_release(output, msg.data);
+ break;
+ default:
+ weston_log("Recieved unknown message! msg=%d\n", msg.type);
+ }
+ return 1;
+}
+
+static int
+remoting_gstpipe_init(struct weston_compositor *c,
+ struct remoted_output *output)
+{
+ struct wl_event_loop *loop;
+ int fd[2];
+
+ if (pipe2(fd, O_CLOEXEC) == -1)
+ return -1;
+
+ output->gstpipe.readfd = fd[0];
+ output->gstpipe.writefd = fd[1];
+ loop = wl_display_get_event_loop(c->wl_display);
+ output->gstpipe.source =
+ wl_event_loop_add_fd(loop, output->gstpipe.readfd,
+ WL_EVENT_READABLE,
+ remoting_gstpipe_handler, output);
+ if (!output->gstpipe.source) {
+ close(fd[0]);
+ close(fd[1]);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void
+remoting_gstpipe_release(struct remoted_gstpipe *pipe)
+{
+ wl_event_source_remove(pipe->source);
+ close(pipe->readfd);
+ close(pipe->writefd);
+}
+
+static void
+remoting_output_destroy(struct weston_output *output);
+
+static void
+weston_remoting_destroy(struct wl_listener *l, void *data)
+{
+ struct weston_remoting *remoting =
+ container_of(l, struct weston_remoting, destroy_listener);
+ struct remoted_output *output, *next;
+
+ wl_list_for_each_safe(output, next, &remoting->output_list, link)
+ remoting_output_destroy(output->output);
+
+ /* Finalize gstreamer */
+ remoting_gst_deinit(remoting);
+
+ wl_list_remove(&remoting->destroy_listener.link);
+ free(remoting);
+}
+
+static struct weston_remoting *
+weston_remoting_get(struct weston_compositor *compositor)
+{
+ struct wl_listener *listener;
+ struct weston_remoting *remoting;
+
+ listener = wl_signal_get(&compositor->destroy_signal,
+ weston_remoting_destroy);
+ if (!listener)
+ return NULL;
+
+ remoting = wl_container_of(listener, remoting, destroy_listener);
+ return remoting;
+}
+
+static int
+remoting_output_finish_frame_handler(void *data)
+{
+ struct remoted_output *output = data;
+ const struct weston_drm_virtual_output_api *api
+ = output->remoting->virtual_output_api;
+ struct timespec now;
+ int64_t msec;
+
+ if (output->submitted_frame) {
+ struct weston_compositor *c = output->remoting->compositor;
+ output->submitted_frame = false;
+ weston_compositor_read_presentation_clock(c, &now);
+ api->finish_frame(output->output, &now, 0);
+ }
+
+ msec = millihz_to_nsec(output->output->current_mode->refresh) / 1000000;
+ wl_event_source_timer_update(output->finish_frame_timer, msec);
+ return 0;
+}
+
+static void
+remoting_gst_mem_free_cb(struct mem_free_cb_data *cb_data, GstMiniObject *obj)
+{
+ struct remoted_output *output = cb_data->output;
+ struct remoted_gstpipe *pipe = &output->gstpipe;
+ struct gstpipe_msg_data msg = {
+ .type = GSTPIPE_MSG_BUFFER_RELEASE,
+ .data = cb_data->output_buffer
+ };
+ ssize_t ret;
+
+ ret = write(pipe->writefd, &msg, sizeof(msg));
+ if (ret != sizeof(msg))
+ weston_log("ERROR: failed to write, ret=%zd, errno=%d\n", ret,
+ errno);
+ free(cb_data);
+}
+
+static struct remoted_output *
+lookup_remoted_output(struct weston_output *output)
+{
+ struct weston_compositor *c = output->compositor;
+ struct weston_remoting *remoting = weston_remoting_get(c);
+ struct remoted_output *remoted_output;
+
+ wl_list_for_each(remoted_output, &remoting->output_list, link) {
+ if (remoted_output->output == output)
+ return remoted_output;
+ }
+
+ weston_log("%s: %s: could not find output\n", __FILE__, __func__);
+ return NULL;
+}
+
+static void
+remoting_output_gst_push_buffer(struct remoted_output *output,
+ GstBuffer *buffer)
+{
+ struct timespec current_frame_ts;
+ GstClockTime ts, current_frame_time;
+
+ weston_compositor_read_presentation_clock(output->remoting->compositor,
+ &current_frame_ts);
+ current_frame_time = GST_TIMESPEC_TO_TIME(current_frame_ts);
+ if (output->start_time == 0)
+ output->start_time = current_frame_time;
+ ts = current_frame_time - output->start_time;
+
+ if (GST_CLOCK_TIME_IS_VALID(ts))
+ GST_BUFFER_PTS(buffer) = ts;
+ else
+ GST_BUFFER_PTS(buffer) = GST_CLOCK_TIME_NONE;
+ GST_BUFFER_DURATION(buffer) = GST_CLOCK_TIME_NONE;
+
+ gst_app_src_push_buffer(output->appsrc, buffer);
+ output->submitted_frame = true;
+}
+
+static int
+remoting_output_fence_sync_handler(int fd, uint32_t mask, void *data)
+{
+ struct gst_frame_buffer_data *frame_data = data;
+ struct remoted_output *output = frame_data->output;
+
+ remoting_output_gst_push_buffer(output, frame_data->buffer);
+
+ wl_event_source_remove(output->fence_sync_event_source);
+ close(output->fence_sync_fd);
+ free(frame_data);
+
+ return 0;
+}
+
+static int
+remoting_output_frame(struct weston_output *output_base, int fd, int stride,
+ struct drm_fb *output_buffer)
+{
+ struct remoted_output *output = lookup_remoted_output(output_base);
+ struct weston_remoting *remoting = output->remoting;
+ struct weston_mode *mode;
+ const struct weston_drm_virtual_output_api *api
+ = output->remoting->virtual_output_api;
+ struct wl_event_loop *loop;
+ GstBuffer *buf;
+ GstMemory *mem;
+ gsize offset = 0;
+ struct mem_free_cb_data *cb_data;
+ struct gst_frame_buffer_data *frame_data;
+
+ if (!output)
+ return -1;
+
+ cb_data = zalloc(sizeof *cb_data);
+ if (!cb_data)
+ return -1;
+
+ mode = output->output->current_mode;
+ buf = gst_buffer_new();
+ mem = gst_dmabuf_allocator_alloc(remoting->allocator, fd,
+ stride * mode->height);
+ gst_buffer_append_memory(buf, mem);
+ gst_buffer_add_video_meta_full(buf,
+ GST_VIDEO_FRAME_FLAG_NONE,
+ output->format->gst_video_format,
+ mode->width,
+ mode->height,
+ 1,
+ &offset,
+ &stride);
+
+ cb_data->output = output;
+ cb_data->output_buffer = output_buffer;
+ gst_mini_object_weak_ref(GST_MINI_OBJECT(mem),
+ (GstMiniObjectNotify)remoting_gst_mem_free_cb,
+ cb_data);
+
+ output->fence_sync_fd = api->get_fence_sync_fd(output->output);
+ /* Push buffer to gstreamer immediately on get_fence_sync_fd failure */
+ if (output->fence_sync_fd == -1) {
+ remoting_output_gst_push_buffer(output, buf);
+ return 0;
+ }
+
+ frame_data = zalloc(sizeof *frame_data);
+ if (!frame_data) {
+ close(output->fence_sync_fd);
+ remoting_output_gst_push_buffer(output, buf);
+ return 0;
+ }
+
+ frame_data->output = output;
+ frame_data->buffer = buf;
+ loop = wl_display_get_event_loop(remoting->compositor->wl_display);
+ output->fence_sync_event_source =
+ wl_event_loop_add_fd(loop, output->fence_sync_fd,
+ WL_EVENT_READABLE,
+ remoting_output_fence_sync_handler,
+ frame_data);
+
+ return 0;
+}
+
+static void
+remoting_output_destroy(struct weston_output *output)
+{
+ struct remoted_output *remoted_output = lookup_remoted_output(output);
+ struct weston_mode *mode, *next;
+
+ wl_list_for_each_safe(mode, next, &output->mode_list, link) {
+ wl_list_remove(&mode->link);
+ free(mode);
+ }
+
+ remoted_output->saved_destroy(output);
+
+ remoting_gst_pipeline_deinit(remoted_output);
+ remoting_gstpipe_release(&remoted_output->gstpipe);
+
+ if (remoted_output->host)
+ free(remoted_output->host);
+
+ wl_list_remove(&remoted_output->link);
+ weston_head_release(remoted_output->head);
+ free(remoted_output->head);
+ free(remoted_output);
+}
+
+static void
+remoting_output_start_repaint_loop(struct weston_output *output)
+{
+ struct remoted_output *remoted_output = lookup_remoted_output(output);
+ int64_t msec;
+
+ remoted_output->saved_start_repaint_loop(output);
+
+ msec = millihz_to_nsec(remoted_output->output->current_mode->refresh)
+ / 1000000;
+ wl_event_source_timer_update(remoted_output->finish_frame_timer, msec);
+}
+
+static int
+remoting_output_enable(struct weston_output *output)
+{
+ struct remoted_output *remoted_output = lookup_remoted_output(output);
+ struct weston_compositor *c = output->compositor;
+ const struct weston_drm_virtual_output_api *api
+ = remoted_output->remoting->virtual_output_api;
+ struct wl_event_loop *loop;
+ int ret;
+
+ api->set_submit_frame_cb(output, remoting_output_frame);
+
+ ret = remoted_output->saved_enable(output);
+ if (ret < 0)
+ return ret;
+
+ remoted_output->saved_start_repaint_loop = output->start_repaint_loop;
+ output->start_repaint_loop = remoting_output_start_repaint_loop;
+
+ ret = remoting_gst_pipeline_init(remoted_output);
+ if (ret < 0) {
+ remoted_output->saved_disable(output);
+ return ret;
+ }
+
+ loop = wl_display_get_event_loop(c->wl_display);
+ remoted_output->finish_frame_timer =
+ wl_event_loop_add_timer(loop,
+ remoting_output_finish_frame_handler,
+ remoted_output);
+
+ return 0;
+}
+
+static int
+remoting_output_disable(struct weston_output *output)
+{
+ struct remoted_output *remoted_output = lookup_remoted_output(output);
+
+ wl_event_source_remove(remoted_output->finish_frame_timer);
+ remoting_gst_pipeline_deinit(remoted_output);
+
+ return remoted_output->saved_disable(output);
+}
+
+static struct weston_output *
+remoting_output_create(struct weston_compositor *c, char *name)
+{
+ struct weston_remoting *remoting = weston_remoting_get(c);
+ struct remoted_output *output;
+ struct weston_head *head;
+ const struct weston_drm_virtual_output_api *api;
+ const char *make = "Renesas";
+ const char *model = "Virtual Display";
+ const char *serial_number = "unknown";
+ const char *connector_name = "remoting";
+
+ if (!name || !strlen(name))
+ return NULL;
+
+ api = remoting->virtual_output_api;
+
+ output = zalloc(sizeof *output);
+ if (!output)
+ return NULL;
+
+ head = zalloc(sizeof *head);
+ if (!head)
+ goto err;
+
+ if (remoting_gstpipe_init(c, output) < 0) {
+ weston_log("Can not create pipe for gstreamer\n");
+ goto err;
+ }
+
+ output->output = api->create_output(c, name);
+ if (!output->output) {
+ weston_log("Can not create virtual output\n");
+ goto err;
+ }
+
+ output->saved_destroy = output->output->destroy;
+ output->output->destroy = remoting_output_destroy;
+ output->saved_enable = output->output->enable;
+ output->output->enable = remoting_output_enable;
+ output->saved_disable = output->output->disable;
+ output->output->disable = remoting_output_disable;
+ output->remoting = remoting;
+ wl_list_insert(remoting->output_list.prev, &output->link);
+
+ weston_head_init(head, connector_name);
+ weston_head_set_subpixel(head, WL_OUTPUT_SUBPIXEL_NONE);
+ weston_head_set_monitor_strings(head, make, model, serial_number);
+ head->compositor = c;
+
+ weston_output_attach_head(output->output, head);
+ output->head = head;
+
+ /* set XRGB8888 format */
+ output->format = &supported_formats[0];
+
+ return output->output;
+
+err:
+ if (output->gstpipe.source)
+ remoting_gstpipe_release(&output->gstpipe);
+ if (head)
+ free(head);
+ free(output);
+ return NULL;
+}
+
+static bool
+remoting_output_is_remoted(struct weston_output *output)
+{
+ struct remoted_output *remoted_output = lookup_remoted_output(output);
+
+ if (remoted_output)
+ return true;
+
+ return false;
+}
+
+static int
+remoting_output_set_mode(struct weston_output *output, const char *modeline)
+{
+ struct weston_mode *mode;
+ int n, width, height, refresh = 0;
+
+ if (!remoting_output_is_remoted(output)) {
+ weston_log("Output is not remoted.\n");
+ return -1;
+ }
+
+ if (!modeline)
+ return -1;
+
+ n = sscanf(modeline, "%dx%d@%d", &width, &height, &refresh);
+ if (n != 2 && n != 3)
+ return -1;
+
+ mode = zalloc(sizeof *mode);
+ if (!mode)
+ return -1;
+
+ mode->flags = WL_OUTPUT_MODE_CURRENT;
+ mode->width = width;
+ mode->height = height;
+ mode->refresh = (refresh ? refresh : 60) * 1000LL;
+
+ wl_list_insert(output->mode_list.prev, &mode->link);
+
+ output->current_mode = mode;
+
+ return 0;
+}
+
+static void
+remoting_output_set_gbm_format(struct weston_output *output,
+ const char *gbm_format)
+{
+ struct remoted_output *remoted_output = lookup_remoted_output(output);
+ const struct weston_drm_virtual_output_api *api;
+ uint32_t format, i;
+
+ if (!remoted_output)
+ return;
+
+ api = remoted_output->remoting->virtual_output_api;
+ format = api->set_gbm_format(output, gbm_format);
+
+ for (i = 0; i < ARRAY_LENGTH(supported_formats); i++) {
+ if (format == supported_formats[i].gbm_format) {
+ remoted_output->format = &supported_formats[i];
+ return;
+ }
+ }
+}
+
+static void
+remoting_output_set_seat(struct weston_output *output, const char *seat)
+{
+ /* for now, nothing todo */
+}
+
+static void
+remoting_output_set_host(struct weston_output *output, char *host)
+{
+ struct remoted_output *remoted_output = lookup_remoted_output(output);
+
+ if (!remoted_output)
+ return;
+
+ if (remoted_output->host)
+ free(remoted_output->host);
+ remoted_output->host = strdup(host);
+}
+
+static void
+remoting_output_set_port(struct weston_output *output, int port)
+{
+ struct remoted_output *remoted_output = lookup_remoted_output(output);
+
+ if (remoted_output)
+ remoted_output->port = port;
+}
+
+static const struct weston_remoting_api remoting_api = {
+ remoting_output_create,
+ remoting_output_is_remoted,
+ remoting_output_set_mode,
+ remoting_output_set_gbm_format,
+ remoting_output_set_seat,
+ remoting_output_set_host,
+ remoting_output_set_port,
+};
+
+WL_EXPORT int
+weston_module_init(struct weston_compositor *compositor)
+{
+ int ret;
+ struct weston_remoting *remoting;
+ const struct weston_drm_virtual_output_api *api =
+ weston_drm_virtual_output_get_api(compositor);
+
+ if (!api)
+ return -1;
+
+ remoting = zalloc(sizeof *remoting);
+ if (!remoting)
+ return -1;
+
+ remoting->virtual_output_api = api;
+ remoting->compositor = compositor;
+ wl_list_init(&remoting->output_list);
+
+ ret = weston_plugin_api_register(compositor, WESTON_REMOTING_API_NAME,
+ &remoting_api, sizeof(remoting_api));
+
+ if (ret < 0) {
+ weston_log("Failed to register remoting API.\n");
+ goto failed;
+ }
+
+ /* Initialize gstreamer */
+ ret = remoting_gst_init(remoting);
+ if (ret < 0) {
+ weston_log("Failed to initialize gstreamer.\n");
+ goto failed;
+ }
+
+ remoting->destroy_listener.notify = weston_remoting_destroy;
+ wl_signal_add(&compositor->destroy_signal, &remoting->destroy_listener);
+ return 0;
+
+failed:
+ free(remoting);
+ return -1;
+}
diff --git a/remoting/remoting-plugin.h b/remoting/remoting-plugin.h
new file mode 100644
index 00000000..9a7ca365
--- /dev/null
+++ b/remoting/remoting-plugin.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright © 2018 Renesas Electronics Corp.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial
+ * portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Authors: IGEL Co., Ltd.
+ */
+
+#ifndef REMOTING_PLUGIN_H
+#define REMOTING_PLUGIN_H
+
+#include "compositor.h"
+#include "plugin-registry.h"
+
+#define WESTON_REMOTING_API_NAME "weston_remoting_api_v1"
+
+struct weston_remoting_api {
+ /** Create remoted outputs
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+ struct weston_output *(*create_output)(struct weston_compositor *c,
+ char *name);
+
+ /** Check if output is remoted */
+ bool (*is_remoted_output)(struct weston_output *output);
+
+ /** Set mode */
+ int (*set_mode)(struct weston_output *output, const char *modeline);
+
+ /** Set gbm format */
+ void (*set_gbm_format)(struct weston_output *output,
+ const char *gbm_format);
+
+ /** Set seat */
+ void (*set_seat)(struct weston_output *output, const char *seat);
+
+ /** Set the destination Host(IP Address) */
+ void (*set_host)(struct weston_output *output, char *ip);
+
+ /** Set the port number */
+ void (*set_port)(struct weston_output *output, int port);
+};
+
+static inline const struct weston_remoting_api *
+weston_remoting_get_api(struct weston_compositor *compositor)
+{
+ const void *api;
+ api = weston_plugin_api_get(compositor, WESTON_REMOTING_API_NAME,
+ sizeof(struct weston_remoting_api));
+
+ return (const struct weston_remoting_api *)api;
+}
+
+#endif /* REMOTING_PLUGIN_H */