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>
This commit is contained in:
parent
f59dc1112b
commit
f709d22038
17
Makefile.am
17
Makefile.am
|
@ -402,6 +402,23 @@ drm_backend_la_LIBADD += $(LIBVA_LIBS)
|
|||
drm_backend_la_LDFLAGS += -pthread
|
||||
drm_backend_la_CFLAGS += $(LIBVA_CFLAGS)
|
||||
endif
|
||||
|
||||
# remoting
|
||||
if ENABLE_REMOTING
|
||||
libweston_module_LTLIBRARIES += remoting-plugin.la
|
||||
remoting_plugin_la_LDFLAGS = -module -avoid-version
|
||||
remoting_plugin_la_LIBADD = \
|
||||
$(COMPOSITOR_LIBS) \
|
||||
$(REMOTING_GST_LIBS)
|
||||
remoting_plugin_la_CFLAGS = \
|
||||
$(COMPOSITOR_CFLAGS) \
|
||||
$(REMOTING_GST_CFLAGS) \
|
||||
$(AM_CFLAGS)
|
||||
remoting_plugin_la_SOURCES = \
|
||||
remoting/remoting-plugin.c \
|
||||
remoting/remoting-plugin.h
|
||||
endif
|
||||
|
||||
endif
|
||||
|
||||
if ENABLE_WAYLAND_COMPOSITOR
|
||||
|
|
|
@ -61,6 +61,7 @@
|
|||
#include "compositor-wayland.h"
|
||||
#include "windowed-output-api.h"
|
||||
#include "weston-debug.h"
|
||||
#include "../remoting/remoting-plugin.h"
|
||||
|
||||
#define WINDOW_TITLE "Weston Compositor"
|
||||
|
||||
|
@ -1973,6 +1974,154 @@ drm_heads_changed(struct wl_listener *listener, void *arg)
|
|||
wet->init_failed = true;
|
||||
}
|
||||
|
||||
static int
|
||||
drm_backend_remoted_output_configure(struct weston_output *output,
|
||||
struct weston_config_section *section,
|
||||
char *modeline,
|
||||
const struct weston_remoting_api *api)
|
||||
{
|
||||
char *gbm_format = NULL;
|
||||
char *seat = NULL;
|
||||
char *host = NULL;
|
||||
int port, ret;
|
||||
|
||||
ret = api->set_mode(output, modeline);
|
||||
if (ret < 0) {
|
||||
weston_log("Cannot configure an output \"%s\" using "
|
||||
"weston_remoting_api. Invalid mode\n",
|
||||
output->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
wet_output_set_scale(output, section, 1, 0);
|
||||
wet_output_set_transform(output, section, WL_OUTPUT_TRANSFORM_NORMAL,
|
||||
UINT32_MAX);
|
||||
|
||||
weston_config_section_get_string(section, "gbm-format", &gbm_format,
|
||||
NULL);
|
||||
api->set_gbm_format(output, gbm_format);
|
||||
free(gbm_format);
|
||||
|
||||
weston_config_section_get_string(section, "seat", &seat, "");
|
||||
|
||||
api->set_seat(output, seat);
|
||||
free(seat);
|
||||
|
||||
weston_config_section_get_string(section, "host", &host, NULL);
|
||||
if (!host) {
|
||||
weston_log("Cannot configure an output \"%s\". Invalid host\n",
|
||||
output->name);
|
||||
return -1;
|
||||
}
|
||||
api->set_host(output, host);
|
||||
free(host);
|
||||
|
||||
weston_config_section_get_int(section, "port", &port, 0);
|
||||
if (port <= 0 || 65533 < port) {
|
||||
weston_log("Cannot configure an output \"%s\". Invalid port\n",
|
||||
output->name);
|
||||
return -1;
|
||||
}
|
||||
api->set_port(output, port);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
remoted_output_init(struct weston_compositor *c,
|
||||
struct weston_config_section *section,
|
||||
const struct weston_remoting_api *api)
|
||||
{
|
||||
struct weston_output *output = NULL;
|
||||
char *output_name, *modeline = NULL;
|
||||
int ret;
|
||||
|
||||
weston_config_section_get_string(section, "name", &output_name,
|
||||
NULL);
|
||||
if (!output_name)
|
||||
return;
|
||||
|
||||
weston_config_section_get_string(section, "mode", &modeline, "off");
|
||||
if (strcmp(modeline, "off") == 0)
|
||||
goto err;
|
||||
|
||||
output = api->create_output(c, output_name);
|
||||
if (!output) {
|
||||
weston_log("Cannot create remoted output \"%s\".\n",
|
||||
output_name);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = drm_backend_remoted_output_configure(output, section, modeline,
|
||||
api);
|
||||
if (ret < 0) {
|
||||
weston_log("Cannot configure remoted output \"%s\".\n",
|
||||
output_name);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (weston_output_enable(output) < 0) {
|
||||
weston_log("Enabling remoted output \"%s\" failed.\n",
|
||||
output_name);
|
||||
goto err;
|
||||
}
|
||||
|
||||
free(modeline);
|
||||
free(output_name);
|
||||
weston_log("remoted output '%s' enabled\n", output->name);
|
||||
return;
|
||||
|
||||
err:
|
||||
free(modeline);
|
||||
free(output_name);
|
||||
if (output)
|
||||
weston_output_destroy(output);
|
||||
}
|
||||
|
||||
static void
|
||||
load_remoting(struct weston_compositor *c, struct weston_config *wc)
|
||||
{
|
||||
const struct weston_remoting_api *api = NULL;
|
||||
int (*module_init)(struct weston_compositor *ec);
|
||||
struct weston_config_section *section = NULL;
|
||||
const char *section_name;
|
||||
|
||||
/* read remote-output section in weston.ini */
|
||||
while (weston_config_next_section(wc, §ion, §ion_name)) {
|
||||
if (strcmp(section_name, "remote-output"))
|
||||
continue;
|
||||
|
||||
if (!api) {
|
||||
char *module_name;
|
||||
struct weston_config_section *core_section =
|
||||
weston_config_get_section(wc, "core", NULL,
|
||||
NULL);
|
||||
|
||||
weston_config_section_get_string(core_section,
|
||||
"remoting",
|
||||
&module_name,
|
||||
"remoting-plugin.so");
|
||||
module_init = weston_load_module(module_name,
|
||||
"weston_module_init");
|
||||
free(module_name);
|
||||
if (!module_init) {
|
||||
weston_log("Can't load remoting-plugin\n");
|
||||
return;
|
||||
}
|
||||
if (module_init(c) < 0) {
|
||||
weston_log("Remoting-plugin init failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
api = weston_remoting_get_api(c);
|
||||
if (!api)
|
||||
return;
|
||||
}
|
||||
|
||||
remoted_output_init(c, section, api);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
load_drm_backend(struct weston_compositor *c,
|
||||
int *argc, char **argv, struct weston_config *wc)
|
||||
|
@ -2015,6 +2164,9 @@ load_drm_backend(struct weston_compositor *c,
|
|||
ret = weston_compositor_load_backend(c, WESTON_BACKEND_DRM,
|
||||
&config.base);
|
||||
|
||||
/* remoting */
|
||||
load_remoting(c, wc);
|
||||
|
||||
free(config.gbm_format);
|
||||
free(config.seat_id);
|
||||
|
||||
|
|
11
configure.ac
11
configure.ac
|
@ -226,6 +226,16 @@ if test x$enable_drm_compositor = xyes; then
|
|||
[AC_MSG_WARN([GBM does not support dmabuf import, will omit that capability])])
|
||||
fi
|
||||
|
||||
AC_ARG_ENABLE(remoting, [ --enable-remoting],,
|
||||
enable_remoting=no)
|
||||
AM_CONDITIONAL(ENABLE_REMOTING, test x$enable_remoting = xyes)
|
||||
if test x$enable_remoting = xyes; then
|
||||
if test x$enable_drm_compositor != xyes; then
|
||||
AC_MSG_WARN([The remoting-plugin.so module requires the DRM backend.])
|
||||
fi
|
||||
PKG_CHECK_MODULES(REMOTING_GST, [gstreamer-1.0 gstreamer-allocators-1.0 gstreamer-app-1.0 gstreamer-video-1.0])
|
||||
fi
|
||||
|
||||
|
||||
PKG_CHECK_MODULES(LIBINPUT_BACKEND, [libinput >= 0.8.0])
|
||||
PKG_CHECK_MODULES(COMPOSITOR, [$COMPOSITOR_MODULES])
|
||||
|
@ -727,6 +737,7 @@ AC_MSG_RESULT([
|
|||
systemd notify support ${enable_systemd_notify}
|
||||
|
||||
DRM Compositor ${enable_drm_compositor}
|
||||
Remoting ${enable_remoting}
|
||||
X11 Compositor ${enable_x11_compositor}
|
||||
Wayland Compositor ${enable_wayland_compositor}
|
||||
Headless Compositor ${enable_headless_compositor}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
#!/bin/bash
|
||||
|
||||
# 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.
|
||||
|
||||
# By using this script, client can receive remoted output via gstreamer.
|
||||
# Usage:
|
||||
# remoting-client-receive.bash <PORT NUMBER>
|
||||
|
||||
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=$1 ! \
|
||||
rtpbin.recv_rtp_sink_0 \
|
||||
rtpbin. ! rtpjpegdepay ! jpegdec ! autovideosink \
|
||||
udpsrc port=$(($1 + 1)) ! rtpbin.recv_rtcp_sink_0 \
|
||||
rtpbin.send_rtcp_src_0 ! \
|
||||
udpsink port=$(($1 + 2)) sync=false async=false
|
|
@ -45,6 +45,13 @@ including
|
|||
can run on other VTs. On switching back to Weston's VT, input devices
|
||||
and DRM master are re-acquired through the parent process
|
||||
.BR weston-launch .
|
||||
|
||||
The DRM backend also supports virtual outputs that are transmitted over
|
||||
an RTP session as a series of JPEG images (RTP payload type 26) to a remote
|
||||
client. Virtual outputs are configured in the
|
||||
.BR remote-output
|
||||
section of
|
||||
.BR weston.ini.
|
||||
.
|
||||
.\" ***************************************************************
|
||||
.SH CONFIGURATION
|
||||
|
@ -138,6 +145,30 @@ Defaults to false. Note that
|
|||
When a connector is disconnected, there is no EDID information to provide
|
||||
a list of video modes. Therefore a forced output should also have a
|
||||
detailed mode line specified.
|
||||
|
||||
.SS Section remote-output
|
||||
.TP
|
||||
\fBname\fR=\fIname\fR
|
||||
Specify unique name for the output.
|
||||
.TP
|
||||
\fBmode\fR=\fImode\fR
|
||||
Specify the video mode for the output. The argument
|
||||
.I mode
|
||||
is a resolution setting, such as:
|
||||
.TP
|
||||
\fBmode\fR=\fIwidthxheight\fR
|
||||
.TP
|
||||
\fBmode\fR=\fIwidthxheight@refresh_rate
|
||||
If refresh_rate is not specified it will default to a 60Hz.
|
||||
.TP
|
||||
\fBhost\fR=\fIhost\fR
|
||||
Specify the host name or IP Address that the remote output will be
|
||||
transmitted to.
|
||||
.TP
|
||||
\fBport\fR=\fIport\fR
|
||||
Specify the port number to transmit the remote output to. Usable port range
|
||||
is 1-65533.
|
||||
|
||||
.
|
||||
.\" ***************************************************************
|
||||
.SH OPTIONS
|
||||
|
|
|
@ -189,6 +189,19 @@ useful for debugging a crash on start-up when it would be inconvenient to
|
|||
launch weston directly from a debugger. Boolean, defaults to
|
||||
.BR false .
|
||||
There is also a command line option to do the same.
|
||||
.TP 7
|
||||
.BI "remoting="remoting-plugin.so
|
||||
specifies a plugin for remote output to load (string). This can be used to load
|
||||
your own implemented remoting plugin or one with Weston as default. Available
|
||||
remoting plugins in the
|
||||
.IR "__libweston_modules_dir__"
|
||||
directroy are:
|
||||
.PP
|
||||
.RS 10
|
||||
.nf
|
||||
.BR remoting-plugin.so
|
||||
.fi
|
||||
.RE
|
||||
|
||||
.SH "LIBINPUT SECTION"
|
||||
The
|
||||
|
|
|
@ -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).
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
Loading…
Reference in New Issue