aboutsummaryrefslogtreecommitdiffstats
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/CMakeLists.txt54
-rw-r--r--app/hmi-debug.h117
-rw-r--r--app/main.cpp85
-rw-r--r--app/surface.cpp193
-rw-r--r--app/surface.hpp94
5 files changed, 543 insertions, 0 deletions
diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt
new file mode 100644
index 0000000..7fef810
--- /dev/null
+++ b/app/CMakeLists.txt
@@ -0,0 +1,54 @@
+###########################################################################
+# Copyright 2018 Konsulko Group
+#
+# Author: Scott Murray <scott.murray@konsulko.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###########################################################################
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_CXX_STANDARD 14)
+
+find_package(PkgConfig REQUIRED)
+
+PROJECT_TARGET_ADD(cluster-receiver)
+
+add_executable(${TARGET_NAME}
+ main.cpp
+ surface.cpp
+ ${RESOURCES}
+)
+
+pkg_check_modules(GSTREAMER REQUIRED gstreamer-1.0)
+pkg_check_modules(ILMCONTROL REQUIRED ilmControl)
+
+include_directories(
+ "${ILMCONTROL_INCLUDE_DIRS}"
+ "${GSTREAMER_INCLUDE_DIRS}"
+)
+
+set_target_properties(${TARGET_NAME} PROPERTIES
+ LABELS "EXECUTABLE"
+ PREFIX ""
+ COMPILE_FLAGS "${EXTRAS_CFLAGS} -DFOR_AFB_BINDING"
+ LINK_FLAGS "${BINDINGS_LINK_FLAG}"
+ LINK_LIBRARIES "${EXTRAS_LIBRARIES}"
+ OUTPUT_NAME "${TARGET_NAME}"
+)
+
+target_link_libraries(${TARGET_NAME}
+ windowmanager
+ ${GSTREAMER_LIBRARIES}
+ ${ILMCONTROL_LIBRARIES}
+)
diff --git a/app/hmi-debug.h b/app/hmi-debug.h
new file mode 100644
index 0000000..697ac80
--- /dev/null
+++ b/app/hmi-debug.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2017 TOYOTA MOTOR CORPORATION
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __HMI_DEBUG_H__
+#define __HMI_DEBUG_H__
+
+#include <time.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+
+enum LOG_LEVEL{
+ LOG_LEVEL_NONE = 0,
+ LOG_LEVEL_ERROR,
+ LOG_LEVEL_WARNING,
+ LOG_LEVEL_NOTICE,
+ LOG_LEVEL_INFO,
+ LOG_LEVEL_DEBUG,
+ LOG_LEVEL_MAX = LOG_LEVEL_DEBUG
+};
+
+#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILE__)
+
+#define HMI_ERROR(prefix, args,...) _HMI_LOG(LOG_LEVEL_ERROR, __FILENAME__, __FUNCTION__, __LINE__, prefix, args, ##__VA_ARGS__)
+#define HMI_WARNING(prefix, args,...) _HMI_LOG(LOG_LEVEL_WARNING, __FILENAME__, __FUNCTION__,__LINE__, prefix, args,##__VA_ARGS__)
+#define HMI_NOTICE(prefix, args,...) _HMI_LOG(LOG_LEVEL_NOTICE, __FILENAME__, __FUNCTION__,__LINE__, prefix, args,##__VA_ARGS__)
+#define HMI_INFO(prefix, args,...) _HMI_LOG(LOG_LEVEL_INFO, __FILENAME__, __FUNCTION__,__LINE__, prefix, args,##__VA_ARGS__)
+#define HMI_DEBUG(prefix, args,...) _HMI_LOG(LOG_LEVEL_DEBUG, __FILENAME__, __FUNCTION__,__LINE__, prefix, args,##__VA_ARGS__)
+
+#define HMI_SEQ_ERROR(seq_num, args,...) _HMI_SEQ_LOG(LOG_LEVEL_ERROR, __FILENAME__, __FUNCTION__, __LINE__, seq_num, args, ##__VA_ARGS__)
+#define HMI_SEQ_WARNING(seq_num, args,...) _HMI_SEQ_LOG(LOG_LEVEL_WARNING, __FILENAME__, __FUNCTION__, __LINE__, seq_num, args, ##__VA_ARGS__)
+#define HMI_SEQ_NOTICE(seq_num, args,...) _HMI_SEQ_LOG(LOG_LEVEL_NOTICE, __FILENAME__, __FUNCTION__, __LINE__, seq_num, args, ##__VA_ARGS__)
+#define HMI_SEQ_INFO(seq_num, args,...) _HMI_SEQ_LOG(LOG_LEVEL_INFO, __FILENAME__, __FUNCTION__, __LINE__, seq_num, args, ##__VA_ARGS__)
+#define HMI_SEQ_DEBUG(seq_num, args,...) _HMI_SEQ_LOG(LOG_LEVEL_DEBUG, __FILENAME__, __FUNCTION__, __LINE__, seq_num, args, ##__VA_ARGS__)
+
+#define DUMP(args, ...) _DUMP(LOG_LEVEL_DEBUG, args, ##__VA_ARGS__)
+
+static char ERROR_FLAG[6][20] = {"NONE", "ERROR", "WARNING", "NOTICE", "INFO", "DEBUG"};
+
+static void _HMI_LOG(enum LOG_LEVEL level, const char* file, const char* func, const int line, const char* prefix, const char* log, ...)
+{
+ const int log_level = (getenv("USE_HMI_DEBUG") == NULL)?LOG_LEVEL_ERROR:atoi(getenv("USE_HMI_DEBUG"));
+ if(log_level < level)
+ {
+ return;
+ }
+
+ char *message;
+ struct timespec tp;
+ unsigned int time;
+
+ clock_gettime(CLOCK_REALTIME, &tp);
+ time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000);
+
+ va_list args;
+ va_start(args, log);
+ if (log == NULL || vasprintf(&message, log, args) < 0)
+ message = NULL;
+ fprintf(stderr, "[%10.3f] [%s %s] [%s, %s(), Line:%d] >>> %s \n", time / 1000.0, prefix, ERROR_FLAG[level], file, func, line, message);
+ va_end(args);
+ free(message);
+}
+
+static void _HMI_SEQ_LOG(enum LOG_LEVEL level, const char* file, const char* func, const int line, unsigned seq_num, const char* log, ...){
+ const int log_level = (getenv("USE_HMI_DEBUG") == NULL) ? LOG_LEVEL_ERROR:atoi(getenv("USE_HMI_DEBUG"));
+ if(log_level < level)
+ {
+ return;
+ }
+
+ char *message;
+ struct timespec tp;
+ unsigned int time;
+
+ clock_gettime(CLOCK_REALTIME, &tp);
+ time = (tp.tv_sec * 1000000L) + (tp.tv_nsec / 1000);
+
+ va_list args;
+ va_start(args, log);
+ if (log == NULL || vasprintf(&message, log, args) < 0)
+ message = NULL;
+ fprintf(stderr, "[%10.3f] [wm %s] [%s, %s(), Line:%d] >>> req %d: %s \n", time / 1000.0, ERROR_FLAG[level], file, func, line, seq_num, message);
+ va_end(args);
+ free(message);
+}
+
+static void _DUMP(enum LOG_LEVEL level, const char *log, ...)
+{
+ const int log_level = (getenv("USE_HMI_DEBUG") == NULL) ? LOG_LEVEL_ERROR : atoi(getenv("USE_HMI_DEBUG"));
+ if (log_level < level)
+ {
+ return;
+ }
+ char *message;
+ va_list args;
+ va_start(args, log);
+ if (log == NULL || vasprintf(&message, log, args) < 0)
+ message = NULL;
+ fprintf(stderr, "%s \n", message);
+ va_end(args);
+ free(message);
+}
+#endif //__HMI_DEBUG_H__ \ No newline at end of file
diff --git a/app/main.cpp b/app/main.cpp
new file mode 100644
index 0000000..400a066
--- /dev/null
+++ b/app/main.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 Konsulko Group
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+#include <iostream>
+#include <cstring>
+#include <gst/gst.h>
+#include "surface.hpp"
+
+int main(int argc, char *argv[])
+{
+ int port = 0;
+ std::string token;
+ std::string role = "receiver";
+
+ try {
+ port = std::stol(argv[1]);
+ token = argv[2];
+ } catch (const std::invalid_argument& e) {
+ std::cerr << "Invalid argument" << std::endl;
+ exit(1);
+ } catch (const std::out_of_range& e) {
+ std::cerr << "Port out of range" << std::endl;
+ exit(1);
+ }
+
+ // NOTES:
+ // (1) For reference, the pipeline used is based on the gst-launch-1.0 command in the ad hoc unit
+ // file in the previous hand-rolled CES demo:
+ //
+ // udpsrc port=5005 ! application/x-rtp,media=video,encoding-name=H264 ! queue ! rtph264depay ! h264parse config-interval=1 disable-passthrough=true ! decodebin ! vaapisink
+ //
+ // (2) waylandsink is a bit broken, as it needs a RGB format, but its caps include non-RGB formats
+ // This results in crashes when videoconvert doesn't end up in the pipeline, so care must be taken
+ // if it is used, e.g. for testing:
+ //
+ // videotestsrc pattern=smpte ! video/x-raw,format=BGRx,width=384,height=368 ! waylandsink
+ //
+ std::string pipeline_str = \
+ "udpsrc port=5005 ! application/x-rtp,media=video,encoding-name=H264 ! queue ! rtph264depay ! h264parse config-interval=1 disable-passthrough=true ! decodebin ! vaapisink";
+
+ // Initialize GStreamer
+ gst_init(NULL, NULL);
+
+ // Create our IVI surface handler
+ SurfaceHandler handler(port, token, role);
+
+ GstElement *pipeline = gst_parse_launch(pipeline_str.c_str(), NULL);
+ if(!pipeline) {
+ std::cerr << "gstreamer pipeline construction failed!" << std::endl;
+ exit(1);
+ }
+
+ // Start pipeline
+ gst_element_set_state(pipeline, GST_STATE_PLAYING);
+ std::cout << "gstreamer pipeline running" << std::endl;
+
+ // Wait until error or EOS
+ GstBus *bus = gst_element_get_bus(pipeline);
+ GstMessage *msg = gst_bus_timed_pop_filtered(bus,
+ GST_CLOCK_TIME_NONE,
+ (GstMessageType) (GST_MESSAGE_ERROR | GST_MESSAGE_EOS));
+
+ // Free resources
+ if(msg != NULL)
+ gst_message_unref(msg);
+ gst_object_unref(bus);
+ gst_element_set_state(pipeline, GST_STATE_NULL);
+ gst_object_unref(pipeline);
+
+ return 0;
+}
diff --git a/app/surface.cpp b/app/surface.cpp
new file mode 100644
index 0000000..8f6c49f
--- /dev/null
+++ b/app/surface.cpp
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2017 Panasonic Corporation
+ * Copyright (c) 2018 Konsulko Group
+ *
+ * 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 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.
+ */
+
+#include <cstdio>
+#include <cstdarg>
+#include "surface.hpp"
+#include "hmi-debug.h"
+
+#define AREA_NORMAL_FULL "normal.full"
+
+SurfaceHandler::SurfaceHandler(const int port, const std::string &token, const std::string &role)
+{
+ m_port = port;
+ m_token = token;
+ m_role = role;
+
+ m_rid = getpid();
+
+ // Setup HomeScreen/WindowManager API
+ if (init_wm()) {
+ HMI_ERROR("receiver:sh", "cannot setup windowmanager API");
+ exit(1);
+ }
+
+ // Setup ilmController API
+ m_ic = new ILMControl(notify_ivi_control_cb_static, this);
+ if (!m_ic) {
+ HMI_ERROR("receiver:sh", "cannot setup IVI layer management API");
+ exit(1);
+ }
+}
+
+int SurfaceHandler::init_wm(void)
+{
+ m_wm = new LibWindowmanager();
+ if (m_wm->init(m_port, m_token.c_str())) {
+ HMI_ERROR("receiver:sh", "cannot initialize windowmanager");
+ return -1;
+ }
+
+ std::function< void(json_object*) > h_active =
+ [](json_object* object) {
+ HMI_DEBUG("receiver:sh", "Got Event_Active");
+ };
+
+ std::function< void(json_object*) > h_inactive =
+ [](json_object* object) {
+ HMI_DEBUG("receiver:sh", "Got Event_Inactive");
+ };
+
+ std::function< void(json_object*) > h_visible =
+ [](json_object* object) {
+ HMI_DEBUG("receiver:sh", "Got Event_Visible");
+ };
+
+ std::function< void(json_object*) > h_invisible =
+ [](json_object* object) {
+ HMI_DEBUG("receiver:sh", "Got Event_Invisible");
+ };
+
+ std::function< void(json_object*) > h_syncdraw =
+ [this](json_object* object) {
+ HMI_DEBUG("receiver:sh", "Got Event_SyncDraw");
+ this->m_wm->endDraw(this->m_role.c_str());
+ };
+
+ std::function< void(json_object*) > h_flushdraw =
+ [](json_object* object) {
+ HMI_DEBUG("receiver:sh", "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;
+}
+
+void SurfaceHandler::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) {
+ HMI_DEBUG("receiver:sh", "ivi surface (id=%d, pid=%d) destroyed.", id, surf_pid);
+ unregister_surfpid(surf_pid);
+ m_surfaces.erase(surf_pid);
+ return;
+ }
+
+ HMI_DEBUG("receiver:sh", "ivi surface (id=%d, pid=%d) is created.", id, surf_pid);
+
+ register_surfpid(surf_pid);
+ if (m_rid && surf_pid == find_surfpid_by_rid(m_rid)) {
+ setup_surface(id);
+ }
+ m_surfaces[surf_pid] = id;
+ } else if (object == ILM_LAYER) {
+ if (created)
+ HMI_DEBUG("receiver:sh", "ivi layer: %d created.", id);
+ else
+ HMI_DEBUG("receiver:sh", "ivi layer: %d destroyed.", id);
+ }
+
+}
+
+void SurfaceHandler::notify_ivi_control_cb_static (ilmObjectType object,
+ t_ilm_uint id,
+ t_ilm_bool created,
+ void *user_data)
+{
+ SurfaceHandler *handler = static_cast<SurfaceHandler*>(user_data);
+ handler->notify_ivi_control_cb(object, id, created);
+}
+
+void SurfaceHandler::register_surfpid (pid_t surf_pid)
+{
+ if (surf_pid == m_rid) {
+ if (!std::count(m_pid_v.begin(), m_pid_v.end(), surf_pid)) {
+ HMI_DEBUG("receiver:sh", "surface creator(pid=%d) registered", surf_pid);
+ m_pid_v.push_back(surf_pid);
+ HMI_DEBUG("receiver:sh", "m_pid_v.count(%d) = %d", surf_pid,
+ std::count(m_pid_v.begin(), m_pid_v.end(), surf_pid));
+ }
+ }
+}
+
+void SurfaceHandler::unregister_surfpid (pid_t surf_pid)
+{
+ auto itr = m_pid_v.begin();
+ while (itr != m_pid_v.end()) {
+ if (*itr == surf_pid) {
+ m_pid_v.erase(itr++);
+ } else {
+ ++itr;
+ }
+ }
+}
+
+pid_t SurfaceHandler::find_surfpid_by_rid (pid_t rid)
+{
+ HMI_DEBUG("receiver:sh", "find surfpid by rid(%d)", rid);
+ if (std::count(m_pid_v.begin(), m_pid_v.end(), rid)) {
+ HMI_DEBUG("receiver:sh", "found return(%d)", rid);
+ return rid;
+ }
+
+ return -1;
+}
+
+void SurfaceHandler::setup_surface (int id)
+{
+ std::string sid = std::to_string(id);
+
+ // This surface is mine, register pair app_name and ivi id.
+ HMI_DEBUG("receiver:sh", "requestSurfaceXDG(%s,%d)", m_role.c_str(), id);
+ m_wm->requestSurfaceXDG(m_role.c_str(), id);
+
+ if (m_pending_create) {
+ // Activate window if it has not been yet
+ HMI_DEBUG("receiver:sh", "calling activateWindow on (%s,%d)", m_role.c_str(), id);
+ m_pending_create = false;
+ m_wm->activateWindow(m_role.c_str(), AREA_NORMAL_FULL);
+ }
+}
diff --git a/app/surface.hpp b/app/surface.hpp
new file mode 100644
index 0000000..88be3bb
--- /dev/null
+++ b/app/surface.hpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2017 Panasonic Corporation
+ * Copyright (c) 2018 Konsulko Group
+ *
+ * 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 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.
+ */
+
+#ifndef SURFACE_HPP
+#define SURFACE_HPP
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+#include <vector>
+#include <map>
+#include <algorithm>
+
+#include <ilm/ilm_control.h>
+#include <libwindowmanager.h>
+
+class ILMControl
+{
+public:
+ ILMControl(notificationFunc callback, void *user_data) {
+ ilm_init();
+ ilm_registerNotification(callback, user_data);
+ }
+
+ ~ILMControl(void) {
+ ilm_unregisterNotification();
+ ilm_destroy();
+ }
+};
+
+class SurfaceHandler
+{
+public:
+ SurfaceHandler(const int port, const std::string &token, const std::string &role);
+
+ void notify_ivi_control_cb(ilmObjectType object,
+ t_ilm_uint id,
+ t_ilm_bool created);
+
+ static void notify_ivi_control_cb_static (ilmObjectType object,
+ t_ilm_uint id,
+ t_ilm_bool created,
+ void *user_data);
+
+private:
+ int m_port;
+ std::string m_token;
+
+ std::string m_role;
+
+ int m_rid = 0;
+
+ LibWindowmanager *m_wm;
+ ILMControl *m_ic;
+
+ std::vector<pid_t> m_pid_v;
+
+ std::map<int, int> m_surfaces; // pair of <afm:rid, ivi:id>
+
+ //bool m_pending_create = false;
+ bool m_pending_create = true;
+
+ // Private functions
+
+ int init_wm(void);
+
+ void register_surfpid(pid_t surf_pid);
+ void unregister_surfpid(pid_t surf_pid);
+ pid_t find_surfpid_by_rid(pid_t rid);
+ void setup_surface(int id);
+};
+
+#endif // SURFACE_HPP