From 833d0d106df43110e18229c9c654606ebf4b5841 Mon Sep 17 00:00:00 2001 From: wang_zhiqiang Date: Wed, 19 Jun 2019 17:33:07 +0800 Subject: test --- src/gst-wayland-demo.cpp | 1360 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1360 insertions(+) create mode 100644 src/gst-wayland-demo.cpp (limited to 'src/gst-wayland-demo.cpp') diff --git a/src/gst-wayland-demo.cpp b/src/gst-wayland-demo.cpp new file mode 100644 index 0000000..06bf1e5 --- /dev/null +++ b/src/gst-wayland-demo.cpp @@ -0,0 +1,1360 @@ +/* + * Copyright (C) 2016 Renesas Electronics Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#include + +#include +#include + +#include + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define INFINITE_LOOP_PLAYBACK -1 +#define USE_AGL +#define USE_SDL + +#ifdef USE_AGL +#include "gst-wayland-demo.hpp" +#endif + +#ifdef USE_SDL +#include +#include +#include +#include +#include + +#include "parson.h" + +#include +#include + +// Can受信. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +extern "C" { + void websocket_init(); + void lws_touch_handle_down(uint32_t time, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w); + void lws_touch_handle_up(uint32_t time, int32_t id); + void lws_touch_handle_motion(uint32_t time, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w); +} +#endif + +typedef struct +{ + GMainLoop *loop; + GSource *source; + GstElement *pipeline; + + gint fullscreen; + gboolean nloop; + + struct wl_display *display; + struct wl_event_queue *queue; + struct wl_registry *registry; + struct wl_compositor *compositor; + struct wl_shell *shell; + struct wl_shm *shm; + struct wl_seat *seat; + struct wl_pointer *pointer; + struct wl_touch *touch; + GList *surfaces; + GList *shell_surfaces; + GList *outputs; + + struct wl_surface *focused_surface; + gint min_refresh; + gint64 frame_cnt; + + guint signal_watch_id; +} GstWlDemo; + +#ifdef USE_AGL +void fatal(const char* format, ...) +{ + va_list va_args; + va_start(va_args, format); + vfprintf(stderr, format, va_args); + va_end(va_args); + + exit(EXIT_FAILURE); +} + +void warn(const char* format, ...) +{ + va_list va_args; + va_start(va_args, format); + vfprintf(stderr, format, va_args); + va_end(va_args); +} + +void debug(const char* format, ...) +{ + va_list va_args; + va_start(va_args, format); + vfprintf(stderr, format, va_args); + va_end(va_args); +} + +static void event_loop_run(struct sd_event* loop){ + sd_event_loop(loop); + sd_event_unref(loop); +} + +/** + * websocket + */ + +static void _on_hangup_static(void *closure, struct afb_wsj1 *wsj) +{ +} + +static void _on_call_static(void *closure, const char *api, const char *verb, struct afb_wsj1_msg *msg) +{ +} + +static void _on_event_static(void* closure, const char* event, struct afb_wsj1_msg *msg) +{ + static_cast(closure)->on_event(NULL,event,msg); +} + +static void _on_reply_static(void *closure, struct afb_wsj1_msg *msg) +{ + AGL_DEBUG("_on_reply_static msg: (%s)", afb_wsj1_msg_object_s(msg)); +} + +void RunXDG::notify_ivi_control_cb (ilmObjectType object, t_ilm_uint id, + t_ilm_bool created) +{ + if (object == ILM_SURFACE) { + struct ilmSurfaceProperties surf_props; + + ilm_getPropertiesOfSurface(id, &surf_props); + pid_t surf_pid = surf_props.creatorPid; + if (!created) { + fprintf(stderr, "ivi surface (id=%d, pid=%d) destroyed. \n", id, surf_pid); + return; + } + + fprintf(stderr, "ivi surface (id=%d, pid=%d) is created. \n", id, surf_pid); + if (surf_pid == m_pid) + { + m_ivi_id = id; + setup_ivi_surface(); + } + } + else{ + fprintf(stderr, "ivi surface is not created. \n"); + } +} + +void RunXDG::notify_ivi_control_cb_static (ilmObjectType object, t_ilm_uint id, + t_ilm_bool created, void *user_data) +{ + fprintf(stderr, "notify_ivi_control_cb_static called. \n"); + RunXDG *runxdg = static_cast(user_data); + runxdg->notify_ivi_control_cb(object, id, created); +} + +int RunXDG::init_wm (void) +{ + m_wm = new LibWindowmanager(); + if (m_wm->init(m_port, m_token.c_str())) { + AGL_DEBUG("cannot initialize windowmanager"); + return -1; + } + + std::function< void(json_object*) > h_active = [this](json_object* object) { + AGL_DEBUG("Got Event_Active"); + t_ilm_surface s_ids[1] = { this->m_ivi_id }; + ilm_setInputFocus(s_ids, 1, ILM_INPUT_DEVICE_KEYBOARD, ILM_TRUE); + }; + + std::function< void(json_object*) > h_inactive = [this](json_object* object) { + AGL_DEBUG("Got Event_Inactive"); + t_ilm_surface s_ids[1] = { this->m_ivi_id }; + ilm_setInputFocus(s_ids, 1, ILM_INPUT_DEVICE_KEYBOARD, ILM_FALSE); + }; + + std::function< void(json_object*) > h_visible = [](json_object* object) { + AGL_DEBUG("Got Event_Visible"); + }; + + std::function< void(json_object*) > h_invisible = [](json_object* object) { + AGL_DEBUG("Got Event_Invisible"); + }; + + std::function< void(json_object*) > h_syncdraw = + [this](json_object* object) { + AGL_DEBUG("Got Event_SyncDraw"); + this->m_wm->endDraw(this->m_role.c_str()); + }; + + std::function< void(json_object*) > h_flushdraw= [](json_object* object) { + AGL_DEBUG("Got Event_FlushDraw"); + }; + + m_wm->set_event_handler(LibWindowmanager::Event_Active, h_active); + m_wm->set_event_handler(LibWindowmanager::Event_Inactive, h_inactive); + m_wm->set_event_handler(LibWindowmanager::Event_Visible, h_visible); + m_wm->set_event_handler(LibWindowmanager::Event_Invisible, h_invisible); + m_wm->set_event_handler(LibWindowmanager::Event_SyncDraw, h_syncdraw); + m_wm->set_event_handler(LibWindowmanager::Event_FlushDraw, h_flushdraw); + + return 0; +} + +int RunXDG::init_hs (void) +{ + m_hs = new LibHomeScreen(); + if (m_hs->init(m_port, m_token.c_str())) { + AGL_DEBUG("cannot initialize homescreen"); + return -1; + } + + std::function< void(json_object*) > handler = [this] (json_object* object) { + AGL_DEBUG("Activesurface %s ", this->m_role.c_str()); + this->m_wm->activateWindow(this->m_role.c_str(), "normal.full"); + }; + m_hs->set_event_handler(LibHomeScreen::Event_ShowWindow, handler); + + std::function< void(json_object*) > h_default= [](json_object* object) { + const char *j_str = json_object_to_json_string(object); + AGL_DEBUG("Got event [%s]", j_str); + }; + m_hs->set_event_handler(LibHomeScreen::Event_OnScreenMessage, h_default); + + return 0; +} + +int RunXDG::init_carlaclient(void) +{ + memset(&m_carla_info, 0, sizeof(struct Carla_Info)); + int ret = sd_event_new(&mploop); + if(ret < 0) + { + AGL_DEBUG("Failed to create event loop"); + return -1; + } + + { + // enforce context to avoid initialization/goto error + std::thread th(event_loop_run, mploop); + th.detach(); + } + + /* Initialize interface from websocket */ + minterface.on_hangup = _on_hangup_static; + minterface.on_call = _on_call_static; + minterface.on_event = _on_event_static; + muri += "ws://localhost:" + std::to_string(m_port) + "/api?token=" + m_token; /*To be modified*/ + sp_websock = afb_ws_client_connect_wsj1(mploop, muri.c_str(), &minterface, this); + if(sp_websock == NULL) { + AGL_DEBUG("Failed to create websocket connection"); + return -1; + } + + struct json_object* j_obj = json_object_new_object(); + json_object_object_add(j_obj, "event", json_object_new_int(0)); + + ret = afb_wsj1_call_j(sp_websock, "carlaclient", "subscribe", j_obj, _on_reply_static, this); + if (ret < 0) { + AGL_DEBUG("Failed to call carlaclient subscribe verb"); + } + + struct json_object* speed_obj = json_object_new_object(); + json_object_object_add(speed_obj, "event", json_object_new_string("vehicle.average.speed")); + + ret = afb_wsj1_call_j(sp_websock, "low-can", "subscribe", speed_obj, _on_reply_static, this); + if (ret < 0) { + AGL_DEBUG("Failed to call low-can subscribe verb"); + } + + return 0; +} + +void RunXDG::on_event(void *closure, const char *event, struct afb_wsj1_msg *msg) +{ + AGL_DEBUG("event: (%s) msg: (%s).", event, afb_wsj1_msg_object_s(msg)); + struct json_object* ev_contents = afb_wsj1_msg_object_j(msg); + if(!strcasecmp(event, "carlaclient/positionUpdated")) { + struct json_object *json_data; + if(!json_object_object_get_ex(ev_contents, "data", &json_data)) { + AGL_DEBUG("got ev_contents error."); + return; + } + + struct json_object *longitude_obj, *latitude_obj, *yaw_obj; + if(json_object_object_get_ex(json_data, "longitude", &longitude_obj) + && json_object_object_get_ex(json_data, "latitude", &latitude_obj) + && json_object_object_get_ex(json_data, "yaw", &yaw_obj)) { + AGL_DEBUG("got yaw(%s) longitude(%s) latitude(%s).", json_object_get_string(yaw_obj), json_object_get_string(longitude_obj), json_object_get_string(latitude_obj)); + m_carla_info.yaw = json_object_get_string(yaw_obj); + std::string::size_type sz; + m_carla_info.longitude = std::stod(json_object_get_string(longitude_obj), &sz); + m_carla_info.latitude = std::stod(json_object_get_string(latitude_obj), &sz); + // TBD: call sdl interface + } + } + else if(!strcasecmp(event, "low-can/messages.vehicle.average.speed")) { + // [5,"low-can/messages.vehicle.average.speed",{"event":"low-can\/messages.vehicle.average.speed","data":{"value":10},"jtype":"afb-event"}] + + if(json_object_get_type(ev_contents) == json_type_array) { + int array_len = json_object_array_length(ev_contents); + struct json_object *event_obj = json_object_array_get_idx(ev_contents, 2); + struct json_object *data_obj, *val_obj; + if(json_object_object_get_ex(event_obj, "data", &data_obj) + &&json_object_object_get_ex(data_obj, "value", &val_obj)) { + m_carla_info.speed = json_object_get_int(val_obj); + } + } + } +} + +RunXDG::RunXDG (int port, const char* token, const char* id) +{ + m_id = std::string(id); + m_port = port; + m_token = std::string(token); + m_role = "gstwayland"; + m_pid = getpid(); + + // Setup HomeScreen/WindowManager API + if (init_wm()) + AGL_FATAL("cannot setup wm API"); + + if (init_hs()) + AGL_FATAL("cannot setup hs API"); + + if (init_carlaclient()) + AGL_FATAL("cannot setup carlaclient API"); + + // Setup ilmController API + m_ic = new ILMControl(notify_ivi_control_cb_static, this); + + fprintf(stderr, "RunXDG created. \n"); +} + +void RunXDG::setup_ivi_surface (void) +{ + std::string sid = std::to_string(m_ivi_id); + + // This surface is mine, register pair app_name and ivi id. + AGL_DEBUG("requestSurfaceXDG(%s,%d)", m_role.c_str(), m_ivi_id); + m_wm->requestSurfaceXDG(this->m_role.c_str(), (unsigned int)m_ivi_id); + + if (m_pending_create) { + // Recovering 1st time tap_shortcut is dropped because + // the application has not been run yet (1st time launch) + m_pending_create = false; + // m_wm->activateWindow(this->m_role.c_str(), "normal.full"); + m_hs->publishSubscription(); + } +} + +void RunXDG::start (void) +{ + // take care 1st time launch + AGL_DEBUG("waiting for notification: surafce created"); + m_pending_create = true; + + ilm_commitChanges(); +} +#endif + +static void +pointer_handle_enter (void *data, struct wl_pointer *pointer, + uint32_t serial, struct wl_surface *surface, wl_fixed_t sx, wl_fixed_t sy) +{ + fprintf(stderr, "%s called. \n", __func__); + // GstWlDemo *priv = data; + + // priv->focused_surface = surface; +} + +static void +pointer_handle_leave (void *data, struct wl_pointer *pointer, + uint32_t serial, struct wl_surface *surface) +{ + fprintf(stderr, "%s called. \n", __func__); + // GstWlDemo *priv = data; + + // priv->focused_surface = NULL; +} + +static void +pointer_handle_motion (void *data, struct wl_pointer *pointer, + uint32_t time, wl_fixed_t sx, wl_fixed_t sy) +{ + fprintf(stderr, "%s called. \n", __func__); +} + +static void +pointer_handle_button (void *data, struct wl_pointer *wl_pointer, + uint32_t serial, uint32_t time, uint32_t button, uint32_t state) +{ + fprintf(stderr, "%s called. \n", __func__); + // GstWlDemo *priv = data; + // struct wl_shell_surface *shell_surface; + + // if (!priv->focused_surface) + // return; + + // shell_surface = wl_surface_get_user_data (priv->focused_surface); + + // if (button == BTN_LEFT && state == WL_POINTER_BUTTON_STATE_PRESSED) + // wl_shell_surface_move (shell_surface, priv->seat, serial); +} + +static void +pointer_handle_axis (void *data, struct wl_pointer *wl_pointer, + uint32_t time, uint32_t axis, wl_fixed_t value) +{ + fprintf(stderr, "%s called. \n", __func__); +} + +static const struct wl_pointer_listener pointer_listener = { + pointer_handle_enter, + pointer_handle_leave, + pointer_handle_motion, + pointer_handle_button, + pointer_handle_axis, +}; + +static void +touch_handle_down (void *data, struct wl_touch *wl_touch, + uint32_t serial, uint32_t time, struct wl_surface *surface, + int32_t id, wl_fixed_t x_w, wl_fixed_t y_w) +{ + fprintf(stderr, "%s called. \n", __func__); + if( data ) { + double cx = wl_fixed_to_double(x_w); + double cy = wl_fixed_to_double(y_w); + fprintf(stderr, "log: %s: cx=%f cy=%f", __func__, cx, cy); + } + // GstWlDemo *priv = data; + // struct wl_shell_surface *shell_surface; + + // shell_surface = wl_surface_get_user_data (surface); + + // wl_shell_surface_move (shell_surface, priv->seat, serial); + lws_touch_handle_down(time, id, x_w, y_w); +} + +static void +touch_handle_up (void *data, struct wl_touch *wl_touch, + uint32_t serial, uint32_t time, int32_t id) +{ + fprintf(stderr, "%s called. \n", __func__); + lws_touch_handle_up(time, id); +} + +static void +touch_handle_motion (void *data, struct wl_touch *wl_touch, + uint32_t time, int32_t id, wl_fixed_t x_w, wl_fixed_t y_w) +{ + fprintf(stderr, "%s called. \n", __func__); + lws_touch_handle_motion(time, id, x_w, y_w); +} + +static void +touch_handle_frame (void *data, struct wl_touch *wl_touch) +{ + fprintf(stderr, "%s called. \n", __func__); +} + +static void +touch_handle_cancel (void *data, struct wl_touch *wl_touch) +{ + fprintf(stderr, "%s called. \n", __func__); +} + +static const struct wl_touch_listener touch_listener = { + touch_handle_down, + touch_handle_up, + touch_handle_motion, + touch_handle_frame, + touch_handle_cancel, +}; + +static void +seat_handle_capabilities (void *data, struct wl_seat *seat, + enum wl_seat_capability caps) +{ + GstWlDemo *priv = data; + + if ((caps & WL_SEAT_CAPABILITY_POINTER) && !priv->pointer) { + priv->pointer = wl_seat_get_pointer (seat); + wl_proxy_set_queue ((struct wl_proxy *) priv->pointer, priv->queue); + wl_pointer_add_listener (priv->pointer, &pointer_listener, priv); + } else if (!(caps & WL_SEAT_CAPABILITY_POINTER) && priv->pointer) { + wl_pointer_destroy (priv->pointer); + priv->pointer = NULL; + } + + if ((caps & WL_SEAT_CAPABILITY_TOUCH) && !priv->touch) { + priv->touch = wl_seat_get_touch (seat); + wl_touch_set_user_data (priv->touch, priv); + wl_proxy_set_queue ((struct wl_proxy *) priv->touch, priv->queue); + wl_touch_add_listener (priv->touch, &touch_listener, priv); + } else if (!(caps & WL_SEAT_CAPABILITY_TOUCH) && priv->touch) { + wl_touch_destroy (priv->touch); + priv->touch = NULL; + } +} + +static const struct wl_seat_listener seat_listener = { + seat_handle_capabilities, +}; + +static void +handle_ping (void *data, struct wl_shell_surface *shell_surface, + uint32_t serial) +{ + wl_shell_surface_pong (shell_surface, serial); +} + +static void +handle_configure (void *data, struct wl_shell_surface *shell_surface, + uint32_t edges, int32_t width, int32_t height) +{ +} + +static void +handle_popup_done (void *data, struct wl_shell_surface *shell_surface) +{ +} + +static const struct wl_shell_surface_listener shell_surface_listener = { + handle_ping, + handle_configure, + handle_popup_done +}; + +static void +shm_format (void *data, struct wl_shm *wl_shm, uint32_t format) +{ + GST_DEBUG ("supported format=%08x", format); +} + +static const struct wl_shm_listener shm_listener = { + shm_format +}; + +static void +display_handle_geometry (void *data, struct wl_output *wl_output, int x, int y, + int physical_width, int physical_height, int subpixel, const char *make, + const char *model, int transform) +{ +} + +static void +display_handle_mode (void *data, struct wl_output *wl_output, uint32_t flags, + int width, int height, int refresh) +{ + GstWlDemo *priv = data; + + if (flags & WL_OUTPUT_MODE_CURRENT && priv->min_refresh > refresh) + priv->min_refresh = refresh; +} + +static void +display_handle_done (void *data, struct wl_output *wl_output) +{ +} + +static void +display_handle_scale (void *data, struct wl_output *wl_output, int32_t scale) +{ +} + +static const struct wl_output_listener output_listener = { + display_handle_geometry, + display_handle_mode, + display_handle_done, + display_handle_scale +}; + +static void +registry_handle_global (void *data, struct wl_registry *registry, + uint32_t id, const char *interface, uint32_t version) +{ + GstWlDemo *priv = data; + struct wl_output *output; + + if (g_strcmp0 (interface, "wl_compositor") == 0) { + priv->compositor = wl_registry_bind (registry, id, &wl_compositor_interface, + MIN (version, 3)); + } else if (g_strcmp0 (interface, "wl_shell") == 0) { + priv->shell = wl_registry_bind (registry, id, &wl_shell_interface, 1); + } else if (g_strcmp0 (interface, "wl_shm") == 0) { + priv->shm = wl_registry_bind (registry, id, &wl_shm_interface, 1); + } else if (g_strcmp0 (interface, "wl_seat") == 0) { + priv->seat = wl_registry_bind (registry, id, &wl_seat_interface, 1); + wl_proxy_set_queue ((struct wl_proxy *) priv->seat, priv->queue); + wl_seat_add_listener (priv->seat, &seat_listener, priv); + } else if (g_strcmp0 (interface, "wl_output") == 0) { + output = wl_registry_bind (registry, id, &wl_output_interface, 1); + wl_proxy_set_queue ((struct wl_proxy *) output, priv->queue); + wl_output_add_listener (output, &output_listener, priv); + priv->outputs = g_list_append (priv->outputs, output); + } +} + +static const struct wl_registry_listener registry_listener = { + registry_handle_global +}; + +static gboolean +setup_surface (GstWlDemo * priv, struct wl_surface *surface, + struct wl_shell_surface *shell_surface, gint width, gint height) +{ + char filename[1024]; + static int cnt = 0; + int fd; + GstVideoInfo vinfo; + void *data; + struct wl_shm_pool *shm_pool; + struct wl_buffer *wlbuffer; + + /* + * waylandsink creates a wl_subsurface from an external wl_surface passed by + * the application and attaches buffers from the upstream to + * the wl_subsurface. A wl_subsurface becomes visible by mapping + * its parent wl_surface, so we have to draw the wl_surface that will be passed + * to waylandsink. + */ + /* Transparently draw the area of the same size as the video resolution + by using a shm buffer. */ + gst_video_info_set_format (&vinfo, GST_VIDEO_FORMAT_BGRA, width, height); + + snprintf (filename, 1024, "%s/%s-demo-%d-%s", g_get_user_runtime_dir (), + "wayland-shm", cnt++, "XXXXXX"); + + fd = mkstemp (filename); + if (fd < 0) { + g_printerr ("temp file %s creation failed: %s\n", filename, + strerror (errno)); + return FALSE; + } + + if (ftruncate (fd, vinfo.size) < 0) { + g_printerr ("ftruncate failed: %s\n", strerror (errno)); + close (fd); + return FALSE; + } + + data = mmap (NULL, vinfo.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (data == MAP_FAILED) { + g_printerr ("mmap failed: %s\n", strerror (errno)); + close (fd); + return FALSE; + } + + memset (data, 0, vinfo.size); + + munmap (data, vinfo.size); + + shm_pool = wl_shm_create_pool (priv->shm, fd, vinfo.size); + wlbuffer = + wl_shm_pool_create_buffer (shm_pool, 0, GST_VIDEO_INFO_WIDTH (&vinfo), + GST_VIDEO_INFO_HEIGHT (&vinfo), + GST_VIDEO_INFO_PLANE_STRIDE (&vinfo, 0), WL_SHM_FORMAT_ARGB8888); + wl_shm_pool_destroy (shm_pool); + unlink (filename); + close (fd); + + wl_proxy_set_queue ((struct wl_proxy *) shell_surface, priv->queue); + + wl_shell_surface_add_listener (shell_surface, &shell_surface_listener, priv); + wl_shell_surface_set_toplevel (shell_surface); + + wl_surface_set_user_data (surface, shell_surface); + + if (priv->fullscreen != -1) { + struct wl_event_queue *queue; + struct wl_region *region; + struct wl_output *output; + + region = wl_compositor_create_region (priv->compositor); + wl_region_add (region, 0, 0, width, height); + wl_surface_set_opaque_region (surface, region); + wl_region_destroy (region); + + output = g_list_nth_data (priv->outputs, priv->fullscreen); + if (!output) { + g_printerr + ("failed to get wl_output object, so could not set fullscreen\n"); + return FALSE; + } + + queue = wl_display_create_queue (priv->display); + + wl_shell_surface_set_fullscreen (shell_surface, + WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0, output); + wl_display_roundtrip_queue (priv->display, queue); + + wl_event_queue_destroy (queue); + } + + wl_surface_attach (surface, wlbuffer, 0, 0); + wl_surface_damage (surface, 0, 0, GST_VIDEO_INFO_WIDTH (&vinfo), + GST_VIDEO_INFO_HEIGHT (&vinfo)); + wl_surface_commit (surface); + wl_display_flush (priv->display); + + return TRUE; +} + +static GstBusSyncReply +bus_sync_handler (GstBus * bus, GstMessage * message, gpointer user_data) +{ + GstWlDemo *priv = user_data; + + if (gst_is_wayland_display_handle_need_context_message (message)) { + GstContext *context; + + context = gst_wayland_display_handle_context_new (priv->display); + gst_element_set_context (GST_ELEMENT (GST_MESSAGE_SRC (message)), context); + + goto drop; + } else if (gst_is_video_overlay_prepare_window_handle_message (message)) { + GstPad *pad; + GstCaps *caps; + GstStructure *structure; + struct wl_surface *surface; + struct wl_shell_surface *shell_surface; + gint width, height; + + if (!g_str_has_prefix (GST_MESSAGE_SRC_NAME (message), "waylandsink")) + return GST_BUS_PASS; + + pad = gst_element_get_static_pad ((GstElement *) GST_MESSAGE_SRC (message), + "sink"); + caps = gst_pad_get_current_caps (pad); + structure = gst_caps_get_structure (caps, 0); + gst_structure_get_int (structure, "width", &width); + gst_structure_get_int (structure, "height", &height); + gst_caps_unref (caps); + gst_object_unref (pad); + + surface = wl_compositor_create_surface (priv->compositor); + shell_surface = wl_shell_get_shell_surface (priv->shell, surface); + + if (!setup_surface (priv, surface, shell_surface, width, height)) { + wl_shell_surface_destroy (shell_surface); + wl_surface_destroy (surface); + return GST_BUS_PASS; + } + + priv->surfaces = g_list_append (priv->surfaces, surface); + priv->shell_surfaces = g_list_append (priv->shell_surfaces, shell_surface); + + gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (GST_MESSAGE_SRC + (message)), (guintptr) surface); + gst_video_overlay_set_render_rectangle (GST_VIDEO_OVERLAY (GST_MESSAGE_SRC + (message)), 0, 0, width, height); + + goto drop; + } + + return GST_BUS_PASS; + +drop: + gst_message_unref (message); + return GST_BUS_DROP; +} + +static gboolean +bus_async_handler (GstBus * bus, GstMessage * message, gpointer user_data) +{ + GstWlDemo *priv = user_data; + + switch (GST_MESSAGE_TYPE (message)) { + case GST_MESSAGE_ERROR:{ + GError *err; + gchar *debug; + + gst_message_parse_error (message, &err, &debug); + g_print ("Error: %s\n", err->message); + g_error_free (err); + g_free (debug); + + goto quit; + } + case GST_MESSAGE_APPLICATION: + { + const GstStructure *s; + + s = gst_message_get_structure (message); + + if (gst_structure_has_name (s, "GstWaylandDemoInterrupt")) + goto quit; + } + break; + case GST_MESSAGE_EOS: + if (priv->nloop > 1 || priv->nloop == INFINITE_LOOP_PLAYBACK) { + if (!gst_element_seek (priv->pipeline, + 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH, + GST_SEEK_TYPE_SET, 0, + GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE)) { + g_printerr ("seek event sending failed\n"); + goto quit; + } + + if (priv->nloop != INFINITE_LOOP_PLAYBACK) + priv->nloop--; + break; + } + + goto quit; + default: + break; + } + + return TRUE; + +quit: + g_main_loop_quit (priv->loop); + g_source_destroy (priv->source); + priv->source = NULL; + return TRUE; +} + +typedef struct _GstWaylandEventSource +{ + GSource source; + GPollFD pfd; + struct wl_display *display; + struct wl_event_queue *queue; + gboolean reading; +} GstWaylandEventSource; + +static gboolean +event_source_prepare (GSource * source, gint * timeout) +{ + GstWaylandEventSource *wl_source = (GstWaylandEventSource *) source; + + *timeout = -1; + + if (wl_source->reading) + return FALSE; + + while (wl_display_prepare_read_queue (wl_source->display, + wl_source->queue) != 0) { + wl_display_dispatch_queue_pending (wl_source->display, wl_source->queue); + } + wl_display_flush (wl_source->display); + + wl_source->reading = TRUE; + + return FALSE; +} + +static gboolean +event_source_check (GSource * source) +{ + GstWaylandEventSource *wl_source = (GstWaylandEventSource *) source; + + return ! !(wl_source->pfd.revents & G_IO_IN); +} + +static gboolean +event_source_dispatch (GSource * source, GSourceFunc callback, + gpointer user_data) +{ + GstWaylandEventSource *wl_source = (GstWaylandEventSource *) source; + + wl_display_read_events (wl_source->display); + wl_display_dispatch_queue_pending (wl_source->display, wl_source->queue); + + wl_source->reading = FALSE; + + return TRUE; +} + +static void +event_source_finalize (GSource * source) +{ + GstWaylandEventSource *wl_source = (GstWaylandEventSource *) source; + + if (wl_source->reading) { + wl_display_cancel_read (wl_source->display); + wl_source->reading = FALSE; + } +} + +static GSourceFuncs GstWaylandEventSourceFuncs = { + event_source_prepare, + event_source_check, + event_source_dispatch, + event_source_finalize +}; + +static void +setup_framerate_adjustment (const GValue * item, gpointer user_data) +{ + GstWlDemo *priv = user_data; + GstElement *elem; + GstElement *peer_elem; + GstPad *pad; + GstPad *peer_pad; + GstCaps *caps; + + elem = g_value_get_object (item); + + if (g_str_has_prefix (GST_ELEMENT_NAME (elem), "videorate")) { + /* Get the element immediately after this videorate */ + pad = gst_element_get_static_pad (elem, "src"); + peer_pad = gst_pad_get_peer (pad); + peer_elem = gst_pad_get_parent_element (peer_pad); + gst_object_unref (pad); + gst_object_unref (peer_pad); + + caps = gst_caps_new_simple ("video/x-raw", "framerate", + GST_TYPE_FRACTION, priv->min_refresh / 1000, 1, NULL); + + gst_element_unlink (elem, peer_elem); + gst_element_link_filtered (elem, peer_elem, caps); + gst_object_unref (peer_elem); + } +} + +static GstPadProbeReturn +cb_have_data (GstPad * pad, GstPadProbeInfo * info, gpointer user_data) +{ + GstWlDemo *priv = user_data; + time_t t; + struct tm *tm; + gint64 cur_time; + gint64 framesinsec; + static gint64 prev_time = 0; + static gint64 prev_cnt = 0; + + priv->frame_cnt++; + + if (prev_time == 0) { + prev_time = g_get_monotonic_time (); + return GST_PAD_PROBE_OK; + } + + cur_time = g_get_monotonic_time (); + + if (cur_time - prev_time >= 1000000) { + framesinsec = priv->frame_cnt - prev_cnt; + t = time (NULL); + tm = localtime (&t); + + g_print ("FPS: %3ld TIME %02d:%02d:%02d\n", framesinsec, tm->tm_hour, + tm->tm_min, tm->tm_sec); + + prev_cnt = priv->frame_cnt; + prev_time = cur_time; + } + + return GST_PAD_PROBE_OK; +} + +static gboolean +sigint_handler (gpointer user_data) +{ + GstWlDemo *priv = user_data; + + gst_element_post_message (GST_ELEMENT (priv->pipeline), + gst_message_new_application (GST_OBJECT (priv->pipeline), + gst_structure_new ("GstWaylandDemoInterrupt", "message", + G_TYPE_STRING, "Pipeline interrupted", NULL))); + + priv->signal_watch_id = 0; + + return FALSE; +} + +static void +shell_surface_destroy (struct wl_shell_surface *shell_surface) +{ + wl_shell_surface_destroy (shell_surface); +} + +static void +surface_destroy (struct wl_surface *surface) +{ + wl_surface_destroy (surface); +} + +static void +output_destroy (struct wl_output *output) +{ + wl_output_destroy (output); +} + +int +main (int argc, char **argv) +{ + GstWaylandEventSource *wl_source; + GstWlDemo *priv; + GOptionContext *context; + GstBus *bus; + GstIterator *it; + GstElement *elem; + GstPad *pad; + GstStateChangeReturn state_ret; + GTimer *timer = NULL; + GError *error = NULL; + guint bus_watch_id; + gint fullscreen = -1; + gint nloop = 0; + gchar *measuring_pad = NULL; + gchar *elem_name; + gchar *pad_name; + gdouble elapsed; + gchar **argvn; + gint i; + int ret = EXIT_FAILURE; + GOptionEntry options[] = { + {"fullscreen", 'f', 0, G_OPTION_ARG_INT, &fullscreen, + "Display in fullscreen mode on dest_num output [-f dest_num]", NULL}, + {"loop", 'l', 0, G_OPTION_ARG_INT, &nloop, + "Loop Playback for loop_count [-l loop_count]", NULL}, + {"fps", 'p', 0, G_OPTION_ARG_STRING, &measuring_pad, + "Framerate display (specify a measuring point as regular format [-p element:pad])", + NULL}, + {NULL} + }; + +#ifdef USE_AGL + + //char *static_options[] = { argv[0], "filesrc", "location=/home/0/3.mp4", "!", "decodebin", "!", "videoconvert", "!", "waylandsink", "sync=false", NULL }; + //char **static_argv = static_options; + //int static_argc = 10; + char **static_argv = NULL; + int static_argc = 0; + + // Parse args + int port; + const char *token; + + // Get app id + const char *afm_id = getenv("AFM_ID"); + if (afm_id == NULL || !afm_id[0]) { + afm_id = argv[0]; + } + + // Get pipeline information + auto path = std::string(getenv("AFM_APP_INSTALL_DIR")); + path = path + "/pipeline.ini"; + + FILE *pFile; + long lSize; + size_t res; + char * pipe = NULL; + // Read pipeline from file + pFile = fopen ( path.c_str() , "rb" ); + if (pFile==NULL){ + fprintf(stderr, "failed to open file\n"); + return -1; + } + + // obtain file size + fseek (pFile , 0 , SEEK_END); + lSize = ftell (pFile); + rewind (pFile); + + // allocate memory to contain the whole file + pipe = (char*) malloc (sizeof(char)*lSize); + if (pipe == NULL){ + fprintf(stderr,"Cannot allocate memory\n"); + return -1; + } + + // copy the file into the buffer + res = fread (pipe,1,lSize,pFile); + if (res != lSize){ + fprintf(stderr,"File read error\n"); + return -1; + } + //fprintf(stderr,pipe); + + /* close file */ + fclose (pFile); + + char seg[] = " "; + char *charlist[50]; + static_argv = charlist; + + int memlen = 0; + memlen = strlen(argv[0])+1; + charlist[0] = (char*) malloc (memlen); + memset(charlist[0],0,memlen); + strcpy(charlist[0],argv[0]); + + int cnt = 1; + + char *substr= strtok(pipe,seg); + while (substr != NULL) { + memlen = strlen(substr)+1; + charlist[cnt] = (char*) malloc (memlen); + memset(charlist[cnt],0,memlen); + strcpy(charlist[cnt],substr); + cnt++; + substr = strtok(NULL,seg); + } + + if(pipe != NULL) + free(pipe); + + port = std::stol(argv[1]); + token = argv[2]; + + fprintf(stderr, "port: { %d }, token: { %s}, id: { %s } \n", port, token, afm_id); + + static_argc = cnt; + gst_init (&static_argc, &static_argv); + + RunXDG runxdg(port, token, afm_id); + + // @tobi websocket + websocket_init(); + GST_ERROR("[%s] [%s] end", "WebSocket_Debug", __func__); + + runxdg.start(); +#else + gst_init (&argc, &argv); +#endif + + context = g_option_context_new ("PIPELINE-DESCRIPTION"); + g_option_context_add_main_entries (context, options, NULL); + g_option_context_add_group (context, gst_init_get_option_group ()); +#ifdef USE_AGL + if (!g_option_context_parse (context, &static_argc, &static_argv, &error)) { +#else + if (!g_option_context_parse (context, &argc, &argv, &error)) { +#endif + g_printerr ("option parsing failed: %s\n", + (error) ? error->message : "Unknown error"); + return EXIT_FAILURE; + } + g_option_context_free (context); + + priv = g_slice_new0 (GstWlDemo); + + priv->fullscreen = fullscreen; + priv->nloop = nloop; + priv->min_refresh = G_MAXINT; + + priv->loop = g_main_loop_new (NULL, FALSE); + + /* Construct a pipeline from the result of parsing argv + similarly to gst-launch. */ +#ifdef USE_AGL + argvn = g_new0 (char *, static_argc); + memcpy (argvn, static_argv + 1, sizeof (char *) * (static_argc - 1)); +#else + argvn = g_new0 (char *, argc); + memcpy (argvn, argv + 1, sizeof (char *) * (argc - 1)); +#endif + error = NULL; + priv->pipeline = gst_parse_launchv ((const gchar **) argvn, &error); + g_free (argvn); + + if (!priv->pipeline) { + g_printerr ("pipeline could not be constructed: %s\n", + (error) ? error->message : "Unknown error"); + return EXIT_FAILURE; + } + + priv->signal_watch_id = + g_unix_signal_add (SIGINT, (GSourceFunc) sigint_handler, priv); + + bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline)); + gst_bus_set_sync_handler (bus, bus_sync_handler, priv, NULL); + bus_watch_id = gst_bus_add_watch (bus, bus_async_handler, priv); + gst_object_unref (bus); + + priv->display = wl_display_connect (NULL); + if (!priv->display) { + g_printerr ("display connection failed\n"); + goto leave; + } + + priv->queue = wl_display_create_queue (priv->display); + + priv->registry = wl_display_get_registry (priv->display); + wl_proxy_set_queue ((struct wl_proxy *) priv->registry, priv->queue); + wl_registry_add_listener (priv->registry, ®istry_listener, priv); + + /* Need 2 roundtrips to do all the global objects processing. */ + for (i = 0; i < 2; i++) + wl_display_roundtrip_queue (priv->display, priv->queue); + + priv->source = g_source_new (&GstWaylandEventSourceFuncs, + sizeof (GstWaylandEventSource)); + wl_source = (GstWaylandEventSource *) priv->source; + + wl_source->pfd.fd = wl_display_get_fd (priv->display); + wl_source->pfd.events = G_IO_IN | G_IO_ERR | G_IO_HUP; + g_source_add_poll (priv->source, &wl_source->pfd); + + wl_source->display = priv->display; + wl_source->queue = priv->queue; + wl_source->reading = FALSE; + g_source_attach (priv->source, NULL); + g_source_unref (priv->source); + + /* Setup the framerate adjustment by videorate. */ + it = gst_bin_iterate_elements (GST_BIN (priv->pipeline)); + gst_iterator_foreach (it, setup_framerate_adjustment, priv); + gst_iterator_free (it); + + if (measuring_pad) { + elem_name = strtok (measuring_pad, ":"); + pad_name = strtok (NULL, ":"); + if (!elem_name || !pad_name) { + g_printerr ("tokens extraction failed\n"); + goto leave; + } + + elem = gst_bin_get_by_name (GST_BIN (priv->pipeline), elem_name); + if (!elem) { + g_printerr ("failed to get the element by name\n"); + goto leave; + } + + pad = gst_element_get_static_pad (elem, pad_name); + if (!pad) { + g_printerr ("failed to get the static pad by name\n"); + gst_object_unref (elem); + goto leave; + } + + gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BUFFER, + (GstPadProbeCallback) cb_have_data, priv, NULL); + + gst_object_unref (pad); + gst_object_unref (elem); + + /* To calculate the average framerate throughout the playback. */ + timer = g_timer_new (); + } + + state_ret = gst_element_set_state (priv->pipeline, GST_STATE_PAUSED); + while (state_ret == GST_STATE_CHANGE_ASYNC) { + state_ret = gst_element_get_state (priv->pipeline, NULL, NULL, + GST_CLOCK_TIME_NONE); + } + + if (timer) + g_timer_start (timer); + + gst_element_set_state (priv->pipeline, GST_STATE_PLAYING); + + g_main_loop_run (priv->loop); + + if (timer) { + g_timer_stop (timer); + elapsed = g_timer_elapsed (timer, NULL); + g_timer_destroy (timer); + g_print ("Avg. FPS: %.2lf\n", priv->frame_cnt / elapsed); + } + + gst_element_set_state (priv->pipeline, GST_STATE_NULL); + + ret = 0; + +leave: + GST_ERROR ("log: gst_wl_display_finalize"); + + if (priv->source) + g_source_destroy (priv->source); + + gst_object_unref (priv->pipeline); + + if (priv->shell_surfaces) { + g_list_foreach (priv->shell_surfaces, (GFunc) shell_surface_destroy, NULL); + g_list_free (priv->shell_surfaces); + } + + if (priv->surfaces) { + g_list_foreach (priv->surfaces, (GFunc) surface_destroy, NULL); + g_list_free (priv->surfaces); + } + + if (priv->outputs) { + g_list_foreach (priv->outputs, (GFunc) output_destroy, NULL); + g_list_free (priv->outputs); + } + + if (priv->shm) + wl_shm_destroy (priv->shm); + if (priv->shell) + wl_shell_destroy (priv->shell); + if (priv->registry) + wl_registry_destroy (priv->registry); + if (priv->compositor) + wl_compositor_destroy (priv->compositor); + if (priv->queue) + wl_event_queue_destroy (priv->queue); + if (priv->display) + wl_display_disconnect (priv->display); + + g_source_remove (bus_watch_id); + if (priv->signal_watch_id > 0) + g_source_remove (priv->signal_watch_id); + g_main_loop_unref (priv->loop); + g_slice_free (GstWlDemo, priv); + + if (measuring_pad) + g_free (measuring_pad); + +#ifdef USE_AGL + for(int i=0;i