diff options
author | Tomohito Esaki <etom@igel.co.jp> | 2018-01-24 17:08:02 +0900 |
---|---|---|
committer | Tomohito Esaki <etom@igel.co.jp> | 2018-10-30 17:09:01 +0900 |
commit | f709d220388f1c57a699412f6df13ab5342b9453 (patch) | |
tree | 5ee24a4d6f3eba0334bdca4034221a676c651608 /remoting | |
parent | f59dc1112be50467b7c0f8aeba68f3aa10d36725 (diff) | |
download | weston-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/README | 28 | ||||
-rw-r--r-- | remoting/remoting-plugin.c | 907 | ||||
-rw-r--r-- | remoting/remoting-plugin.h | 74 |
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, + ¤t_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 */ |