diff --git a/CMakeLists.txt b/CMakeLists.txt index 03e73dd..28b4376 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,7 @@ set(base_directory ${CMAKE_CURRENT_SOURCE_DIR}) set(resources_directory ${base_directory}/assets) set(sources_directory ${base_directory}/src) set(include_directory ${base_directory}/include) +include(${base_directory}/cmake_modules/functions.cmake) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${base_directory}/lib) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${base_directory}/lib) @@ -29,28 +30,46 @@ add_definitions(-DBOOST_ALL_DYN_LINK) find_package(Boost REQUIRED COMPONENTS system log OPTIONAL_COMPONENTS unit_test_framework) find_package(libusb-1.0 REQUIRED) -find_package(Qt5 COMPONENTS Multimedia MultimediaWidgets Bluetooth) +find_package(Qt5 COMPONENTS Multimedia MultimediaWidgets Bluetooth Qml Quick QuickWidgets REQUIRED) find_package(Protobuf REQUIRED) find_package(OpenSSL REQUIRED) find_package(rtaudio REQUIRED) - +find_package(GObject) if(WIN32) set(WINSOCK2_LIBRARIES "ws2_32") endif(WIN32) -if(RPI_BUILD) +if(RPI_BUILD AND NOT GST_BUILD) add_definitions(-DUSE_OMX -DOMX_SKIP64BIT) set(BCM_HOST_LIBRARIES "/opt/vc/lib/libbcm_host.so") set(BCM_HOST_INCLUDE_DIRS "/opt/vc/include") set(ILCLIENT_INCLUDE_DIRS "/opt/vc/src/hello_pi/libs/ilclient") set(ILCLIENT_LIBRARIES "/opt/vc/src/hello_pi/libs/ilclient/libilclient.a;/opt/vc/lib/libvcos.so;/opt/vc/lib/libvcilcs.a;/opt/vc/lib/libvchiq_arm.so") -endif(RPI_BUILD) +endif(RPI_BUILD AND NOT GST_BUILD) +if(GST_BUILD) + find_package(Qt5GStreamer) + find_package(PkgConfig REQUIRED) + pkg_check_modules(GST REQUIRED gstreamer-1.0>=1.4 + gstreamer-sdp-1.0>=1.4 + gstreamer-video-1.0>=1.4 + gstreamer-app-1.0>=1.4) + add_definitions(-DUSE_GST) + if(RPI_BUILD) + add_definitions(-DRPI) + findRpiRevision( RPI_REVISION ) + if(RPI_REVISION MATCHES "a03111" OR RPI_REVISION MATCHES "b03111" OR RPI_REVISION MATCHES "a03111") + message("Raspberry Pi 4 Found") + add_definitions(-DPI4) + endif(RPI_REVISION MATCHES "a03111" OR RPI_REVISION MATCHES "b03111" OR RPI_REVISION MATCHES "a03111") + endif(RPI_BUILD) +endif(GST_BUILD) include_directories(${CMAKE_CURRENT_BINARY_DIR} ${Qt5Multimedia_INCLUDE_DIRS} ${Qt5MultimediaWidgets_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Bluetooth_INCLUDE_DIRS} + ${QTGSTREAMER_INCLUDE_DIR} ${Boost_INCLUDE_DIRS} ${LIBUSB_1_INCLUDE_DIRS} ${PROTOBUF_INCLUDE_DIR} @@ -60,8 +79,9 @@ include_directories(${CMAKE_CURRENT_BINARY_DIR} ${AASDK_INCLUDE_DIRS} ${BCM_HOST_INCLUDE_DIRS} ${ILCLIENT_INCLUDE_DIRS} + ${GST_INCLUDE_DIRS} ${include_directory}) - + link_directories(${CMAKE_LIBRARY_OUTPUT_DIRECTORY}) set(common_include_directory ${include_directory}/f1x/openauto/Common) @@ -75,11 +95,13 @@ add_executable(autoapp ${autoapp_source_files}) file(GLOB_RECURSE autoapp_lib_source_files ${autoapp_sources_directory}/App.cpp ${autoapp_sources_directory}/Configuration/*.cpp ${autoapp_sources_directory}/Projection/*.cpp ${autoapp_sources_directory}/Service/*.cpp) file(GLOB_RECURSE autoapp_lib_inlcude_files ${autoapp_include_directory}/App.hpp ${autoapp_include_directory}/Configuration/*.hpp ${autoapp_include_directory}/Projection/*.hpp ${autoapp_include_directory}/Service/*.hpp) add_library(auto SHARED ${autoapp_lib_source_files} ${autoapp_lib_inlcude_files}) - +message(STATUS "${GST_LIBRARIES}") target_link_libraries(autoapp + ${GST_LIBRARIES} ${Boost_LIBRARIES} ${Qt5Multimedia_LIBRARIES} ${Qt5MultimediaWidgets_LIBRARIES} + ${Qt5QuickWidgets_LIBRARIES} ${Qt5Bluetooth_LIBRARIES} ${LIBUSB_1_LIBRARIES} ${PROTOBUF_LIBRARIES} @@ -88,7 +110,16 @@ target_link_libraries(autoapp ${WINSOCK2_LIBRARIES} ${RTAUDIO_LIBRARIES} ${AASDK_PROTO_LIBRARIES} - ${AASDK_LIBRARIES}) + ${AASDK_LIBRARIES} + ${QTGLIB_LIBRARY} + ${QTGLIB_LIBRARIES} + ${QTGSTREAMER_LIBRARY} + ${QTGSTREAMER_LIBRARIES} + ${QTGSTREAMER_UI_LIBRARY} + ${QTGSTREAMER_UI_LIBRARIES} + ${QTGSTREAMER_QUICK_LIBRARIES} + ${QTGSTREAMER_QUICK_LIBRARY} + ${GOBJECT_LIBRARIES}) set(btservice_sources_directory ${sources_directory}/btservice) set(btservice_include_directory ${include_directory}/f1x/openauto/btservice) diff --git a/assets/aa_video.qml b/assets/aa_video.qml new file mode 100644 index 0000000..f0f7dc7 --- /dev/null +++ b/assets/aa_video.qml @@ -0,0 +1,9 @@ +import QtQuick 2.0 +import QtGStreamer 1.0 + +VideoItem { + id: aaVideo + width: 300 + height: 300 + surface: videoSurface +} diff --git a/assets/resources.qrc b/assets/resources.qrc index 1070e2d..c159d57 100644 --- a/assets/resources.qrc +++ b/assets/resources.qrc @@ -4,5 +4,6 @@ ico_warning.png ico_setting.png ico_info.png + aa_video.qml diff --git a/cmake_modules/FindGObject.cmake b/cmake_modules/FindGObject.cmake new file mode 100644 index 0000000..d9e8df9 --- /dev/null +++ b/cmake_modules/FindGObject.cmake @@ -0,0 +1,80 @@ +# - Try to find GObject +# Once done this will define +# +# GOBJECT_FOUND - system has GObject +# GOBJECT_INCLUDE_DIR - the GObject include directory +# GOBJECT_LIBRARIES - the libraries needed to use GObject +# GOBJECT_DEFINITIONS - Compiler switches required for using GObject + +# Copyright (c) 2006, Tim Beaulen +# +# Redistribution and use is allowed according to the terms of the BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. + + +IF (GOBJECT_INCLUDE_DIR AND GOBJECT_LIBRARIES) + # in cache already + SET(GObject_FIND_QUIETLY TRUE) +ELSE (GOBJECT_INCLUDE_DIR AND GOBJECT_LIBRARIES) + SET(GObject_FIND_QUIETLY FALSE) +ENDIF (GOBJECT_INCLUDE_DIR AND GOBJECT_LIBRARIES) + +IF (NOT WIN32) + # use pkg-config to get the directories and then use these values + # in the FIND_PATH() and FIND_LIBRARY() calls + FIND_PACKAGE(PkgConfig) + PKG_CHECK_MODULES(PC_GOBJECT gobject-2.0) + #MESSAGE(STATUS "DEBUG: GObject include directory = ${GOBJECT_INCLUDE_DIRS}") + #MESSAGE(STATUS "DEBUG: GObject link directory = ${GOBJECT_LIBRARY_DIRS}") + #MESSAGE(STATUS "DEBUG: GObject CFlags = ${GOBJECT_CFLAGS}") + SET(GOBJECT_DEFINITIONS ${PC_GOBJECT_CFLAGS_OTHER}) +ENDIF (NOT WIN32) + +FIND_PATH(GOBJECT_INCLUDE_DIR gobject.h + PATHS + ${PC_GOBJECT_INCLUDEDIR} + ${PC_GOBJECT_INCLUDE_DIRS} + PATH_SUFFIXES glib-2.0/gobject/ + ) + +FIND_LIBRARY(_GObjectLibs NAMES gobject-2.0 + PATHS + ${PC_GOBJECT_LIBDIR} + ${PC_GOBJECT_LIBRARY_DIRS} + ) +FIND_LIBRARY(_GModuleLibs NAMES gmodule-2.0 + PATHS + ${PC_GOBJECT_LIBDIR} + ${PC_GOBJECT_LIBRARY_DIRS} + ) +FIND_LIBRARY(_GThreadLibs NAMES gthread-2.0 + PATHS + ${PC_GOBJECT_LIBDIR} + ${PC_GOBJECT_LIBRARY_DIRS} + ) +FIND_LIBRARY(_GLibs NAMES glib-2.0 + PATHS + ${PC_GOBJECT_LIBDIR} + ${PC_GOBJECT_LIBRARY_DIRS} + ) + +SET( GOBJECT_LIBRARIES ${_GObjectLibs} ${_GModuleLibs} ${_GThreadLibs} ${_GLibs} ) + +IF (GOBJECT_INCLUDE_DIR AND GOBJECT_LIBRARIES) + SET(GOBJECT_FOUND TRUE) +ELSE (GOBJECT_INCLUDE_DIR AND GOBJECT_LIBRARIES) + SET(GOBJECT_FOUND FALSE) +ENDIF (GOBJECT_INCLUDE_DIR AND GOBJECT_LIBRARIES) + +IF (GOBJECT_FOUND) + IF (NOT GObject_FIND_QUIETLY) + MESSAGE(STATUS "Found GObject libraries: ${GOBJECT_LIBRARIES}") + MESSAGE(STATUS "Found GObject includes : ${GOBJECT_INCLUDE_DIR}") + ENDIF (NOT GObject_FIND_QUIETLY) +ELSE (GOBJECT_FOUND) + IF (GObject_FIND_REQUIRED) + MESSAGE(STATUS "Could NOT find GObject") + ENDIF(GObject_FIND_REQUIRED) +ENDIF (GOBJECT_FOUND) + +MARK_AS_ADVANCED(GOBJECT_INCLUDE_DIR _GObjectLibs _GModuleLibs _GThreadLibs _GLibs) diff --git a/cmake_modules/FindGStreamer.cmake b/cmake_modules/FindGStreamer.cmake new file mode 100644 index 0000000..b508efe --- /dev/null +++ b/cmake_modules/FindGStreamer.cmake @@ -0,0 +1,135 @@ +# - Try to find GStreamer and its plugins +# Once done, this will define +# +# GSTREAMER_FOUND - system has GStreamer +# GSTREAMER_INCLUDE_DIRS - the GStreamer include directories +# GSTREAMER_LIBRARIES - link these to use GStreamer +# +# Additionally, gstreamer-base is always looked for and required, and +# the following related variables are defined: +# +# GSTREAMER_BASE_INCLUDE_DIRS - gstreamer-base's include directory +# GSTREAMER_BASE_LIBRARIES - link to these to use gstreamer-base +# +# Optionally, the COMPONENTS keyword can be passed to find_package() +# and GStreamer plugins can be looked for. Currently, the following +# plugins can be searched, and they define the following variables if +# found: +# +# gstreamer-app: GSTREAMER_APP_INCLUDE_DIRS and GSTREAMER_APP_LIBRARIES +# gstreamer-audio: GSTREAMER_AUDIO_INCLUDE_DIRS and GSTREAMER_AUDIO_LIBRARIES +# gstreamer-fft: GSTREAMER_FFT_INCLUDE_DIRS and GSTREAMER_FFT_LIBRARIES +# gstreamer-gl: GSTREAMER_GL_INCLUDE_DIRS and GSTREAMER_GL_LIBRARIES +# gstreamer-mpegts: GSTREAMER_MPEGTS_INCLUDE_DIRS and GSTREAMER_MPEGTS_LIBRARIES +# gstreamer-pbutils: GSTREAMER_PBUTILS_INCLUDE_DIRS and GSTREAMER_PBUTILS_LIBRARIES +# gstreamer-tag: GSTREAMER_TAG_INCLUDE_DIRS and GSTREAMER_TAG_LIBRARIES +# gstreamer-video: GSTREAMER_VIDEO_INCLUDE_DIRS and GSTREAMER_VIDEO_LIBRARIES +# gstreamer-codecparser:GSTREAMER_CODECPARSERS_INCLUDE_DIRS and GSTREAMER_CODECPARSERS_LIBRARIES +# +# Copyright (C) 2012 Raphael Kubo da Costa +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND ITS CONTRIBUTORS ``AS +# IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ITS +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +find_package(PkgConfig QUIET) + +# Helper macro to find a GStreamer plugin (or GStreamer itself) +# _component_prefix is prepended to the _INCLUDE_DIRS and _LIBRARIES variables (eg. "GSTREAMER_AUDIO") +# _pkgconfig_name is the component's pkg-config name (eg. "gstreamer-1.0", or "gstreamer-video-1.0"). +# _library is the component's library name (eg. "gstreamer-1.0" or "gstvideo-1.0") +macro(FIND_GSTREAMER_COMPONENT _component_prefix _pkgconfig_name _library) + + string(REGEX MATCH "(.*)>=(.*)" _dummy "${_pkgconfig_name}") + if ("${CMAKE_MATCH_2}" STREQUAL "") + pkg_check_modules(PC_${_component_prefix} "${_pkgconfig_name} >= ${GStreamer_FIND_VERSION}") + else () + pkg_check_modules(PC_${_component_prefix} ${_pkgconfig_name}) + endif () + set(${_component_prefix}_INCLUDE_DIRS ${PC_${_component_prefix}_INCLUDE_DIRS}) + + find_library(${_component_prefix}_LIBRARIES + NAMES ${_library} + HINTS ${PC_${_component_prefix}_LIBRARY_DIRS} ${PC_${_component_prefix}_LIBDIR} + ) +endmacro() + +# ------------------------ +# 1. Find GStreamer itself +# ------------------------ + +# 1.1. Find headers and libraries +FIND_GSTREAMER_COMPONENT(GSTREAMER gstreamer-1.0 gstreamer-1.0) +FIND_GSTREAMER_COMPONENT(GSTREAMER_BASE gstreamer-base-1.0 gstbase-1.0) + +# ------------------------- +# 2. Find GStreamer plugins +# ------------------------- + +FIND_GSTREAMER_COMPONENT(GSTREAMER_APP gstreamer-app-1.0 gstapp-1.0) +FIND_GSTREAMER_COMPONENT(GSTREAMER_AUDIO gstreamer-audio-1.0 gstaudio-1.0) +FIND_GSTREAMER_COMPONENT(GSTREAMER_FFT gstreamer-fft-1.0 gstfft-1.0) +FIND_GSTREAMER_COMPONENT(GSTREAMER_GL gstreamer-gl-1.0 gstgl-1.0) +FIND_GSTREAMER_COMPONENT(GSTREAMER_MPEGTS gstreamer-mpegts-1.0>=1.4.0 gstmpegts-1.0) +FIND_GSTREAMER_COMPONENT(GSTREAMER_PBUTILS gstreamer-pbutils-1.0 gstpbutils-1.0) +FIND_GSTREAMER_COMPONENT(GSTREAMER_TAG gstreamer-tag-1.0 gsttag-1.0) +FIND_GSTREAMER_COMPONENT(GSTREAMER_VIDEO gstreamer-video-1.0 gstvideo-1.0) +FIND_GSTREAMER_COMPONENT(GSTREAMER_CODECPARSERS gstreamer-codecparsers-1.0 gstcodecparsers-1.0) + +# ------------------------------------------------ +# 3. Process the COMPONENTS passed to FIND_PACKAGE +# ------------------------------------------------ +set(_GSTREAMER_REQUIRED_VARS GSTREAMER_INCLUDE_DIRS GSTREAMER_LIBRARIES GSTREAMER_VERSION GSTREAMER_BASE_INCLUDE_DIRS GSTREAMER_BASE_LIBRARIES) + +foreach (_component ${GStreamer_FIND_COMPONENTS}) + set(_gst_component "GSTREAMER_${_component}") + string(TOUPPER ${_gst_component} _UPPER_NAME) + + list(APPEND _GSTREAMER_REQUIRED_VARS ${_UPPER_NAME}_INCLUDE_DIRS ${_UPPER_NAME}_LIBRARIES) +endforeach () + +include(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(GStreamer REQUIRED_VARS _GSTREAMER_REQUIRED_VARS + VERSION_VAR GSTREAMER_VERSION) + +mark_as_advanced( + GSTREAMER_APP_INCLUDE_DIRS + GSTREAMER_APP_LIBRARIES + GSTREAMER_AUDIO_INCLUDE_DIRS + GSTREAMER_AUDIO_LIBRARIES + GSTREAMER_BASE_INCLUDE_DIRS + GSTREAMER_BASE_LIBRARIES + GSTREAMER_FFT_INCLUDE_DIRS + GSTREAMER_FFT_LIBRARIES + GSTREAMER_GL_INCLUDE_DIRS + GSTREAMER_GL_LIBRARIES + GSTREAMER_INCLUDE_DIRS + GSTREAMER_LIBRARIES + GSTREAMER_MPEGTS_INCLUDE_DIRS + GSTREAMER_MPEGTS_LIBRARIES + GSTREAMER_PBUTILS_INCLUDE_DIRS + GSTREAMER_PBUTILS_LIBRARIES + GSTREAMER_TAG_INCLUDE_DIRS + GSTREAMER_TAG_LIBRARIES + GSTREAMER_VIDEO_INCLUDE_DIRS + GSTREAMER_VIDEO_LIBRARIES + GSTREAMER_CODECPARSERS_INCLUDE_DIRS + GSTREAMER_CODECPARSERS_LIBRARIES +) diff --git a/cmake_modules/functions.cmake b/cmake_modules/functions.cmake new file mode 100644 index 0000000..b01988e --- /dev/null +++ b/cmake_modules/functions.cmake @@ -0,0 +1,15 @@ + +function( findRpiRevision OUTPUT ) + # Find it with an automated script + execute_process( COMMAND grep -Po "^Revision\\s*:\\s*\\K[[:xdigit:]]+" /proc/cpuinfo OUTPUT_VARIABLE TMP ) + + # If have not found the Revision number, use the last version + if ( TMP ) + message( "-- Detecting Raspberry Pi Revision Number: ${TMP}" ) + else() + set( TMP "0006" ) + message( WARNING "-- Could NOT find Raspberry Pi revision!" ) + endif() + + set( ${OUTPUT} "${TMP}" PARENT_SCOPE ) +endfunction() diff --git a/include/f1x/openauto/autoapp/Projection/GSTVideoOutput.hpp b/include/f1x/openauto/autoapp/Projection/GSTVideoOutput.hpp new file mode 100644 index 0000000..33bb2f8 --- /dev/null +++ b/include/f1x/openauto/autoapp/Projection/GSTVideoOutput.hpp @@ -0,0 +1,92 @@ +/* +* This file is part of openauto project. +* Copyright (C) 2018 f1x.studio (Michal Szwaj) +* +* openauto is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 3 of the License, or +* (at your option) any later version. + +* openauto 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 General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with openauto. If not, see . +*/ +#ifdef USE_GST + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace f1x +{ +namespace openauto +{ +namespace autoapp +{ +namespace projection +{ + +class GSTVideoOutput: public QObject, public VideoOutput, boost::noncopyable +{ + Q_OBJECT + +public: + GSTVideoOutput(configuration::IConfiguration::Pointer configuration, QWidget* videoContainer=nullptr, std::function activeCallback=nullptr); + ~GSTVideoOutput(); + bool open() override; + bool init() override; + void write(uint64_t timestamp, const aasdk::common::DataConstBuffer& buffer) override; + void stop() override; + void resize(); +signals: + void startPlayback(); + void stopPlayback(); +protected slots: + void onStartPlayback(); + void onStopPlayback(); +private: + static GstPadProbeReturn convert_probe(GstPad* pad, GstPadProbeInfo* info, void* user_data); + static gboolean bus_callback(GstBus* bus, GstMessage* message, gpointer* ptr); + + QGst::ElementPtr videoSink_; + QQuickWidget* videoWidget_; + GstElement* vidPipeline_ = nullptr; + GstAppSrc* vidSrc_ = nullptr; + QWidget* videoContainer_; + QGst::Quick::VideoSurface* surface_; + std::function activeCallback_; +}; + +} +} +} +} + +#endif diff --git a/include/f1x/openauto/autoapp/Service/ServiceFactory.hpp b/include/f1x/openauto/autoapp/Service/ServiceFactory.hpp index 464cc12..8a1d02e 100644 --- a/include/f1x/openauto/autoapp/Service/ServiceFactory.hpp +++ b/include/f1x/openauto/autoapp/Service/ServiceFactory.hpp @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include @@ -61,6 +63,8 @@ private: std::shared_ptr inputDevice_; #ifdef USE_OMX std::shared_ptr omxVideoOutput_; +#elif defined USE_GST + std::shared_ptr gstVideoOutput_; #else projection::QtVideoOutput *qtVideoOutput_; #endif diff --git a/src/autoapp/Projection/GSTVideoOutput.cpp b/src/autoapp/Projection/GSTVideoOutput.cpp new file mode 100644 index 0000000..b29d260 --- /dev/null +++ b/src/autoapp/Projection/GSTVideoOutput.cpp @@ -0,0 +1,233 @@ +/* +* This file is part of openauto project. +* Copyright (C) 2018 f1x.studio (Michal Szwaj) +* +* openauto is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 3 of the License, or +* (at your option) any later version. + +* openauto 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 General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with openauto. If not, see . +*/ + +#ifdef USE_GST + +#include +#include +#include + +namespace f1x +{ +namespace openauto +{ +namespace autoapp +{ +namespace projection +{ + +GSTVideoOutput::GSTVideoOutput(configuration::IConfiguration::Pointer configuration, QWidget* videoContainer, std::function activeCallback) + : VideoOutput(std::move(configuration)) + , videoContainer_(videoContainer) + , activeCallback_(activeCallback) +{ + this->moveToThread(QApplication::instance()->thread()); + videoWidget_ = new QQuickWidget(videoContainer_); + + surface_ = new QGst::Quick::VideoSurface; + videoWidget_->rootContext()->setContextProperty(QLatin1String("videoSurface"), surface_); + videoWidget_->setSource(QUrl("qrc:/aa_video.qml")); + videoWidget_->setResizeMode(QQuickWidget::SizeRootObjectToView); + + videoSink_ = surface_->videoSink(); + GstBus* bus; + + GError* error = NULL; + const char* vidLaunchStr = "appsrc name=mysrc is-live=true block=false max-latency=100 do-timestamp=true stream-type=stream ! " + "queue ! " + + "h264parse ! " + #ifdef RPI + #ifdef PI4 + " v4l2h264dec !" + #else + " omxh264dec ! " + #endif + #else + "avdec_h264 ! " + #endif + "capsfilter caps=video/x-raw name=mycapsfilter"; + #ifdef RPI + OPENAUTO_LOG(info) << "[GSTVideoOutput] RPI Build, running with " << + #ifdef PI4 + "v4l2h264dec"; + #else + "omxh264dec"; + #endif + #endif + + vidPipeline_ = gst_parse_launch(vidLaunchStr, &error); + bus = gst_pipeline_get_bus(GST_PIPELINE(vidPipeline_)); + gst_bus_add_watch(bus, (GstBusFunc)GSTVideoOutput::bus_callback, this); + gst_object_unref(bus); + + GstElement* sink = QGlib::RefPointer(videoSink_); + g_object_set (sink, "force-aspect-ratio", false, nullptr); + + g_object_set (sink, "sync", false, nullptr); + g_object_set (sink, "async", false, nullptr); + GstElement* capsFilter = gst_bin_get_by_name(GST_BIN(vidPipeline_), "mycapsfilter"); + gst_bin_add(GST_BIN(vidPipeline_), GST_ELEMENT(sink)); + gst_element_link(capsFilter, GST_ELEMENT(sink)); + + vidSrc_ = GST_APP_SRC(gst_bin_get_by_name(GST_BIN(vidPipeline_), "mysrc")); + gst_app_src_set_stream_type(vidSrc_, GST_APP_STREAM_TYPE_STREAM); + + connect(this, &GSTVideoOutput::startPlayback, this, &GSTVideoOutput::onStartPlayback, Qt::QueuedConnection); + connect(this, &GSTVideoOutput::stopPlayback, this, &GSTVideoOutput::onStopPlayback, Qt::QueuedConnection); +} + + +GSTVideoOutput::~GSTVideoOutput() +{ + gst_object_unref(vidPipeline_); + gst_object_unref(vidSrc_); +} + +gboolean GSTVideoOutput::bus_callback(GstBus* /* unused*/, GstMessage* message, gpointer* ptr) +{ + gchar* debug; + GError* err; + gchar* name; + + switch(GST_MESSAGE_TYPE(message)) + { + case GST_MESSAGE_ERROR: + gst_message_parse_error(message, &err, &debug); + OPENAUTO_LOG(info) << "[GSTVideoOutput] Error " << err->message; + g_error_free(err); + g_free(debug); + break; + case GST_MESSAGE_WARNING: + gst_message_parse_warning(message, &err, &debug); + OPENAUTO_LOG(info) << "[GSTVideoOutput] Warning " << err->message<<" | Debug " << debug; + name = (gchar*)GST_MESSAGE_SRC_NAME(message); + OPENAUTO_LOG(info) << "[GSTVideoOutput] Name of src " << name ? name : "nil"; + g_error_free(err); + g_free(debug); + break; + case GST_MESSAGE_EOS: + OPENAUTO_LOG(info) << "[GSTVideoOutput] End of stream"; + break; + case GST_MESSAGE_STATE_CHANGED: + break; + default: + break; + } + + return TRUE; +} + +bool GSTVideoOutput::open() +{ + GstElement* capsFilter = gst_bin_get_by_name(GST_BIN(vidPipeline_), "mycapsfilter"); + GstPad* convertPad = gst_element_get_static_pad(capsFilter, "sink"); + gst_pad_add_probe (convertPad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, convert_probe, this, NULL); + gst_element_set_state(vidPipeline_, GST_STATE_PLAYING); + return true; +} + +GstPadProbeReturn GSTVideoOutput::convert_probe(GstPad* pad, GstPadProbeInfo* info, void* user_data) +{ + GstEvent* event = GST_PAD_PROBE_INFO_EVENT(info); + if(GST_PAD_PROBE_INFO_TYPE(info) & GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM) { + if(GST_EVENT_TYPE(event) == GST_EVENT_SEGMENT) { + GstCaps* caps = gst_pad_get_current_caps(pad); + if(caps != NULL) + { + GstVideoInfo* vinfo = gst_video_info_new(); + gst_video_info_from_caps(vinfo, caps); + OPENAUTO_LOG(info) << "[GSTVideoOutput] Video Width: "<< vinfo->width; + OPENAUTO_LOG(info) << "[GSTVideoOutput] Video Height: "<< vinfo->height; + + + } + return GST_PAD_PROBE_REMOVE; + } + } + return GST_PAD_PROBE_OK; +} + +bool GSTVideoOutput::init() +{ + OPENAUTO_LOG(info) << "[GSTVideoOutput] init"; + emit startPlayback(); + return true; +} + +void GSTVideoOutput::write(uint64_t timestamp, const aasdk::common::DataConstBuffer& buffer) +{ + GstBuffer* buffer_ = gst_buffer_new_and_alloc(buffer.size); + gst_buffer_fill(buffer_, 0, buffer.cdata, buffer.size); + int ret = gst_app_src_push_buffer((GstAppSrc*)vidSrc_, buffer_); + if(ret != GST_FLOW_OK) + { + OPENAUTO_LOG(info) << "[GSTVideoOutput] push buffer returned " << ret << " for " << buffer.size << "bytes"; + } +} + +void GSTVideoOutput::onStartPlayback() +{ + if(activeCallback_ != nullptr) + { + activeCallback_(true); + } + if(videoContainer_ == nullptr) + { + OPENAUTO_LOG(info) << "[GSTVideoOutput] No video container, setting projection fullscreen"; + videoWidget_->setFocus(); + videoWidget_->setWindowFlags(Qt::WindowStaysOnTopHint | Qt::FramelessWindowHint); + videoWidget_->showFullScreen(); + } + else + { + OPENAUTO_LOG(info) << "[GSTVideoOutput] Resizing to video container"; + videoWidget_->resize(videoContainer_->size()); + } + videoWidget_->show(); +} + +void GSTVideoOutput::stop() +{ + emit stopPlayback(); +} + +void GSTVideoOutput::onStopPlayback() +{ + if(activeCallback_ != nullptr) + { + activeCallback_(false); + } + OPENAUTO_LOG(info) << "[GSTVideoOutput] stop."; + gst_element_set_state(vidPipeline_, GST_STATE_PAUSED); + videoWidget_->hide(); +} + +void GSTVideoOutput::resize() +{ + OPENAUTO_LOG(info) << "[GSTVideoOutput] Got resize request to "<< videoContainer_->width() << "x" << videoContainer_->height(); + + if(videoWidget_ != nullptr && videoContainer_ != nullptr) videoWidget_->resize(videoContainer_->size()); +} + +} +} +} +} + +#endif diff --git a/src/autoapp/Service/ServiceFactory.cpp b/src/autoapp/Service/ServiceFactory.cpp index cee69a9..22df94f 100644 --- a/src/autoapp/Service/ServiceFactory.cpp +++ b/src/autoapp/Service/ServiceFactory.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,8 @@ ServiceFactory::ServiceFactory(boost::asio::io_service& ioService, configuration , activeCallback_(activeCallback) #ifdef USE_OMX , omxVideoOutput_(std::make_shared(configuration_, this->QRectToDestRect(screenGeometry_), activeCallback_)) +#elif defined USE_GST + , gstVideoOutput_((QGst::init(nullptr, nullptr), std::make_shared(configuration_, activeArea_, activeCallback_))) #else , qtVideoOutput_(nullptr) #endif @@ -88,6 +91,8 @@ IService::Pointer ServiceFactory::createVideoService(aasdk::messenger::IMessenge { #ifdef USE_OMX auto videoOutput(omxVideoOutput_); +#elif defined USE_GST + auto videoOutput(gstVideoOutput_); #else qtVideoOutput_ = new projection::QtVideoOutput(configuration_, activeArea_); if (activeCallback_ != nullptr) { @@ -188,6 +193,8 @@ void ServiceFactory::resize() if (inputDevice_ != nullptr) inputDevice_->setTouchscreenGeometry(screenGeometry_); #ifdef USE_OMX if (omxVideoOutput_ != nullptr) omxVideoOutput_->setDestRect(this->QRectToDestRect(screenGeometry_)); +#elif defined USE_GST + if (gstVideoOutput_ != nullptr) gstVideoOutput_->resize(); #else if (qtVideoOutput_ != nullptr) qtVideoOutput_->resize(); #endif