From 9686b0b9e3805b55318a85c24f26fb1eb2515ffc Mon Sep 17 00:00:00 2001 From: Holden Date: Sun, 6 Oct 2024 04:33:26 -0400 Subject: [PATCH 01/27] Video: GStreamer Plugin Improvements --- .github/actions/gstreamer/action.yml | 1 + .github/workflows/macos.yml | 1 - CMakeLists.txt | 8 +- android/libs/gstreamer_android-1.0.c.in | 49 ++ .../org/freedesktop/gstreamer/GStreamer.java | 105 +++ .../mavlink/qgroundcontrol/QGCActivity.java | 10 +- cmake/CreateAppImage.cmake | 17 +- cmake/CreateMacDMG.cmake | 4 +- cmake/Qt6QGCConfiguration.cmake | 1 + cmake/find-modules/FindEGL.cmake | 107 ++- cmake/find-modules/FindFFmpeg.cmake | 89 +-- cmake/find-modules/FindGLESv2.cmake | 4 +- cmake/find-modules/FindGObject.cmake | 4 +- cmake/find-modules/FindGStreamer.cmake | 728 +++++++++--------- cmake/find-modules/FindTaglib.cmake | 92 +++ cmake/find-modules/FindVAAPI.cmake | 34 +- cmake/find-modules/FindWaylandScanner.cmake | 211 +++-- cmake/find-modules/FindXCB.cmake | 149 ++-- cmake/modules/ECMFindModuleHelpers.cmake | 239 +++--- deploy/linux/AppRun | 23 +- .../apprun-hooks/linux-deploy-gstreamer.sh | 8 - src/CMakeLists.txt | 1 + src/Settings/CMakeLists.txt | 1 + src/Settings/VideoDecoderOptions.h | 20 - src/Settings/VideoSettings.cc | 28 +- src/Settings/VideoSettings.h | 3 - src/VideoManager/VideoManager.cc | 19 +- .../VideoReceiver/GStreamer/CMakeLists.txt | 12 +- .../VideoReceiver/GStreamer/GLVideoItemStub.h | 4 +- .../VideoReceiver/GStreamer/GStreamer.cc | 375 ++++----- .../VideoReceiver/GStreamer/GStreamer.h | 27 +- .../VideoReceiver/GStreamer/gst_ios_init.m | 98 +-- .../VideoReceiver/GStreamer/gstqgc.c | 40 - .../VideoReceiver/GStreamer/gstqgc.cc | 35 + .../VideoReceiver/GStreamer/gstqgcelement.cc | 10 + .../VideoReceiver/GStreamer/gstqgcelements.h | 12 + ...gcvideosinkbin.c => gstqgcvideosinkbin.cc} | 418 +++++----- .../GStreamer/gstqgcvideosinkbin.h | 36 + .../GStreamer/gstqml6gl/CMakeLists.txt | 84 +- tools/setup/install-dependencies-debian.sh | 1 + 40 files changed, 1733 insertions(+), 1375 deletions(-) create mode 100644 android/libs/gstreamer_android-1.0.c.in create mode 100644 android/src/org/freedesktop/gstreamer/GStreamer.java create mode 100644 cmake/find-modules/FindTaglib.cmake delete mode 100644 deploy/linux/apprun-hooks/linux-deploy-gstreamer.sh delete mode 100644 src/Settings/VideoDecoderOptions.h delete mode 100644 src/VideoManager/VideoReceiver/GStreamer/gstqgc.c create mode 100644 src/VideoManager/VideoReceiver/GStreamer/gstqgc.cc create mode 100644 src/VideoManager/VideoReceiver/GStreamer/gstqgcelement.cc create mode 100644 src/VideoManager/VideoReceiver/GStreamer/gstqgcelements.h rename src/VideoManager/VideoReceiver/GStreamer/{gstqgcvideosinkbin.c => gstqgcvideosinkbin.cc} (54%) create mode 100644 src/VideoManager/VideoReceiver/GStreamer/gstqgcvideosinkbin.h diff --git a/.github/actions/gstreamer/action.yml b/.github/actions/gstreamer/action.yml index 7527b9c53a3..89e97b36db5 100644 --- a/.github/actions/gstreamer/action.yml +++ b/.github/actions/gstreamer/action.yml @@ -25,6 +25,7 @@ runs: run: git clone --depth 1 --branch ${{ inputs.gst_version }} https://github.com/GStreamer/gstreamer.git shell: bash + # macos https://github.com/Homebrew/homebrew-core/blob/4e00e17ab49b90949c27cf43a873ca923f3735aa/Formula/g/gstreamer.rb - name: Configure GStreamer working-directory: ${{ inputs.working_directory }}/gstreamer run: meson setup diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index cf53d6a13de..a415db0b775 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -65,7 +65,6 @@ jobs: for package in *.pkg ; do sudo installer -verbose -pkg "$package" -target / done - echo "PKG_CONFIG_PATH=/Library/Frameworks/GStreamer.framework/lib/pkgconfig:${{ env.PKG_CONFIG_PATH }}" >> "$GITHUB_ENV" - name: Set Up Cache uses: hendrikmuhs/ccache-action@v1.2 diff --git a/CMakeLists.txt b/CMakeLists.txt index 1bbc0d818ef..2bf5201b90d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,10 +110,6 @@ endif() set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE) -if(CMAKE_BUILD_TYPE STREQUAL "Release") - add_compile_definitions(QGC_INSTALL_RELEASE) -endif() - ####################################################### # Qt6 Configuration ####################################################### @@ -520,6 +516,10 @@ if(LINUX) FILES ${CMAKE_BINARY_DIR}/metainfo/org.mavlink.qgroundcontrol.metainfo.xml DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo/ ) + install( + FILES ${CMAKE_SOURCE_DIR}/deploy/linux/AppRun + DESTINATION ${CMAKE_BINARY_DIR} + ) install(SCRIPT "${CMAKE_SOURCE_DIR}/cmake/CreateAppImage.cmake") elseif(WIN32) install(SCRIPT "${CMAKE_SOURCE_DIR}/cmake/CreateWinInstaller.cmake") diff --git a/android/libs/gstreamer_android-1.0.c.in b/android/libs/gstreamer_android-1.0.c.in new file mode 100644 index 00000000000..2606cd144fa --- /dev/null +++ b/android/libs/gstreamer_android-1.0.c.in @@ -0,0 +1,49 @@ +#include +#include + +#define GST_G_IO_MODULE_DECLARE(name) \ +extern void G_PASTE(g_io_, G_PASTE(name, _load)) (gpointer data) + +#define GST_G_IO_MODULE_LOAD(name) \ +G_PASTE(g_io_, G_PASTE(name, _load)) (NULL) + +/* Declaration of static plugins */ + @PLUGINS_DECLARATION@ + +/* Declaration of static gio modules */ + @G_IO_MODULES_DECLARE@ + +/* Call this function to load GIO modules */ +static void +gst_android_load_gio_modules (void) +{ + GTlsBackend *backend; + const gchar *ca_certs; + + @G_IO_MODULES_LOAD@ + + ca_certs = g_getenv ("CA_CERTIFICATES"); + + backend = g_tls_backend_get_default (); + if (backend && ca_certs) { + GTlsDatabase *db; + GError *error = NULL; + + db = g_tls_file_database_new (ca_certs, &error); + if (db) { + g_tls_backend_set_default_database (backend, db); + g_object_unref (db); + } else { + g_warning ("Failed to create a database from file: %s", + error ? error->message : "Unknown"); + } + } +} + +/* This is called by gst_init() */ +void +gst_init_static_plugins (void) +{ + @PLUGINS_REGISTRATION@ + gst_android_load_gio_modules (); +} diff --git a/android/src/org/freedesktop/gstreamer/GStreamer.java b/android/src/org/freedesktop/gstreamer/GStreamer.java new file mode 100644 index 00000000000..066b2152b49 --- /dev/null +++ b/android/src/org/freedesktop/gstreamer/GStreamer.java @@ -0,0 +1,105 @@ +/** + * Copy this file into your Android project and call init(). If your project + * contains fonts and/or certificates in assets, uncomment copyFonts() and/or + * copyCaCertificates() lines in init(). + */ +package org.freedesktop.gstreamer; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import android.content.Context; +import android.content.res.AssetManager; +import android.system.Os; + +public class GStreamer { + private static native void nativeInit(Context context) throws Exception; + + public static void init(Context context) throws Exception { + //copyFonts(context); + //copyCaCertificates(context); + nativeInit(context); + } + + private static void copyFonts(Context context) { + AssetManager assetManager = context.getAssets(); + File filesDir = context.getFilesDir(); + File fontsFCDir = new File (filesDir, "fontconfig"); + File fontsDir = new File (fontsFCDir, "fonts"); + File fontsCfg = new File (fontsFCDir, "fonts.conf"); + + fontsDir.mkdirs(); + + try { + /* Copy the config file */ + copyFile (assetManager, "fontconfig/fonts.conf", fontsCfg); + /* Copy the fonts */ + for(String filename : assetManager.list("fontconfig/fonts/truetype")) { + File font = new File(fontsDir, filename); + copyFile (assetManager, "fontconfig/fonts/truetype/" + filename, font); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + private static void copyCaCertificates(Context context) { + AssetManager assetManager = context.getAssets(); + File filesDir = context.getFilesDir(); + File sslDir = new File (filesDir, "ssl"); + File certsDir = new File (sslDir, "certs"); + File certs = new File (certsDir, "ca-certificates.crt"); + + certsDir.mkdirs(); + + try { + /* Copy the certificates file */ + copyFile (assetManager, "ssl/certs/ca-certificates.crt", certs); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private static void copyFile(AssetManager assetManager, String assetPath, File outFile) throws IOException { + InputStream in = null; + OutputStream out = null; + IOException exception = null; + + if (outFile.exists()) + outFile.delete(); + + try { + in = assetManager.open(assetPath); + out = new FileOutputStream(outFile); + + byte[] buffer = new byte[1024]; + int read; + while ((read = in.read(buffer)) != -1) { + out.write(buffer, 0, read); + } + out.flush(); + } catch (IOException e) { + exception = e; + } finally { + if (in != null) + try { + in.close(); + } catch (IOException e) { + if (exception == null) + exception = e; + } + if (out != null) + try { + out.close(); + } catch (IOException e) { + if (exception == null) + exception = e; + } + if (exception != null) + throw exception; + } + } +} diff --git a/android/src/org/mavlink/qgroundcontrol/QGCActivity.java b/android/src/org/mavlink/qgroundcontrol/QGCActivity.java index 31d473d300d..f470b11138f 100644 --- a/android/src/org/mavlink/qgroundcontrol/QGCActivity.java +++ b/android/src/org/mavlink/qgroundcontrol/QGCActivity.java @@ -28,6 +28,8 @@ import android.util.Log; import android.view.WindowManager; +// import org.freedesktop.gstreamer.GStreamer; + import com.hoho.android.usbserial.driver.*; import com.hoho.android.usbserial.util.*; @@ -144,7 +146,7 @@ public QGCActivity() public void onInit(int status) { - + // System.loadLibrary("gstreamer_android"); } @Override @@ -160,6 +162,12 @@ public void onCreate(Bundle savedInstanceState) _setupUsbPermissionIntent(); m_usbManager = (UsbManager) m_instance.getSystemService(Context.USB_SERVICE); + + // try { + // GStreamer.init(this); + // } catch (final Exception ex) { + // Log.e(TAG, "Exception GStreamer.init(this)", ex); + // } } @Override diff --git a/cmake/CreateAppImage.cmake b/cmake/CreateAppImage.cmake index 34c57ef5030..e7599d8406b 100644 --- a/cmake/CreateAppImage.cmake +++ b/cmake/CreateAppImage.cmake @@ -2,12 +2,17 @@ message(STATUS "Creating AppImage") # TODO: https://github.com/AppImageCommunity/AppImageUpdate set(APPDIR_PATH "${CMAKE_BINARY_DIR}/AppDir") +# set(APPIMAGETOOL_PATH "${CMAKE_BINARY_DIR}/appimagetool-x86_64.AppImage") set(LD_PATH "${CMAKE_BINARY_DIR}/linuxdeploy-x86_64.AppImage") set(LD_APPIMAGEPLUGIN_PATH "${CMAKE_BINARY_DIR}/linuxdeploy-plugin-appimage-x86_64.AppImage") -set(LD_QTPLUGIN_PATH "${CMAKE_BINARY_DIR}/linuxdeploy-plugin-qt-x86_64.AppImage") +# set(LD_QTPLUGIN_PATH "${CMAKE_BINARY_DIR}/linuxdeploy-plugin-qt-x86_64.AppImage") # set(LD_GSTPLUGIN_PATH "${CMAKE_BINARY_DIR}/linuxdeploy-plugin-gstreamer.sh") # set(LD_GTKPLUGIN_PATH "${CMAKE_BINARY_DIR}/linuxdeploy-plugin-gtk.sh") +# if(NOT EXISTS "${APPIMAGETOOL_PATH}") +# file(DOWNLOAD https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage "${APPIMAGETOOL_PATH}") +# execute_process(COMMAND chmod a+x "${APPIMAGETOOL_PATH}") +# endif() if(NOT EXISTS "${LD_PATH}") file(DOWNLOAD https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage "${LD_PATH}") execute_process(COMMAND chmod a+x "${LD_PATH}") @@ -16,10 +21,10 @@ if(NOT EXISTS "${LD_APPIMAGEPLUGIN_PATH}") file(DOWNLOAD https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage "${LD_APPIMAGEPLUGIN_PATH}") execute_process(COMMAND chmod a+x "${LD_APPIMAGEPLUGIN_PATH}") endif() -if(NOT EXISTS "${LD_QTPLUGIN_PATH}") - file(DOWNLOAD https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage "${LD_QTPLUGIN_PATH}") - execute_process(COMMAND chmod a+x "${LD_QTPLUGIN_PATH}") -endif() +# if(NOT EXISTS "${LD_QTPLUGIN_PATH}") +# file(DOWNLOAD https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage "${LD_QTPLUGIN_PATH}") +# execute_process(COMMAND chmod a+x "${LD_QTPLUGIN_PATH}") +# endif() # if(NOT EXISTS "${LD_GTKPLUGIN_PATH}") # file(DOWNLOAD https://raw.githubusercontent.com/linuxdeploy/linuxdeploy-plugin-gtk/master/linuxdeploy-plugin-gtk.sh "${LD_GTKPLUGIN_PATH}") # execute_process(COMMAND chmod a+x "${LD_GTKPLUGIN_PATH}") @@ -29,7 +34,7 @@ endif() # execute_process(COMMAND chmod a+x "${LD_GSTPLUGIN_PATH}") # endif() -execute_process(COMMAND ${LD_PATH} --appdir ${APPDIR_PATH} --output appimage) +execute_process(COMMAND ${LD_PATH} --appdir ${APPDIR_PATH} --output appimage --custom-apprun ${CMAKE_BINARY_DIR}/AppRun) # --exclude-library "libX*" # --exclude-library "libglib*" # --exclude-library "libgobject*" diff --git a/cmake/CreateMacDMG.cmake b/cmake/CreateMacDMG.cmake index cce72e9c379..3c3770e4186 100644 --- a/cmake/CreateMacDMG.cmake +++ b/cmake/CreateMacDMG.cmake @@ -1,6 +1,6 @@ # include(BundleUtilities) -message(STATUS "Copy GStreamer framework into bundle") +# message(STATUS "Copy GStreamer framework into bundle") file(COPY /Library/Frameworks/GStreamer.framework DESTINATION staging/QGroundControl.app/Contents/Frameworks) file(REMOVE_RECURSE staging/QGroundControl.app/Contents/Frameworks/GStreamer.framework/Versions/1.0/bin) file(REMOVE_RECURSE staging/QGroundControl.app/Contents/Frameworks/GStreamer.framework/Versions/1.0/etc) @@ -23,7 +23,7 @@ file(REMOVE ${REMOVE_LIB_FILES}) file(REMOVE_RECURSE staging/QGroundControl.app/Contents/Frameworks/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0/include) file(REMOVE_RECURSE staging/QGroundControl.app/Contents/Frameworks/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0/pkgconfig) -# Fix up library paths to point into bundle +# # Fix up library paths to point into bundle execute_process(COMMAND install_name_tool -change @rpath/libgstgl-1.0.0.dylib @executable_path/../Frameworks/GStreamer.framework/Libraries/libgstgl-1.0.0.dylib staging/QGroundControl.app/Contents/MacOS/QGroundControl) execute_process(COMMAND install_name_tool -change @rpath/libgstvideo-1.0.0.dylib @executable_path/../Frameworks/GStreamer.framework/Libraries/libgstvideo-1.0.0.dylib staging/QGroundControl.app/Contents/MacOS/QGroundControl) execute_process(COMMAND install_name_tool -change @rpath/libgstbase-1.0.0.dylib @executable_path/../Frameworks/GStreamer.framework/Libraries/libgstbase-1.0.0.dylib staging/QGroundControl.app/Contents/MacOS/QGroundControl) diff --git a/cmake/Qt6QGCConfiguration.cmake b/cmake/Qt6QGCConfiguration.cmake index f00bcf3ad39..cfedfef3824 100644 --- a/cmake/Qt6QGCConfiguration.cmake +++ b/cmake/Qt6QGCConfiguration.cmake @@ -57,4 +57,5 @@ cmake_print_variables(QT_VERSION QT_MKSPEC QT_LIBRARY_HINTS) # if(ANDROID) # set(ENV{PKG_CONFIG_SYSROOT_DIR} ${CMAKE_SYSROOT}) + # message(STATUS "PKG_CONFIG_SYSROOT_DIR $ENV{PKG_CONFIG_SYSROOT_DIR}") # endif() diff --git a/cmake/find-modules/FindEGL.cmake b/cmake/find-modules/FindEGL.cmake index 747901b3a94..9ac8e2fa0cf 100644 --- a/cmake/find-modules/FindEGL.cmake +++ b/cmake/find-modules/FindEGL.cmake @@ -1,42 +1,64 @@ -# SPDX-FileCopyrightText: 2014 Alex Merry -# SPDX-FileCopyrightText: 2014 Martin Gräßlin +#.rst: +# FindEGL +# ------- # -# SPDX-License-Identifier: BSD-3-Clause - -#[=======================================================================[.rst: -FindEGL -------- - -Try to find EGL. - -This will define the following variables: - -``EGL_FOUND`` - True if (the requested version of) EGL is available -``EGL_VERSION`` - The version of EGL; note that this is the API version defined in the - headers, rather than the version of the implementation (eg: Mesa) -``EGL_LIBRARIES`` - This can be passed to target_link_libraries() instead of the ``EGL::EGL`` - target -``EGL_INCLUDE_DIRS`` - This should be passed to target_include_directories() if the target is not - used for linking -``EGL_DEFINITIONS`` - This should be passed to target_compile_options() if the target is not - used for linking - -If ``EGL_FOUND`` is TRUE, it will also define the following imported target: - -``EGL::EGL`` - The EGL library - -In general we recommend using the imported target, as it is easier to use. -Bear in mind, however, that if the target is in the link interface of an -exported library, it must be made available by the package config file. +# Try to find EGL. +# +# This will define the following variables: +# +# ``EGL_FOUND`` +# True if (the requested version of) EGL is available +# ``EGL_VERSION`` +# The version of EGL; note that this is the API version defined in the +# headers, rather than the version of the implementation (eg: Mesa) +# ``EGL_LIBRARIES`` +# This can be passed to target_link_libraries() instead of the ``EGL::EGL`` +# target +# ``EGL_INCLUDE_DIRS`` +# This should be passed to target_include_directories() if the target is not +# used for linking +# ``EGL_DEFINITIONS`` +# This should be passed to target_compile_options() if the target is not +# used for linking +# +# If ``EGL_FOUND`` is TRUE, it will also define the following imported target: +# +# ``EGL::EGL`` +# The EGL library +# +# In general we recommend using the imported target, as it is easier to use. +# Bear in mind, however, that if the target is in the link interface of an +# exported library, it must be made available by the package config file. +# +# Since pre-1.0.0. -Since pre-1.0.0. -#]=======================================================================] +#============================================================================= +# Copyright 2014 Alex Merry +# Copyright 2014 Martin Gräßlin +# +# 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 copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +#============================================================================= include(${CMAKE_CURRENT_LIST_DIR}/ECMFindModuleHelpersStub.cmake) include(CheckCXXSourceCompiles) @@ -60,7 +82,6 @@ find_path(EGL_INCLUDE_DIR find_library(EGL_LIBRARY NAMES EGL - libEGL HINTS ${PKG_EGL_LIBRARY_DIRS} ) @@ -97,12 +118,18 @@ endif() cmake_push_check_state(RESET) list(APPEND CMAKE_REQUIRED_LIBRARIES "${EGL_LIBRARY}") list(APPEND CMAKE_REQUIRED_INCLUDES "${EGL_INCLUDE_DIR}") +list(APPEND CMAKE_REQUIRED_DEFINITIONS "${EGL_DEFINITIONS}") + +if(_qt_igy_gui_libs) + list(APPEND CMAKE_REQUIRED_LIBRARIES "${_qt_igy_gui_libs}") +endif() check_cxx_source_compiles(" #include -int main(int argc, char *argv[]) { - EGLint x = 0; EGLDisplay dpy = 0; EGLContext ctx = 0; +int main(int, char **) { + [[maybe_unused]] EGLint x = 0; + EGLDisplay dpy = 0; EGLContext ctx = 0; eglDestroyContext(dpy, ctx); }" HAVE_EGL) diff --git a/cmake/find-modules/FindFFmpeg.cmake b/cmake/find-modules/FindFFmpeg.cmake index 27b49a2a509..ecf9b07cb16 100644 --- a/cmake/find-modules/FindFFmpeg.cmake +++ b/cmake/find-modules/FindFFmpeg.cmake @@ -20,10 +20,7 @@ # :: # # FFMPEG_FOUND - System has the all required components. -# FFMPEG_INCLUDE_DIRS - Include directory necessary for using the required components headers. -# FFMPEG_LIBRARIES - Link these to use the required FFmpeg components. -# FFMPEG_LIBRARY_DIRS - Link directories -# FFMPEG_DEFINITIONS - Compiler switches required for using the required FFmpeg components. +# FFMPEG_SHARED_LIBRARIES - Found FFmpeg shared libraries. # # For each of the components it will additionally set. # @@ -203,11 +200,8 @@ endmacro() # Clear the previously cached variables, because they are recomputed every time # the Find script is included. -unset(FFMPEG_INCLUDE_DIRS) -unset(FFMPEG_LIBRARIES) -unset(FFMPEG_SHARED_LIBRARIES) -unset(FFMPEG_DEFINITIONS) -unset(FFMPEG_LIBRARY_DIRS) +unset(FFMPEG_SHARED_LIBRARIES CACHE) +unset(FFMPEG_STUBS CACHE) # Check for components. foreach (_component ${FFmpeg_FIND_COMPONENTS}) @@ -232,33 +226,34 @@ foreach (_component ${FFmpeg_FIND_COMPONENTS}) endif() endforeach() -if (NOT FFMPEG_SHARED_COMPONENTS AND (ANDROID OR LINUX)) - set(ENABLE_DYNAMIC_RESOLVE_OPENSSL_SYMBOLS TRUE CACHE INTERNAL "") -endif() -set(ENABLE_DYNAMIC_RESOLVE_VAAPI_SYMBOLS ${LINUX} CACHE INTERNAL "") +function(qt_internal_multimedia_try_add_dynamic_resolve_dependency _component dep) + set(dynamic_resolve_added FALSE PARENT_SCOPE) -function(__try_add_dynamic_resolve_dependency dep added) - set(added TRUE PARENT_SCOPE) + if (NOT ANDROID AND NOT LINUX) + return() + endif() - if(ENABLE_DYNAMIC_RESOLVE_OPENSSL_SYMBOLS AND - (${dep} STREQUAL "ssl" OR ${dep} STREQUAL "crypto")) - set(DYNAMIC_RESOLVE_OPENSSL_SYMBOLS TRUE CACHE INTERNAL "") - elseif(ENABLE_DYNAMIC_RESOLVE_VAAPI_SYMBOLS AND ${dep} STREQUAL "va") - set(DYNAMIC_RESOLVE_VAAPI_SYMBOLS TRUE CACHE INTERNAL "") - elseif(ENABLE_DYNAMIC_RESOLVE_VAAPI_SYMBOLS AND ${dep} STREQUAL "va-drm") - set(DYNAMIC_RESOLVE_VA_DRM_SYMBOLS TRUE CACHE INTERNAL "") - elseif(ENABLE_DYNAMIC_RESOLVE_VAAPI_SYMBOLS AND ${dep} STREQUAL "va-x11") - set(DYNAMIC_RESOLVE_VA_X11_SYMBOLS TRUE CACHE INTERNAL "") - else() - set(added FALSE PARENT_SCOPE) + set(supported_stubs "ssl|crypto|va|va-drm|va-x11") + if(${_component}_SHARED_LIBRARIES) + set(stub_prefix "Qt${PROJECT_VERSION_MAJOR}FFmpegStub-") + if (${dep} MATCHES "^${stub_prefix}(${supported_stubs})$") + string(REPLACE "${stub_prefix}" "" dep "${dep}") + set(FFMPEG_STUBS ${FFMPEG_STUBS} ${dep} CACHE INTERNAL "") + + set(dynamic_resolve_added TRUE PARENT_SCOPE) + endif() + elseif (${dep} MATCHES "^(${supported_stubs})$") + set(FFMPEG_STUBS ${FFMPEG_STUBS} ${dep} CACHE INTERNAL "") + set(dynamic_resolve_added TRUE PARENT_SCOPE) endif() endfunction() # Function parses package config file to find the static library dependencies # and adds them to the target library. -function(__ffmpeg_internal_set_dependencies lib) - set(PC_FILE ${FFMPEG_DIR}/lib/pkgconfig/lib${lib}.pc) +function(__ffmpeg_internal_set_dependencies _component) + string(TOLOWER ${_component} lib) + set(PC_FILE ${${_component}_LIBRARY_DIR}/pkgconfig/lib${lib}.pc) if(EXISTS ${PC_FILE}) file(READ ${PC_FILE} pcfile) @@ -277,18 +272,22 @@ function(__ffmpeg_internal_set_dependencies lib) foreach(dependency ${deps_no_suffix}) string(REGEX REPLACE ${prefix_l} "" dependency ${dependency}) if(NOT ${lib} STREQUAL ${dependency}) - __try_add_dynamic_resolve_dependency(${dependency} added) - if(NOT added) + qt_internal_multimedia_try_add_dynamic_resolve_dependency(${_component} ${dependency}) + if(NOT dynamic_resolve_added AND NOT ${_component}_SHARED_LIBRARIES) target_link_libraries(FFmpeg::${lib} INTERFACE ${dependency}) endif() endif() endforeach() - list(APPEND deps_lib_suffix ${libs_dependency_lib} ${libs_private_dependency_lib}) - foreach(dependency ${deps_lib_suffix}) - string(REGEX REPLACE ${suffix_lib} "" dependency ${dependency}) - target_link_libraries(FFmpeg::${lib} INTERFACE ${dependency}) - endforeach() + if(NOT ${_component}_SHARED_LIBRARIES) + list(APPEND deps_lib_suffix ${libs_dependency_lib} ${libs_private_dependency_lib}) + foreach(dependency ${deps_lib_suffix}) + string(REGEX REPLACE ${suffix_lib} "" dependency ${dependency}) + target_link_libraries(FFmpeg::${lib} INTERFACE ${dependency}) + endforeach() + endif() + else() + message(WARNING "FFmpeg pc file ${PC_FILE} is not found") endif() endfunction() @@ -309,9 +308,8 @@ endfunction() INTERFACE_LINK_LIBRARIES "${${_component}_LIBRARY_NAME}" INTERFACE_LINK_DIRECTORIES "${${_component}_LIBRARY_DIR}" ) - if(NOT ${_component}_SHARED_LIBRARIES) - __ffmpeg_internal_set_dependencies(${_lowerComponent}) - endif() + + __ffmpeg_internal_set_dependencies(${_component}) target_link_libraries(FFmpeg::${_lowerComponent} INTERFACE "${${_component}_LIBRARY_NAME}") if (UNIX AND NOT APPLE) target_link_options(FFmpeg::${_lowerComponent} INTERFACE "-Wl,--exclude-libs=lib${_lowerComponent}") @@ -324,22 +322,17 @@ endfunction() list(REMOVE_DUPLICATES FFMPEG_INCLUDE_DIRS) list(REMOVE_DUPLICATES FFMPEG_LIBRARY_DIRS) list(REMOVE_DUPLICATES FFMPEG_SHARED_LIBRARIES) + list(REMOVE_DUPLICATES FFMPEG_STUBS) message(STATUS "FFmpeg shared libs: ${FFMPEG_SHARED_LIBRARIES}") + message(STATUS "FFmpeg stubs: ${FFMPEG_STUBS}") # cache the vars. - set(FFMPEG_INCLUDE_DIRS ${FFMPEG_INCLUDE_DIRS} CACHE STRING "The FFmpeg include directories." FORCE) - set(FFMPEG_LIBRARIES ${FFMPEG_LIBRARIES} CACHE STRING "The FFmpeg libraries." FORCE) - set(FFMPEG_DEFINITIONS ${FFMPEG_DEFINITIONS} CACHE STRING "The FFmpeg cflags." FORCE) - set(FFMPEG_LIBRARY_DIRS ${FFMPEG_LIBRARY_DIRS} CACHE STRING "The FFmpeg library dirs." FORCE) set(FFMPEG_SHARED_LIBRARIES ${FFMPEG_SHARED_LIBRARIES} CACHE STRING "The FFmpeg dynamic libraries." FORCE) + set(FFMPEG_STUBS ${FFMPEG_STUBS} CACHE STRING "The FFmpeg stubs." FORCE) - mark_as_advanced(FFMPEG_INCLUDE_DIRS - FFMPEG_LIBRARIES - FFMPEG_DEFINITIONS - FFMPEG_LIBRARY_DIRS - FFMPEG_SHARED_LIBRARIES - ) + mark_as_advanced(FFMPEG_SHARED_LIBRARIES) + mark_as_advanced(FFMPEG_STUBS) # endif () list(LENGTH FFMPEG_LIBRARY_DIRS DIRS_COUNT) diff --git a/cmake/find-modules/FindGLESv2.cmake b/cmake/find-modules/FindGLESv2.cmake index 4730418209a..645d13079a2 100644 --- a/cmake/find-modules/FindGLESv2.cmake +++ b/cmake/find-modules/FindGLESv2.cmake @@ -80,7 +80,9 @@ if(GLESv2_FOUND AND NOT TARGET GLESv2::GLESv2) IMPORTED_LOCATION "${GLESv2_LIBRARY}" INTERFACE_INCLUDE_DIRECTORIES "${GLESv2_INCLUDE_DIR}") - if(EGL_LIBRARY) + if(TARGET EGL::EGL) + target_link_libraries(GLESv2::GLESv2 INTERFACE "EGL::EGL") + elseif(EGL_LIBRARY) target_link_libraries(GLESv2::GLESv2 INTERFACE "${EGL_LIBRARY}") endif() endif() diff --git a/cmake/find-modules/FindGObject.cmake b/cmake/find-modules/FindGObject.cmake index 54dd35eadb7..89a02435a14 100644 --- a/cmake/find-modules/FindGObject.cmake +++ b/cmake/find-modules/FindGObject.cmake @@ -23,7 +23,7 @@ qt_internal_disable_find_package_global_promotion(GLIB2::GLIB2) if(NOT TARGET GObject::GObject) find_package(PkgConfig QUIET) pkg_check_modules(PC_GOBJECT gobject-2.0 IMPORTED_TARGET) - if (TARGET PkgConfig::PC_GOBJECT) + if(TARGET PkgConfig::PC_GOBJECT) add_library(GObject::GObject INTERFACE IMPORTED) target_link_libraries(GObject::GObject INTERFACE PkgConfig::PC_GOBJECT @@ -35,7 +35,7 @@ if(NOT TARGET GObject::GObject) PATH_SUFFIXES glib-2.0/gobject/ ) find_library(GObject_LIBRARY NAMES gobject-2.0) - if (GObject_LIBRARY AND GObject_INCLUDE_DIR) + if(GObject_LIBRARY AND GObject_INCLUDE_DIR) add_library(GObject::GObject INTERFACE IMPORTED) target_include_directories(GObject::GObject INTERFACE ${GObject_INCLUDE_DIR} diff --git a/cmake/find-modules/FindGStreamer.cmake b/cmake/find-modules/FindGStreamer.cmake index a7f9b9f23d9..5bd6b2230bf 100644 --- a/cmake/find-modules/FindGStreamer.cmake +++ b/cmake/find-modules/FindGStreamer.cmake @@ -1,6 +1,4 @@ if(ANDROID OR IOS) - set(QGC_GST_STATIC_BUILD ON CACHE BOOL "Build GST Statically") - if(DEFINED ENV{GST_VERSION}) set(QGC_GST_TARGET_VERSION $ENV{GST_VERSION} CACHE STRING "Environment Provided GStreamer Version") else() @@ -8,6 +6,12 @@ if(ANDROID OR IOS) endif() endif() +if(ANDROID OR IOS) + set(QGC_GST_STATIC_BUILD ON CACHE BOOL "Build GST Statically") +endif() + +find_package(PkgConfig QUIET) + if(QGC_GST_STATIC_BUILD) list(APPEND PKG_CONFIG_ARGN --static) endif() @@ -40,11 +44,15 @@ if(WIN32) ) elseif(MACOS) list(APPEND CMAKE_FRAMEWORK_PATH "/Library/Frameworks") - set(GSTREAMER_PREFIX "/Library/Frameworks/GStreamer.framework") - set(ENV{PKG_CONFIG_PATH} "${GSTREAMER_PREFIX}/Versions/Current/lib/pkgconfig:$ENV{PKG_CONFIG_PATH}") + set(GSTREAMER_PREFIX "/Library/Frameworks/GStreamer.framework/Versions/1.0") + find_program(PKG_CONFIG_PROGRAM pkg-config PATHS ${GSTREAMER_PREFIX}/bin) + if(PKG_CONFIG_PROGRAM) + set(PKG_CONFIG_EXECUTABLE ${PKG_CONFIG_PROGRAM}) + endif() + set(ENV{PKG_CONFIG_PATH} "${GSTREAMER_PREFIX}/lib/pkgconfig:${GSTREAMER_PREFIX}/lib/gstreamer-1.0/pkgconfig:$ENV{PKG_CONFIG_PATH}") elseif(LINUX) set(GSTREAMER_PREFIX "/usr") - set(ENV{PKG_CONFIG_PATH} "${GSTREAMER_PREFIX}/lib/pkgconfig:${GSTREAMER_PREFIX}/lib/x86_64-linux-gnu/pkgconfig:$ENV{PKG_CONFIG_PATH}") + set(ENV{PKG_CONFIG_PATH} "${GSTREAMER_PREFIX}/lib/x86_64-linux-gnu/pkgconfig:$ENV{PKG_CONFIG_PATH}") elseif(IOS) list(APPEND CMAKE_FRAMEWORK_PATH "~/Library/Developer/GStreamer/iPhone.sdk") if(DEFINED ENV{GSTREAMER_PREFIX_IOS} AND EXISTS $ENV{GSTREAMER_PREFIX_IOS}) @@ -60,7 +68,6 @@ elseif(IOS) endif() set(GSTREAMER_PREFIX ${GSTREAMER_PREFIX_IOS}) elseif(ANDROID) - set(GSTREAMER_PREFIX_ANDROID) if(DEFINED ENV{GSTREAMER_PREFIX_ANDROID} AND EXISTS $ENV{GSTREAMER_PREFIX_ANDROID}) set(GSTREAMER_PREFIX_ANDROID $ENV{GSTREAMER_PREFIX_ANDROID}) else() @@ -79,6 +86,7 @@ elseif(ANDROID) endif() set(GSTREAMER_PREFIX_ANDROID ${GSTREAMER_INSTALL_DIR}) endif() + if(${CMAKE_ANDROID_ARCH_ABI} STREQUAL armeabi-v7a) set(GSTREAMER_PREFIX ${GSTREAMER_PREFIX_ANDROID}/armv7) elseif(${CMAKE_ANDROID_ARCH_ABI} STREQUAL arm64-v8a) @@ -88,24 +96,29 @@ elseif(ANDROID) elseif(${CMAKE_ANDROID_ARCH_ABI} STREQUAL x86_64) set(GSTREAMER_PREFIX ${GSTREAMER_PREFIX_ANDROID}/x86_64) endif() - set(ENV{PKG_CONFIG_PATH} "") - set(ENV{PKG_CONFIG_LIBDIR} "${GSTREAMER_PREFIX}/lib/pkgconfig:${GSTREAMER_PREFIX}/lib/gstreamer-1.0/pkgconfig") - list(APPEND PKG_CONFIG_ARGN - --dont-define-prefix - --define-variable=prefix=${GSTREAMER_PREFIX} - --define-variable=libdir=${GSTREAMER_PREFIX}/lib - --define-variable=includedir=${GSTREAMER_PREFIX}/include - ) + set(ENV{PKG_CONFIG_PATH} "") if(CMAKE_HOST_WIN32) find_program(PKG_CONFIG_PROGRAM pkg-config PATHS ${GSTREAMER_PREFIX}/share/gst-android/ndk-build/tools/windows) if(PKG_CONFIG_PROGRAM) set(PKG_CONFIG_EXECUTABLE ${PKG_CONFIG_PROGRAM}) + set(ENV{PKG_CONFIG_LIBDIR} "${GSTREAMER_PREFIX}/lib/pkgconfig;${GSTREAMER_PREFIX}/lib/gstreamer-1.0/pkgconfig") + endif() + elseif(CMAKE_HOST_LINUX) + if(PkgConfig_FOUND) + set(ENV{PKG_CONFIG_LIBDIR} "${GSTREAMER_PREFIX}/lib/pkgconfig:${GSTREAMER_PREFIX}/lib/gstreamer-1.0/pkgconfig") endif() endif() + + list(APPEND PKG_CONFIG_ARGN + --dont-define-prefix + --define-variable=prefix=${GSTREAMER_PREFIX} + --define-variable=libdir=${GSTREAMER_PREFIX}/lib + --define-variable=includedir=${GSTREAMER_PREFIX}/include + ) endif() -cmake_print_variables(GSTREAMER_PREFIX) list(PREPEND CMAKE_PREFIX_PATH ${GSTREAMER_PREFIX}) +cmake_print_variables(GSTREAMER_PREFIX) ################################################################################ @@ -113,11 +126,9 @@ include(CMakeFindDependencyMacro) find_dependency(GObject) set(GStreamer_VERSION ${QGC_GST_TARGET_VERSION}) -find_package(PkgConfig QUIET) if(PkgConfig_FOUND) message(STATUS "PKG_CONFIG_PATH $ENV{PKG_CONFIG_PATH}") message(STATUS "PKG_CONFIG_LIBDIR $ENV{PKG_CONFIG_LIBDIR}") - # message(STATUS "PKG_CONFIG_SYSROOT_DIR $ENV{PKG_CONFIG_SYSROOT_DIR}") cmake_print_variables(PKG_CONFIG_EXECUTABLE PKG_CONFIG_ARGN) pkg_check_modules(GStreamer gstreamer-1.0) else() @@ -151,31 +162,63 @@ else() endif() cmake_print_variables(GStreamer_VERSION) +# Use Latest Revisions for each minor version: 1.16.3, 1.18.6, 1.20.7, 1.22.12, 1.24.8 +string(REPLACE "." ";" GST_VERSION_LIST ${GStreamer_VERSION}) +list(GET GST_VERSION_LIST 0 GST_VERSION_MAJOR) +list(GET GST_VERSION_LIST 1 GST_VERSION_MINOR) +list(GET GST_VERSION_LIST 2 GST_VERSION_PATCH) +cmake_print_variables(GST_VERSION_MAJOR GST_VERSION_MINOR GST_VERSION_PATCH) + +if(GST_VERSION_MINOR EQUAL 16) + set(GST_VERSION_PATCH 3) +elseif(GST_VERSION_MINOR EQUAL 18) + set(GST_VERSION_PATCH 6) +elseif(GST_VERSION_MINOR EQUAL 20) + set(GST_VERSION_PATCH 7) +elseif(GST_VERSION_MINOR EQUAL 22) + set(GST_VERSION_PATCH 12) +elseif(GST_VERSION_MINOR EQUAL 24) + set(GST_VERSION_PATCH 8) +endif() + +set(GST_PLUGINS_VERSION ${GST_VERSION_MAJOR}.${GST_VERSION_MINOR}.${GST_VERSION_PATCH}) +cmake_print_variables(GST_PLUGINS_VERSION) + ################################################################################ -function(find_gstreamer_component component prefix header library) - if(NOT TARGET GStreamer::${component}) +function(find_gstreamer_component component) + cmake_parse_arguments(PARSE_ARGV 1 ARGS "" "PC_NAME;HEADER;LIBRARY" "DEPENDENCIES") + + set(pkgconfig_name ${ARGS_PC_NAME}) + set(header ${ARGS_HEADER}) + set(library ${ARGS_LIBRARY}) + + set(target GStreamer::${component}) + + if(NOT TARGET ${target}) string(TOUPPER ${component} upper) - if(PkgConfig_FOUND) - pkg_check_modules(PC_GSTREAMER_${upper} ${prefix} IMPORTED_TARGET) - endif() + pkg_check_modules(PC_GSTREAMER_${upper} IMPORTED_TARGET ${pkgconfig_name}) if(TARGET PkgConfig::PC_GSTREAMER_${upper}) add_library(GStreamer::${component} INTERFACE IMPORTED) target_link_libraries(GStreamer::${component} INTERFACE PkgConfig::PC_GSTREAMER_${upper}) set_target_properties(GStreamer::${component} PROPERTIES VERSION ${PC_GSTREAMER_${upper}_VERSION}) else() + foreach(dependency IN LISTS ARGS_DEPENDENCIES) + if (NOT TARGET ${dependency}) + set(GStreamer_${component}_FOUND FALSE PARENT_SCOPE) + return() + endif() + endforeach() + find_path(GStreamer_${component}_INCLUDE_DIR NAMES ${header} PATH_SUFFIXES gstreamer-1.0 - PATHS ${GSTREAMER_PREFIX}/include ) find_library(GStreamer_${component}_LIBRARY NAMES ${library} - PATHS ${GSTREAMER_PREFIX}/lib ) if(${component} STREQUAL "Gl") # search the gstglconfig.h include dir under the same root where the library is found - # TODO: replace with cmake_path get_filename_component(gstglLibDir "${GStreamer_Gl_LIBRARY}" PATH) find_path(GStreamer_GlConfig_INCLUDE_DIR NAMES gst/gl/gstglconfig.h @@ -191,15 +234,18 @@ function(find_gstreamer_component component prefix header library) add_library(GStreamer::${component} INTERFACE IMPORTED) target_include_directories(GStreamer::${component} INTERFACE ${GStreamer_${component}_INCLUDE_DIR}) target_link_libraries(GStreamer::${component} INTERFACE ${GStreamer_${component}_LIBRARY}) + if(ARGS_DEPENDENCIES) + target_link_libraries(GStreamer::${component} INTERFACE ${ARGS_DEPENDENCIES}) + endif() set_target_properties(GStreamer::${component} PROPERTIES VERSION ${GStreamer_VERSION}) endif() mark_as_advanced(GStreamer_${component}_INCLUDE_DIR GStreamer_${component}_LIBRARY) endif() endif() - if(TARGET GStreamer::${component}) - # TODO; define_property + if(TARGET ${target}) set(GStreamer_${component}_FOUND TRUE PARENT_SCOPE) + # TODO; define_property get_target_property(Component_VERSION GStreamer::${component} VERSION) set(GStreamer_${component}_VERSION ${Component_VERSION} PARENT_SCOPE) endif() @@ -207,186 +253,223 @@ endfunction() ################################################################################ -# GStreamer required dependencies -find_gstreamer_component(Core gstreamer-1.0 gst/gst.h gstreamer-1.0) -find_gstreamer_component(Base gstreamer-base-1.0 gst/gst.h gstbase-1.0) -find_gstreamer_component(Video gstreamer-video-1.0 gst/video/video.h gstvideo-1.0) -find_gstreamer_component(Gl gstreamer-gl-1.0 gst/gl/gl.h gstgl-1.0) +find_gstreamer_component(Core + PC_NAME gstreamer-1.0 + HEADER gst/gst.h + LIBRARY gstreamer-1.0 + DEPENDENCIES GLIB2::GLIB2 GObject::GObject) +find_gstreamer_component(Base + PC_NAME gstreamer-base-1.0 + HEADER gst/gst.h + LIBRARY gstbase-1.0 + DEPENDENCIES GStreamer::Core) +find_gstreamer_component(Video + PC_NAME gstreamer-video-1.0 + HEADER gst/video/video.h + LIBRARY gstvideo-1.0 + DEPENDENCIES GStreamer::Core GStreamer::Base) +find_gstreamer_component(Gl + PC_NAME gstreamer-gl-1.0 + HEADER gst/gl/gl.h + LIBRARY gstgl-1.0 + DEPENDENCIES GStreamer::Core GStreamer::Base GStreamer::Video) -if(TARGET GStreamer::Core) - target_link_libraries(GStreamer::Core INTERFACE GObject::GObject) +################################################################################ + +if(Allocators IN_LIST GStreamer_FIND_COMPONENTS) + find_gstreamer_component(Allocators + PC_NAME gstreamer-allocators-1.0 + HEADER gst/allocators/allocators.h + LIBRARY gstallocators-1.0 + DEPENDENCIES GStreamer::Core) endif() -if(TARGET GStreamer::Base AND TARGET GStreamer::Core) - target_link_libraries(GStreamer::Base INTERFACE GStreamer::Core) + +if(App IN_LIST GStreamer_FIND_COMPONENTS) + find_gstreamer_component(App + PC_NAME gstreamer-app-1.0 + HEADER gst/app/gstappsink.h + LIBRARY gstapp-1.0 + DEPENDENCIES GStreamer::Core GStreamer::Base) endif() -if(TARGET GStreamer::Video AND TARGET GStreamer::Base) - target_link_libraries(GStreamer::Video INTERFACE GStreamer::Base) + +if(Audio IN_LIST GStreamer_FIND_COMPONENTS) + find_gstreamer_component(Audio + PC_NAME gstreamer-audio-1.0 + HEADER gst/audio/audio.h + LIBRARY gstaudio-1.0 + DEPENDENCIES GStreamer::Core GStreamer::Base) endif() -if(TARGET GStreamer::Gl AND TARGET GStreamer::Video) - target_link_libraries(GStreamer::Gl INTERFACE GStreamer::Video) + +if(Codecparsers IN_LIST GStreamer_FIND_COMPONENTS) + find_gstreamer_component(Codecparsers + PC_NAME gstreamer-codecparsers-1.0 + HEADER gst/codecparsers/codecparsers-prelude.h + LIBRARY gstcodecparsers-1.0 + DEPENDENCIES GStreamer::Core GStreamer::Base) endif() -################################################################################ +if(Controller IN_LIST GStreamer_FIND_COMPONENTS) + find_gstreamer_component(Controller + PC_NAME gstreamer-controller-1.0 + HEADER gst/controller/controller.h + LIBRARY gstcontroller-1.0 + DEPENDENCIES GStreamer::Core) +endif() -# GStreamer optional components -foreach(component ${GStreamer_FIND_COMPONENTS}) - if (${component} STREQUAL "Allocators") - find_gstreamer_component(Allocators gstreamer-allocators-1.0 gst/allocators/allocators.h gstallocators-1.0) - if(TARGET GStreamer::Allocators AND TARGET GStreamer::Core) - target_link_libraries(GStreamer::Allocators INTERFACE GStreamer::Core) - endif() - elseif (${component} STREQUAL "App") - find_gstreamer_component(App gstreamer-app-1.0 gst/app/gstappsink.h gstapp-1.0) - if(TARGET GStreamer::App AND TARGET GStreamer::Base) - target_link_libraries(GStreamer::App INTERFACE GStreamer::Base) - endif() - elseif (${component} STREQUAL "Audio") - find_gstreamer_component(Audio gstreamer-audio-1.0 gst/audio/audio.h gstaudio-1.0) - if(TARGET GStreamer::Audio AND TARGET GStreamer::Base) - target_link_libraries(GStreamer::Audio INTERFACE GStreamer::Base) - endif() - elseif (${component} STREQUAL "Controller") - find_gstreamer_component(Controller gstreamer-controller-1.0 gst/controller/controller.h gstcontroller-1.0) - if(TARGET GStreamer::Controller AND TARGET GStreamer::Core) - target_link_libraries(GStreamer::Controller INTERFACE GStreamer::Core) - endif() - elseif (${component} STREQUAL "Codecparsers") - find_gstreamer_component(Codecparsers gstreamer-codecparsers-1.0 gst/codecparsers/codecparsers-prelude.h gstcodecparsers-1.0) - if(TARGET GStreamer::Codecparsers AND TARGET GStreamer::Base) - target_link_libraries(GStreamer::Codecparsers INTERFACE GStreamer::Base) - endif() - elseif (${component} STREQUAL "Fft") - find_gstreamer_component(Fft gstreamer-fft-1.0 gst/fft/fft.h gstfft-1.0) - if(TARGET GStreamer::Fft AND TARGET GStreamer::Core) - target_link_libraries(GStreamer::Fft INTERFACE GStreamer::Core) - endif() - elseif (${component} STREQUAL "Mpegts") - find_gstreamer_component(Mpegts gstreamer-mpegts-1.0 gst/mpegts/mpegts.h gstmpegts-1.0) - if(TARGET GStreamer::Mpegts AND TARGET GStreamer::Base) - target_link_libraries(GStreamer::Mpegts INTERFACE GStreamer::Base) - endif() - elseif (${component} STREQUAL "Net") - find_gstreamer_component(Net gstreamer-net-1.0 gst/net/net.h gstnet-1.0) - if(TARGET GStreamer::Net AND TARGET GStreamer::Base) - target_link_libraries(GStreamer::Net INTERFACE GStreamer::Base) - endif() - elseif (${component} STREQUAL "Pbutils") - find_gstreamer_component(Pbutils gstreamer-pbutils-1.0 gst/pbutils/pbutils.h gstpbutils-1.0) - if(TARGET GStreamer::Pbutils AND TARGET GStreamer::Audio AND TARGET GStreamer::Video) - target_link_libraries(GStreamer::Pbutils INTERFACE GStreamer::Audio GStreamer::Video) - endif() - elseif (${component} STREQUAL "Photography") - find_gstreamer_component(Photography gstreamer-photography-1.0 gst/interfaces/photography.h gstphotography-1.0) - if(TARGET GStreamer::Photography AND TARGET GStreamer::Core) - target_link_libraries(GStreamer::Photography INTERFACE GStreamer::Core) - endif() - elseif (${component} STREQUAL "Riff") - find_gstreamer_component(Riff gstreamer-riff-1.0 gst/riff/riff.h gstriff-1.0) - if(TARGET GStreamer::Riff AND TARGET GStreamer::Base) - target_link_libraries(GStreamer::Riff INTERFACE GStreamer::Base) - endif() - elseif (${component} STREQUAL "Rtp") - find_gstreamer_component(Rtp gstreamer-rtp-1.0 gst/rtp/rtp.h gstrtp-1.0) - if(TARGET GStreamer::Rtp AND TARGET GStreamer::Base) - target_link_libraries(GStreamer::Rtp INTERFACE GStreamer::Base) - endif() - elseif (${component} STREQUAL "Rtsp") - find_gstreamer_component(Rtsp gstreamer-rtsp-1.0 gst/rtsp/rtsp.h gstrtsp-1.0) - if(TARGET GStreamer::Rtsp AND TARGET GStreamer::Rtp) - target_link_libraries(GStreamer::Rtsp INTERFACE GStreamer::Rtp) - endif() - elseif (${component} STREQUAL "Sdp") - find_gstreamer_component(Sdp gstreamer-sdp-1.0 gst/sdp/sdp.h gstsdp-1.0) - if(TARGET GStreamer::Sdp AND TARGET GStreamer::Rtp) - target_link_libraries(GStreamer::Sdp INTERFACE GStreamer::Rtp) - endif() - elseif (${component} STREQUAL "Tag") - find_gstreamer_component(Tag gstreamer-tag-1.0 gst/tag/tag.h gsttag-1.0) - if(TARGET GStreamer::Tag AND TARGET GStreamer::Base) - target_link_libraries(GStreamer::Tag INTERFACE GStreamer::Base) - endif() - elseif (${component} STREQUAL "Va") - find_gstreamer_component(Va gstreamer-va-1.0 gst/va/gstva.h gstva-1.0) - if(TARGET GStreamer::Va AND TARGET GStreamer::Base AND TARGET GStreamer::Allocators) - target_link_libraries(GStreamer::Va INTERFACE GStreamer::Base GStreamer::Allocators) - endif() - elseif (${component} STREQUAL "Prototypes") - find_gstreamer_component(Prototypes gstreamer-gl-prototypes-1.0 gst/gl/glprototypes/all_functions.h gstglproto-1.0) - if(TARGET GStreamer::Prototypes AND TARGET GStreamer::Gl) - target_link_libraries(GStreamer::Prototypes INTERFACE GStreamer::Gl) - endif() - elseif (${component} STREQUAL "X11") - find_gstreamer_component(X11 gstreamer-gl-x11-1.0 gst/gl/x11/x11.h x11-xcb) - if(TARGET GStreamer::X11) - if(GStreamer::Gl) - target_link_libraries(GStreamer::X11 INTERFACE GStreamer::Gl) - endif() - find_package(X11) - if(X11_FOUND) - target_link_libraries(GStreamer::X11 INTERFACE X11::X11) - endif() - find_package(XCB COMPONENTS XCB GLX) - if(XCB_FOUND) - target_link_libraries(GStreamer::X11 INTERFACE XCB::XCB XCB::GLX) - endif() - find_package(X11_XCB) - if(X11_XCB_FOUND) - target_link_libraries(GStreamer::X11 INTERFACE X11::XCB) - endif() - endif() - elseif (${component} STREQUAL "EGL") - find_gstreamer_component(EGL gstreamer-gl-egl-1.0 gst/gl/egl/egl.h egl) - if(TARGET GStreamer::EGL) - if(TARGET GStreamer::Gl) - target_link_libraries(GStreamer::EGL INTERFACE GStreamer::Gl) - endif() - find_package(EGL) - if(EGL_FOUND) - target_link_libraries(GStreamer::EGL INTERFACE EGL::EGL) - endif() - endif() - elseif (${component} STREQUAL "Wayland") - find_gstreamer_component(Wayland gstreamer-gl-wayland-1.0 gst/gl/wayland/wayland.h wayland-egl) - if(TARGET GStreamer::Wayland) - if(TARGET GStreamer::Gl) - target_link_libraries(GStreamer::Wayland INTERFACE GStreamer::Gl) - endif() - find_package(Wayland COMPONENTS Client Cursor Egl) - if(Wayland_FOUND) - target_link_libraries(GStreamer::Wayland INTERFACE Wayland::Client Wayland::Cursor Wayland::Egl) - endif() - find_package(WaylandProtocols) - if(WaylandProtocols_FOUND) - # WaylandProtocols_DATADIR - endif() - find_package(WaylandScanner) - if(WaylandScanner_FOUND) - # target_link_libraries(GStreamer::Wayland INTERFACE Wayland::Scanner) - endif() - find_package(Qt6 COMPONENTS WaylandClient) - if(Qt6WaylandClient_FOUND) - target_link_libraries(GStreamer::Wayland INTERFACE Qt6::WaylandClient) - endif() - endif() - elseif (${component} STREQUAL "PluginsBase") - find_gstreamer_component(PluginsBase gstreamer-plugins-base-1.0 gst/gst.h ) - if(TARGET GStreamer::PluginsBase AND TARGET GStreamer::Core) - target_link_libraries(GStreamer::PluginsBase INTERFACE GStreamer::Core) - endif() - elseif (${component} STREQUAL "PluginsGood") - find_gstreamer_component(PluginsGood gstreamer-plugins-good-1.0 gst/gst.h ) - if(TARGET GStreamer::PluginsGood AND TARGET GStreamer::Base) - target_link_libraries(GStreamer::PluginsGood INTERFACE GStreamer::Base) - endif() - elseif (${component} STREQUAL "PluginsBad") - find_gstreamer_component(PluginsBad gstreamer-plugins-bad-1.0 gst/gst.h ) - if(TARGET GStreamer::PluginsBad AND TARGET GStreamer::PluginsGood) - target_link_libraries(GStreamer::PluginsBad INTERFACE GStreamer::PluginsGood) - endif() - else() - message(WARNING "FindGStreamer.cmake: Invalid Gstreamer component \"${component}\" requested") - endif() -endforeach() +if(Fft IN_LIST GStreamer_FIND_COMPONENTS) + find_gstreamer_component(Fft + PC_NAME gstreamer-fft-1.0 + HEADER gst/fft/cfft.h + LIBRARY gstfft-1.0 + DEPENDENCIES GStreamer::Core) +endif() + +if(GlEgl IN_LIST GStreamer_FIND_COMPONENTS) + # find_package(EGL) + find_gstreamer_component(GlEgl + PC_NAME gstreamer-gl-egl-1.0 + HEADER gst/gl/egl/gstgldisplay_egl.h + LIBRARY gstgl-1.0 + DEPENDENCIES GStreamer::Gl EGL::EGL) +endif() + +if(GlPrototypes IN_LIST GStreamer_FIND_COMPONENTS) + # find_package(GLESv2) + # find_package(OpenGL OPTIONAL_COMPONENTS EGL GLX OpenGL) # GLES2 GLES3 + find_gstreamer_component(GlPrototypes + PC_NAME gstreamer-gl-prototypes-1.0 + HEADER gst/gl/glprototypes/all_functions.h + LIBRARY gstglproto-1.0 + DEPENDENCIES GStreamer::Gl GLESv2::GLESv2 OpenGL::GL) +endif() + +if(GlWayland IN_LIST GStreamer_FIND_COMPONENTS) + # find_package(Wayland COMPONENTS Client Cursor Egl) + # find_package(WaylandProtocols) + # find_package(WaylandScanner) + # find_package(Qt6 COMPONENTS WaylandClient) + find_gstreamer_component(GlWayland + PC_NAME gstreamer-gl-wayland-1.0 + HEADER gst/gl/wayland/gstgldisplay_wayland.h + LIBRARY gstgl-1.0 + DEPENDENCIES GStreamer::Gl Wayland::EGL Wayland::Client) +endif() + +if(GlX11 IN_LIST GStreamer_FIND_COMPONENTS) + # find_package(X11) + # find_package(XCB COMPONENTS XCB GLX) + # find_package(X11_XCB) + find_gstreamer_component(GlX11 + PC_NAME gstreamer-gl-x11-1.0 + HEADER gst/gl/x11/gstgldisplay_x11.h + LIBRARY gstgl-1.0 + DEPENDENCIES GStreamer::Gl X11::XCB) +endif() + +if(Mpegts IN_LIST GStreamer_FIND_COMPONENTS) + find_gstreamer_component(Mpegts + PC_NAME gstreamer-mpegts-1.0 + HEADER gst/mpegts/mpegts.h + LIBRARY gstmpegts-1.0 + DEPENDENCIES GStreamer::Core GStreamer::Base) +endif() + +if(Net IN_LIST GStreamer_FIND_COMPONENTS) + find_gstreamer_component(Net + PC_NAME gstreamer-net-1.0 + HEADER gst/net/net.h + LIBRARY gstnet-1.0 + DEPENDENCIES GStreamer::Core) +endif() + +if(Pbutils IN_LIST GStreamer_FIND_COMPONENTS) + find_gstreamer_component(Pbutils + PC_NAME gstreamer-pbutils-1.0 + HEADER gst/pbutils/pbutils.h + LIBRARY gstpbutils-1.0 + DEPENDENCIES GStreamer::Video GStreamer::Audio GStreamer::Core GStreamer::Base) +endif() + +if(Photography IN_LIST GStreamer_FIND_COMPONENTS) + find_gstreamer_component(Photography + PC_NAME gstreamer-photography-1.0 + HEADER gst/interfaces/photography.h + LIBRARY gstphotography-1.0 + DEPENDENCIES GStreamer::Core GStreamer::Base) +endif() + +if(PluginsBad IN_LIST GStreamer_FIND_COMPONENTS) + find_gstreamer_component(PluginsBad + PC_NAME gstreamer-plugins-bad-1.0 + HEADER gst/gst.h + LIBRARY gstreamer-1.0 + DEPENDENCIES GStreamer::Core) +endif() + +if(PluginsBase IN_LIST GStreamer_FIND_COMPONENTS) + find_gstreamer_component(PluginsBase + PC_NAME gstreamer-plugins-base-1.0 + HEADER gst/gst.h + LIBRARY gstreamer-1.0 + DEPENDENCIES GStreamer::Core) +endif() + +if(PluginsGood IN_LIST GStreamer_FIND_COMPONENTS) + find_gstreamer_component(PluginsGood + PC_NAME gstreamer-plugins-good-1.0 + HEADER gst/gst.h + LIBRARY gstphotography-1.0 + DEPENDENCIES GStreamer::Core GStreamer::Base) +endif() + +if(Riff IN_LIST GStreamer_FIND_COMPONENTS) + find_gstreamer_component(Riff + PC_NAME gstreamer-riff-1.0 + HEADER gst/riff/riff.h + LIBRARY gstriff-1.0 + DEPENDENCIES GStreamer::Core) +endif() + +if(Rtp IN_LIST GStreamer_FIND_COMPONENTS) + find_gstreamer_component(Rtp + PC_NAME gstreamer-rtp-1.0 + HEADER gst/rtp/rtp.h + LIBRARY gstrtp-1.0 + DEPENDENCIES GStreamer::Core GStreamer::Base) +endif() + +if(Sdp IN_LIST GStreamer_FIND_COMPONENTS) + find_gstreamer_component(Sdp + PC_NAME gstreamer-sdp-1.0 + HEADER gst/sdp/sdp.h + LIBRARY gstsdp-1.0 + DEPENDENCIES GStreamer::Core) +endif() + +if(Rtsp IN_LIST GStreamer_FIND_COMPONENTS) + find_gstreamer_component(Rtsp + PC_NAME gstreamer-rtsp-1.0 + HEADER gst/rtsp/rtsp.h + LIBRARY gstrtsp-1.0 + DEPENDENCIES GStreamer::Sdp GStreamer::Core GLIB2::GIO) +endif() + +if(Tag IN_LIST GStreamer_FIND_COMPONENTS) + find_gstreamer_component(Tag + PC_NAME gstreamer-tag-1.0 + HEADER gst/tag/tag.h + LIBRARY gsttag-1.0 + DEPENDENCIES GStreamer::Core) +endif() + +if(Va IN_LIST GStreamer_FIND_COMPONENTS) + # find_package(VAAPI) + find_gstreamer_component(Va + PC_NAME gstreamer-va-1.0 + HEADER gst/va/gstva.h + LIBRARY gstva-1.0 + DEPENDENCIES GStreamer::Core GStreamer::Video) +endif() ################################################################################ @@ -401,8 +484,6 @@ if(TARGET PkgConfig::PC_GSTREAMER_GL) set_property(TARGET PkgConfig::PC_GSTREAMER_GL PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${__qt_fixed_incs}") endif() -################################################################################ - # Create target GStreamer::GStreamer include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GStreamer @@ -427,7 +508,48 @@ endif() ################################################################################ -set(GST_TARGET_PLUGINS +if(ANDROID) + target_link_options(GStreamer::GStreamer INTERFACE "-Wl,-Bsymbolic") +endif() + +if(QGC_GST_STATIC_BUILD) + target_compile_definitions(GStreamer::GStreamer INTERFACE QGC_GST_STATIC_BUILD) +endif() + +# TODO: find_path +if(LINUX) + set(GSTREAMER_LIB_PATH ${GSTREAMER_PREFIX}/lib/x86_64-linux-gnu) +elseif(MACOS) + set(GSTREAMER_LIB_PATH ${GSTREAMER_PREFIX}/lib) +elseif(ANDROID OR WIN32) + set(GSTREAMER_LIB_PATH ${GSTREAMER_PREFIX}/lib) +elseif(IOS) + +endif() +set(GSTREAMER_PLUGIN_PATH ${GSTREAMER_LIB_PATH}/gstreamer-1.0) + +target_include_directories(GStreamer::GStreamer + INTERFACE + ${GSTREAMER_PREFIX}/include + ${GSTREAMER_PREFIX}/include/glib-2.0 + ${GSTREAMER_PREFIX}/include/graphene-1.0 + ${GSTREAMER_PREFIX}/include/gstreamer-1.0 + ${GSTREAMER_LIB_PATH}/glib-2.0/include + ${GSTREAMER_LIB_PATH}/graphene-1.0/include + ${GSTREAMER_PLUGIN_PATH}/include +) + +target_link_directories(GStreamer::GStreamer + INTERFACE + ${GSTREAMER_LIB_PATH} + ${GSTREAMER_PLUGIN_PATH} +) + +################################################################################ + +add_library(GStreamer::Plugins INTERFACE IMPORTED) + +set(GST_PLUGINS gstcoreelements gstisomp4 gstlibav @@ -443,197 +565,61 @@ set(GST_TARGET_PLUGINS gstudp gstvideoparsersbad gstx264 - # gstqml6 gstasf gstva + # gstqml6 ) if(ANDROID) - list(APPEND GST_TARGET_PLUGINS gstandroidmedia) + list(APPEND GST_PLUGINS gstandroidmedia) elseif(IOS) - list(APPEND GST_TARGET_PLUGINS gstapplemedia) + list(APPEND GST_PLUGINS gstapplemedia) endif() -find_package(PkgConfig QUIET) -foreach(plugin IN LISTS GST_TARGET_PLUGINS) +foreach(plugin IN LISTS GST_PLUGINS) if(PkgConfig_FOUND) - pkg_check_modules(GST_PLUGIN_${plugin} IMPORTED_TARGET GST_PLUGIN_${plugin} QUIET) + pkg_check_modules(GST_PLUGIN_${plugin} IMPORTED_TARGET ${plugin}) if(GST_PLUGIN_${plugin}_FOUND) - cmake_print_variables(plugin) - target_link_libraries(GStreamer::GStreamer INTERFACE PkgConfig::GST_PLUGIN_${plugin}) + target_link_libraries(GStreamer::Plugins INTERFACE PkgConfig::GST_PLUGIN_${plugin}) endif() endif() + if(NOT GST_PLUGIN_${plugin}_FOUND) find_library(GST_PLUGIN_${plugin}_LIBRARY NAMES ${plugin} PATHS - ${GSTREAMER_PREFIX}/lib - ${GSTREAMER_PREFIX}/lib/gstreamer-1.0 - ${GSTREAMER_PREFIX}/lib/x86_64-linux-gnu - ${GSTREAMER_PREFIX}/lib/x86_64-linux-gnu/gstreamer-1.0 + ${GSTREAMER_LIB_PATH} + ${GSTREAMER_PLUGIN_PATH} ) if(GST_PLUGIN_${plugin}_LIBRARY) cmake_print_variables(plugin) - target_link_libraries(GStreamer::GStreamer INTERFACE ${GST_PLUGIN_${plugin}_LIBRARY}) + target_link_libraries(GStreamer::Plugins INTERFACE ${GST_PLUGIN_${plugin}_LIBRARY}) + set(GST_PLUGIN_${plugin}_FOUND TRUE) endif() endif() endforeach() -# set(GST_DEPENDENCIES -# gstreamer-plugins-base-1.0 -# gstreamer-plugins-good-1.0 -# gstreamer-plugins-bad-1.0 -# glib-2.0 -# gio -# gobject-2.0 -# gthread-2.0 -# gmodule-2.0 -# gmodule-no-export-2.0 -# zlib -# drm -# graphene-1.0 -# opus -# ffi -# egl -# dl -# m -# pcre2-8 -# gudev-1.0 -# avcodec -# avdevice -# avfilter -# avformat -# avutil -# postproc -# swscale -# va -# va-drm -# va-glx -# va-wayland -# va-x11 -# orc -# pango -# vpl -# vdpau -# vpx -# x11 -# x264 -# x265 -# x11-xcb -# drm -# png -# zlib -# ) - -pkg_check_modules(GRAPHENE IMPORTED_TARGET graphene-1.0) -if(GRAPHENE_FOUND) - target_link_libraries(GStreamer::GStreamer INTERFACE PkgConfig::GRAPHENE) -endif() - -pkg_check_modules(X264 IMPORTED_TARGET x264) -if(X264_FOUND) - target_link_libraries(GStreamer::GStreamer INTERFACE PkgConfig::X264) -endif() - -find_package(VAAPI) -if(VAAPI_FOUND) - target_link_libraries(GStreamer::GStreamer INTERFACE VAAPI::VAAPI) -endif() - -find_package(ZLIB) -if(ZLIB_FOUND) - target_link_libraries(GStreamer::GStreamer INTERFACE ZLIB::ZLIB) -endif() - -find_package(OpenGL) -if(OpenGL_FOUND) - target_link_libraries(GStreamer::GStreamer INTERFACE OpenGL::GL) -endif() - -find_package(GLESv2) -if(GLESv2_FOUND) - target_link_libraries(GStreamer::GStreamer INTERFACE GLESv2::GLESv2) -endif() - -find_package(FFmpeg COMPONENTS AVCODEC AVFORMAT AVUTIL AVFILTER SWRESAMPLE) # AVDEVICE POSTPROC SWSCALE -if(FFMPEG_FOUND) - target_link_libraries(GStreamer::GStreamer INTERFACE FFmpeg::FFmpeg) -endif() - -find_package(BZip2) -if(BZIP2_FOUND) - target_link_libraries(GStreamer::GStreamer INTERFACE BZip2::BZip2) -endif() - -find_package(JPEG) -if(JPEG_FOUND) - target_link_libraries(GStreamer::GStreamer INTERFACE JPEG::JPEG) -endif() - -find_package(PNG) -if(PNG_FOUND) - target_link_libraries(GStreamer::GStreamer INTERFACE PNG::PNG) -endif() - -find_package(Intl) -if(Intl_FOUND) - target_link_libraries(GStreamer::GStreamer INTERFACE Intl::Intl) -endif() - -find_package(Iconv) -if(Iconv_FOUND) - target_link_libraries(GStreamer::GStreamer INTERFACE Iconv::Iconv) -endif() - -find_package(Threads) -if(Threads_FOUND) - target_link_libraries(GStreamer::GStreamer INTERFACE Threads::Threads) -endif() - -if(ANDROID) - target_link_options(GStreamer::GStreamer INTERFACE "-Wl,-Bsymbolic") -endif() - -if(QGC_GST_STATIC_BUILD) - target_compile_definitions(GStreamer::GStreamer INTERFACE QGC_GST_STATIC_BUILD) -endif() - -if(ANDROID OR WIN32) - # find_path(GStreamer_INCLUDE_DIR - # NAMES GStreamer - # PATH_SUFFIXES gstreamer-1.0 - # PATHS ${GSTREAMER_PREFIX}/include - # ) - # target_include_directories(GStreamer::GStreamer - # INTERFACE - # ${GSTREAMER_PREFIX}/include/gstreamer-1.0 - # ${GSTREAMER_PREFIX}/include/glib-2.0 - # ${GSTREAMER_PREFIX}/lib/glib-2.0/include - # ${GSTREAMER_PREFIX}/lib/graphene-1.0/include - # ${GSTREAMER_PREFIX}/lib/gstreamer-1.0/include - # ${GSTREAMER_PREFIX}/include - # ) +if(NOT MACOS) + target_link_libraries(GStreamer::GStreamer INTERFACE GStreamer::Plugins) endif() ################################################################################ -# Use Latest Revisions for each minor version: 1.16.3, 1.18.6, 1.20.7, 1.22.12, 1.24.7 -string(REPLACE "." ";" GST_VERSION_LIST ${GStreamer_VERSION}) -list(GET GST_VERSION_LIST 0 GST_VERSION_MAJOR) -list(GET GST_VERSION_LIST 1 GST_VERSION_MINOR) -list(GET GST_VERSION_LIST 2 GST_VERSION_PATCH) -cmake_print_variables(GST_VERSION_MAJOR GST_VERSION_MINOR GST_VERSION_PATCH) - -if(GST_VERSION_MINOR EQUAL 16) - set(GST_VERSION_PATCH 3) -elseif(GST_VERSION_MINOR EQUAL 18) - set(GST_VERSION_PATCH 6) -elseif(GST_VERSION_MINOR EQUAL 20) - set(GST_VERSION_PATCH 7) -elseif(GST_VERSION_MINOR EQUAL 22) - set(GST_VERSION_PATCH 12) -elseif(GST_VERSION_MINOR EQUAL 24) - set(GST_VERSION_PATCH 7) -endif() - -set(GST_PLUGINS_VERSION ${GST_VERSION_MAJOR}.${GST_VERSION_MINOR}.${GST_VERSION_PATCH}) -cmake_print_variables(GST_PLUGINS_VERSION) +# set(PLUGINS_DECLARATION) +# set(PLUGINS_REGISTRATION) +# foreach(GST_P ${GST_PLUGINS}) +# list(APPEND LINK_LIBS "gst${GST_P}") +# list(APPEND PLUGINS_DECLARATION "\nGST_PLUGIN_STATIC_DECLARE(${GST_P})") +# list(APPEND PLUGINS_REGISTRATION "\nGST_PLUGIN_STATIC_REGISTER(${GST_P})") +# endforeach() + +# if(ANDROID) +# if(EXISTS ${GSTREAMER_PREFIX}/share/gst-android/ndk-build/androidmedia) +# install(DIRECTORY ${GSTREAMER_PREFIX}/share/gst-android/ndk-build/androidmedia DESTINATION ${CMAKE_BINARY_DIR}/android-build/src/org/freedesktop/androidmedia) +# endif() +# if(EXISTS ${GSTREAMER_PREFIX}/share/gst-android/ndk-build/GStreamer.java) +# install(FILES ${GSTREAMER_PREFIX}/share/gst-android/ndk-build/GStreamer.java DESTINATION ${CMAKE_BINARY_DIR}/android-build/src/org/freedesktop/GStreamer.java) +# endif() +# if(EXISTS ${GSTREAMER_PREFIX}/share/gst-android/ndk-build/gstreamer_android-1.0.c.in) +# configure_file(${GSTREAMER_PREFIX}/share/gst-android/ndk-build/gstreamer_android-1.0.c.in ${CMAKE_CURRENT_BINARY_DIR}/gst_plugin_init_android.c) +# endif() +# endif() diff --git a/cmake/find-modules/FindTaglib.cmake b/cmake/find-modules/FindTaglib.cmake new file mode 100644 index 00000000000..e3a0014fccb --- /dev/null +++ b/cmake/find-modules/FindTaglib.cmake @@ -0,0 +1,92 @@ +# SPDX-FileCopyrightText: 2006 Laurent Montel +# SPDX-FileCopyrightText: 2019 Heiko Becker +# SPDX-FileCopyrightText: 2020 Elvis Angelaccio +# +# SPDX-License-Identifier: BSD-3-Clause + +#[=======================================================================[.rst: +FindTaglib +---------- + +Try to find the Taglib library. + +This will define the following variables: + +``Taglib_FOUND`` + True if the system has the taglib library of at least the minimum + version specified by the version parameter to find_package() +``Taglib_INCLUDE_DIRS`` + The taglib include dirs for use with target_include_directories +``Taglib_LIBRARIES`` + The taglib libraries for use with target_link_libraries() +``Taglib_VERSION`` + The version of taglib that was found + +If ``Taglib_FOUND`` is TRUE, it will also define the following imported +target: + +``Taglib::Taglib`` + The Taglib library + +Since 5.72.0 +#]=======================================================================] + +find_package(PkgConfig QUIET) + +pkg_check_modules(PC_TAGLIB QUIET taglib) + +find_path(Taglib_INCLUDE_DIRS + NAMES tag.h + PATH_SUFFIXES taglib + HINTS ${PC_TAGLIB_INCLUDEDIR} +) + +find_library(Taglib_LIBRARIES + NAMES tag + HINTS ${PC_TAGLIB_LIBDIR} +) + +set(Taglib_VERSION ${PC_TAGLIB_VERSION}) + +if (Taglib_INCLUDE_DIRS AND NOT Taglib_VERSION) + if(EXISTS "${Taglib_INCLUDE_DIRS}/taglib.h") + file(READ "${Taglib_INCLUDE_DIRS}/taglib.h" TAGLIB_H) + + string(REGEX MATCH "#define TAGLIB_MAJOR_VERSION[ ]+[0-9]+" TAGLIB_MAJOR_VERSION_MATCH ${TAGLIB_H}) + string(REGEX MATCH "#define TAGLIB_MINOR_VERSION[ ]+[0-9]+" TAGLIB_MINOR_VERSION_MATCH ${TAGLIB_H}) + string(REGEX MATCH "#define TAGLIB_PATCH_VERSION[ ]+[0-9]+" TAGLIB_PATCH_VERSION_MATCH ${TAGLIB_H}) + + string(REGEX REPLACE ".*_MAJOR_VERSION[ ]+(.*)" "\\1" TAGLIB_MAJOR_VERSION "${TAGLIB_MAJOR_VERSION_MATCH}") + string(REGEX REPLACE ".*_MINOR_VERSION[ ]+(.*)" "\\1" TAGLIB_MINOR_VERSION "${TAGLIB_MINOR_VERSION_MATCH}") + string(REGEX REPLACE ".*_PATCH_VERSION[ ]+(.*)" "\\1" TAGLIB_PATCH_VERSION "${TAGLIB_PATCH_VERSION_MATCH}") + + set(Taglib_VERSION "${TAGLIB_MAJOR_VERSION}.${TAGLIB_MINOR_VERSION}.${TAGLIB_PATCH_VERSION}") + endif() +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Taglib + FOUND_VAR + Taglib_FOUND + REQUIRED_VARS + Taglib_LIBRARIES + Taglib_INCLUDE_DIRS + VERSION_VAR + Taglib_VERSION +) + +if (Taglib_FOUND AND NOT TARGET Taglib::Taglib) + add_library(Taglib::Taglib UNKNOWN IMPORTED) + set_target_properties(Taglib::Taglib PROPERTIES + IMPORTED_LOCATION "${Taglib_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${Taglib_INCLUDE_DIRS}" + ) +endif() + +mark_as_advanced(Taglib_LIBRARIES Taglib_INCLUDE_DIRS) + +include(FeatureSummary) +set_package_properties(Taglib PROPERTIES + URL "https://taglib.org/" + DESCRIPTION "A library for reading and editing the meta-data of audio formats" +) diff --git a/cmake/find-modules/FindVAAPI.cmake b/cmake/find-modules/FindVAAPI.cmake index a3ea6cd586e..b1170dc8edc 100644 --- a/cmake/find-modules/FindVAAPI.cmake +++ b/cmake/find-modules/FindVAAPI.cmake @@ -4,13 +4,40 @@ find_package(PkgConfig QUIET) +function(qt_internal_multimedia_set_va_outputs component include_dir lib_path) + if ("${component}" STREQUAL "VA") + set(VAAPI_INCLUDE_DIR "${include_dir}" CACHE INTERNAL "") + get_filename_component(lib_realpath "${lib_path}" REALPATH) + + string(REGEX MATCH "[0-9]+(\\.[0-9]+)*$" VAAPI_SUFFIX "${lib_realpath}") + set(VAAPI_SUFFIX "${VAAPI_SUFFIX}" CACHE INTERNAL "") + + mark_as_advanced(VAAPI_SUFFIX VAAPI_INCLUDE_DIR) + endif() +endfunction() + function(find_component component prefix header library) if(NOT TARGET VAAPI::${component}) string(TOUPPER ${component} upper) - pkg_check_modules(PC_VAAPI_${upper} ${prefix} IMPORTED_TARGET) + pkg_search_module(PC_VAAPI_${upper} ${prefix} IMPORTED_TARGET) if(TARGET PkgConfig::PC_VAAPI_${upper}) add_library(VAAPI::${component} INTERFACE IMPORTED) target_link_libraries(VAAPI::${component} INTERFACE PkgConfig::PC_VAAPI_${upper}) + + if (NOT PC_VAAPI_${upper}_LINK_LIBRARIES) + get_target_property(PC_VAAPI_${upper}_LINK_LIBRARIES PkgConfig::PC_VAAPI_${upper} INTERFACE_LINK_LIBRARIES) + message(STATUS "PC_VAAPI_${upper}_LINK_LIBRARIES is not defined by PkgConfig; " + "Get the value from target properties: ${PC_VAAPI_${upper}_LINK_LIBRARIES}") + endif() + + foreach (lib_path ${PC_VAAPI_${upper}_LINK_LIBRARIES}) + get_filename_component(lib_name "${lib_path}" NAME_WLE) + if (${lib_name} STREQUAL ${prefix}) + qt_internal_multimedia_set_va_outputs(${component} + "${PC_VAAPI_${upper}_INCLUDEDIR}" "${lib_path}") + break() + endif() + endforeach() else() find_path(VAAPI_${component}_INCLUDE_DIR NAMES ${header} @@ -25,6 +52,9 @@ function(find_component component prefix header library) target_link_libraries(VAAPI::${component} INTERFACE ${VAAPI_${component}_LIBRARY}) endif() mark_as_advanced(VAAPI_${component}_INCLUDE_DIR VAAPI_${component}_LIBRARY) + + qt_internal_multimedia_set_va_outputs(${component} + "${VAAPI_${component}_INCLUDE_DIR}" "${VAAPI_${component}_LIBRARY}") endif() endif() @@ -48,6 +78,8 @@ find_package_handle_standard_args(VAAPI REQUIRED_VARS VAAPI_VA_FOUND VAAPI_DRM_FOUND + VAAPI_INCLUDE_DIR + VAAPI_SUFFIX HANDLE_COMPONENTS ) diff --git a/cmake/find-modules/FindWaylandScanner.cmake b/cmake/find-modules/FindWaylandScanner.cmake index 2fa1fd6e8bc..60adaf41845 100644 --- a/cmake/find-modules/FindWaylandScanner.cmake +++ b/cmake/find-modules/FindWaylandScanner.cmake @@ -1,79 +1,81 @@ -# SPDX-FileCopyrightText: 2012-2014 Pier Luigi Fiorini +#.rst: +# FindWaylandScanner +# ------------------ # -# SPDX-License-Identifier: BSD-3-Clause - -#[=======================================================================[.rst: -FindWaylandScanner ------------------- - -Try to find wayland-scanner. - -If the wayland-scanner executable is not in your PATH, you can provide -an alternative name or full path location with the ``WaylandScanner_EXECUTABLE`` -variable. - -This will define the following variables: - -``WaylandScanner_FOUND`` - True if wayland-scanner is available. - -``WaylandScanner_EXECUTABLE`` - The wayland-scanner executable. - -If ``WaylandScanner_FOUND`` is TRUE, it will also define the following imported -target: - -``Wayland::Scanner`` - The wayland-scanner executable. - -This module provides the following functions to generate C protocol -implementations: - - - ``ecm_add_wayland_client_protocol`` - - ``ecm_add_wayland_server_protocol`` - -:: - - ecm_add_wayland_client_protocol( - PROTOCOL - BASENAME - [PRIVATE_CODE]) - - ecm_add_wayland_client_protocol( - PROTOCOL - BASENAME - [PRIVATE_CODE]) - -Generate Wayland client protocol files from ```` XML -definition for the ```` interface and append those files -to ```` or ````. - -``PRIVATE_CODE`` instructs wayland-scanner to hide marshalling code -from the compiled DSO for use in other DSOs. The default is to -export this code. - -:: - - ecm_add_wayland_server_protocol( - PROTOCOL - BASENAME - [PRIVATE_CODE]) - - ecm_add_wayland_server_protocol( - PROTOCOL - BASENAME - [PRIVATE_CODE]) - -Generate Wayland server protocol files from ```` XML -definition for the ```` interface and append those files -to ```` or ````. - -``PRIVATE_CODE`` instructs wayland-scanner to hide marshalling code -from the compiled DSO for use in other DSOs. The default is to -export this code. +# Try to find wayland-scanner. +# +# If the wayland-scanner executable is not in your PATH, you can provide +# an alternative name or full path location with the ``WaylandScanner_EXECUTABLE`` +# variable. +# +# This will define the following variables: +# +# ``WaylandScanner_FOUND`` +# True if wayland-scanner is available. +# +# ``WaylandScanner_EXECUTABLE`` +# The wayland-scanner executable. +# +# If ``WaylandScanner_FOUND`` is TRUE, it will also define the following imported +# target: +# +# ``Wayland::Scanner`` +# The wayland-scanner executable. +# +# This module provides the following functions to generate C protocol +# implementations: +# +# - ``ecm_add_wayland_client_protocol`` +# - ``ecm_add_wayland_server_protocol`` +# +# :: +# +# ecm_add_wayland_client_protocol( +# PROTOCOL +# BASENAME ) +# +# Generate Wayland client protocol files from ```` XML +# definition for the ```` interface and append those files +# to ````. +# +# :: +# +# ecm_add_wayland_server_protocol( +# PROTOCOL +# BASENAME ) +# +# Generate Wayland server protocol files from ```` XML +# definition for the ```` interface and append those files +# to ````. +# +# Since 1.4.0. -Since 1.4.0. -#]=======================================================================] +#============================================================================= +# Copyright 2012-2014 Pier Luigi Fiorini +# +# 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 copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +#============================================================================= include(${CMAKE_CURRENT_LIST_DIR}/ECMFindModuleHelpersStub.cmake) @@ -105,11 +107,10 @@ set_package_properties(WaylandScanner PROPERTIES DESCRIPTION "Executable that converts XML protocol files to C code" ) -function(ecm_add_wayland_client_protocol target_or_sources_var) +function(ecm_add_wayland_client_protocol out_var) # Parse arguments - set(options PRIVATE_CODE) set(oneValueArgs PROTOCOL BASENAME) - cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "" ${ARGN}) + cmake_parse_arguments(ARGS "" "${oneValueArgs}" "" ${ARGN}) if(ARGS_UNPARSED_ARGUMENTS) message(FATAL_ERROR "Unknown keywords given to ecm_add_wayland_client_protocol(): \"${ARGS_UNPARSED_ARGUMENTS}\"") @@ -118,66 +119,52 @@ function(ecm_add_wayland_client_protocol target_or_sources_var) get_filename_component(_infile ${ARGS_PROTOCOL} ABSOLUTE) set(_client_header "${CMAKE_CURRENT_BINARY_DIR}/wayland-${ARGS_BASENAME}-client-protocol.h") set(_code "${CMAKE_CURRENT_BINARY_DIR}/wayland-${ARGS_BASENAME}-protocol.c") - if(ARGS_PRIVATE_CODE) - set(_code_type private-code) - else() - set(_code_type public-code) - endif() set_source_files_properties(${_client_header} GENERATED) set_source_files_properties(${_code} GENERATED) - set_property(SOURCE ${_client_header} ${_code} PROPERTY SKIP_AUTOMOC ON) + set_property(SOURCE ${_client_header} PROPERTY SKIP_AUTOMOC ON) add_custom_command(OUTPUT "${_client_header}" COMMAND ${WaylandScanner_EXECUTABLE} client-header ${_infile} ${_client_header} - DEPENDS ${_infile} VERBATIM) + DEPENDS ${WaylandScanner_EXECUTABLE} ${_infile} + VERBATIM + ) add_custom_command(OUTPUT "${_code}" - COMMAND ${WaylandScanner_EXECUTABLE} ${_code_type} ${_infile} ${_code} - DEPENDS ${_infile} ${_client_header} VERBATIM) - - if (TARGET ${target_or_sources_var}) - target_sources(${target_or_sources_var} PRIVATE "${_client_header}" "${_code}") - else() - list(APPEND ${target_or_sources_var} "${_client_header}" "${_code}") - set(${target_or_sources_var} ${${target_or_sources_var}} PARENT_SCOPE) - endif() + COMMAND ${WaylandScanner_EXECUTABLE} code ${_infile} ${_code} + DEPENDS ${WaylandScanner_EXECUTABLE} ${_infile} ${_client_header} + VERBATIM + ) + + list(APPEND ${out_var} "${_client_header}" "${_code}") + set(${out_var} ${${out_var}} PARENT_SCOPE) endfunction() -function(ecm_add_wayland_server_protocol target_or_sources_var) +function(ecm_add_wayland_server_protocol out_var) # Parse arguments - set(options PRIVATE_CODE) set(oneValueArgs PROTOCOL BASENAME) - cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "" ${ARGN}) + cmake_parse_arguments(ARGS "" "${oneValueArgs}" "" ${ARGN}) if(ARGS_UNPARSED_ARGUMENTS) message(FATAL_ERROR "Unknown keywords given to ecm_add_wayland_server_protocol(): \"${ARGS_UNPARSED_ARGUMENTS}\"") endif() - if(ARGS_PRIVATE_CODE) - set(_private_code_option PRIVATE_CODE) - endif() - - ecm_add_wayland_client_protocol(${target_or_sources_var} + ecm_add_wayland_client_protocol(${out_var} PROTOCOL ${ARGS_PROTOCOL} - BASENAME ${ARGS_BASENAME} - ${_private_code_option}) + BASENAME ${ARGS_BASENAME}) get_filename_component(_infile ${ARGS_PROTOCOL} ABSOLUTE) set(_server_header "${CMAKE_CURRENT_BINARY_DIR}/wayland-${ARGS_BASENAME}-server-protocol.h") - set(_server_code "${CMAKE_CURRENT_BINARY_DIR}/wayland-${ARGS_BASENAME}-protocol.c") - set_property(SOURCE ${_server_header} ${_server_code} PROPERTY SKIP_AUTOMOC ON) + set_property(SOURCE ${_server_header} PROPERTY SKIP_AUTOMOC ON) set_source_files_properties(${_server_header} GENERATED) add_custom_command(OUTPUT "${_server_header}" COMMAND ${WaylandScanner_EXECUTABLE} server-header ${_infile} ${_server_header} - DEPENDS ${_infile} VERBATIM) + DEPENDS ${WaylandScanner_EXECUTABLE} ${_infile} + VERBATIM + ) - if (TARGET ${target_or_sources_var}) - target_sources(${target_or_sources_var} PRIVATE "${_server_header}") - else() - list(APPEND ${target_or_sources_var} "${_server_header}") - set(${target_or_sources_var} ${${target_or_sources_var}} PARENT_SCOPE) - endif() + list(APPEND ${out_var} "${_server_header}") + set(${out_var} ${${out_var}} PARENT_SCOPE) endfunction() diff --git a/cmake/find-modules/FindXCB.cmake b/cmake/find-modules/FindXCB.cmake index f18c2fd0959..26b9bf89633 100644 --- a/cmake/find-modules/FindXCB.cmake +++ b/cmake/find-modules/FindXCB.cmake @@ -1,66 +1,88 @@ -# SPDX-FileCopyrightText: 2011 Fredrik Höglund -# SPDX-FileCopyrightText: 2013 Martin Gräßlin -# SPDX-FileCopyrightText: 2014-2015 Alex Merry +#.rst: +# FindXCB +# ------- # -# SPDX-License-Identifier: BSD-3-Clause - -#[=======================================================================[.rst: -FindXCB -------- - -Try to find XCB. - -This is a component-based find module, which makes use of the COMPONENTS and -OPTIONAL_COMPONENTS arguments to find_module. The following components are -available:: - - XCB - ATOM AUX COMPOSITE CURSOR DAMAGE - DPMS DRI2 DRI3 EVENT EWMH - GLX ICCCM IMAGE KEYSYMS PRESENT - RANDR RECORD RENDER RENDERUTIL RES - SCREENSAVER SHAPE SHM SYNC UTIL - XF86DRI XFIXES XINERAMA XINPUT XKB - XTEST XV XVMC - -If no components are specified, this module will act as though all components -were passed to OPTIONAL_COMPONENTS. Before 5.82 this excluded XINPUT. Since 5.82 -all components are searched for. - -This module will define the following variables, independently of the -components searched for or found: - -``XCB_FOUND`` - True if (the requestion version of) xcb is available -``XCB_VERSION`` - Found xcb version -``XCB_TARGETS`` - A list of all targets imported by this module (note that there may be more - than the components that were requested) -``XCB_LIBRARIES`` - This can be passed to target_link_libraries() instead of the imported - targets -``XCB_INCLUDE_DIRS`` - This should be passed to target_include_directories() if the targets are - not used for linking -``XCB_DEFINITIONS`` - This should be passed to target_compile_options() if the targets are not - used for linking - -For each searched-for components, ``XCB__FOUND`` will be set to -true if the corresponding xcb library was found, and false otherwise. If -``XCB__FOUND`` is true, the imported target ``XCB::`` -will be defined. This module will also attempt to determine -``XCB_*_VERSION`` variables for each imported target, although -``XCB_VERSION`` should normally be sufficient. - -In general we recommend using the imported targets, as they are easier to use -and provide more control. Bear in mind, however, that if any target is in the -link interface of an exported library, it must be made available by the -package config file. +# Try to find XCB. +# +# This is a component-based find module, which makes use of the COMPONENTS and +# OPTIONAL_COMPONENTS arguments to find_module. The following components are +# available:: +# +# XCB +# ATOM AUX COMPOSITE CURSOR DAMAGE +# DPMS DRI2 DRI3 EVENT EWMH +# GLX ICCCM IMAGE KEYSYMS PRESENT +# RANDR RECORD RENDER RENDERUTIL RES +# SCREENSAVER SHAPE SHM SYNC UTIL +# XEVIE XF86DRI XFIXES XINERAMA XINPUT +# XKB XPRINT XTEST XV XVMC +# +# If no components are specified, this module will act as though all components +# except XINPUT (which is considered unstable) were passed to +# OPTIONAL_COMPONENTS. +# +# This module will define the following variables, independently of the +# components searched for or found: +# +# ``XCB_FOUND`` +# True if (the requestion version of) xcb is available +# ``XCB_VERSION`` +# Found xcb version +# ``XCB_TARGETS`` +# A list of all targets imported by this module (note that there may be more +# than the components that were requested) +# ``XCB_LIBRARIES`` +# This can be passed to target_link_libraries() instead of the imported +# targets +# ``XCB_INCLUDE_DIRS`` +# This should be passed to target_include_directories() if the targets are +# not used for linking +# ``XCB_DEFINITIONS`` +# This should be passed to target_compile_options() if the targets are not +# used for linking +# +# For each searched-for components, ``XCB__FOUND`` will be set to +# true if the corresponding xcb library was found, and false otherwise. If +# ``XCB__FOUND`` is true, the imported target ``XCB::`` +# will be defined. This module will also attempt to determine +# ``XCB_*_VERSION`` variables for each imported target, although +# ``XCB_VERSION`` should normally be sufficient. +# +# In general we recommend using the imported targets, as they are easier to use +# and provide more control. Bear in mind, however, that if any target is in the +# link interface of an exported library, it must be made available by the +# package config file. +# +# Since pre-1.0.0. -Since pre-1.0.0. -#]=======================================================================] +#============================================================================= +# Copyright 2011 Fredrik Höglund +# Copyright 2013 Martin Gräßlin +# Copyright 2014-2015 Alex Merry +# +# 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 copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +#============================================================================= include(${CMAKE_CURRENT_LIST_DIR}/ECMFindModuleHelpersStub.cmake) @@ -96,10 +118,12 @@ set(XCB_known_components SCREENSAVER SYNC UTIL + XEVIE XF86DRI XINERAMA XINPUT XKB + XPRINT XTEST XV XVMC @@ -113,6 +137,9 @@ foreach(_comp ${XCB_known_components}) set(XCB_${_comp}_pkg_config "xcb-${_lc_comp}") set(XCB_${_comp}_lib "xcb-${_lc_comp}") set(XCB_${_comp}_header "xcb/${_lc_comp}.h") + if(USE_XCB_${_comp}_STATIC) + set(XCB_${_comp}_lib "lib${XCB_${_comp}_lib}.a") + endif() endforeach() # exceptions set(XCB_XCB_component_deps) diff --git a/cmake/modules/ECMFindModuleHelpers.cmake b/cmake/modules/ECMFindModuleHelpers.cmake index 92c796e6bdd..a5dabb1074b 100644 --- a/cmake/modules/ECMFindModuleHelpers.cmake +++ b/cmake/modules/ECMFindModuleHelpers.cmake @@ -1,114 +1,134 @@ -# SPDX-FileCopyrightText: 2014 Alex Merry +#.rst: +# ECMFindModuleHelpers +# -------------------- # -# SPDX-License-Identifier: BSD-3-Clause - -#[=======================================================================[.rst: -ECMFindModuleHelpers --------------------- - -Helper macros for find modules: ``ecm_find_package_version_check()``, -``ecm_find_package_parse_components()`` and -``ecm_find_package_handle_library_components()``. - -:: - - ecm_find_package_version_check() - -Prints warnings if the CMake version or the project's required CMake version -is older than that required by extra-cmake-modules. - -:: - - ecm_find_package_parse_components( - RESULT_VAR - KNOWN_COMPONENTS [ [...]] - [SKIP_DEPENDENCY_HANDLING]) - -This macro will populate with a list of components found in -_FIND_COMPONENTS, after checking that all those components are in the -list of ``KNOWN_COMPONENTS``; if there are any unknown components, it will print -an error or warning (depending on the value of _FIND_REQUIRED) and call -``return()``. - -The order of components in is guaranteed to match the order they -are listed in the ``KNOWN_COMPONENTS`` argument. - -If ``SKIP_DEPENDENCY_HANDLING`` is not set, for each component the variable -__component_deps will be checked for dependent components. -If is listed in _FIND_COMPONENTS, then all its (transitive) -dependencies will also be added to . - -:: - - ecm_find_package_handle_library_components( - COMPONENTS [ [...]] - [SKIP_DEPENDENCY_HANDLING]) - [SKIP_PKG_CONFIG]) - -Creates an imported library target for each component. The operation of this -macro depends on the presence of a number of CMake variables. - -The __lib variable should contain the name of this library, -and __header variable should contain the name of a header -file associated with it (whatever relative path is normally passed to -'#include'). __header_subdir variable can be used to specify -which subdirectory of the include path the headers will be found in. -``ecm_find_package_components()`` will then search for the library -and include directory (creating appropriate cache variables) and create an -imported library target named ::. - -Additional variables can be used to provide additional information: - -If ``SKIP_PKG_CONFIG``, the __pkg_config variable is set, and -pkg-config is found, the pkg-config module given by -__pkg_config will be searched for and used to help locate the -library and header file. It will also be used to set -__VERSION. - -Note that if version information is found via pkg-config, -__FIND_VERSION can be set to require a particular version -for each component. - -If ``SKIP_DEPENDENCY_HANDLING`` is not set, the ``INTERFACE_LINK_LIBRARIES`` property -of the imported target for will be set to contain the imported -targets for the components listed in __component_deps. -_FOUND will also be set to ``FALSE`` if any of the components in -__component_deps are not found. This requires the components -in __component_deps to be listed before in the -``COMPONENTS`` argument. - -The following variables will be set: - -``_TARGETS`` - the imported targets -``_LIBRARIES`` - the found libraries -``_INCLUDE_DIRS`` - the combined required include directories for the components -``_DEFINITIONS`` - the "other" CFLAGS provided by pkg-config, if any -``_VERSION`` - the value of ``__VERSION`` for the first component that - has this variable set (note that components are searched for in the order - they are passed to the macro), although if it is already set, it will not - be altered - -.. note:: - These variables are never cleared, so if - ``ecm_find_package_handle_library_components()`` is called multiple times with - different components (typically because of multiple ``find_package()`` calls) then - ``_TARGETS``, for example, will contain all the targets found in any - call (although no duplicates). +# Helper macros for find modules: ecm_find_package_version_check(), +# ecm_find_package_parse_components() and +# ecm_find_package_handle_library_components(). +# +# :: +# +# ecm_find_package_version_check() +# +# Prints warnings if the CMake version or the project's required CMake version +# is older than that required by extra-cmake-modules. +# +# :: +# +# ecm_find_package_parse_components( +# RESULT_VAR +# KNOWN_COMPONENTS [ [...]] +# [SKIP_DEPENDENCY_HANDLING]) +# +# This macro will populate with a list of components found in +# _FIND_COMPONENTS, after checking that all those components are in the +# list of KNOWN_COMPONENTS; if there are any unknown components, it will print +# an error or warning (depending on the value of _FIND_REQUIRED) and call +# return(). +# +# The order of components in is guaranteed to match the order they +# are listed in the KNOWN_COMPONENTS argument. +# +# If SKIP_DEPENDENCY_HANDLING is not set, for each component the variable +# __component_deps will be checked for dependent components. +# If is listed in _FIND_COMPONENTS, then all its (transitive) +# dependencies will also be added to . +# +# :: +# +# ecm_find_package_handle_library_components( +# COMPONENTS [ [...]] +# [SKIP_DEPENDENCY_HANDLING]) +# [SKIP_PKG_CONFIG]) +# +# Creates an imported library target for each component. The operation of this +# macro depends on the presence of a number of CMake variables. +# +# The __lib variable should contain the name of this library, +# and __header variable should contain the name of a header +# file associated with it (whatever relative path is normally passed to +# '#include'). __header_subdir variable can be used to specify +# which subdirectory of the include path the headers will be found in. +# ecm_find_package_components() will then search for the library +# and include directory (creating appropriate cache variables) and create an +# imported library target named ::. +# +# Additional variables can be used to provide additional information: +# +# If SKIP_PKG_CONFIG, the __pkg_config variable is set, and +# pkg-config is found, the pkg-config module given by +# __pkg_config will be searched for and used to help locate the +# library and header file. It will also be used to set +# __VERSION. +# +# Note that if version information is found via pkg-config, +# __FIND_VERSION can be set to require a particular version +# for each component. +# +# If SKIP_DEPENDENCY_HANDLING is not set, the INTERFACE_LINK_LIBRARIES property +# of the imported target for will be set to contain the imported +# targets for the components listed in __component_deps. +# _FOUND will also be set to false if any of the components in +# __component_deps are not found. This requires the components +# in __component_deps to be listed before in the +# COMPONENTS argument. +# +# The following variables will be set: +# +# ``_TARGETS`` +# the imported targets +# ``_LIBRARIES`` +# the found libraries +# ``_INCLUDE_DIRS`` +# the combined required include directories for the components +# ``_DEFINITIONS`` +# the "other" CFLAGS provided by pkg-config, if any +# ``_VERSION`` +# the value of ``__VERSION`` for the first component that +# has this variable set (note that components are searched for in the order +# they are passed to the macro), although if it is already set, it will not +# be altered +# +# Note that these variables are never cleared, so if +# ecm_find_package_handle_library_components() is called multiple times with +# different components (typically because of multiple find_package() calls) then +# ``_TARGETS``, for example, will contain all the targets found in any +# call (although no duplicates). +# +# Since pre-1.0.0. -Since pre-1.0.0. -#]=======================================================================] +#============================================================================= +# Copyright 2014 Alex Merry +# +# 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 copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. macro(ecm_find_package_version_check module_name) - if(CMAKE_VERSION VERSION_LESS 3.16.0) - message(FATAL_ERROR "CMake 3.16.0 is required by Find${module_name}.cmake") + if(CMAKE_VERSION VERSION_LESS 2.8.12) + message(FATAL_ERROR "CMake 2.8.12 is required by Find${module_name}.cmake") endif() - if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 3.16.0) - message(AUTHOR_WARNING "Your project should require at least CMake 3.16.0 to use Find${module_name}.cmake") + if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.12) + message(AUTHOR_WARNING "Your project should require at least CMake 2.8.12 to use Find${module_name}.cmake") endif() endmacro() @@ -223,7 +243,10 @@ macro(ecm_find_package_handle_library_components module_name) set(${module_name}_VERSION ${${module_name}_${ecm_fpwc_comp}_VERSION}) endif() - set(FPHSA_NAME_MISMATCHED 1) + set(_name_mismatched_arg) + if(NOT CMAKE_VERSION VERSION_LESS 3.17) + set(_name_mismatched_arg NAME_MISMATCHED) + endif() find_package_handle_standard_args(${module_name}_${ecm_fpwc_comp} FOUND_VAR ${module_name}_${ecm_fpwc_comp}_FOUND @@ -233,8 +256,8 @@ macro(ecm_find_package_handle_library_components module_name) ${ecm_fpwc_dep_vars} VERSION_VAR ${module_name}_${ecm_fpwc_comp}_VERSION + ${_name_mismatched_arg} ) - unset(FPHSA_NAME_MISMATCHED) mark_as_advanced( ${module_name}_${ecm_fpwc_comp}_LIBRARY diff --git a/deploy/linux/AppRun b/deploy/linux/AppRun index aab85507839..1373a15a9da 100755 --- a/deploy/linux/AppRun +++ b/deploy/linux/AppRun @@ -1,12 +1,21 @@ -#! /usr/bin/env bash +#!/bin/bash -# autogenerated by linuxdeploy - -# make sure errors in sourced scripts will cause this script to stop set -e -this_dir="$(readlink -f "$(dirname "$0")")" +HERE="$(dirname "$(readlink -f "${0}")")" + +# export LD_LIBRARY_PATH="$HERE/usr/lib:${LD_LIBRARY_PATH}" + +export GST_REGISTRY_REUSE_PLUGIN_SCANNER="no" +export GIO_EXTRA_MODULES="$HERE/usr/lib/gio/modules" -source "$this_dir"/apprun-hooks/"linuxdeploy-plugin-gstreamer.sh" +export GST_PLUGIN_SYSTEM_PATH="$HERE/usr/lib/gstreamer-1.0" +export GST_PLUGIN_SYSTEM_PATH_1_0="$HERE/usr/lib/gstreamer-1.0" +export GST_PLUGIN_PATH="$HERE/usr/lib/gstreamer-1.0" +export GST_PLUGIN_PATH_1_0="$HERE/usr/lib/gstreamer-1.0" +export GST_PLUGIN_SCANNER="$HERE/usr/lib/gstreamer1.0/gstreamer-1.0/gst-plugin-scanner" +export GST_PLUGIN_SCANNER_1_0="$HERE/usr/lib/gstreamer1.0/gstreamer-1.0/gst-plugin-scanner" +export GST_PTP_HELPER="$HERE/usr/lib/gstreamer1.0/gstreamer-1.0/gst-ptp-helper" +export GST_PTP_HELPER_1_0="$HERE/usr/lib/gstreamer1.0/gstreamer-1.0/gst-ptp-helper" -exec "$this_dir"/AppRun.wrapped "$@" +exec "$HERE/usr/bin/QGroundControl" "$@" diff --git a/deploy/linux/apprun-hooks/linux-deploy-gstreamer.sh b/deploy/linux/apprun-hooks/linux-deploy-gstreamer.sh deleted file mode 100644 index eba949d216a..00000000000 --- a/deploy/linux/apprun-hooks/linux-deploy-gstreamer.sh +++ /dev/null @@ -1,8 +0,0 @@ -#! /bin/bash - -export GST_REGISTRY_REUSE_PLUGIN_SCANNER="no" -export GST_PLUGIN_SYSTEM_PATH_1_0="${APPDIR}/usr/lib/gstreamer-1.0" -export GST_PLUGIN_PATH_1_0="${APPDIR}/usr/lib/gstreamer-1.0" - -export GST_PLUGIN_SCANNER_1_0="${APPDIR}/usr/lib/gstreamer1.0/gstreamer-1.0/gst-plugin-scanner" -export GST_PTP_HELPER_1_0="${APPDIR}/usr/lib/gstreamer1.0/gstreamer-1.0/gst-ptp-helper" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2f5b46a0107..93b769f7eea 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -83,6 +83,7 @@ target_link_libraries(QGC FollowMe Gimbal GPS + GStreamerReceiver Joystick MAVLink MissionManager diff --git a/src/Settings/CMakeLists.txt b/src/Settings/CMakeLists.txt index 9db4d8cf668..3a9b440c2c2 100644 --- a/src/Settings/CMakeLists.txt +++ b/src/Settings/CMakeLists.txt @@ -51,6 +51,7 @@ target_link_libraries(Settings PRIVATE Qt6::Multimedia API + GStreamerReceiver QmlControls Vehicle VideoManager diff --git a/src/Settings/VideoDecoderOptions.h b/src/Settings/VideoDecoderOptions.h deleted file mode 100644 index f8ef9fec947..00000000000 --- a/src/Settings/VideoDecoderOptions.h +++ /dev/null @@ -1,20 +0,0 @@ -/**************************************************************************** -* - * (c) 2009-2024 QGROUNDCONTROL PROJECT - * - * QGroundControl is licensed according to the terms in the file - * COPYING.md in the root of the source code directory. - * - ****************************************************************************/ - -#pragma once - - -enum VideoDecoderOptions { - ForceVideoDecoderDefault = 0, - ForceVideoDecoderSoftware, - ForceVideoDecoderNVIDIA, - ForceVideoDecoderVAAPI, - ForceVideoDecoderDirectX3D, - ForceVideoDecoderVideoToolbox, -}; diff --git a/src/Settings/VideoSettings.cc b/src/Settings/VideoSettings.cc index 3b0b0df594a..2d8931c6009 100644 --- a/src/Settings/VideoSettings.cc +++ b/src/Settings/VideoSettings.cc @@ -14,6 +14,10 @@ #include #include +#ifdef QGC_GST_STREAMING +#include "GStreamer.h" +#endif + #ifndef QGC_DISABLE_UVC #include #include @@ -56,27 +60,29 @@ DECLARE_SETTINGGROUP(Video, "Video") _nameToMetaDataMap[videoSourceName]->setEnumInfo(videoSourceCookedList, videoSourceList); +#ifdef QGC_GST_STREAMING const QVariantList removeForceVideoDecodeList{ #if defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) - VideoDecoderOptions::ForceVideoDecoderDirectX3D, - VideoDecoderOptions::ForceVideoDecoderVideoToolbox, + GStreamer::VideoDecoderOptions::ForceVideoDecoderDirectX3D, + GStreamer::VideoDecoderOptions::ForceVideoDecoderVideoToolbox, #elif defined(Q_OS_WIN) - VideoDecoderOptions::ForceVideoDecoderVAAPI, - VideoDecoderOptions::ForceVideoDecoderVideoToolbox, + GStreamer::VideoDecoderOptions::ForceVideoDecoderVAAPI, + GStreamer::VideoDecoderOptions::ForceVideoDecoderVideoToolbox, #elif defined(Q_OS_MAC) - VideoDecoderOptions::ForceVideoDecoderDirectX3D, - VideoDecoderOptions::ForceVideoDecoderVAAPI, + GStreamer::VideoDecoderOptions::ForceVideoDecoderDirectX3D, + GStreamer::VideoDecoderOptions::ForceVideoDecoderVAAPI, #elif defined(Q_OS_ANDROID) - VideoDecoderOptions::ForceVideoDecoderDirectX3D, - VideoDecoderOptions::ForceVideoDecoderVideoToolbox, - VideoDecoderOptions::ForceVideoDecoderVAAPI, - VideoDecoderOptions::ForceVideoDecoderNVIDIA, + GStreamer::VideoDecoderOptions::ForceVideoDecoderDirectX3D, + GStreamer::VideoDecoderOptions::ForceVideoDecoderVideoToolbox, + GStreamer::VideoDecoderOptions::ForceVideoDecoderVAAPI, + GStreamer::VideoDecoderOptions::ForceVideoDecoderNVIDIA, #endif }; - for(const auto& value : removeForceVideoDecodeList) { + for (const auto &value : removeForceVideoDecodeList) { _nameToMetaDataMap[forceVideoDecoderName]->removeEnumInfo(value); } +#endif // Set default value for videoSource _setDefaults(); diff --git a/src/Settings/VideoSettings.h b/src/Settings/VideoSettings.h index daed65b6a6a..51437f6cb86 100644 --- a/src/Settings/VideoSettings.h +++ b/src/Settings/VideoSettings.h @@ -10,7 +10,6 @@ #pragma once #include "SettingsGroup.h" -#include "VideoDecoderOptions.h" class VideoSettings : public SettingsGroup { @@ -37,8 +36,6 @@ class VideoSettings : public SettingsGroup DEFINE_SETTINGFACT(lowLatencyMode) DEFINE_SETTINGFACT(forceVideoDecoder) - Q_ENUM(VideoDecoderOptions) - Q_PROPERTY(bool streamConfigured READ streamConfigured NOTIFY streamConfiguredChanged) Q_PROPERTY(QString rtspVideoSource READ rtspVideoSource CONSTANT) Q_PROPERTY(QString udp264VideoSource READ udp264VideoSource CONSTANT) diff --git a/src/VideoManager/VideoManager.cc b/src/VideoManager/VideoManager.cc index ee2502322ec..fbc9066eeef 100644 --- a/src/VideoManager/VideoManager.cc +++ b/src/VideoManager/VideoManager.cc @@ -21,7 +21,6 @@ #include "SubtitleWriter.h" #ifdef QGC_GST_STREAMING #include "GStreamer.h" -#include "VideoDecoderOptions.h" #else #include "GLVideoItemStub.h" #endif @@ -38,6 +37,7 @@ #include #include +#include QGC_LOGGING_CATEGORY(VideoManagerLog, "qgc.videomanager.videomanager") @@ -53,20 +53,7 @@ VideoManager::VideoManager(QGCApplication* app, QGCToolbox* toolbox) , _subtitleWriter(new SubtitleWriter(this)) { #ifdef QGC_GST_STREAMING - // Gstreamer debug settings - int gstDebugLevel = 0; - QSettings settings; - if (settings.contains(AppSettings::gstDebugLevelName)) { - gstDebugLevel = settings.value(AppSettings::gstDebugLevelName).toInt(); - } - QStringList args = _app->arguments(); - const qsizetype argc = args.size(); - char** argv = new char*[argc]; - for (qsizetype i = 0; i < argc; i++) { - argv[i] = args[i].toUtf8().data(); - } - GStreamer::initialize(argc, argv, gstDebugLevel); - delete[] argv; + GStreamer::initialize(); #else qmlRegisterType("org.freedesktop.gstreamer.Qt6GLVideoItem", 1, 0, "GstGLQt6VideoItem"); #endif @@ -118,7 +105,7 @@ VideoManager::setToolbox(QGCToolbox *toolbox) connect(pVehicleMgr, &MultiVehicleManager::activeVehicleChanged, this, &VideoManager::_setActiveVehicle); #ifdef QGC_GST_STREAMING - GStreamer::blacklist(static_cast(_videoSettings->forceVideoDecoder()->rawValue().toInt())); + GStreamer::blacklist(static_cast(_videoSettings->forceVideoDecoder()->rawValue().toInt())); #endif int index = 0; diff --git a/src/VideoManager/VideoReceiver/GStreamer/CMakeLists.txt b/src/VideoManager/VideoReceiver/GStreamer/CMakeLists.txt index bf4b14e47c4..531b5b591a7 100644 --- a/src/VideoManager/VideoReceiver/GStreamer/CMakeLists.txt +++ b/src/VideoManager/VideoReceiver/GStreamer/CMakeLists.txt @@ -20,8 +20,11 @@ endif() target_sources(GStreamerReceiver PRIVATE - gstqgc.c - gstqgcvideosinkbin.c + gstqgc.cc + gstqgcelement.cc + gstqgcelements.h + gstqgcvideosinkbin.cc + gstqgcvideosinkbin.h GStreamer.cc GStreamer.h GstVideoReceiver.cc @@ -31,13 +34,14 @@ target_sources(GStreamerReceiver if(IOS) target_sources(GStreamerReceiver PRIVATE - gst_ios_init.h gst_ios_init.m + gst_ios_init.h ) endif() target_link_libraries(GStreamerReceiver PRIVATE + QGC Utilities PUBLIC Qt6::Core @@ -46,4 +50,4 @@ target_link_libraries(GStreamerReceiver VideoReceiver ) -target_compile_definitions(QGC PUBLIC QGC_GST_STREAMING) +target_compile_definitions(GStreamerReceiver PUBLIC QGC_GST_STREAMING) diff --git a/src/VideoManager/VideoReceiver/GStreamer/GLVideoItemStub.h b/src/VideoManager/VideoReceiver/GStreamer/GLVideoItemStub.h index 38d58d46881..63e454b66e7 100644 --- a/src/VideoManager/VideoReceiver/GStreamer/GLVideoItemStub.h +++ b/src/VideoManager/VideoReceiver/GStreamer/GLVideoItemStub.h @@ -18,7 +18,5 @@ class GLVideoItemStub : public QQuickItem public: GLVideoItemStub(QQuickItem *parent = nullptr) : - QQuickItem(parent) - {} - ~GLVideoItemStub() = default; + QQuickItem(parent) {} }; diff --git a/src/VideoManager/VideoReceiver/GStreamer/GStreamer.cc b/src/VideoManager/VideoReceiver/GStreamer/GStreamer.cc index 00b3c502432..7635b30d1ad 100644 --- a/src/VideoManager/VideoReceiver/GStreamer/GStreamer.cc +++ b/src/VideoManager/VideoReceiver/GStreamer/GStreamer.cc @@ -7,30 +7,59 @@ * ****************************************************************************/ - -/** - * @file - * @brief QGC Video Streaming Initialization - * @author Gus Grubba - */ - #include "GStreamer.h" #include "GstVideoReceiver.h" +#include "AppSettings.h" #include "QGCLoggingCategory.h" +#ifdef Q_OS_IOS +#include "gst_ios_init.h" +#endif -#include +#include +#include +#include -QGC_LOGGING_CATEGORY(GStreamerLog, "GStreamerLog") -QGC_LOGGING_CATEGORY(GStreamerAPILog, "GStreamerAPILog") +QGC_LOGGING_CATEGORY(GStreamerLog, "qgc.videomanager.videoreceiver.gstreamer") +QGC_LOGGING_CATEGORY(GStreamerAPILog, "qgc.videomanager.videoreceiver.gstreamer.api") + +G_BEGIN_DECLS +#ifdef QGC_GST_STATIC_BUILD +GST_PLUGIN_STATIC_DECLARE(coreelements); +GST_PLUGIN_STATIC_DECLARE(playback); +GST_PLUGIN_STATIC_DECLARE(libav); +GST_PLUGIN_STATIC_DECLARE(rtp); +GST_PLUGIN_STATIC_DECLARE(rtsp); +GST_PLUGIN_STATIC_DECLARE(udp); +GST_PLUGIN_STATIC_DECLARE(videoparsersbad); +GST_PLUGIN_STATIC_DECLARE(x264); +GST_PLUGIN_STATIC_DECLARE(rtpmanager); +GST_PLUGIN_STATIC_DECLARE(isomp4); +GST_PLUGIN_STATIC_DECLARE(matroska); +GST_PLUGIN_STATIC_DECLARE(mpegtsdemux); +GST_PLUGIN_STATIC_DECLARE(opengl); +GST_PLUGIN_STATIC_DECLARE(tcp); +GST_PLUGIN_STATIC_DECLARE(asf); +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_MAC) +GST_PLUGIN_STATIC_DECLARE(va); +#endif +#ifdef Q_OS_ANDROID +GST_PLUGIN_STATIC_DECLARE(androidmedia); +#elif defined(Q_OS_IOS) +GST_PLUGIN_STATIC_DECLARE(applemedia); +#endif +#endif +GST_PLUGIN_STATIC_DECLARE(qml6); +GST_PLUGIN_STATIC_DECLARE(qgc); +G_END_DECLS -static void qt_gst_log(GstDebugCategory * category, - GstDebugLevel level, - const gchar * file, - const gchar * function, - gint line, - GObject * object, - GstDebugMessage * message, - gpointer data) +static void qt_gst_log(GstDebugCategory *category, + GstDebugLevel level, + const gchar *file, + const gchar *function, + gint line, + GObject *object, + GstDebugMessage *message, + gpointer data) { Q_UNUSED(data); @@ -40,7 +69,7 @@ static void qt_gst_log(GstDebugCategory * category, QMessageLogger log(file, line, function); - char* object_info = gst_info_strdup_printf("%" GST_PTR_FORMAT, static_cast(object)); + char *object_info = gst_info_strdup_printf("%" GST_PTR_FORMAT, static_cast(object)); switch (level) { default: @@ -66,165 +95,40 @@ static void qt_gst_log(GstDebugCategory * category, object_info = nullptr; } -#if defined(Q_OS_IOS) -#include "gst_ios_init.h" -#endif - -#include "VideoReceiver.h" - -G_BEGIN_DECLS -// The static plugins we use -#ifdef QGC_GST_STATIC_BUILD - GST_PLUGIN_STATIC_DECLARE(coreelements); - GST_PLUGIN_STATIC_DECLARE(playback); - GST_PLUGIN_STATIC_DECLARE(libav); - GST_PLUGIN_STATIC_DECLARE(rtp); - GST_PLUGIN_STATIC_DECLARE(rtsp); - GST_PLUGIN_STATIC_DECLARE(udp); - GST_PLUGIN_STATIC_DECLARE(videoparsersbad); - GST_PLUGIN_STATIC_DECLARE(x264); - GST_PLUGIN_STATIC_DECLARE(rtpmanager); - GST_PLUGIN_STATIC_DECLARE(isomp4); - GST_PLUGIN_STATIC_DECLARE(matroska); - GST_PLUGIN_STATIC_DECLARE(mpegtsdemux); - GST_PLUGIN_STATIC_DECLARE(opengl); - GST_PLUGIN_STATIC_DECLARE(tcp); -#if defined(Q_OS_ANDROID) - GST_PLUGIN_STATIC_DECLARE(androidmedia); -#elif defined(Q_OS_IOS) - GST_PLUGIN_STATIC_DECLARE(applemedia); -#endif -#endif - GST_PLUGIN_STATIC_DECLARE(qml6); - GST_PLUGIN_STATIC_DECLARE(qgc); -G_END_DECLS - -#if (defined(Q_OS_MAC) && defined(QGC_INSTALL_RELEASE)) || defined(Q_OS_WIN) || defined(Q_OS_LINUX) -static void qgcputenv(const QString& key, const QString& root, const QString& path) +static void _qgcputenv(const QString &key, const QString &root, const QString &path = "") { - const QString value = root + path; - qputenv(key.toStdString().c_str(), QByteArray(value.toStdString().c_str())); + const QByteArray keyArray = key.toLocal8Bit(); + const QByteArray valueArray = (root + path).toLocal8Bit(); + (void) qputenv(keyArray, valueArray); } -#endif -void -GStreamer::blacklist(VideoDecoderOptions option) +static void _setGstEnvVars() { - GstRegistry* registry = gst_registry_get(); - - if (registry == nullptr) { - qCCritical(GStreamerLog) << "Failed to get gstreamer registry."; - return; - } - - auto changeRank = [registry](const char* featureName, uint16_t rank) { - GstPluginFeature* feature = gst_registry_lookup_feature(registry, featureName); - if (feature == nullptr) { - qCDebug(GStreamerLog) << "Failed to change ranking of feature. Featuer does not exist:" << featureName; - return; - } - - qCDebug(GStreamerLog) << "Changing feature (" << featureName << ") to use rank:" << rank; - gst_plugin_feature_set_rank(feature, rank); - gst_registry_add_feature(registry, feature); - gst_object_unref(feature); - }; - - // Set rank for specific features - changeRank("bcmdec", GST_RANK_NONE); - - switch (option) { - case ForceVideoDecoderDefault: - break; - case ForceVideoDecoderSoftware: - for(auto name : {"avdec_h264", "avdec_h265"}) { - changeRank(name, GST_RANK_PRIMARY + 1); - } - break; - case ForceVideoDecoderVAAPI: - for(auto name : {"vaapimpeg2dec", "vaapimpeg4dec", "vaapih263dec", "vaapih264dec", "vaapih265dec", "vaapivc1dec"}) { - changeRank(name, GST_RANK_PRIMARY + 1); - } - break; - case ForceVideoDecoderNVIDIA: - for(auto name : {"nvh265dec", "nvh265sldec", "nvh264dec", "nvh264sldec"}) { - changeRank(name, GST_RANK_PRIMARY + 1); - } - break; - case ForceVideoDecoderDirectX3D: - for(auto name : {"d3d11vp9dec", "d3d11h265dec", "d3d11h264dec"}) { - changeRank(name, GST_RANK_PRIMARY + 1); - } - break; - case ForceVideoDecoderVideoToolbox: - changeRank("vtdec", GST_RANK_PRIMARY + 1); - break; - default: - qCWarning(GStreamerLog) << "Can't handle decode option:" << option; - } -} - -void -GStreamer::initialize(int argc, char* argv[], int debuglevel) -{ - qRegisterMetaType("STATUS"); - -#ifdef Q_OS_MAC - #ifdef QGC_INSTALL_RELEASE - const QString currentDir = QCoreApplication::applicationDirPath(); - qgcputenv("GST_REGISTRY_REUSE_PLUGIN_SCANNER", "no", ""); - qgcputenv("GST_PLUGIN_SCANNER", currentDir, "/../Frameworks/GStreamer.framework/Versions/Current/libexec/gstreamer-1.0/gst-plugin-scanner"); - qgcputenv("GST_PTP_HELPER_1_0", currentDir, "/../Frameworks/GStreamer.framework/Versions/Current/libexec/gstreamer-1.0/gst-ptp-helper"); - // qgcputenv("GTK_PATH", currentDir, "/../Frameworks/GStreamer.framework/Versions/Current"); - qgcputenv("GIO_EXTRA_MODULES", currentDir, "/../Frameworks/GStreamer.framework/Versions/Current/lib/gio/modules"); - qgcputenv("GST_PLUGIN_SYSTEM_PATH_1_0", currentDir, "/../Frameworks/GStreamer.framework/Versions/Current/lib/gstreamer-1.0"); - qgcputenv("GST_PLUGIN_SYSTEM_PATH", currentDir, "/../Frameworks/GStreamer.framework/Versions/Current/lib/gstreamer-1.0"); - qgcputenv("GST_PLUGIN_PATH_1_0", currentDir, "/../Frameworks/GStreamer.framework/Versions/Current/lib/gstreamer-1.0"); - qgcputenv("GST_PLUGIN_PATH", currentDir, "/../Frameworks/GStreamer.framework/Versions/Current/lib/gstreamer-1.0"); - #endif -#elif defined(Q_OS_WIN) const QString currentDir = QCoreApplication::applicationDirPath(); - // qgcputenv("GST_PLUGIN_SCANNER", "C:/gstreamer/1.0/msvc_x86_64", "/libexec/gstreamer-1.0/gst-plugin-scanner"); - // qgcputenv("GST_PTP_HELPER_1_0", "C:/gstreamer/1.0/msvc_x86_64", "/libexec/gstreamer-1.0/gst-ptp-helper"); - // qgcputenv("GTK_PATH", "C:/gstreamer/1.0/msvc_x86_64", ""); - // qgcputenv("GIO_EXTRA_MODULES", "C:/gstreamer/1.0/msvc_x86_64", "/lib/gio/modules"); - // qgcputenv("GST_PLUGIN_SYSTEM_PATH_1_0", "C:/gstreamer/1.0/msvc_x86_64", "/lib/gstreamer-1.0"); - // qgcputenv("GST_PLUGIN_SYSTEM_PATH", "C:/gstreamer/1.0/msvc_x86_64", "/lib/gstreamer-1.0"); - qgcputenv("GST_PLUGIN_PATH_1_0", currentDir, ""); - qgcputenv("GST_PLUGIN_PATH", currentDir, ""); -#elif defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID) - // const QString currentDir = QCoreApplication::applicationDirPath(); - // qgcputenv("GST_REGISTRY_REUSE_PLUGIN_SCANNER", "no", ""); - // qgcputenv("GST_PLUGIN_SCANNER", currentDir, "/../lib/gstreamer1.0/gstreamer-1.0/gst-plugin-scanner"); - // qgcputenv("GST_PTP_HELPER_1_0", currentDir, "/../lib/gstreamer1.0/gstreamer-1.0/gst-ptp-helper"); - // qgcputenv("GTK_PATH", currentDir, ""); - // qgcputenv("GIO_EXTRA_MODULES", currentDir, "/../lib/gio/modules"); - // qgcputenv("GST_PLUGIN_SYSTEM_PATH_1_0", currentDir, "/../lib"); - // qgcputenv("GST_PLUGIN_SYSTEM_PATH", currentDir, "/../lib"); - // qgcputenv("GST_PLUGIN_PATH_1_0", currentDir, "/../lib"); - // qgcputenv("GST_PLUGIN_PATH", currentDir, "/../lib"); -#endif - - //-- If gstreamer debugging is not configured via environment then use internal QT logging - if (qEnvironmentVariableIsEmpty("GST_DEBUG")) { - gst_debug_set_default_threshold(static_cast(debuglevel)); - gst_debug_remove_log_function(gst_debug_log_default); - gst_debug_add_log_function(qt_gst_log, nullptr, nullptr); - } - // Initialize GStreamer -#if defined(Q_OS_IOS) - //-- iOS specific initialization - gst_ios_pre_init(); +#if defined(Q_OS_MAC) + _qgcputenv("GST_REGISTRY_REUSE_PLUGIN_SCANNER", "no"); + _qgcputenv("GST_PLUGIN_SCANNER", currentDir, "/../Frameworks/GStreamer.framework/Versions/1.0/libexec/gstreamer-1.0/gst-plugin-scanner"); + _qgcputenv("GST_PTP_HELPER_1_0", currentDir, "/../Frameworks/GStreamer.framework/Versions/1.0/libexec/gstreamer-1.0/gst-ptp-helper"); + _qgcputenv("GIO_EXTRA_MODULES", currentDir, "/../Frameworks/GStreamer.framework/Versions/1.0/lib/gio/modules"); + _qgcputenv("GST_PLUGIN_SYSTEM_PATH_1_0", currentDir, "/../Frameworks/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0"); // PlugIns/gstreamer + _qgcputenv("GST_PLUGIN_SYSTEM_PATH", currentDir, "/../Frameworks/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0"); + _qgcputenv("GST_PLUGIN_PATH_1_0", currentDir, "/../Frameworks/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0"); + _qgcputenv("GST_PLUGIN_PATH", currentDir, "/../Frameworks/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0"); +#elif defined(Q_OS_WIN) + _qgcputenv("GST_REGISTRY_REUSE_PLUGIN_SCANNER", "no"); + _qgcputenv("GST_PLUGIN_SCANNER", currentDir, "/../libexec/gstreamer-1.0/gst-plugin-scanner"); + _qgcputenv("GST_PTP_HELPER_1_0", currentDir, "/../libexec/gstreamer-1.0/gst-ptp-helper"); + _qgcputenv("GIO_EXTRA_MODULES", currentDir, "/../lib/gio/modules"); + _qgcputenv("GST_PLUGIN_SYSTEM_PATH_1_0", currentDir, "/../lib/gstreamer-1.0"); + _qgcputenv("GST_PLUGIN_SYSTEM_PATH", currentDir, "/../lib/gstreamer-1.0"); + _qgcputenv("GST_PLUGIN_PATH_1_0", currentDir, "/../lib/gstreamer-1.0"); + _qgcputenv("GST_PLUGIN_PATH", currentDir, "/../lib/gstreamer-1.0"); #endif +} - GError* error = nullptr; - if (!gst_init_check(&argc, &argv, &error)) { - qCCritical(GStreamerLog) << "gst_init_check() failed: " << error->message; - g_error_free(error); - } - - // The static plugins we use +static void _registerPlugins() +{ #ifdef QGC_GST_STATIC_BUILD GST_PLUGIN_STATIC_REGISTER(coreelements); GST_PLUGIN_STATIC_REGISTER(playback); @@ -240,15 +144,63 @@ GStreamer::initialize(int argc, char* argv[], int debuglevel) GST_PLUGIN_STATIC_REGISTER(mpegtsdemux); GST_PLUGIN_STATIC_REGISTER(opengl); GST_PLUGIN_STATIC_REGISTER(tcp); - -#if defined(Q_OS_ANDROID) + GST_PLUGIN_STATIC_REGISTER(asf); +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_MAC) + GST_PLUGIN_STATIC_REGISTER(va); +#endif +#ifdef Q_OS_ANDROID GST_PLUGIN_STATIC_REGISTER(androidmedia); #elif defined(Q_OS_IOS) GST_PLUGIN_STATIC_REGISTER(applemedia); #endif #endif +} + +namespace GStreamer +{ + +void initialize() +{ + (void) qRegisterMetaType("STATUS"); + + _setGstEnvVars(); + + if (qEnvironmentVariableIsEmpty("GST_DEBUG")) { + int gstDebugLevel = 0; + QSettings settings; + if (settings.contains(AppSettings::gstDebugLevelName)) { + gstDebugLevel = settings.value(AppSettings::gstDebugLevelName).toInt(); + } + gst_debug_set_default_threshold(static_cast(gstDebugLevel)); + gst_debug_remove_log_function(gst_debug_log_default); + gst_debug_add_log_function(qt_gst_log, nullptr, nullptr); + } + +#ifdef Q_OS_IOS + gst_ios_pre_init(); +#endif + + const QStringList args = QCoreApplication::arguments(); + int argc = args.size(); + QList argList; + argList.reserve(argc); + + char **argv = new char*[argc]; + for (int i = 0; i < argc; i++) { + (void) argList.append(args[i].toUtf8()); + argv[i] = argList[i].data(); + } + + GError *error = nullptr; + if (!gst_init_check(&argc, &argv, &error)) { + qCCritical(GStreamerLog) << Q_FUNC_INFO << error->message; + g_error_free(error); + } + delete[] argv; -#if defined(Q_OS_IOS) + _registerPlugins(); + +#ifdef Q_OS_IOS gst_ios_post_init(); #endif @@ -256,14 +208,68 @@ GStreamer::initialize(int argc, char* argv[], int debuglevel) GST_PLUGIN_STATIC_REGISTER(qgc); } -void* -GStreamer::createVideoSink(QObject* parent, QQuickItem* widget) +void blacklist(VideoDecoderOptions option) { - Q_UNUSED(parent) + GstRegistry *const registry = gst_registry_get(); + + if (!registry) { + qCCritical(GStreamerLog) << "Failed to get gstreamer registry."; + return; + } + + const auto changeRank = [registry](const char *featureName, uint16_t rank) { + GstPluginFeature *const feature = gst_registry_lookup_feature(registry, featureName); + if (!feature) { + qCDebug(GStreamerLog) << "Failed to change ranking of feature. Featuer does not exist:" << featureName; + return; + } + + qCDebug(GStreamerLog) << "Changing feature (" << featureName << ") to use rank:" << rank; + gst_plugin_feature_set_rank(feature, rank); + (void) gst_registry_add_feature(registry, feature); + gst_object_unref(feature); + }; + + changeRank("bcmdec", GST_RANK_NONE); + + switch (option) { + case ForceVideoDecoderDefault: + break; + case ForceVideoDecoderSoftware: + for (const char *name : {"avdec_h264", "avdec_h265"}) { + changeRank(name, GST_RANK_PRIMARY + 1); + } + break; + case ForceVideoDecoderVAAPI: + for (const char *name : {"vaapimpeg2dec", "vaapimpeg4dec", "vaapih263dec", "vaapih264dec", "vaapih265dec", "vaapivc1dec"}) { + changeRank(name, GST_RANK_PRIMARY + 1); + } + break; + case ForceVideoDecoderNVIDIA: + for (const char *name : {"nvh265dec", "nvh265sldec", "nvh264dec", "nvh264sldec"}) { + changeRank(name, GST_RANK_PRIMARY + 1); + } + break; + case ForceVideoDecoderDirectX3D: + for (const char *name : {"d3d11vp9dec", "d3d11h265dec", "d3d11h264dec"}) { + changeRank(name, GST_RANK_PRIMARY + 1); + } + break; + case ForceVideoDecoderVideoToolbox: + changeRank("vtdec", GST_RANK_PRIMARY + 1); + break; + default: + qCWarning(GStreamerLog) << "Can't handle decode option:" << option; + break; + } +} - GstElement* sink; +void *createVideoSink(QObject *parent, QQuickItem *widget) +{ + Q_UNUSED(parent) - if ((sink = gst_element_factory_make("qgcvideosinkbin", nullptr)) != nullptr) { + GstElement *const sink = gst_element_factory_make("qgcvideosinkbin", NULL); + if (sink) { g_object_set(sink, "widget", widget, NULL); } else { qCCritical(GStreamerLog) << "gst_element_factory_make('qgcvideosinkbin') failed"; @@ -272,17 +278,16 @@ GStreamer::createVideoSink(QObject* parent, QQuickItem* widget) return sink; } -void -GStreamer::releaseVideoSink(void* sink) +void releaseVideoSink(void *sink) { - if (sink != nullptr) { + if (sink) { gst_object_unref(GST_ELEMENT(sink)); } } -VideoReceiver* -GStreamer::createVideoReceiver(QObject* parent) +VideoReceiver *createVideoReceiver(QObject *parent) { - Q_UNUSED(parent) - return new GstVideoReceiver(nullptr); + return new GstVideoReceiver(parent); } + +} // namespace GStreamer diff --git a/src/VideoManager/VideoReceiver/GStreamer/GStreamer.h b/src/VideoManager/VideoReceiver/GStreamer/GStreamer.h index 71b185b18f5..f70cad0b120 100644 --- a/src/VideoManager/VideoReceiver/GStreamer/GStreamer.h +++ b/src/VideoManager/VideoReceiver/GStreamer/GStreamer.h @@ -10,20 +10,27 @@ #pragma once #include -#include - -#include "Settings/VideoDecoderOptions.h" Q_DECLARE_LOGGING_CATEGORY(GStreamerLog) Q_DECLARE_LOGGING_CATEGORY(GStreamerAPILog) class VideoReceiver; +class QQuickItem; + +namespace GStreamer +{ +enum VideoDecoderOptions { + ForceVideoDecoderDefault = 0, + ForceVideoDecoderSoftware, + ForceVideoDecoderNVIDIA, + ForceVideoDecoderVAAPI, + ForceVideoDecoderDirectX3D, + ForceVideoDecoderVideoToolbox, +}; -class GStreamer { -public: - static void blacklist(VideoDecoderOptions option); - static void initialize(int argc, char* argv[], int debuglevel); - static void* createVideoSink(QObject* parent, QQuickItem* widget); - static void releaseVideoSink(void* sink); - static VideoReceiver* createVideoReceiver(QObject* parent); +void initialize(); +void blacklist(VideoDecoderOptions option); +void *createVideoSink(QObject *parent, QQuickItem *widget); +void releaseVideoSink(void *sink); +VideoReceiver *createVideoReceiver(QObject *parent = nullptr); }; diff --git a/src/VideoManager/VideoReceiver/GStreamer/gst_ios_init.m b/src/VideoManager/VideoReceiver/GStreamer/gst_ios_init.m index 6158e1bca87..c0f54f190ff 100644 --- a/src/VideoManager/VideoReceiver/GStreamer/gst_ios_init.m +++ b/src/VideoManager/VideoReceiver/GStreamer/gst_ios_init.m @@ -22,59 +22,61 @@ extern void G_PASTE(g_io_module_, G_PASTE(name, _load_static)) (void) void gst_ios_pre_init(void) { - NSString *resources = [[NSBundle mainBundle] resourcePath]; - NSString *tmp = NSTemporaryDirectory(); - NSString *cache = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Caches"]; - NSString *docs = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"]; - - const gchar *resources_dir = [resources UTF8String]; - const gchar *tmp_dir = [tmp UTF8String]; - const gchar *cache_dir = [cache UTF8String]; - const gchar *docs_dir = [docs UTF8String]; - gchar *ca_certificates; - - g_setenv ("TMP", tmp_dir, TRUE); - g_setenv ("TEMP", tmp_dir, TRUE); - g_setenv ("TMPDIR", tmp_dir, TRUE); - g_setenv ("XDG_RUNTIME_DIR", resources_dir, TRUE); - g_setenv ("XDG_CACHE_HOME", cache_dir, TRUE); - - g_setenv ("HOME", docs_dir, TRUE); - g_setenv ("XDG_DATA_DIRS", resources_dir, TRUE); - g_setenv ("XDG_CONFIG_DIRS", resources_dir, TRUE); - g_setenv ("XDG_CONFIG_HOME", cache_dir, TRUE); - g_setenv ("XDG_DATA_HOME", resources_dir, TRUE); - g_setenv ("FONTCONFIG_PATH", resources_dir, TRUE); + const NSString *const resources = [[NSBundle mainBundle] resourcePath]; + const NSString *const tmp = NSTemporaryDirectory(); + const NSString *const cache = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Caches"]; + const NSString *const docs = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"]; - ca_certificates = g_build_filename (resources_dir, "ssl", "certs", "ca-certificates.crt", NULL); - g_setenv ("CA_CERTIFICATES", ca_certificates, TRUE); - g_free (ca_certificates); + const gchar *const resources_dir = [resources UTF8String]; + const gchar *const tmp_dir = [tmp UTF8String]; + const gchar *const cache_dir = [cache UTF8String]; + const gchar *const docs_dir = [docs UTF8String]; + + g_setenv("TMP", tmp_dir, TRUE); + g_setenv("TEMP", tmp_dir, TRUE); + g_setenv("TMPDIR", tmp_dir, TRUE); + g_setenv("XDG_RUNTIME_DIR", resources_dir, TRUE); + g_setenv("XDG_CACHE_HOME", cache_dir, TRUE); + + g_setenv("HOME", docs_dir, TRUE); + g_setenv("XDG_DATA_DIRS", resources_dir, TRUE); + g_setenv("XDG_CONFIG_DIRS", resources_dir, TRUE); + g_setenv("XDG_CONFIG_HOME", cache_dir, TRUE); + g_setenv("XDG_DATA_HOME", resources_dir, TRUE); + g_setenv("FONTCONFIG_PATH", resources_dir, TRUE); + + gchar *const ca_certificates = g_build_filename(resources_dir, "ssl", "certs", "ca-certificates.crt", NULL); + g_setenv("CA_CERTIFICATES", ca_certificates, TRUE); + g_free(ca_certificates); } -void gst_ios_post_init(void) +void gst_ios_post_init() { - GstPluginFeature *plugin; - GstRegistry *reg; - /* Lower the ranks of filesrc and giosrc so iosavassetsrc is - * tried first in gst_element_make_from_uri() for file:// */ + GstPluginFeature *plugin; + GstRegistry *reg; + /* Lower the ranks of filesrc and giosrc so iosavassetsrc is + * tried first in gst_element_make_from_uri() for file:// */ -#if defined(GST_IOS_GIO_MODULE_GNUTLS) - GST_G_IO_MODULE_LOAD(gnutls); -#endif + #if defined(GST_IOS_GIO_MODULE_GNUTLS) + GST_G_IO_MODULE_LOAD(gnutls); + #endif - reg = gst_registry_get(); - plugin = gst_registry_lookup_feature(reg, "filesrc"); - if (plugin) - gst_plugin_feature_set_rank(plugin, GST_RANK_SECONDARY); - plugin = gst_registry_lookup_feature(reg, "giosrc"); - if (plugin) - gst_plugin_feature_set_rank(plugin, GST_RANK_SECONDARY-1); - if (!gst_registry_lookup_feature(reg, "vtdec_hw")) { - /* Usually there is no vtdec_hw plugin on iOS - in that case - * we are increasing vtdec rank since VideoToolbox on iOS - * tries to use hardware implementation first */ - plugin = gst_registry_lookup_feature(reg, "vtdec"); - if (plugin) - gst_plugin_feature_set_rank(plugin, GST_RANK_PRIMARY + 1); + reg = gst_registry_get(); + plugin = gst_registry_lookup_feature(reg, "filesrc"); + if (plugin) { + gst_plugin_feature_set_rank(plugin, GST_RANK_SECONDARY); + } + plugin = gst_registry_lookup_feature(reg, "giosrc"); + if (plugin) { + gst_plugin_feature_set_rank(plugin, GST_RANK_SECONDARY-1); + } + if (!gst_registry_lookup_feature(reg, "vtdec_hw")) { + /* Usually there is no vtdec_hw plugin on iOS - in that case + * we are increasing vtdec rank since VideoToolbox on iOS + * tries to use hardware implementation first */ + plugin = gst_registry_lookup_feature(reg, "vtdec"); + if (plugin) { + gst_plugin_feature_set_rank(plugin, GST_RANK_PRIMARY + 1); + } } } diff --git a/src/VideoManager/VideoReceiver/GStreamer/gstqgc.c b/src/VideoManager/VideoReceiver/GStreamer/gstqgc.c deleted file mode 100644 index f8b8cf5d813..00000000000 --- a/src/VideoManager/VideoReceiver/GStreamer/gstqgc.c +++ /dev/null @@ -1,40 +0,0 @@ -/**************************************************************************** - * - * (c) 2009-2020 QGROUNDCONTROL PROJECT - * - * QGroundControl is licensed according to the terms in the file - * COPYING.md in the root of the source code directory. - * - ****************************************************************************/ - -/** - * @file - * @brief GStreamer plugin for QGC's Video Receiver - * @author Andrew Voznyts - * @author Tomaz Canabrava - */ - -#include - -gboolean gst_qgc_video_sink_bin_plugin_init(GstPlugin *plugin); - -static gboolean -plugin_init(GstPlugin* plugin) -{ - if (!gst_qgc_video_sink_bin_plugin_init(plugin)) { - return FALSE; - } - - return TRUE; -} - -#define PACKAGE "QGC Video Receiver" -#define PACKAGE_VERSION "current" -#define GST_LICENSE "LGPL" -#define GST_PACKAGE_NAME "GStreamer plugin for QGC's Video Receiver" -#define GST_PACKAGE_ORIGIN "http://qgroundcontrol.com/" - -GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, - qgc, "QGC Video Receiver plugin", - plugin_init, PACKAGE_VERSION, - GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN) diff --git a/src/VideoManager/VideoReceiver/GStreamer/gstqgc.cc b/src/VideoManager/VideoReceiver/GStreamer/gstqgc.cc new file mode 100644 index 00000000000..f2a3fe1d357 --- /dev/null +++ b/src/VideoManager/VideoReceiver/GStreamer/gstqgc.cc @@ -0,0 +1,35 @@ +/**************************************************************************** + * + * (c) 2009-2024 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "gstqgcelements.h" + +static gboolean +plugin_init(GstPlugin *plugin) +{ + gboolean ret = FALSE; + + // ret |= GST_ELEMENT_REGISTER(qgcvideosinkbin, plugin); + ret |= gst_element_register_qgcvideosinkbin(plugin); + + return ret; +} + +#define GST_PACKAGE_NAME "GStreamer plugin for QGC's Video Receiver" +#define GST_PACKAGE_ORIGIN "https://qgroundcontrol.com/" +#define GST_LICENSE "LGPL" +#define PACKAGE "QGC Video Receiver" +#define PACKAGE_VERSION "current" + +GST_PLUGIN_DEFINE( + GST_VERSION_MAJOR, GST_VERSION_MINOR, + qgc, + "QGC Video Receiver Plugin", + plugin_init, + PACKAGE_VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN +) diff --git a/src/VideoManager/VideoReceiver/GStreamer/gstqgcelement.cc b/src/VideoManager/VideoReceiver/GStreamer/gstqgcelement.cc new file mode 100644 index 00000000000..eaba411bc33 --- /dev/null +++ b/src/VideoManager/VideoReceiver/GStreamer/gstqgcelement.cc @@ -0,0 +1,10 @@ +#include "gstqgcelements.h" + +void +qgc_element_init(GstPlugin *plugin) +{ + static gsize res = FALSE; + if (g_once_init_enter(&res)) { + g_once_init_leave(&res, TRUE); + } +} diff --git a/src/VideoManager/VideoReceiver/GStreamer/gstqgcelements.h b/src/VideoManager/VideoReceiver/GStreamer/gstqgcelements.h new file mode 100644 index 00000000000..cf5d283c11f --- /dev/null +++ b/src/VideoManager/VideoReceiver/GStreamer/gstqgcelements.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +G_BEGIN_DECLS + +void qgc_element_init(GstPlugin *plugin); + +gboolean gst_element_register_qgcvideosinkbin(GstPlugin * plugin); +// GST_ELEMENT_REGISTER_DECLARE(qgcvideosinkbin); + +G_END_DECLS diff --git a/src/VideoManager/VideoReceiver/GStreamer/gstqgcvideosinkbin.c b/src/VideoManager/VideoReceiver/GStreamer/gstqgcvideosinkbin.cc similarity index 54% rename from src/VideoManager/VideoReceiver/GStreamer/gstqgcvideosinkbin.c rename to src/VideoManager/VideoReceiver/GStreamer/gstqgcvideosinkbin.cc index 4e26e158f02..ca957f0315f 100644 --- a/src/VideoManager/VideoReceiver/GStreamer/gstqgcvideosinkbin.c +++ b/src/VideoManager/VideoReceiver/GStreamer/gstqgcvideosinkbin.cc @@ -7,38 +7,30 @@ * ****************************************************************************/ -/** - * @file - * @brief GStreamer plugin for QGC's Video Receiver - * @author Andrew Voznyts - * @author Tomaz Canabrava - */ - -#include -#include -#include - -GST_DEBUG_CATEGORY_STATIC(gst_qgc_video_sink_bin_debug); +#include "gstqgcvideosinkbin.h" +#include "gstqgcelements.h" + #define GST_CAT_DEFAULT gst_qgc_video_sink_bin_debug +GST_DEBUG_CATEGORY_STATIC(GST_CAT_DEFAULT); -typedef struct _GstQgcVideoSinkElement GstQgcVideoSinkElement; +static void gst_qgc_video_sink_bin_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); +static void gst_qgc_video_sink_bin_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); +static void gst_qgc_video_sink_bin_dispose(GObject *object); -typedef struct _GstQgcVideoSinkBin { - GstBin bin; - GstElement* glupload; - GstElement* qmlglsink; -} GstQgcVideoSinkBin; +static gboolean gst_qgc_video_sink_bin_sink_pad_query(GstPad *pad, GstObject *parent, GstQuery *query); -typedef struct _GstQgcVideoSinkBinClass { - GstBinClass parent_class; -} GstQgcVideoSinkBinClass; +#define DEFAULT_ENABLE_LAST_SAMPLE TRUE +#define DEFAULT_FORCE_ASPECT_RATIO TRUE +#define DEFAULT_PAR_N 0 +#define DEFAULT_PAR_D 1 +#define DEFAULT_SYNC TRUE -#define GST_TYPE_VIDEO_SINK_BIN (_vsb_get_type()) -#define GST_QGC_VIDEO_SINK_BIN_CAST(obj) ((GstQgcVideoSinkBin *)(obj)) -#define GST_QGC_VIDEO_SINK_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_VIDEO_SINK_BIN, GstQgcVideoSinkBin)) -#define GST_QGC_VIDEO_SINK_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_VIDEO_SINK_BIN, GstQgcVideoSinkBinClass)) -#define GST_IS_VIDEO_SINK_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_VIDEO_SINK_BIN)) -#define GST_IS_VIDEO_SINK_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_VIDEO_SINK_BIN)) +#define PROP_ENABLE_LAST_SAMPLE_NAME "enable-last-sample" +#define PROP_LAST_SAMPLE_NAME "last-sample" +#define PROP_WIDGET_NAME "widget" +#define PROP_FORCE_ASPECT_RATIO_NAME "force-aspect-ratio" +#define PROP_PIXEL_ASPECT_RATIO_NAME "pixel-aspect-ratio" +#define PROP_SYNC_NAME "sync" enum { PROP_0, @@ -50,191 +42,207 @@ enum { PROP_SYNC, }; -#define PROP_ENABLE_LAST_SAMPLE_NAME "enable-last-sample" -#define PROP_LAST_SAMPLE_NAME "last-sample" -#define PROP_WIDGET_NAME "widget" -#define PROP_FORCE_ASPECT_RATIO_NAME "force-aspect-ratio" -#define PROP_PIXEL_ASPECT_RATIO_NAME "pixel-aspect-ratio" -#define PROP_SYNC_NAME "sync" +#define gst_qgc_video_sink_bin_parent_class parent_class +G_DEFINE_TYPE_WITH_CODE(GstQgcVideoSinkBin, gst_qgc_video_sink_bin, GST_TYPE_BIN, GST_DEBUG_CATEGORY_INIT(GST_CAT_DEFAULT, "qgcsinkbin", 0, "QGC Video Sink Bin")); -#define DEFAULT_ENABLE_LAST_SAMPLE TRUE -#define DEFAULT_FORCE_ASPECT_RATIO TRUE -#define DEFAULT_PAR_N 0 -#define DEFAULT_PAR_D 1 -#define DEFAULT_SYNC TRUE +// GST_ELEMENT_REGISTER_DEFINE_WITH_CODE(qgcvideosinkbin, "qgcvideosinkbin", GST_RANK_NONE, GST_TYPE_QGC_VIDEO_SINK_BIN, qgc_element_init(plugin)); +G_BEGIN_DECLS +gboolean G_PASTE(gst_element_register_, qgcvideosinkbin)(GstPlugin *plugin) +{ + { + { + qgc_element_init(plugin); + } + } + return gst_element_register(plugin, "qgcvideosinkbin", GST_RANK_NONE, (gst_qgc_video_sink_bin_get_type())); +} +G_END_DECLS; -static GstBinClass *parent_class; +static void +gst_qgc_video_sink_bin_class_init(GstQgcVideoSinkBinClass *klass) +{ + GObjectClass *const gobject_klass = G_OBJECT_CLASS(klass); + GstElementClass *const gstelement_klass = GST_ELEMENT_CLASS(klass); -static void _vsb_init(GTypeInstance *instanceData, void *vsbVoid); -static void _vsb_dispose(GObject *object); -static void _vsb_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); -static void _vsb_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); -static GType _vsb_get_type(void); -static void _vsb_class_init(void *klass, void *classData); + parent_class = g_type_class_peek_parent(GST_BIN_CLASS(klass)); -static gboolean -_vsb_sink_pad_query(GstPad* pad, GstObject* parent, GstQuery* query) -{ - GstQgcVideoSinkBin *vsb; - GstElement* element; - - vsb = GST_QGC_VIDEO_SINK_BIN(parent); + gobject_klass->dispose = gst_qgc_video_sink_bin_dispose; + gobject_klass->get_property = gst_qgc_video_sink_bin_get_property; + gobject_klass->set_property = gst_qgc_video_sink_bin_set_property; - switch (GST_QUERY_TYPE(query)) { - case GST_QUERY_CAPS: - element = vsb->glupload; - break; - case GST_QUERY_CONTEXT: - element = vsb->qmlglsink; - break; - default: - return gst_pad_query_default (pad, parent, query); - } + g_object_class_install_property(gobject_klass, PROP_ENABLE_LAST_SAMPLE, + g_param_spec_boolean(PROP_ENABLE_LAST_SAMPLE_NAME, "Enable Last Buffer", + "Enable the last-sample property", DEFAULT_ENABLE_LAST_SAMPLE, + (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); - if (element == NULL) { - GST_ERROR_OBJECT(vsb, "No element found"); - return FALSE; - } + g_object_class_install_property(gobject_klass, PROP_LAST_SAMPLE, + g_param_spec_boxed(PROP_LAST_SAMPLE_NAME, "Last Sample", + "The last sample received in the sink", GST_TYPE_SAMPLE, + (GParamFlags)(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); - GstPad* sinkpad = gst_element_get_static_pad(element, "sink"); + g_object_class_install_property(gobject_klass, PROP_WIDGET, + g_param_spec_pointer(PROP_WIDGET_NAME, "QQuickItem", + "The QQuickItem to place in the object hierarchy", + (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); - if (sinkpad == NULL) { - GST_ERROR_OBJECT(vsb, "No sink pad found"); - return FALSE; - } + g_object_class_install_property(gobject_klass, PROP_FORCE_ASPECT_RATIO, + g_param_spec_boolean(PROP_FORCE_ASPECT_RATIO_NAME, "Force aspect ratio", + "When enabled, scaling will respect original aspect ratio", + DEFAULT_FORCE_ASPECT_RATIO, + (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); - const gboolean ret = gst_pad_query(sinkpad, query); + g_object_class_install_property(gobject_klass, PROP_PIXEL_ASPECT_RATIO, + gst_param_spec_fraction(PROP_PIXEL_ASPECT_RATIO_NAME, "Pixel Aspect Ratio", + "The pixel aspect ratio of the device", DEFAULT_PAR_N, DEFAULT_PAR_D, + G_MAXINT, 1, 1, 1, + (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); - gst_object_unref(sinkpad); - sinkpad = NULL; + g_object_class_install_property(gobject_klass, PROP_SYNC, + g_param_spec_boolean(PROP_SYNC_NAME, "Sync", + "Sync on the clock", DEFAULT_SYNC, + (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); - return ret; + gst_element_class_set_static_metadata(gstelement_klass, + "QGC Video Sink Bin", "Sink/Video/Bin", + "Video rendering for QGC", + "Andrew Voznytsa , Tomaz Canabrava "); } static void -_vsb_init(GTypeInstance *instanceData, void *vsbVoid) +gst_qgc_video_sink_bin_init(GstQgcVideoSinkBin *vsb) { - Q_UNUSED(vsbVoid); - - GstQgcVideoSinkBin *vsb; - vsb = (GstQgcVideoSinkBin *)instanceData; - - gboolean initialized = FALSE; - GstElement* glcolorconvert = NULL; - GstPad* pad = NULL; - - do { - if ((vsb->glupload = gst_element_factory_make("glupload", NULL)) == NULL) { - GST_ERROR_OBJECT(vsb, "gst_element_factory_make('glupload') failed"); - break; - } - - if ((vsb->qmlglsink = gst_element_factory_make("qml6glsink", NULL)) == NULL) { - GST_ERROR_OBJECT(vsb, "gst_element_factory_make('qml6glsink') failed"); - break; - } - - if ((glcolorconvert = gst_element_factory_make("glcolorconvert", NULL)) == NULL) { - GST_ERROR_OBJECT(vsb, "gst_element_factory_make('glcolorconvert' failed)"); - break; - } - - if ((pad = gst_element_get_static_pad(vsb->glupload, "sink")) == NULL) { - GST_ERROR_OBJECT(vsb, "gst_element_get_static_pad(glupload, 'sink') failed"); - break; - } + gboolean initialized = FALSE; + gboolean ret = FALSE; + GstElement *glcolorconvert = NULL; + GstPad *pad = NULL; + GstPad *ghostpad = NULL; + + vsb->glupload = gst_element_factory_make("glupload", NULL); + if (!vsb->glupload) { + GST_ERROR_OBJECT(vsb, "gst_element_factory_make('glupload') failed"); + goto init_failed; + } - gst_object_ref(vsb->glupload); - gst_object_ref(vsb->qmlglsink); + vsb->qmlglsink = gst_element_factory_make("qml6glsink", NULL); + if (!vsb->qmlglsink) { + GST_ERROR_OBJECT(vsb, "gst_element_factory_make('qml6glsink') failed"); + goto init_failed; + } - gst_bin_add_many(GST_BIN(vsb), vsb->glupload, glcolorconvert, vsb->qmlglsink, NULL); + glcolorconvert = gst_element_factory_make("glcolorconvert", NULL); + if (!glcolorconvert) { + GST_ERROR_OBJECT(vsb, "gst_element_factory_make('glcolorconvert' failed)"); + goto init_failed; + } - gboolean ret = gst_element_link_many(vsb->glupload, glcolorconvert, vsb->qmlglsink, NULL); + pad = gst_element_get_static_pad(vsb->glupload, "sink"); + if (!pad) { + GST_ERROR_OBJECT(vsb, "gst_element_get_static_pad(glupload, 'sink') failed"); + goto init_failed; + } - glcolorconvert = NULL; + (void) gst_object_ref(vsb->glupload); + (void) gst_object_ref(vsb->qmlglsink); - if (!ret) { - GST_ERROR_OBJECT(vsb, "gst_element_link_many() failed"); - break; - } + gst_bin_add_many(GST_BIN(vsb), vsb->glupload, glcolorconvert, vsb->qmlglsink, NULL); - GstPad* ghostpad; + ret = gst_element_link_many(vsb->glupload, glcolorconvert, vsb->qmlglsink, NULL); + glcolorconvert = NULL; + if (!ret) { + GST_ERROR_OBJECT(vsb, "gst_element_link_many() failed"); + goto init_failed; + } - if ((ghostpad = gst_ghost_pad_new("sink", pad)) == NULL) { - GST_ERROR_OBJECT(vsb, "gst_ghost_pad_new('sink') failed"); - break; - } + ghostpad = gst_ghost_pad_new("sink", pad); + if (!ghostpad) { + GST_ERROR_OBJECT(vsb, "gst_ghost_pad_new('sink') failed"); + goto init_failed; + } - gst_pad_set_query_function(ghostpad, _vsb_sink_pad_query); + gst_pad_set_query_function(ghostpad, gst_qgc_video_sink_bin_sink_pad_query); - if (!gst_element_add_pad(GST_ELEMENT(vsb), ghostpad)) { - GST_ERROR_OBJECT(vsb, "gst_element_add_pad() failed"); - break; - } + if (!gst_element_add_pad(GST_ELEMENT(vsb), ghostpad)) { + GST_ERROR_OBJECT(vsb, "gst_element_add_pad() failed"); + goto init_failed; + } - initialized = TRUE; - } while(0); + initialized = TRUE; - if (pad != NULL) { +init_failed: + if (pad) { gst_object_unref(pad); pad = NULL; } - if (glcolorconvert != NULL) { + if (glcolorconvert) { gst_object_unref(glcolorconvert); glcolorconvert = NULL; } if (!initialized) { - if (vsb->qmlglsink != NULL) { + if (vsb->qmlglsink) { gst_object_unref(vsb->qmlglsink); vsb->qmlglsink = NULL; } - if (vsb->glupload != NULL) { + if (vsb->glupload) { gst_object_unref(vsb->glupload); vsb->glupload = NULL; } } } -static void -_vsb_dispose(GObject *object) +static gboolean +gst_qgc_video_sink_bin_sink_pad_query(GstPad *pad, GstObject *parent, GstQuery *query) { - GstQgcVideoSinkBin *vsb; + GstQgcVideoSinkBin *const vsb = GST_QGC_VIDEO_SINK_BIN(parent); + GstElement *element = NULL; - vsb = GST_QGC_VIDEO_SINK_BIN(object); + switch (GST_QUERY_TYPE(query)) { + case GST_QUERY_CAPS: + element = vsb->glupload; + break; + case GST_QUERY_CONTEXT: + element = vsb->qmlglsink; + break; + default: + return gst_pad_query_default(pad, parent, query); + } - if (vsb->qmlglsink != NULL) { - gst_object_unref(vsb->qmlglsink); - vsb->qmlglsink = NULL; + if (!element) { + GST_ERROR_OBJECT(vsb, "No element found"); + return FALSE; } - if (vsb->glupload != NULL) { - gst_object_unref(vsb->glupload); - vsb->glupload = NULL; + GstPad *sinkpad = gst_element_get_static_pad(element, "sink"); + if (!sinkpad) { + GST_ERROR_OBJECT(vsb, "No sink pad found"); + return FALSE; } - G_OBJECT_CLASS(parent_class)->dispose(object); + const gboolean ret = gst_pad_query(sinkpad, query); + + gst_object_unref(sinkpad); + sinkpad = NULL; + + return ret; } static void -_vsb_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +gst_qgc_video_sink_bin_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { - GstQgcVideoSinkBin *vsb; - - vsb = GST_QGC_VIDEO_SINK_BIN(object); + GstQgcVideoSinkBin *const vsb = GST_QGC_VIDEO_SINK_BIN(object); switch (prop_id) { case PROP_ENABLE_LAST_SAMPLE: - do { + if (vsb->qmlglsink) { gboolean enable = FALSE; g_object_get(G_OBJECT(vsb->qmlglsink), PROP_ENABLE_LAST_SAMPLE_NAME, &enable, NULL); g_value_set_boolean(value, enable); - } while(0); + } break; case PROP_LAST_SAMPLE: - do { + if (vsb->qmlglsink) { GstSample *sample = NULL; g_object_get(G_OBJECT(vsb->qmlglsink), PROP_LAST_SAMPLE_NAME, &sample, NULL); gst_value_set_sample(value, sample); @@ -242,35 +250,35 @@ _vsb_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *psp gst_sample_unref(sample); sample = NULL; } - } while(0); + } break; case PROP_WIDGET: - do { + if (vsb->qmlglsink) { gpointer widget = NULL; g_object_get(G_OBJECT(vsb->qmlglsink), PROP_WIDGET_NAME, &widget, NULL); g_value_set_pointer(value, widget); - } while(0); + } break; case PROP_FORCE_ASPECT_RATIO: - do { + if (vsb->qmlglsink) { gboolean enable = FALSE; g_object_get(G_OBJECT(vsb->qmlglsink), PROP_FORCE_ASPECT_RATIO_NAME, &enable, NULL); g_value_set_boolean(value, enable); - } while(0); + } break; case PROP_PIXEL_ASPECT_RATIO: - do { + if (vsb->qmlglsink) { gint num = 0, den = 1; g_object_get(G_OBJECT(vsb->qmlglsink), PROP_PIXEL_ASPECT_RATIO_NAME, &num, &den, NULL); gst_value_set_fraction(value, num, den); - } while(0); + } break; case PROP_SYNC: - do { + if (vsb->qmlglsink) { gboolean enable = FALSE; g_object_get(G_OBJECT(vsb->qmlglsink), PROP_SYNC_NAME, &enable, NULL); g_value_set_boolean(value, enable); - } while(0); + } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); @@ -279,11 +287,9 @@ _vsb_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *psp } static void -_vsb_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +gst_qgc_video_sink_bin_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { - GstQgcVideoSinkBin *vsb; - - vsb = GST_QGC_VIDEO_SINK_BIN(object); + GstQgcVideoSinkBin *const vsb = GST_QGC_VIDEO_SINK_BIN(object); switch (prop_id) { case PROP_ENABLE_LAST_SAMPLE: @@ -307,88 +313,20 @@ _vsb_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpe } } -static GType -_vsb_get_type(void) -{ - static GType _vsb_type = 0; - - if (!_vsb_type) { - static const GTypeInfo _vsb_info = { - sizeof(GstQgcVideoSinkBinClass), - NULL, - NULL, - (GClassInitFunc)_vsb_class_init, - NULL, - NULL, - sizeof(GstQgcVideoSinkBin), - 0, - (GInstanceInitFunc)_vsb_init, - NULL}; - - _vsb_type = g_type_register_static(GST_TYPE_BIN, "GstQgcVideoSinkBin", &_vsb_info, (GTypeFlags)0); - } - - return _vsb_type; -} - static void -_vsb_class_init(void *klass, void *classData) +gst_qgc_video_sink_bin_dispose(GObject *object) { - Q_UNUSED(classData); - - GObjectClass *gobject_klass; - GstElementClass *gstelement_klass; + GstQgcVideoSinkBin *const vsb = GST_QGC_VIDEO_SINK_BIN(object); - gobject_klass = (GObjectClass *)klass; - gstelement_klass = (GstElementClass *)klass; - - parent_class = g_type_class_peek_parent(klass); - - gobject_klass->dispose = _vsb_dispose; - gobject_klass->get_property = _vsb_get_property; - gobject_klass->set_property = _vsb_set_property; - - g_object_class_install_property(gobject_klass, PROP_ENABLE_LAST_SAMPLE, - g_param_spec_boolean(PROP_ENABLE_LAST_SAMPLE_NAME, "Enable Last Buffer", - "Enable the last-sample property", DEFAULT_ENABLE_LAST_SAMPLE, - (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); - - g_object_class_install_property(gobject_klass, PROP_LAST_SAMPLE, - g_param_spec_boxed(PROP_LAST_SAMPLE_NAME, "Last Sample", - "The last sample received in the sink", GST_TYPE_SAMPLE, - (GParamFlags)(G_PARAM_READABLE | G_PARAM_STATIC_STRINGS))); - - g_object_class_install_property(gobject_klass, PROP_WIDGET, - g_param_spec_pointer(PROP_WIDGET_NAME, "QQuickItem", - "The QQuickItem to place in the object hierarchy", - (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); - - g_object_class_install_property(gobject_klass, PROP_FORCE_ASPECT_RATIO, - g_param_spec_boolean(PROP_FORCE_ASPECT_RATIO_NAME, "Force aspect ratio", - "When enabled, scaling will respect original aspect ratio", - DEFAULT_FORCE_ASPECT_RATIO, - (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); - - g_object_class_install_property(gobject_klass, PROP_PIXEL_ASPECT_RATIO, - gst_param_spec_fraction(PROP_PIXEL_ASPECT_RATIO_NAME, "Pixel Aspect Ratio", - "The pixel aspect ratio of the device", DEFAULT_PAR_N, DEFAULT_PAR_D, - G_MAXINT, 1, 1, 1, - (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); - - g_object_class_install_property(gobject_klass, PROP_SYNC, - g_param_spec_boolean(PROP_SYNC_NAME, "Sync", - "Sync on the clock", DEFAULT_SYNC, - (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + if (vsb->qmlglsink) { + gst_object_unref(vsb->qmlglsink); + vsb->qmlglsink = NULL; + } - gst_element_class_set_static_metadata(gstelement_klass, - "QGC Video Sink Bin", "Sink/Video/Bin", - "Video rendering for QGC", - "Andrew Voznytsa , Tomaz Canabrava "); -} + if (vsb->glupload) { + gst_object_unref(vsb->glupload); + vsb->glupload = NULL; + } -gboolean -gst_qgc_video_sink_bin_plugin_init(GstPlugin *plugin) -{ - GST_DEBUG_CATEGORY_INIT(gst_qgc_video_sink_bin_debug, "qgcvideosinkbin", 0, "QGC Video Sink Bin"); - return gst_element_register(plugin, "qgcvideosinkbin", GST_RANK_NONE, GST_TYPE_VIDEO_SINK_BIN); + G_OBJECT_CLASS(parent_class)->dispose(object); } diff --git a/src/VideoManager/VideoReceiver/GStreamer/gstqgcvideosinkbin.h b/src/VideoManager/VideoReceiver/GStreamer/gstqgcvideosinkbin.h new file mode 100644 index 00000000000..bce8bdabf12 --- /dev/null +++ b/src/VideoManager/VideoReceiver/GStreamer/gstqgcvideosinkbin.h @@ -0,0 +1,36 @@ +/**************************************************************************** + * + * (c) 2009-2024 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#pragma once + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_QGC_VIDEO_SINK_BIN (gst_qgc_video_sink_bin_get_type()) +G_DECLARE_FINAL_TYPE (GstQgcVideoSinkBin, gst_qgc_video_sink_bin, GST, QGC_VIDEO_SINK_BIN, GstBin) +#define GST_QGC_VIDEO_SINK_BIN_CAST(obj) ((GstQgcVideoSinkBin *)(obj)) +#define GST_QGC_VIDEO_SINK_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_QGC_VIDEO_SINK_BIN, GstQgcVideoSinkBin)) +#define GST_QGC_VIDEO_SINK_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_QGC_VIDEO_SINK_BIN, GstQgcVideoSinkBinClass)) +#define GST_IS_QGC_VIDEO_SINK_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_QGC_VIDEO_SINK_BIN)) +#define GST_IS_QGC_VIDEO_SINK_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_QGC_VIDEO_SINK_BIN)) + +struct _GstQgcVideoSinkBin { + GstBin bin; + GstElement *glupload; + GstElement *qmlglsink; +}; + +struct _GstQgcVideoSinkBinClass { + GstBinClass parent_class; +}; + +GstQgcVideoSinkBin* gst_qgc_video_sink_bin_new(void); + +G_END_DECLS diff --git a/src/VideoManager/VideoReceiver/GStreamer/gstqml6gl/CMakeLists.txt b/src/VideoManager/VideoReceiver/GStreamer/gstqml6gl/CMakeLists.txt index f4e8fb30b53..ae73d784916 100644 --- a/src/VideoManager/VideoReceiver/GStreamer/gstqml6gl/CMakeLists.txt +++ b/src/VideoManager/VideoReceiver/GStreamer/gstqml6gl/CMakeLists.txt @@ -1,5 +1,5 @@ find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick) -find_package(GStreamer REQUIRED COMPONENTS Allocators Audio Codecparsers Controller Fft Mpegts Net Pbutils Riff Rtp Rtsp Sdp Tag OPTIONAL_COMPONENTS Photography Prototypes Va X11 EGL Wayland) +find_package(GStreamer REQUIRED COMPONENTS Allocators Audio Codecparsers Controller Fft Mpegts Net Pbutils Riff Rtp Rtsp Sdp Tag OPTIONAL_COMPONENTS Photography GlPrototypes Va GlX11 GlEGL GlWayland) qt_add_library(gstqml6gl STATIC) @@ -32,8 +32,8 @@ if(GStreamer_Photography_FOUND) target_link_libraries(gstqml6gl PUBLIC GStreamer::Photography) endif() -if(GStreamer_Prototypes_FOUND) - target_link_libraries(gstqml6gl PUBLIC GStreamer::Prototypes) +if(GStreamer_GlPrototypes_FOUND) + target_link_libraries(gstqml6gl PUBLIC GStreamer::GlPrototypes) endif() if(GStreamer_Va_FOUND) @@ -45,7 +45,7 @@ endif() # TODO: Don't Download & Build if gstreamer1.0-qt6 was found if(GStreamer_VERSION VERSION_GREATER_EQUAL 1.22) FetchContent_Declare(gstreamer_good_plugins - # URL https://gitlab.freedesktop.org/gstreamer/gstreamer/-/archive/${GStreamer_VERSION}/gstreamer-${GStreamer_VERSION}.zip?path=subprojects/gst-plugins-good/ext/qt6 + # URL https://gitlab.freedesktop.org/gstreamer/gstreamer/-/archive/${GST_PLUGINS_VERSION}/gstreamer-${GST_PLUGINS_VERSION}.zip?path=subprojects/gst-plugins-good/ext/qt6 URL https://gstreamer.freedesktop.org/src/gst-plugins-good/gst-plugins-good-${GST_PLUGINS_VERSION}.tar.xz DOWNLOAD_EXTRACT_TIMESTAMP true ) @@ -77,18 +77,18 @@ target_include_directories(gstqml6gl PUBLIC ${QGC_GST_QT6_PLUGIN_PATH}) ################################################################################ -if(GStreamer_X11_FOUND) - target_link_libraries(gstqml6gl PUBLIC GStreamer::X11) +if(GStreamer_GlX11_FOUND) + target_link_libraries(gstqml6gl PUBLIC GStreamer::GlX11) target_compile_definitions(gstqml6gl PRIVATE HAVE_QT_X11) endif() -if(GStreamer_EGL_FOUND) - target_link_libraries(gstqml6gl PUBLIC GStreamer::EGL) +if(GStreamer_GlEGL_FOUND) + target_link_libraries(gstqml6gl PUBLIC GStreamer::GlEGL) target_compile_definitions(gstqml6gl PRIVATE HAVE_QT_EGLFS) endif() -if(GStreamer_Wayland_FOUND) - target_link_libraries(gstqml6gl PUBLIC GStreamer::Wayland) +if(GStreamer_GlWayland_FOUND) + target_link_libraries(gstqml6gl PUBLIC GStreamer::GlWayland) target_compile_definitions(gstqml6gl PRIVATE HAVE_QT_WAYLAND) endif() @@ -132,12 +132,6 @@ if(EXISTS "${QGC_GST_QT6_PLUGIN_PATH}/resources.qrc") LIST(APPEND SHADERS ${QGC_GST_QT6_PLUGIN_PATH}/YUV_BIPLANAR.frag) LIST(APPEND OUTPUTS YUV_BIPLANAR.frag.qsb) endif() - if(EXISTS "${QGC_GST_QT6_PLUGIN_PATH}/RGBA_gles.frag") - LIST(APPEND SHADERS ${QGC_GST_QT6_PLUGIN_PATH}/RGBA.frag@glsl,100es,${QGC_GST_QT6_PLUGIN_PATH}/RGBA_gles.frag) - LIST(APPEND OUTPUTS RGBA_gles.frag.qsb) - # file(COPY_FILE RGBA_gles.frag RGBA_gles.frag.qsb) - # LIST(APPEND OUTPUTS RGBA_gles.frag.qsb.external) - endif() qt6_add_shaders(gstqml6gl "gstqml6gl_shaders" PREFIX "/org/freedesktop/gstreamer/qml6" GLSL "100 es,120,330" @@ -146,6 +140,16 @@ if(EXISTS "${QGC_GST_QT6_PLUGIN_PATH}/resources.qrc") OUTPUTS ${OUTPUTS} BATCHABLE ) + + if(EXISTS "${QGC_GST_QT6_PLUGIN_PATH}/RGBA_gles.frag") + file(COPY_FILE "${CMAKE_CURRENT_BINARY_DIR}/.qsb/RGBA.frag.qsb" "${CMAKE_CURRENT_BINARY_DIR}/.qsb/RGBA.frag.qsb.external") + qt6_add_shaders(gstqml6gl "gstqml6gl_shaders1" + PREFIX "/org/freedesktop/gstreamer/qml6" + OUTPUT_TARGETS gstqml6gl_shaders1 + FILES "${CMAKE_CURRENT_BINARY_DIR}/.qsb/RGBA.frag.qsb.external@glsl,100es,${QGC_GST_QT6_PLUGIN_PATH}/RGBA_gles.frag" + OUTPUTS "RGBA.frag.qsb.external" + ) + endif() endif() endif() @@ -163,27 +167,43 @@ endif() ################################################################################ if(LINUX) - install(DIRECTORY ${GSTREAMER_PREFIX}/lib/x86_64-linux-gnu/gstreamer1.0 DESTINATION ${CMAKE_INSTALL_LIBDIR}) - install(DIRECTORY ${GSTREAMER_PREFIX}/lib/x86_64-linux-gnu/gio DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install(DIRECTORY ${GSTREAMER_LIB_PATH}/gstreamer1.0 DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install(DIRECTORY ${GSTREAMER_LIB_PATH}/gio DESTINATION ${CMAKE_INSTALL_LIBDIR}) + get_target_property(LINKED_PLUGINS GStreamer::Plugins INTERFACE_LINK_LIBRARIES) + install(FILES ${LINKED_PLUGINS} DESTINATION ${CMAKE_INSTALL_LIBDIR}/gstreamer-1.0) elseif(WIN32) cmake_path(CONVERT "${GSTREAMER_PREFIX}/bin/*.dll" TO_CMAKE_PATH_LIST GST_WIN_BINS_PATH) file(GLOB GST_WIN_BINS ${GST_WIN_BINS_PATH}) install(FILES ${GST_WIN_BINS} DESTINATION ${CMAKE_INSTALL_BINDIR}) - cmake_path(CONVERT "${GSTREAMER_PREFIX}/lib/gio/modules/*.dll" TO_CMAKE_PATH_LIST GST_GIO_MODULES_PATH) + cmake_path(CONVERT "${GSTREAMER_LIB_PATH}/gio/modules/*.dll" TO_CMAKE_PATH_LIST GST_GIO_MODULES_PATH) file(GLOB GST_GIO_MODULES ${GST_GIO_MODULES_PATH}) install(FILES ${GST_GIO_MODULES} DESTINATION ${CMAKE_INSTALL_LIBDIR}/gio/modules) - cmake_path(CONVERT "${GSTREAMER_PREFIX}/lib/gstreamer-1.0/*.dll" TO_CMAKE_PATH_LIST GST_WIN_PLUGINS_PATH) + cmake_path(CONVERT "${GSTREAMER_PLUGIN_PATH}/*.dll" TO_CMAKE_PATH_LIST GST_WIN_PLUGINS_PATH) file(GLOB GST_WIN_PLUGINS ${GST_WIN_PLUGINS_PATH}) install(FILES ${GST_WIN_PLUGINS} DESTINATION ${CMAKE_INSTALL_LIBDIR}/gstreamer-1.0) cmake_path(CONVERT "${GSTREAMER_PREFIX}/libexec/gstreamer-1.0/*.exe" TO_CMAKE_PATH_LIST GST_HELPER_BINS_PATH) file(GLOB GST_HELPER_BINS ${GST_HELPER_BINS_PATH}) install(FILES ${GST_HELPER_BINS} DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/gstreamer-1.0) +elseif(MACOS) + # cmake_path(CONVERT "${GSTREAMER_PREFIX}/bin/*.dylib" TO_CMAKE_PATH_LIST GST_MACOS_BINS_PATH) + # file(GLOB GST_MACOS_BINS ${GST_MACOS_BINS_PATH}) + # install(FILES ${GST_MACOS_BINS} DESTINATION ${CMAKE_INSTALL_BINDIR}) + + # cmake_path(CONVERT "${GSTREAMER_LIB_PATH}/gio/modules/*.dylib" TO_CMAKE_PATH_LIST GST_GIO_MODULES_PATH) + # file(GLOB GST_GIO_MODULES ${GST_GIO_MODULES_PATH}) + # install(FILES ${GST_GIO_MODULES} DESTINATION ${CMAKE_INSTALL_LIBDIR}/gio/modules) + + # cmake_path(CONVERT "${GSTREAMER_PLUGIN_PATH}/*.dylib" TO_CMAKE_PATH_LIST GST_MACOS_PLUGINS_PATH) + # file(GLOB GST_MACOS_PLUGINS ${GST_MACOS_PLUGINS_PATH}) + # install(FILES ${GST_MACOS_PLUGINS} DESTINATION ${CMAKE_INSTALL_LIBDIR}/gstreamer-1.0) + + # cmake_path(CONVERT "${GSTREAMER_PREFIX}/libexec/gstreamer-1.0/*" TO_CMAKE_PATH_LIST GST_HELPER_BINS_PATH) + # file(GLOB GST_HELPER_BINS ${GST_HELPER_BINS_PATH}) + # install(FILES ${GST_HELPER_BINS} DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/gstreamer-1.0) elseif(ANDROID) - # install(DIRECTORY ${GSTREAMER_PREFIX}/share/gst-android/ndk-build/androidmedia DESTINATION ${CMAKE_SOURCE_DIR}/android/src/org/freedesktop/androidmedia) - # install(FILES ${GSTREAMER_PREFIX}/share/gst-android/ndk-build/GStreamer.java DESTINATION ${CMAKE_SOURCE_DIR}/android/src/org/freedesktop/androidmedia) if(CMAKE_HOST_WIN32) cmake_path(CONVERT "${GSTREAMER_PREFIX}/share/gst-android/ndk-build/tools/windows/*.dll" TO_CMAKE_PATH_LIST GST_WIN_TOOLS_PATH) file(GLOB GST_WIN_TOOLS ${GST_WIN_TOOLS_PATH}) @@ -192,3 +212,23 @@ elseif(ANDROID) endif() ################################################################################ + +# LSEnvironment +# +# GST_REGISTRY_REUSE_PLUGIN_SCANNER +# no +# GST_PLUGIN_SCANNER +# @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/libexec/gstreamer-1.0/gst-plugin-scanner +# GST_PTP_HELPER_1_0 +# @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/libexec/gstreamer-1.0/gst-ptp-helper +# GIO_EXTRA_MODULES +# @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/lib/gio/modules +# GST_PLUGIN_SYSTEM_PATH_1_0 +# @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0 +# GST_PLUGIN_SYSTEM_PATH +# @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0 +# GST_PLUGIN_PATH_1_0 +# @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0 +# GST_PLUGIN_PATH +# @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0 +# diff --git a/tools/setup/install-dependencies-debian.sh b/tools/setup/install-dependencies-debian.sh index eae9bbd0afa..81d9f306fc1 100755 --- a/tools/setup/install-dependencies-debian.sh +++ b/tools/setup/install-dependencies-debian.sh @@ -109,6 +109,7 @@ DEBIAN_FRONTEND=noninteractive apt -y --quiet install \ libgles2-mesa-dev \ libglu1-mesa-dev \ libglfw3-dev \ + libgraphene-1.0-dev \ libopenal-dev \ libpulse-dev \ libsdl2-dev \ From 161686dd27ce3e1fb9ea4babc869316256fb297b Mon Sep 17 00:00:00 2001 From: Jacob Dahl Date: Mon, 14 Oct 2024 15:44:19 -0800 Subject: [PATCH 02/27] Vehicle: fix actuators --- src/Vehicle/Vehicle.h | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index 23f65549eb6..9f96661f9da 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -82,10 +82,6 @@ class GimbalController; #ifdef QGC_UTM_ADAPTER class UTMSPVehicle; #endif -#ifndef OPAQUE_PTR_VEHICLE - #define OPAQUE_PTR_VEHICLE - Q_DECLARE_OPAQUE_POINTER(Actuators*) -#endif namespace events { namespace parser { @@ -105,6 +101,7 @@ class Vehicle : public VehicleFactGroup Q_MOC_INCLUDE("Autotune.h") Q_MOC_INCLUDE("RemoteIDManager.h") Q_MOC_INCLUDE("QGCCameraManager.h") + Q_MOC_INCLUDE("Actuators/Actuators.h") friend class InitialConnectStateMachine; friend class VehicleLinkManager; From 45eeaac059a4a0e735c0194e215b00b6e236d0d1 Mon Sep 17 00:00:00 2001 From: Holden Date: Sun, 22 Sep 2024 01:08:14 -0400 Subject: [PATCH 03/27] CI: Create Continuous Prerelease --- .github/actions/upload/action.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/actions/upload/action.yml b/.github/actions/upload/action.yml index dd6204492d7..7e664d9b34c 100644 --- a/.github/actions/upload/action.yml +++ b/.github/actions/upload/action.yml @@ -43,3 +43,11 @@ runs: working-directory: ${{ runner.temp }}/shadow_build_dir/${{ inputs.source }} run: aws s3 cp ${{ inputs.artifact_name }} s3://qgroundcontrol/latest/${{ inputs.artifact_name }} --acl public-read shell: bash + + - name: Create Continuous Release + if: ${{ github.event_name != 'pull_request' && github.ref_name == 'master' && !github.event.pull_request.head.repo.fork }} + uses: softprops/action-gh-release@v2 + with: + prerelease: true + files: | + ${{ runner.temp }}/shadow_build_dir/${{ inputs.source }}/${{ inputs.artifact_name }} From 13031c3f6ca5a58ef3d47cb3b9891c429f696387 Mon Sep 17 00:00:00 2001 From: Holden Date: Wed, 16 Oct 2024 05:56:36 -0400 Subject: [PATCH 04/27] CI: Fix Continuous Release --- .github/actions/upload/action.yml | 14 +++++++++++--- .github/workflows/android-linux.yml | 1 + .github/workflows/linux.yml | 1 + .github/workflows/macos.yml | 1 + .github/workflows/windows.yml | 1 + 5 files changed, 15 insertions(+), 3 deletions(-) diff --git a/.github/actions/upload/action.yml b/.github/actions/upload/action.yml index 7e664d9b34c..f41bad71cb1 100644 --- a/.github/actions/upload/action.yml +++ b/.github/actions/upload/action.yml @@ -14,6 +14,9 @@ inputs: description: Source location required: false default: package + github_token: + description: GitHub Token + required: false runs: using: "composite" @@ -45,9 +48,14 @@ runs: shell: bash - name: Create Continuous Release - if: ${{ github.event_name != 'pull_request' && github.ref_name == 'master' && !github.event.pull_request.head.repo.fork }} + if: ${{ github.event_name != 'pull_request' && github.ref_name == 'master' && inputs.github_token != '' }} uses: softprops/action-gh-release@v2 with: + tag_name: latest + target_commitish: master + files: ${{ runner.temp }}/shadow_build_dir/${{ inputs.source }}/${{ inputs.artifact_name }} + name: "Continuous Release" + body: "This release is continuously updated with every commit to master." + draft: false prerelease: true - files: | - ${{ runner.temp }}/shadow_build_dir/${{ inputs.source }}/${{ inputs.artifact_name }} + token: ${{ inputs.github_token }} diff --git a/.github/workflows/android-linux.yml b/.github/workflows/android-linux.yml index 6ee800330fc..c31977a654c 100644 --- a/.github/workflows/android-linux.yml +++ b/.github/workflows/android-linux.yml @@ -99,3 +99,4 @@ jobs: aws_key_id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} source: '' + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 6d477fec36e..b41104b729d 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -138,3 +138,4 @@ jobs: aws_key_id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} source: '' + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index a415db0b775..5a5e851f51b 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -113,3 +113,4 @@ jobs: aws_key_id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} source: 'package' + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 9bb21ce684c..c8eaa9331ef 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -140,3 +140,4 @@ jobs: aws_key_id: ${{ secrets.AWS_ACCESS_KEY_ID }} aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} source: '' + github_token: ${{ secrets.GITHUB_TOKEN }} From d36ae1961482065e214b450f4c3a18630dc3ad2d Mon Sep 17 00:00:00 2001 From: Jacob Dahl Date: Mon, 14 Oct 2024 16:23:15 -0800 Subject: [PATCH 05/27] fix application crash when selecting Airframe type --- src/AutoPilotPlugins/PX4/AirframeComponentController.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AutoPilotPlugins/PX4/AirframeComponentController.cc b/src/AutoPilotPlugins/PX4/AirframeComponentController.cc index cb5f3db3617..4641ce21d62 100644 --- a/src/AutoPilotPlugins/PX4/AirframeComponentController.cc +++ b/src/AutoPilotPlugins/PX4/AirframeComponentController.cc @@ -90,7 +90,7 @@ void AirframeComponentController::changeAutostart(void) return; } - QGuiApplication::overrideCursor()->setShape(Qt::WaitCursor); + QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); Fact* sysAutoStartFact = getParameterFact(-1, "SYS_AUTOSTART"); Fact* sysAutoConfigFact = getParameterFact(-1, "SYS_AUTOCONFIG"); @@ -100,7 +100,7 @@ void AirframeComponentController::changeAutostart(void) connect(sysAutoStartFact, &Fact::vehicleUpdated, this, &AirframeComponentController::_waitParamWriteSignal); connect(sysAutoConfigFact, &Fact::vehicleUpdated, this, &AirframeComponentController::_waitParamWriteSignal); - // We use forceSetValue to params are sent even if the previous value is that same as the new value + // We use forceSetValue to ensure params are sent even if the previous value is that same as the new value sysAutoStartFact->forceSetRawValue(_autostartId); sysAutoConfigFact->forceSetRawValue(1); } From fe4f8a42c02fab96251905c5476ff02e8a9b4ea1 Mon Sep 17 00:00:00 2001 From: Jacob Dahl Date: Wed, 16 Oct 2024 10:35:16 -0800 Subject: [PATCH 06/27] fix application crash when ardusub params are loaded --- src/AutoPilotPlugins/APM/APMAirframeComponentController.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/AutoPilotPlugins/APM/APMAirframeComponentController.cc b/src/AutoPilotPlugins/APM/APMAirframeComponentController.cc index 767bbf37bea..12c65487188 100644 --- a/src/AutoPilotPlugins/APM/APMAirframeComponentController.cc +++ b/src/AutoPilotPlugins/APM/APMAirframeComponentController.cc @@ -213,7 +213,7 @@ void APMAirframeComponentController::_loadParametersFromDownloadFile(const QStri void APMAirframeComponentController::loadParameters(const QString& paramFile) { - QGuiApplication::overrideCursor()->setShape(Qt::WaitCursor); + QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); QString paramFileUrl = QStringLiteral("https://api.github.com/repos/ArduPilot/ardupilot/contents/Tools/Frame_params/%1?ref=master"); From 4165a0d656ea317c558ead8bc7eec791b4f7ba32 Mon Sep 17 00:00:00 2001 From: Don Gagne Date: Mon, 14 Oct 2024 10:44:43 -0700 Subject: [PATCH 07/27] Fix status text escaping/signalling Regular status text messages should move around system un-formatted Add missing textMessageReceived signaling --- src/MAVLink/StatusTextHandler.cc | 5 +++-- src/MAVLink/StatusTextHandler.h | 2 +- src/Vehicle/Vehicle.cc | 5 +++-- test/MAVLink/StatusTextHandlerTest.cc | 4 ++-- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/MAVLink/StatusTextHandler.cc b/src/MAVLink/StatusTextHandler.cc index f27ed1f76d7..05799e4f71e 100644 --- a/src/MAVLink/StatusTextHandler.cc +++ b/src/MAVLink/StatusTextHandler.cc @@ -137,9 +137,10 @@ void StatusTextHandler::resetErrorLevelMessages() } } -void StatusTextHandler::handleTextMessage(MAV_COMPONENT compId, MAV_SEVERITY severity, const QString &text, const QString &description) +void StatusTextHandler::handleHTMLEscapedTextMessage(MAV_COMPONENT compId, MAV_SEVERITY severity, const QString &text, const QString &description) { QString htmlText(text); + (void) htmlText.replace("\n", "
"); // TODO: handle text + description separately in the UI @@ -335,7 +336,7 @@ void StatusTextHandler::_chunkedStatusTextCompleted(MAV_COMPONENT compId) (void) m_chunkedStatusTextInfoMap.remove(compId); - emit textMessageReceived(compId, severity, messageText.toHtmlEscaped(), ""); + emit textMessageReceived(compId, severity, messageText, ""); } void StatusTextHandler::_handleTextMessage(uint32_t newCount, MessageType messageType) diff --git a/src/MAVLink/StatusTextHandler.h b/src/MAVLink/StatusTextHandler.h index 8bffd4b483b..6eba5ec6f2f 100644 --- a/src/MAVLink/StatusTextHandler.h +++ b/src/MAVLink/StatusTextHandler.h @@ -57,7 +57,7 @@ class StatusTextHandler : public QObject ~StatusTextHandler(); void mavlinkMessageReceived(const mavlink_message_t &message); - void handleTextMessage(MAV_COMPONENT componentid, MAV_SEVERITY severity, const QString &text, const QString &description); + void handleHTMLEscapedTextMessage(MAV_COMPONENT componentid, MAV_SEVERITY severity, const QString &text, const QString &description); void clearMessages(); void resetAllMessages(); diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 27145ba2df8..9fbf14621e6 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -1227,7 +1227,7 @@ void Vehicle::_handleEvent(uint8_t comp_id, std::unique_ptrhandleTextMessage(static_cast(comp_id), static_cast(severity), text, QString::fromStdString(description)); + m_statusTextHandler->handleHTMLEscapedTextMessage(static_cast(comp_id), static_cast(severity), text, QString::fromStdString(description)); } } } @@ -4047,7 +4047,8 @@ void Vehicle::_textMessageReceived(MAV_COMPONENT componentid, MAV_SEVERITY sever _say(text); } - m_statusTextHandler->handleTextMessage(componentid, severity, text.toHtmlEscaped(), description); + emit textMessageReceived(id(), componentid, severity, text, description); + m_statusTextHandler->handleHTMLEscapedTextMessage(componentid, severity, text.toHtmlEscaped(), description); } void Vehicle::_errorMessageReceived(QString message) diff --git a/test/MAVLink/StatusTextHandlerTest.cc b/test/MAVLink/StatusTextHandlerTest.cc index 4f72e88b441..eeea3f5172a 100644 --- a/test/MAVLink/StatusTextHandlerTest.cc +++ b/test/MAVLink/StatusTextHandlerTest.cc @@ -26,14 +26,14 @@ void StatusTextHandlerTest::_testHandleTextMessage() { StatusTextHandler* statusTextHandler = new StatusTextHandler(this); - statusTextHandler->handleTextMessage(MAV_COMP_ID_USER1, MAV_SEVERITY_INFO, "StatusTextHandlerTestInfo", "This is the StatusTextHandlerTestInfo Test"); + statusTextHandler->handleHTMLEscapedTextMessage(MAV_COMP_ID_USER1, MAV_SEVERITY_INFO, "StatusTextHandlerTestInfo", "This is the StatusTextHandlerTestInfo Test"); QString messages = statusTextHandler->formattedMessages(); QVERIFY(!messages.isEmpty()); QVERIFY(messages.contains("StatusTextHandlerTestInfo")); QCOMPARE(statusTextHandler->getNormalCount(), 1); QCOMPARE(statusTextHandler->messageCount(), 1); - statusTextHandler->handleTextMessage(MAV_COMP_ID_USER1, MAV_SEVERITY_WARNING, "StatusTextHandlerTestWarning", "This is the StatusTextHandlerTestWarning Test"); + statusTextHandler->handleHTMLEscapedTextMessage(MAV_COMP_ID_USER1, MAV_SEVERITY_WARNING, "StatusTextHandlerTestWarning", "This is the StatusTextHandlerTestWarning Test"); messages = statusTextHandler->formattedMessages(); QVERIFY(messages.contains("StatusTextHandlerTestInfo")); QVERIFY(messages.contains("StatusTextHandlerTestWarning")); From 68afef9e621af179a9222b16a17a8e790b1f90cf Mon Sep 17 00:00:00 2001 From: Holden Date: Thu, 17 Oct 2024 11:44:45 -0400 Subject: [PATCH 08/27] Linux: Fix AppRun for GStreamer --- .github/actions/gstreamer/action.yml | 17 +- .github/workflows/linux.yml | 2 + .github/workflows/macos.yml | 4 +- cmake/CreateAppImage.cmake | 42 +++-- cmake/find-modules/FindGStreamer.cmake | 160 +++++++++++++----- deploy/linux/AppRun | 30 ++-- .../VideoReceiver/GStreamer/GStreamer.cc | 17 +- .../GStreamer/gstqml6gl/CMakeLists.txt | 53 +++--- tools/setup/install-dependencies-debian.sh | 62 +++++-- 9 files changed, 247 insertions(+), 140 deletions(-) diff --git a/.github/actions/gstreamer/action.yml b/.github/actions/gstreamer/action.yml index 89e97b36db5..58cfc117c3c 100644 --- a/.github/actions/gstreamer/action.yml +++ b/.github/actions/gstreamer/action.yml @@ -4,7 +4,7 @@ inputs: gst_version: description: Version of GStreamer to Build required: true - default: 1.22.11 + default: 1.22.12 build_type: description: Build Type "release" or "debug" required: true @@ -25,7 +25,10 @@ runs: run: git clone --depth 1 --branch ${{ inputs.gst_version }} https://github.com/GStreamer/gstreamer.git shell: bash - # macos https://github.com/Homebrew/homebrew-core/blob/4e00e17ab49b90949c27cf43a873ca923f3735aa/Formula/g/gstreamer.rb + - name: Install Dependencies + run: python3 -m pip install --user ninja meson + shell: bash + - name: Configure GStreamer working-directory: ${{ inputs.working_directory }}/gstreamer run: meson setup @@ -35,7 +38,7 @@ runs: --wrap-mode=forcefallback --strip -Dauto_features=disabled - -Dgst-full-libraries=gstreamer,base,controller,net,app,audio,fft,pbutils,riff,rtp,rtsp,tag,video,gl,codecparsers,photography + -Dgst-full-libraries=gstreamer,base,video,gl -Dgpl=enabled -Dlibav=enabled -Dorc=enabled @@ -88,5 +91,11 @@ runs: - name: Setup Environment working-directory: ${{ runner.temp }}/gstreamer - run: echo "PKG_CONFIG_PATH=${{ runner.temp }}/gst/lib/x86_64-linux-gnu/pkgconfig:${{ env.PKG_CONFIG_PATH }}" >> "$GITHUB_ENV" + run: echo "PKG_CONFIG_PATH=${{ inputs.install_directory }}/lib/x86_64-linux-gnu/pkgconfig:${{ inputs.install_directory }}/lib/x86_64-linux-gnu/gstreamer-1.0/pkgconfig:${{ env.PKG_CONFIG_PATH }}" >> "$GITHUB_ENV" shell: bash + + - name: Save artifact + uses: actions/upload-artifact@v4 + with: + name: GStreamer-${{ inputs.build_type }} + path: ${{ inputs.install_directory }} diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index b41104b729d..59a79602712 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -41,6 +41,8 @@ jobs: steps: - name: Free Disk Space (Ubuntu) uses: jlumbroso/free-disk-space@main + with: + large-packages: false continue-on-error: true - name: Checkout repo diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 5a5e851f51b..12155f1ad8e 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -56,7 +56,8 @@ jobs: - name: Install Dependencies run: | brew update - brew install cmake ninja ccache geographiclib SDL2 exiv2 expat zlib shapelib + brew install cmake ninja ccache geographiclib SDL2 exiv2 expat zlib shapelib pkgconfig + # pkgconf - name: Install Gstreamer run: | @@ -65,6 +66,7 @@ jobs: for package in *.pkg ; do sudo installer -verbose -pkg "$package" -target / done + echo "PKG_CONFIG_PATH=/Library/Frameworks/GStreamer.framework/lib/pkgconfig:/Library/Frameworks/GStreamer.framework/lib/gstreamer-1.0/pkgconfig:${{ env.PKG_CONFIG_PATH }}" >> "$GITHUB_ENV" - name: Set Up Cache uses: hendrikmuhs/ccache-action@v1.2 diff --git a/cmake/CreateAppImage.cmake b/cmake/CreateAppImage.cmake index e7599d8406b..0606da935fe 100644 --- a/cmake/CreateAppImage.cmake +++ b/cmake/CreateAppImage.cmake @@ -2,25 +2,26 @@ message(STATUS "Creating AppImage") # TODO: https://github.com/AppImageCommunity/AppImageUpdate set(APPDIR_PATH "${CMAKE_BINARY_DIR}/AppDir") -# set(APPIMAGETOOL_PATH "${CMAKE_BINARY_DIR}/appimagetool-x86_64.AppImage") +set(APPIMAGETOOL_PATH "${CMAKE_BINARY_DIR}/appimagetool-x86_64.AppImage") set(LD_PATH "${CMAKE_BINARY_DIR}/linuxdeploy-x86_64.AppImage") -set(LD_APPIMAGEPLUGIN_PATH "${CMAKE_BINARY_DIR}/linuxdeploy-plugin-appimage-x86_64.AppImage") +# set(LD_APPIMAGEPLUGIN_PATH "${CMAKE_BINARY_DIR}/linuxdeploy-plugin-appimage-x86_64.AppImage") # set(LD_QTPLUGIN_PATH "${CMAKE_BINARY_DIR}/linuxdeploy-plugin-qt-x86_64.AppImage") # set(LD_GSTPLUGIN_PATH "${CMAKE_BINARY_DIR}/linuxdeploy-plugin-gstreamer.sh") # set(LD_GTKPLUGIN_PATH "${CMAKE_BINARY_DIR}/linuxdeploy-plugin-gtk.sh") -# if(NOT EXISTS "${APPIMAGETOOL_PATH}") -# file(DOWNLOAD https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage "${APPIMAGETOOL_PATH}") -# execute_process(COMMAND chmod a+x "${APPIMAGETOOL_PATH}") -# endif() +if(NOT EXISTS "${APPIMAGETOOL_PATH}") + file(DOWNLOAD https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-x86_64.AppImage "${APPIMAGETOOL_PATH}") + # file(DOWNLOAD https://github.com/probonopd/go-appimage/releases/download/832/appimagetool-823-x86_64.AppImage "${APPIMAGETOOL_PATH}") # TODO: Use Continuous Release + execute_process(COMMAND chmod a+x "${APPIMAGETOOL_PATH}") +endif() if(NOT EXISTS "${LD_PATH}") file(DOWNLOAD https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage "${LD_PATH}") execute_process(COMMAND chmod a+x "${LD_PATH}") endif() -if(NOT EXISTS "${LD_APPIMAGEPLUGIN_PATH}") - file(DOWNLOAD https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage "${LD_APPIMAGEPLUGIN_PATH}") - execute_process(COMMAND chmod a+x "${LD_APPIMAGEPLUGIN_PATH}") -endif() +# if(NOT EXISTS "${LD_APPIMAGEPLUGIN_PATH}") +# file(DOWNLOAD https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage "${LD_APPIMAGEPLUGIN_PATH}") +# execute_process(COMMAND chmod a+x "${LD_APPIMAGEPLUGIN_PATH}") +# endif() # if(NOT EXISTS "${LD_QTPLUGIN_PATH}") # file(DOWNLOAD https://github.com/linuxdeploy/linuxdeploy-plugin-qt/releases/download/continuous/linuxdeploy-plugin-qt-x86_64.AppImage "${LD_QTPLUGIN_PATH}") # execute_process(COMMAND chmod a+x "${LD_QTPLUGIN_PATH}") @@ -34,17 +35,14 @@ endif() # execute_process(COMMAND chmod a+x "${LD_GSTPLUGIN_PATH}") # endif() -execute_process(COMMAND ${LD_PATH} --appdir ${APPDIR_PATH} --output appimage --custom-apprun ${CMAKE_BINARY_DIR}/AppRun) -# --exclude-library "libX*" -# --exclude-library "libglib*" -# --exclude-library "libgobject*" -# --exclude-library "libgdk_pixbuf*" -# --exclude-library "libwayland*" -# --exclude-library "libgmodule*" -# --exclude-library "libgio*" -# --exclude-library "libxcb*" -# --exclude-library "libxkbcommon*" -# --exclude-library "libdb*" +execute_process(COMMAND ${LD_PATH} + --appdir ${APPDIR_PATH} + --executable ${APPDIR_PATH}/usr/bin/QGroundControl + --desktop-file ${APPDIR_PATH}/usr/share/applications/org.mavlink.qgroundcontrol.desktop + --custom-apprun ${CMAKE_BINARY_DIR}/AppRun) # --exclude-library "libgst*" -# --exclude-library "libgthread*" # --plugin qt --plugin gtk --plugin gstreamer + +set(ENV{ARCH} x86_64) +# set(ENV{VERSION} 5.0) +execute_process(COMMAND ${APPIMAGETOOL_PATH} ${APPDIR_PATH}) diff --git a/cmake/find-modules/FindGStreamer.cmake b/cmake/find-modules/FindGStreamer.cmake index 5bd6b2230bf..c1102892bab 100644 --- a/cmake/find-modules/FindGStreamer.cmake +++ b/cmake/find-modules/FindGStreamer.cmake @@ -52,7 +52,15 @@ elseif(MACOS) set(ENV{PKG_CONFIG_PATH} "${GSTREAMER_PREFIX}/lib/pkgconfig:${GSTREAMER_PREFIX}/lib/gstreamer-1.0/pkgconfig:$ENV{PKG_CONFIG_PATH}") elseif(LINUX) set(GSTREAMER_PREFIX "/usr") - set(ENV{PKG_CONFIG_PATH} "${GSTREAMER_PREFIX}/lib/x86_64-linux-gnu/pkgconfig:$ENV{PKG_CONFIG_PATH}") + set(ENV{PKG_CONFIG_PATH} "${GSTREAMER_PREFIX}/lib/x86_64-linux-gnu/pkgconfig:${GSTREAMER_PREFIX}/lib/x86_64-linux-gnu/gstreamer-1.0/pkgconfig:$ENV{PKG_CONFIG_PATH}") + # if(QGC_GST_STATIC_BUILD) + # list(APPEND PKG_CONFIG_ARGN + # --dont-define-prefix + # --define-variable=prefix=${GSTREAMER_PREFIX} + # --define-variable=libdir=${GSTREAMER_PREFIX}/lib/x86_64-linux-gnu + # --define-variable=includedir=${GSTREAMER_PREFIX}/include + # ) + # endif() elseif(IOS) list(APPEND CMAKE_FRAMEWORK_PATH "~/Library/Developer/GStreamer/iPhone.sdk") if(DEFINED ENV{GSTREAMER_PREFIX_IOS} AND EXISTS $ENV{GSTREAMER_PREFIX_IOS}) @@ -104,7 +112,7 @@ elseif(ANDROID) set(PKG_CONFIG_EXECUTABLE ${PKG_CONFIG_PROGRAM}) set(ENV{PKG_CONFIG_LIBDIR} "${GSTREAMER_PREFIX}/lib/pkgconfig;${GSTREAMER_PREFIX}/lib/gstreamer-1.0/pkgconfig") endif() - elseif(CMAKE_HOST_LINUX) + elseif(CMAKE_HOST_LINUX OR CMAKE_HOST_APPLE) if(PkgConfig_FOUND) set(ENV{PKG_CONFIG_LIBDIR} "${GSTREAMER_PREFIX}/lib/pkgconfig:${GSTREAMER_PREFIX}/lib/gstreamer-1.0/pkgconfig") endif() @@ -120,6 +128,16 @@ endif() list(PREPEND CMAKE_PREFIX_PATH ${GSTREAMER_PREFIX}) cmake_print_variables(GSTREAMER_PREFIX) +# TODO: find_path +if(LINUX) + set(GSTREAMER_LIB_PATH ${GSTREAMER_PREFIX}/lib/x86_64-linux-gnu) +elseif(MACOS OR ANDROID OR WIN32) + set(GSTREAMER_LIB_PATH ${GSTREAMER_PREFIX}/lib) +elseif(IOS) + +endif() +cmake_print_variables(GSTREAMER_LIB_PATH) + ################################################################################ include(CMakeFindDependencyMacro) @@ -238,6 +256,7 @@ function(find_gstreamer_component component) target_link_libraries(GStreamer::${component} INTERFACE ${ARGS_DEPENDENCIES}) endif() set_target_properties(GStreamer::${component} PROPERTIES VERSION ${GStreamer_VERSION}) + cmake_print_variables(component) endif() mark_as_advanced(GStreamer_${component}_INCLUDE_DIR GStreamer_${component}_LIBRARY) endif() @@ -276,18 +295,42 @@ find_gstreamer_component(Gl ################################################################################ +set(GST_DEPENDENCIES) +find_package(GLESv2 QUIET) +list(APPEND GST_DEPENDENCIES GLESv2::GLESv2) +find_package(OpenGL QUIET OPTIONAL_COMPONENTS EGL GLX OpenGL) # GLES2 GLES3 +list(APPEND GST_DEPENDENCIES OpenGL::GL OpenGL::EGL OpenGL::GLX OpenGL::OpenGL) +find_package(X11_XCB QUIET) +list(APPEND GST_DEPENDENCIES X11::XCB) +find_package(EGL QUIET) +list(APPEND GST_DEPENDENCIES EGL::EGL) +pkg_check_modules(PC_libdrm IMPORTED_TARGET libdrm) +list(APPEND GST_DEPENDENCIES PkgConfig::PC_libdrm) +pkg_check_modules(PC_gudev IMPORTED_TARGET gudev-1.0) +list(APPEND GST_DEPENDENCIES PkgConfig::PC_gudev) +cmake_print_variables(GST_DEPENDENCIES) + +################################################################################ + if(Allocators IN_LIST GStreamer_FIND_COMPONENTS) find_gstreamer_component(Allocators PC_NAME gstreamer-allocators-1.0 HEADER gst/allocators/allocators.h LIBRARY gstallocators-1.0 DEPENDENCIES GStreamer::Core) + + if(TARGET GStreamer::Allocators) + pkg_check_modules(PC_libdrm IMPORTED_TARGET libdrm) + if(TARGET PkgConfig::PC_libdrm) + target_link_libraries(GStreamer::Allocators INTERFACE PkgConfig::PC_libdrm) + endif() + endif() endif() if(App IN_LIST GStreamer_FIND_COMPONENTS) find_gstreamer_component(App PC_NAME gstreamer-app-1.0 - HEADER gst/app/gstappsink.h + HEADER gst/app/app.h LIBRARY gstapp-1.0 DEPENDENCIES GStreamer::Core GStreamer::Base) endif() @@ -319,51 +362,84 @@ endif() if(Fft IN_LIST GStreamer_FIND_COMPONENTS) find_gstreamer_component(Fft PC_NAME gstreamer-fft-1.0 - HEADER gst/fft/cfft.h + HEADER gst/fft/fft.h LIBRARY gstfft-1.0 DEPENDENCIES GStreamer::Core) endif() if(GlEgl IN_LIST GStreamer_FIND_COMPONENTS) - # find_package(EGL) find_gstreamer_component(GlEgl PC_NAME gstreamer-gl-egl-1.0 - HEADER gst/gl/egl/gstgldisplay_egl.h + HEADER gst/gl/egl/egl.h LIBRARY gstgl-1.0 - DEPENDENCIES GStreamer::Gl EGL::EGL) + DEPENDENCIES GStreamer::Gl) + + if(TARGET GStreamer::GlEgl) + find_package(EGL QUIET) + if(TARGET EGL::EGL) + target_link_libraries(GStreamer::GlEgl INTERFACE EGL::EGL) + endif() + endif() endif() if(GlPrototypes IN_LIST GStreamer_FIND_COMPONENTS) - # find_package(GLESv2) - # find_package(OpenGL OPTIONAL_COMPONENTS EGL GLX OpenGL) # GLES2 GLES3 find_gstreamer_component(GlPrototypes PC_NAME gstreamer-gl-prototypes-1.0 HEADER gst/gl/glprototypes/all_functions.h LIBRARY gstglproto-1.0 - DEPENDENCIES GStreamer::Gl GLESv2::GLESv2 OpenGL::GL) + DEPENDENCIES GStreamer::Gl) + + if(TARGET GStreamer::GlPrototypes) + find_package(GLESv2 QUIET) + find_package(OpenGL QUIET) + set(GlPrototypes_DEPENDENCIES GLESv2::GLESv2 OpenGL::GL) + foreach(dependency IN LISTS GlPrototypes_DEPENDENCIES) + if(TARGET ${dependency}) + target_link_libraries(GStreamer::GlPrototypes INTERFACE ${dependency}) + endif() + endforeach() + endif() endif() if(GlWayland IN_LIST GStreamer_FIND_COMPONENTS) - # find_package(Wayland COMPONENTS Client Cursor Egl) - # find_package(WaylandProtocols) - # find_package(WaylandScanner) - # find_package(Qt6 COMPONENTS WaylandClient) find_gstreamer_component(GlWayland PC_NAME gstreamer-gl-wayland-1.0 - HEADER gst/gl/wayland/gstgldisplay_wayland.h + HEADER gst/gl/wayland/wayland.h LIBRARY gstgl-1.0 - DEPENDENCIES GStreamer::Gl Wayland::EGL Wayland::Client) + DEPENDENCIES GStreamer::Gl) + + if(TARGET GStreamer::GlWayland) + find_package(Wayland QUIET COMPONENTS Client Cursor Egl) + # find_package(WaylandProtocols QUIET) + # find_package(WaylandScanner QUIET) + # find_package(Qt6 QUIET COMPONENTS WaylandClient) + set(GlWayland_DEPENDENCIES Wayland::Client Wayland::Cursor Wayland::Egl) + foreach(dependency IN LISTS GlWayland_DEPENDENCIES) + if(TARGET ${dependency}) + target_link_libraries(GStreamer::GlWayland INTERFACE ${dependency}) + endif() + endforeach() + endif() endif() if(GlX11 IN_LIST GStreamer_FIND_COMPONENTS) - # find_package(X11) - # find_package(XCB COMPONENTS XCB GLX) - # find_package(X11_XCB) find_gstreamer_component(GlX11 PC_NAME gstreamer-gl-x11-1.0 - HEADER gst/gl/x11/gstgldisplay_x11.h + HEADER gst/gl/x11/x11.h LIBRARY gstgl-1.0 - DEPENDENCIES GStreamer::Gl X11::XCB) + DEPENDENCIES GStreamer::Gl) + + if(TARGET GStreamer::GlX11) + # find_package(X11 QUIET) + # find_package(XCB QUIET) + find_package(X11_XCB QUIET) + set(GlX11_DEPENDENCIES X11::XCB) + foreach(dependency IN LISTS GlX11_DEPENDENCIES) + if(TARGET ${dependency}) + target_link_libraries(GStreamer::GlX11 INTERFACE ${dependency}) + endif() + endforeach() + endif() endif() if(Mpegts IN_LIST GStreamer_FIND_COMPONENTS) @@ -463,12 +539,15 @@ if(Tag IN_LIST GStreamer_FIND_COMPONENTS) endif() if(Va IN_LIST GStreamer_FIND_COMPONENTS) - # find_package(VAAPI) find_gstreamer_component(Va PC_NAME gstreamer-va-1.0 HEADER gst/va/gstva.h LIBRARY gstva-1.0 DEPENDENCIES GStreamer::Core GStreamer::Video) + + if(TARGET GSreamer::Va) + find_package(VAAPI QUIET) + endif() endif() ################################################################################ @@ -516,39 +595,34 @@ if(QGC_GST_STATIC_BUILD) target_compile_definitions(GStreamer::GStreamer INTERFACE QGC_GST_STATIC_BUILD) endif() -# TODO: find_path -if(LINUX) - set(GSTREAMER_LIB_PATH ${GSTREAMER_PREFIX}/lib/x86_64-linux-gnu) -elseif(MACOS) - set(GSTREAMER_LIB_PATH ${GSTREAMER_PREFIX}/lib) -elseif(ANDROID OR WIN32) - set(GSTREAMER_LIB_PATH ${GSTREAMER_PREFIX}/lib) -elseif(IOS) - -endif() -set(GSTREAMER_PLUGIN_PATH ${GSTREAMER_LIB_PATH}/gstreamer-1.0) +foreach(dependency IN LISTS GST_DEPENDENCIES) + if(TARGET ${dependency}) + cmake_print_variables(dependency) + target_link_libraries(GStreamer::GStreamer INTERFACE ${dependency}) + endif() +endforeach() target_include_directories(GStreamer::GStreamer INTERFACE ${GSTREAMER_PREFIX}/include ${GSTREAMER_PREFIX}/include/glib-2.0 - ${GSTREAMER_PREFIX}/include/graphene-1.0 ${GSTREAMER_PREFIX}/include/gstreamer-1.0 ${GSTREAMER_LIB_PATH}/glib-2.0/include - ${GSTREAMER_LIB_PATH}/graphene-1.0/include - ${GSTREAMER_PLUGIN_PATH}/include + # ${GSTREAMER_PREFIX}/include/graphene-1.0 + # ${GSTREAMER_LIB_PATH}/graphene-1.0/include ) -target_link_directories(GStreamer::GStreamer - INTERFACE - ${GSTREAMER_LIB_PATH} - ${GSTREAMER_PLUGIN_PATH} -) +target_link_directories(GStreamer::GStreamer INTERFACE ${GSTREAMER_LIB_PATH}) ################################################################################ add_library(GStreamer::Plugins INTERFACE IMPORTED) +set(GSTREAMER_PLUGIN_PATH ${GSTREAMER_LIB_PATH}/gstreamer-1.0) +target_include_directories(GStreamer::GStreamer INTERFACE ${GSTREAMER_PLUGIN_PATH}/include) +target_link_directories(GStreamer::GStreamer INTERFACE ${GSTREAMER_PLUGIN_PATH}) +cmake_print_variables(GSTREAMER_PLUGIN_PATH) + set(GST_PLUGINS gstcoreelements gstisomp4 @@ -596,6 +670,10 @@ foreach(plugin IN LISTS GST_PLUGINS) set(GST_PLUGIN_${plugin}_FOUND TRUE) endif() endif() + + # if(GST_PLUGIN_${plugin}_FOUND) + # cmake_print_variables(plugin) + # endif() endforeach() if(NOT MACOS) diff --git a/deploy/linux/AppRun b/deploy/linux/AppRun index 1373a15a9da..761682126e8 100755 --- a/deploy/linux/AppRun +++ b/deploy/linux/AppRun @@ -1,21 +1,23 @@ #!/bin/bash -set -e +if [ -z "$APPDIR" ]; then + APPDIR="$(dirname "$(readlink -f "$0")")" +fi -HERE="$(dirname "$(readlink -f "${0}")")" - -# export LD_LIBRARY_PATH="$HERE/usr/lib:${LD_LIBRARY_PATH}" +export LD_LIBRARY_PATH="${APPDIR}/usr/lib:${APPDIR}/usr/lib/gstreamer-1.0:${LD_LIBRARY_PATH}" export GST_REGISTRY_REUSE_PLUGIN_SCANNER="no" -export GIO_EXTRA_MODULES="$HERE/usr/lib/gio/modules" +export GIO_EXTRA_MODULES="${APPDIR}/usr/lib/gio/modules" + +export GST_PLUGIN_SYSTEM_PATH="${APPDIR}/usr/lib/gstreamer-1.0" +export GST_PLUGIN_SYSTEM_PATH_1_0="${APPDIR}/usr/lib/gstreamer-1.0" +export GST_PLUGIN_PATH="${APPDIR}/usr/lib/gstreamer-1.0" +export GST_PLUGIN_PATH_1_0="${APPDIR}/usr/lib/gstreamer-1.0" +export GST_PLUGIN_SCANNER="${APPDIR}/usr/lib/gstreamer1.0/gstreamer-1.0/gst-plugin-scanner" +export GST_PLUGIN_SCANNER_1_0="${APPDIR}/usr/lib/gstreamer1.0/gstreamer-1.0/gst-plugin-scanner" +export GST_PTP_HELPER="${APPDIR}/usr/lib/gstreamer1.0/gstreamer-1.0/gst-ptp-helper" +export GST_PTP_HELPER_1_0="${APPDIR}/usr/lib/gstreamer1.0/gstreamer-1.0/gst-ptp-helper" -export GST_PLUGIN_SYSTEM_PATH="$HERE/usr/lib/gstreamer-1.0" -export GST_PLUGIN_SYSTEM_PATH_1_0="$HERE/usr/lib/gstreamer-1.0" -export GST_PLUGIN_PATH="$HERE/usr/lib/gstreamer-1.0" -export GST_PLUGIN_PATH_1_0="$HERE/usr/lib/gstreamer-1.0" -export GST_PLUGIN_SCANNER="$HERE/usr/lib/gstreamer1.0/gstreamer-1.0/gst-plugin-scanner" -export GST_PLUGIN_SCANNER_1_0="$HERE/usr/lib/gstreamer1.0/gstreamer-1.0/gst-plugin-scanner" -export GST_PTP_HELPER="$HERE/usr/lib/gstreamer1.0/gstreamer-1.0/gst-ptp-helper" -export GST_PTP_HELPER_1_0="$HERE/usr/lib/gstreamer1.0/gstreamer-1.0/gst-ptp-helper" +export QT_QPA_PLATFORM="xcb" -exec "$HERE/usr/bin/QGroundControl" "$@" +exec "${APPDIR}/usr/bin/QGroundControl" "$@" diff --git a/src/VideoManager/VideoReceiver/GStreamer/GStreamer.cc b/src/VideoManager/VideoReceiver/GStreamer/GStreamer.cc index 7635b30d1ad..6ef042f4810 100644 --- a/src/VideoManager/VideoReceiver/GStreamer/GStreamer.cc +++ b/src/VideoManager/VideoReceiver/GStreamer/GStreamer.cc @@ -38,10 +38,10 @@ GST_PLUGIN_STATIC_DECLARE(matroska); GST_PLUGIN_STATIC_DECLARE(mpegtsdemux); GST_PLUGIN_STATIC_DECLARE(opengl); GST_PLUGIN_STATIC_DECLARE(tcp); -GST_PLUGIN_STATIC_DECLARE(asf); -#if !defined(Q_OS_ANDROID) && !defined(Q_OS_MAC) -GST_PLUGIN_STATIC_DECLARE(va); -#endif +// GST_PLUGIN_STATIC_DECLARE(asf); +// #ifndef Q_OS_ANDROID +// GST_PLUGIN_STATIC_DECLARE(va); +// #endif #ifdef Q_OS_ANDROID GST_PLUGIN_STATIC_DECLARE(androidmedia); #elif defined(Q_OS_IOS) @@ -115,6 +115,7 @@ static void _setGstEnvVars() _qgcputenv("GST_PLUGIN_SYSTEM_PATH", currentDir, "/../Frameworks/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0"); _qgcputenv("GST_PLUGIN_PATH_1_0", currentDir, "/../Frameworks/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0"); _qgcputenv("GST_PLUGIN_PATH", currentDir, "/../Frameworks/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0"); + _qgcputenv("GTK_PATH", currentDir, "/../Frameworks/GStreamer.framework/Versions/1.0"); #elif defined(Q_OS_WIN) _qgcputenv("GST_REGISTRY_REUSE_PLUGIN_SCANNER", "no"); _qgcputenv("GST_PLUGIN_SCANNER", currentDir, "/../libexec/gstreamer-1.0/gst-plugin-scanner"); @@ -144,10 +145,10 @@ static void _registerPlugins() GST_PLUGIN_STATIC_REGISTER(mpegtsdemux); GST_PLUGIN_STATIC_REGISTER(opengl); GST_PLUGIN_STATIC_REGISTER(tcp); - GST_PLUGIN_STATIC_REGISTER(asf); -#if !defined(Q_OS_ANDROID) && !defined(Q_OS_MAC) - GST_PLUGIN_STATIC_REGISTER(va); -#endif + // GST_PLUGIN_STATIC_REGISTER(asf); +// #ifndef Q_OS_ANDROID +// GST_PLUGIN_STATIC_REGISTER(va); +// #endif #ifdef Q_OS_ANDROID GST_PLUGIN_STATIC_REGISTER(androidmedia); #elif defined(Q_OS_IOS) diff --git a/src/VideoManager/VideoReceiver/GStreamer/gstqml6gl/CMakeLists.txt b/src/VideoManager/VideoReceiver/GStreamer/gstqml6gl/CMakeLists.txt index ae73d784916..67b7b03ca0d 100644 --- a/src/VideoManager/VideoReceiver/GStreamer/gstqml6gl/CMakeLists.txt +++ b/src/VideoManager/VideoReceiver/GStreamer/gstqml6gl/CMakeLists.txt @@ -1,5 +1,7 @@ find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick) -find_package(GStreamer REQUIRED COMPONENTS Allocators Audio Codecparsers Controller Fft Mpegts Net Pbutils Riff Rtp Rtsp Sdp Tag OPTIONAL_COMPONENTS Photography GlPrototypes Va GlX11 GlEGL GlWayland) + +set(GST_COMPONENTS Allocators Audio Codecparsers Controller Fft GlEGL GlPrototypes GlWayland GlX11 Mpegts Net Pbutils Photography Riff Rtp Rtsp Sdp Tag Va) +find_package(GStreamer REQUIRED COMPONENTS OPTIONAL_COMPONENTS ${GST_COMPONENTS}) qt_add_library(gstqml6gl STATIC) @@ -12,33 +14,13 @@ target_link_libraries(gstqml6gl Qt6::Qml Qt6::Quick GStreamer::GStreamer - GStreamer::Allocators - GStreamer::Audio - GStreamer::Codecparsers - GStreamer::Controller - GStreamer::Fft - GStreamer::Mpegts - GStreamer::Net - GStreamer::Pbutils - GStreamer::Riff - GStreamer::Rtp - GStreamer::Rtsp - GStreamer::Sdp - GStreamer::Tag ) -# Photography not found on ubuntu 20.04? -if(GStreamer_Photography_FOUND) - target_link_libraries(gstqml6gl PUBLIC GStreamer::Photography) -endif() - -if(GStreamer_GlPrototypes_FOUND) - target_link_libraries(gstqml6gl PUBLIC GStreamer::GlPrototypes) -endif() - -if(GStreamer_Va_FOUND) - target_link_libraries(gstqml6gl PUBLIC GStreamer::Va) -endif() +foreach(component IN LISTS GST_COMPONENTS) + if(GStreamer_${component}_FOUND) + target_link_libraries(gstqml6gl PUBLIC GStreamer::${component}) + endif() +endforeach() ################################################################################ @@ -56,6 +38,8 @@ else() endif() cmake_print_variables(QGC_GST_QT6_PLUGIN_PATH) +# TODO: https://gstreamer.freedesktop.org/documentation/qt6d3d11/index.html#qml6d3d11sink-page + ################################################################################ file(READ ${QGC_GST_QT6_PLUGIN_PATH}/qt6glitem.h FILE_CONTENTS) @@ -78,17 +62,14 @@ target_include_directories(gstqml6gl PUBLIC ${QGC_GST_QT6_PLUGIN_PATH}) ################################################################################ if(GStreamer_GlX11_FOUND) - target_link_libraries(gstqml6gl PUBLIC GStreamer::GlX11) target_compile_definitions(gstqml6gl PRIVATE HAVE_QT_X11) endif() if(GStreamer_GlEGL_FOUND) - target_link_libraries(gstqml6gl PUBLIC GStreamer::GlEGL) target_compile_definitions(gstqml6gl PRIVATE HAVE_QT_EGLFS) endif() if(GStreamer_GlWayland_FOUND) - target_link_libraries(gstqml6gl PUBLIC GStreamer::GlWayland) target_compile_definitions(gstqml6gl PRIVATE HAVE_QT_WAYLAND) endif() @@ -167,10 +148,14 @@ endif() ################################################################################ if(LINUX) - install(DIRECTORY ${GSTREAMER_LIB_PATH}/gstreamer1.0 DESTINATION ${CMAKE_INSTALL_LIBDIR}) - install(DIRECTORY ${GSTREAMER_LIB_PATH}/gio DESTINATION ${CMAKE_INSTALL_LIBDIR}) - get_target_property(LINKED_PLUGINS GStreamer::Plugins INTERFACE_LINK_LIBRARIES) - install(FILES ${LINKED_PLUGINS} DESTINATION ${CMAKE_INSTALL_LIBDIR}/gstreamer-1.0) + # if(NOT QGC_GST_STATIC_BUILD) + install(DIRECTORY ${GSTREAMER_LIB_PATH}/gstreamer1.0 DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install(CODE "execute_process(COMMAND chmod +x \"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/gstreamer1.0/gstreamer-1.0/gst-plugin-scanner\")") + install(CODE "execute_process(COMMAND chmod +x \"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/gstreamer1.0/gstreamer-1.0/gst-ptp-helper\")") + install(DIRECTORY ${GSTREAMER_LIB_PATH}/gio DESTINATION ${CMAKE_INSTALL_LIBDIR}) + get_target_property(LINKED_PLUGINS GStreamer::Plugins INTERFACE_LINK_LIBRARIES) + install(FILES ${LINKED_PLUGINS} DESTINATION ${CMAKE_INSTALL_LIBDIR}/gstreamer-1.0) + # endif() elseif(WIN32) cmake_path(CONVERT "${GSTREAMER_PREFIX}/bin/*.dll" TO_CMAKE_PATH_LIST GST_WIN_BINS_PATH) file(GLOB GST_WIN_BINS ${GST_WIN_BINS_PATH}) @@ -213,6 +198,8 @@ endif() ################################################################################ +# DYLD_LIBRARY_PATH + # LSEnvironment # # GST_REGISTRY_REUSE_PLUGIN_SCANNER diff --git a/tools/setup/install-dependencies-debian.sh b/tools/setup/install-dependencies-debian.sh index 81d9f306fc1..225de8958b4 100755 --- a/tools/setup/install-dependencies-debian.sh +++ b/tools/setup/install-dependencies-debian.sh @@ -2,10 +2,10 @@ set -e -apt update -y --quiet +apt-get update -y --quiet # Build Tools -DEBIAN_FRONTEND=noninteractive apt -y --quiet install \ +DEBIAN_FRONTEND=noninteractive apt-get -y --quiet install \ appstream \ binutils \ build-essential \ @@ -29,10 +29,11 @@ DEBIAN_FRONTEND=noninteractive apt -y --quiet install \ pkgconf \ python3 \ python3-pip \ - rsync + rsync \ + zsync # Qt Required - https://doc.qt.io/qt-6/linux-requirements.html -DEBIAN_FRONTEND=noninteractive apt -y --quiet install \ +DEBIAN_FRONTEND=noninteractive apt-get -y --quiet install \ libfontconfig1 \ libfreetype6 \ libx11-6 \ @@ -42,6 +43,7 @@ DEBIAN_FRONTEND=noninteractive apt -y --quiet install \ libxcb-icccm4 \ libxcb-image0 \ libxcb-keysyms1 \ + libxcb-present0 \ libxcb-randr0 \ libxcb-render-util0 \ libxcb-render0 \ @@ -60,8 +62,11 @@ DEBIAN_FRONTEND=noninteractive apt -y --quiet install \ libxkbcommon0 \ libxrender1 +DEBIAN_FRONTEND=noninteractive apt-get -y --quiet install \ + libunwind-dev + # GStreamer -DEBIAN_FRONTEND=noninteractive apt -y --quiet install \ +DEBIAN_FRONTEND=noninteractive apt-get -y --quiet install \ libgstreamer1.0-dev \ libgstreamer-plugins-bad1.0-dev \ libgstreamer-plugins-base1.0-dev \ @@ -83,7 +88,7 @@ if apt-cache show gstreamer1.0-qt6 >/dev/null 2>&1 && apt-cache show gstreamer1. fi # Exiv2 -DEBIAN_FRONTEND=noninteractive apt -y --quiet install \ +DEBIAN_FRONTEND=noninteractive apt-get -y --quiet install \ libbrotli-dev \ libcurl4-openssl-dev \ libexiv2-dev \ @@ -94,43 +99,66 @@ DEBIAN_FRONTEND=noninteractive apt -y --quiet install \ libz-dev \ zlib1g-dev -# Additional -DEBIAN_FRONTEND=noninteractive apt -y --quiet install \ +# Speech +DEBIAN_FRONTEND=noninteractive apt-get -y --quiet install \ flite1-dev \ + libspeechd-dev \ + speech-dispatcher \ + speech-dispatcher-espeak \ + speech-dispatcher-espeak-ng \ + speech-dispatcher-flite \ + +# Additional +DEBIAN_FRONTEND=noninteractive apt-get -y --quiet install \ + gvfs \ intel-media-va-driver \ libasound2-dev \ libass-dev \ libdrm-dev \ + libcairo-dev \ + libelf-dev \ libegl1-mesa-dev \ libgbm-dev \ - libgl1-mesa-dev \ libgl-dev \ - libglx-dev \ + libgl1-mesa-dev \ + libgles-dev \ libgles2-mesa-dev \ - libglu1-mesa-dev \ + libglew-dev \ libglfw3-dev \ + libglib2.0-dev \ + libglu1-mesa-dev \ + libglvnd-dev \ + libglx-dev \ + libglx-mesa0 \ + libgudev-1.0-dev \ libgraphene-1.0-dev \ + libmjpegtools-dev \ + libjpeg-dev \ + libjson-glib-1.0-0 \ + libjson-glib-dev \ libopenal-dev \ + libopenjp2-7-dev \ + libopus-dev \ + libpng-dev \ libpulse-dev \ libsdl2-dev \ - libspeechd-dev \ libshp-dev \ - libunwind-dev \ + libsoup2.4-dev \ + libssl-dev \ + libtheora-dev \ libva-dev \ libvdpau-dev \ libvpx-dev \ libwayland-dev \ + libwxgtk3.*-dev \ libx11-dev \ + libxml2-dev \ libzstd-dev \ mesa-common-dev \ mesa-utils \ mesa-va-drivers \ mesa-vdpau-drivers \ mesa-vulkan-drivers \ - speech-dispatcher \ - speech-dispatcher-espeak \ - speech-dispatcher-espeak-ng \ - speech-dispatcher-flite \ vainfo if apt-cache show libvpl-dev >/dev/null 2>&1 && apt-cache show libvpl-dev 2>/dev/null | grep -q "^Package: libvpl-dev"; then From 2da6f121399c65e19d53e1c011581d102439ebc8 Mon Sep 17 00:00:00 2001 From: Holden Date: Mon, 7 Oct 2024 07:23:29 -0400 Subject: [PATCH 09/27] CI: Create Android Play Store Action --- .github/actions/playstore/action.yml | 21 +++++++++++++++++++++ .github/workflows/android-linux.yml | 7 +++++++ 2 files changed, 28 insertions(+) create mode 100644 .github/actions/playstore/action.yml diff --git a/.github/actions/playstore/action.yml b/.github/actions/playstore/action.yml new file mode 100644 index 00000000000..d3364f9d8b1 --- /dev/null +++ b/.github/actions/playstore/action.yml @@ -0,0 +1,21 @@ +name: Publish Android Build to Play Store +description: Checks out the QGC repo with all the correct settings +inputs: + artifact: + description: Build File To Upload + required: true + service_account_json: + description: The service account json private key file to authorize the upload request + required: true +runs: + using: "composite" + steps: + - name: Deploy to Play Store + if: ${{ github.event_name != 'pull_request' && github.ref_name == 'master' }} + uses: r0adkll/upload-google-play@v1 + with: + serviceAccountJsonPlainText: ${{ inputs.service_account_json }} + packageName: com.mavlink.qgroundcontrol + releaseFiles: ${{ inputs.artifact }} + track: production + status: completed diff --git a/.github/workflows/android-linux.yml b/.github/workflows/android-linux.yml index c31977a654c..be85b07fd79 100644 --- a/.github/workflows/android-linux.yml +++ b/.github/workflows/android-linux.yml @@ -100,3 +100,10 @@ jobs: aws_secret_access_key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} source: '' github_token: ${{ secrets.GITHUB_TOKEN }} + + # - name: Deploy to Play Store + # if: matrix.BuildType == 'Release' + # uses: ./.github/actions/playstore + # with: + # artifact_name: ${{ runner.temp }}/shadow_build_dir/${{ env.ARTIFACT }} + # service_account_json: ${{ secrets.SERVICE_ACCOUNT }} From b858e18f07e00ace6aac4d310da46a4fd599c5d9 Mon Sep 17 00:00:00 2001 From: Holden Date: Tue, 27 Aug 2024 17:46:43 -0400 Subject: [PATCH 10/27] AnalyzeView: Fix GeoTagWorker Threading --- src/AnalyzeView/CMakeLists.txt | 1 - src/AnalyzeView/ExifParser.cc | 16 +- src/AnalyzeView/ExifParser.h | 7 +- src/AnalyzeView/GeoTagController.cc | 196 ++++++++++------ src/AnalyzeView/GeoTagController.h | 75 +++---- src/AnalyzeView/GeoTagPage.qml | 187 ++++++++-------- src/AnalyzeView/GeoTagWorker.cc | 271 ++++++++++++++--------- src/AnalyzeView/GeoTagWorker.h | 85 +++---- src/AnalyzeView/PX4LogParser.cc | 4 +- src/AnalyzeView/PX4LogParser.h | 2 +- src/AnalyzeView/ULogParser.cc | 4 +- src/AnalyzeView/ULogParser.h | 2 +- src/Utilities/QGCLoggingCategory.cc | 1 - src/Utilities/QGCLoggingCategory.h | 1 - test/AnalyzeView/CMakeLists.txt | 2 + test/AnalyzeView/ExifParserTest.cc | 6 +- test/AnalyzeView/GeoTagControllerTest.cc | 98 ++++++++ test/AnalyzeView/GeoTagControllerTest.h | 12 + test/AnalyzeView/PX4LogParserTest.cc | 4 +- test/AnalyzeView/ULogParserTest.cc | 4 +- test/CMakeLists.txt | 1 + test/UnitTestList.cc | 2 + 22 files changed, 611 insertions(+), 370 deletions(-) create mode 100644 test/AnalyzeView/GeoTagControllerTest.cc create mode 100644 test/AnalyzeView/GeoTagControllerTest.h diff --git a/src/AnalyzeView/CMakeLists.txt b/src/AnalyzeView/CMakeLists.txt index 9cd572d2113..88e0c3de644 100644 --- a/src/AnalyzeView/CMakeLists.txt +++ b/src/AnalyzeView/CMakeLists.txt @@ -101,7 +101,6 @@ if(NOT exiv2_FOUND AND NOT LibExiv2_FOUND) GIT_REPOSITORY https://github.com/Exiv2/exiv2.git GIT_TAG v0.28.3 GIT_SHALLOW TRUE - GIT_PROGRESS TRUE ) set(EXIV2_ENABLE_XMP OFF CACHE INTERNAL "" FORCE) set(EXIV2_ENABLE_EXTERNAL_XMP OFF CACHE INTERNAL "" FORCE) diff --git a/src/AnalyzeView/ExifParser.cc b/src/AnalyzeView/ExifParser.cc index 5aa15cf440e..ad10ec2e12f 100644 --- a/src/AnalyzeView/ExifParser.cc +++ b/src/AnalyzeView/ExifParser.cc @@ -26,7 +26,7 @@ void init() ::atexit(Exiv2::XmpParser::terminate); } -double readTime(const QByteArray &buf) +QDateTime readTime(const QByteArray &buf) { try { // Convert QByteArray to std::string for Exiv2 @@ -36,7 +36,7 @@ double readTime(const QByteArray &buf) const Exiv2::ExifData &exifData = image->exifData(); if (exifData.empty()) { qCWarning(ExifParserLog) << "No EXIF data found in the image."; - return -1.0; + return QDateTime(); } // Read DateTimeOriginal @@ -45,7 +45,7 @@ double readTime(const QByteArray &buf) const Exiv2::ExifData::const_iterator pos = exifData.findKey(key); if (pos == exifData.end()) { qCWarning(ExifParserLog) << "No DateTimeOriginal found."; - return -1.0; + return QDateTime(); } const std::string dateTimeOriginal = pos->toString(); @@ -54,7 +54,7 @@ double readTime(const QByteArray &buf) if (createDateList.size() < 2) { qCWarning(ExifParserLog) << "Invalid date/time format: " << createDateList; - return -1.0; + return QDateTime(); } const QStringList dateList = createDateList[0].split(':'); @@ -62,7 +62,7 @@ double readTime(const QByteArray &buf) if ((dateList.size() < 3) || (timeList.size() < 3)) { qCWarning(ExifParserLog) << "Could not parse creation date/time: " << dateList << " " << timeList; - return -1.0; + return QDateTime(); } const QDate date(dateList[0].toInt(), dateList[1].toInt(), dateList[2].toInt()); @@ -70,14 +70,14 @@ double readTime(const QByteArray &buf) const QDateTime tagTime(date, time); - return (tagTime.toMSecsSinceEpoch() / 1000.0); + return tagTime; } catch (const Exiv2::Error &e) { qCWarning(ExifParserLog) << "Error reading EXIF data:" << e.what(); - return -1.0; + return QDateTime(); } } -bool write(QByteArray &buf, const GeoTagWorker::cameraFeedbackPacket &geotag) +bool write(QByteArray &buf, const GeoTagWorker::CameraFeedbackPacket &geotag) { try { // Convert QByteArray to std::string for Exiv2 diff --git a/src/AnalyzeView/ExifParser.h b/src/AnalyzeView/ExifParser.h index dd804758d00..98b4ad0668b 100644 --- a/src/AnalyzeView/ExifParser.h +++ b/src/AnalyzeView/ExifParser.h @@ -17,8 +17,9 @@ class QByteArray; Q_DECLARE_LOGGING_CATEGORY(ExifParserLog) -namespace ExifParser { +namespace ExifParser +{ void init(); - double readTime(const QByteArray &buf); - bool write(QByteArray &buf, const GeoTagWorker::cameraFeedbackPacket &geotag); + QDateTime readTime(const QByteArray &buf); + bool write(QByteArray &buf, const GeoTagWorker::CameraFeedbackPacket &geotag); } diff --git a/src/AnalyzeView/GeoTagController.cc b/src/AnalyzeView/GeoTagController.cc index da32c74ac37..fbad9731363 100644 --- a/src/AnalyzeView/GeoTagController.cc +++ b/src/AnalyzeView/GeoTagController.cc @@ -8,120 +8,172 @@ ****************************************************************************/ #include "GeoTagController.h" +#include "GeoTagWorker.h" #include "QGCLoggingCategory.h" #include +#include +#include #include QGC_LOGGING_CATEGORY(GeoTagControllerLog, "qgc.analyzeview.geotagcontroller") -GeoTagController::GeoTagController() - : _progress(0) - , _inProgress(false) +GeoTagController::GeoTagController(QObject *parent) + : QObject(parent) + , _worker(new GeoTagWorker()) + , _workerThread(new QThread(this)) { - connect(&_worker, &GeoTagWorker::progressChanged, this, &GeoTagController::_workerProgressChanged); - connect(&_worker, &GeoTagWorker::error, this, &GeoTagController::_workerError); - connect(&_worker, &GeoTagWorker::started, this, &GeoTagController::inProgressChanged); - connect(&_worker, &GeoTagWorker::finished, this, &GeoTagController::inProgressChanged); + // qCDebug(GeoTagControllerLog) << Q_FUNC_INFO << this; + + _worker->moveToThread(_workerThread); + + (void) connect(_worker, &GeoTagWorker::progressChanged, this, &GeoTagController::_workerProgressChanged); + (void) connect(_worker, &GeoTagWorker::error, this, &GeoTagController::_workerError); + (void) connect(_workerThread, &QThread::started, _worker, &GeoTagWorker::process); + (void) connect(_workerThread, &QThread::started, this, &GeoTagController::inProgressChanged); + (void) connect(_workerThread, &QThread::finished, this, &GeoTagController::inProgressChanged); } GeoTagController::~GeoTagController() { + cancelTagging(); + delete _worker; + // qCDebug(GeoTagControllerLog) << Q_FUNC_INFO << this; } -void GeoTagController::setLogFile(QString filename) +void GeoTagController::cancelTagging() { - filename = QUrl(filename).toLocalFile(); - if (!filename.isEmpty()) { - _worker.setLogFile(filename); - emit logFileChanged(filename); - } + (void) QMetaObject::invokeMethod(_worker, "cancelTagging", Qt::AutoConnection); + (void) QMetaObject::invokeMethod(_workerThread, "quit", Qt::AutoConnection); + + _workerThread->wait(); } -void GeoTagController::setImageDirectory(QString dir) +QString GeoTagController::logFile() const { - dir = QUrl(dir).toLocalFile(); - if (!dir.isEmpty()) { - _worker.setImageDirectory(dir); - emit imageDirectoryChanged(dir); - if(_worker.saveDirectory() == "") { - QDir saveDirectory = QDir(_worker.imageDirectory() + kTagged); - if(saveDirectory.exists()) { - _setErrorMessage(tr("Images have alreay been tagged. Existing images will be removed.")); - return; - } - } + return _worker->logFile(); +} + +QString GeoTagController::imageDirectory() const +{ + return _worker->imageDirectory(); +} + +QString GeoTagController::saveDirectory() const +{ + return _worker->saveDirectory(); +} + +bool GeoTagController::inProgress() const +{ + return _workerThread->isRunning(); +} + +void GeoTagController::setLogFile(const QString &filename) +{ + if (filename.isEmpty()) { + _setErrorMessage(tr("Empty Filename.")); + return; + } + + const QFileInfo logFileInfo = QFileInfo(filename); + if (!logFileInfo.exists() || !logFileInfo.isFile()) { + _setErrorMessage(tr("Invalid Filename.")); + return; } - _errorMessage.clear(); - emit errorMessageChanged(_errorMessage); + + _worker->setLogFile(filename); + emit logFileChanged(filename); + + _setErrorMessage(QString()); } -void GeoTagController::setSaveDirectory(QString dir) +void GeoTagController::setImageDirectory(const QString &dir) { - dir = QUrl(dir).toLocalFile(); - if (!dir.isEmpty()) { - _worker.setSaveDirectory(dir); - emit saveDirectoryChanged(dir); - //-- Check and see if there are images already there - QDir saveDirectory = QDir(_worker.saveDirectory()); - saveDirectory.setFilter(QDir::Files | QDir::Readable | QDir::NoSymLinks | QDir::Writable); - QStringList nameFilters; - nameFilters << "*.jpg" << "*.JPG"; - saveDirectory.setNameFilters(nameFilters); - QStringList imageList = saveDirectory.entryList(); - if(!imageList.isEmpty()) { - _setErrorMessage(tr("The save folder already contains images.")); + if (dir.isEmpty()) { + _setErrorMessage(tr("Invalid Directory.")); + return; + } + + const QFileInfo imageDirectoryInfo = QFileInfo(dir); + if (!imageDirectoryInfo.exists() || !imageDirectoryInfo.isDir()) { + _setErrorMessage(tr("Invalid Directory.")); + return; + } + + _worker->setImageDirectory(dir); + emit imageDirectoryChanged(dir); + + if (_worker->saveDirectory().isEmpty()) { + const QDir saveDirectory = QDir(_worker->imageDirectory() + kTagged); + if (saveDirectory.exists()) { + _setErrorMessage(tr("Images have already been tagged. Existing images will be removed.")); return; } } - _errorMessage.clear(); - emit errorMessageChanged(_errorMessage); + + _setErrorMessage(QString()); +} + +void GeoTagController::setSaveDirectory(const QString &dir) +{ + if (dir.isEmpty()) { + _setErrorMessage(tr("Invalid Directory.")); + return; + } + + const QFileInfo saveDirectoryInfo = QFileInfo(dir); + if (!saveDirectoryInfo.exists() || !saveDirectoryInfo.isDir()) { + _setErrorMessage(tr("Invalid Directory.")); + return; + } + + _worker->setSaveDirectory(dir); + emit saveDirectoryChanged(dir); + + QDir saveDirectory = QDir(_worker->saveDirectory()); + saveDirectory.setFilter(QDir::Files | QDir::Readable | QDir::NoSymLinks | QDir::Writable); + + QStringList nameFilters; + nameFilters << "*.jpg" << "*.JPG"; + saveDirectory.setNameFilters(nameFilters); + + const QStringList imageList = saveDirectory.entryList(); + if (!imageList.isEmpty()) { + _setErrorMessage(tr("The save folder already contains images.")); + return; + } + + _setErrorMessage(QString()); } void GeoTagController::startTagging() { - _errorMessage.clear(); - emit errorMessageChanged(_errorMessage); - QDir imageDirectory = QDir(_worker.imageDirectory()); - if(!imageDirectory.exists()) { + _setErrorMessage(QString()); + + const QDir imageDirectory = QDir(_worker->imageDirectory()); + if (!imageDirectory.exists()) { _setErrorMessage(tr("Cannot find the image directory.")); return; } - if(_worker.saveDirectory() == "") { - QDir oldTaggedFolder = QDir(_worker.imageDirectory() + kTagged); - if(oldTaggedFolder.exists()) { + + if (_worker->saveDirectory().isEmpty()) { + QDir oldTaggedFolder = QDir(_worker->imageDirectory() + kTagged); + if (oldTaggedFolder.exists()) { oldTaggedFolder.removeRecursively(); - if(!imageDirectory.mkdir(_worker.imageDirectory() + kTagged)) { + if (!imageDirectory.mkdir(_worker->imageDirectory() + kTagged)) { _setErrorMessage(tr("Couldn't replace the previously tagged images")); return; } } } else { - QDir saveDirectory = QDir(_worker.saveDirectory()); - if(!saveDirectory.exists()) { + const QDir saveDirectory = QDir(_worker->saveDirectory()); + if (!saveDirectory.exists()) { _setErrorMessage(tr("Cannot find the save directory.")); return; } } - _worker.start(); -} - -void GeoTagController::_workerProgressChanged(double progress) -{ - _progress = progress; - emit progressChanged(progress); -} -void GeoTagController::_workerError(QString errorMessage) -{ - _errorMessage = errorMessage; - emit errorMessageChanged(errorMessage); -} - - -void GeoTagController::_setErrorMessage(const QString& error) -{ - _errorMessage = error; - emit errorMessageChanged(error); + (void) QMetaObject::invokeMethod(_workerThread, "start", Qt::AutoConnection); } diff --git a/src/AnalyzeView/GeoTagController.h b/src/AnalyzeView/GeoTagController.h index 2311c282678..ae97c6689be 100644 --- a/src/AnalyzeView/GeoTagController.h +++ b/src/AnalyzeView/GeoTagController.h @@ -9,12 +9,13 @@ #pragma once +#include #include #include -#include #include -#include "GeoTagWorker.h" +class GeoTagWorker; +class QThread; Q_DECLARE_LOGGING_CATEGORY(GeoTagControllerLog) @@ -24,56 +25,56 @@ class GeoTagController : public QObject Q_OBJECT QML_ELEMENT -public: - GeoTagController(); - ~GeoTagController(); - Q_PROPERTY(QString logFile READ logFile WRITE setLogFile NOTIFY logFileChanged) Q_PROPERTY(QString imageDirectory READ imageDirectory WRITE setImageDirectory NOTIFY imageDirectoryChanged) Q_PROPERTY(QString saveDirectory READ saveDirectory WRITE setSaveDirectory NOTIFY saveDirectoryChanged) + Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY errorMessageChanged) + Q_PROPERTY(double progress READ progress NOTIFY progressChanged) + Q_PROPERTY(bool inProgress READ inProgress NOTIFY inProgressChanged) - /// Set to an error message is geotagging fails - Q_PROPERTY(QString errorMessage READ errorMessage NOTIFY errorMessageChanged) +public: + explicit GeoTagController(QObject *parent = nullptr); + ~GeoTagController(); + + Q_INVOKABLE void startTagging(); + Q_INVOKABLE void cancelTagging(); + + QString logFile() const; + QString imageDirectory() const; + QString saveDirectory() const; /// Progress indicator: 0-100 - Q_PROPERTY(double progress READ progress NOTIFY progressChanged) + double progress() const { return _progress; } /// true: Currently in the process of tagging - Q_PROPERTY(bool inProgress READ inProgress NOTIFY inProgressChanged) + bool inProgress() const; - Q_INVOKABLE void startTagging(); - Q_INVOKABLE void cancelTagging() { _worker.cancelTagging(); } - - QString logFile () const { return _worker.logFile(); } - QString imageDirectory () const { return _worker.imageDirectory(); } - QString saveDirectory () const { return _worker.saveDirectory(); } - double progress () const { return _progress; } - bool inProgress () const { return _worker.isRunning(); } - QString errorMessage () const { return _errorMessage; } + /// Set to an error message if geotagging fails + QString errorMessage() const { return _errorMessage; } - void setLogFile (QString file); - void setImageDirectory (QString dir); - void setSaveDirectory (QString dir); + void setLogFile(const QString &file); + void setImageDirectory(const QString &dir); + void setSaveDirectory(const QString &dir); signals: - void logFileChanged (QString logFile); - void imageDirectoryChanged (QString imageDirectory); - void saveDirectoryChanged (QString saveDirectory); - void progressChanged (double progress); - void inProgressChanged (); - void errorMessageChanged (QString errorMessage); + void logFileChanged(const QString &logFile); + void imageDirectoryChanged(const QString &imageDirectory); + void saveDirectoryChanged(const QString &saveDirectory); + void progressChanged(double progress); + void inProgressChanged(); + void errorMessageChanged(const QString &errorMessage); private slots: - void _workerProgressChanged (double progress); - void _workerError (QString errorMsg); - void _setErrorMessage (const QString& error); + void _workerProgressChanged(double progress) { if (progress != _progress) { _progress = progress; emit progressChanged(_progress); } } + void _setErrorMessage(const QString &errorMsg) { if (errorMsg != _errorMessage) { _errorMessage = errorMsg; emit errorMessageChanged(_errorMessage); } } + void _workerError(const QString &errorMsg) { _setErrorMessage(errorMsg); } private: - QString _errorMessage; - double _progress; - bool _inProgress; - - GeoTagWorker _worker; + QString _errorMessage; + double _progress = 0.; + bool _inProgress = false; + GeoTagWorker *_worker = nullptr; + QThread *_workerThread = nullptr; - static constexpr const char* kTagged = "/TAGGED"; + static constexpr const char *kTagged = "/TAGGED"; }; diff --git a/src/AnalyzeView/GeoTagPage.qml b/src/AnalyzeView/GeoTagPage.qml index 7c8ec8b3918..c97a377601c 100644 --- a/src/AnalyzeView/GeoTagPage.qml +++ b/src/AnalyzeView/GeoTagPage.qml @@ -13,138 +13,147 @@ import QtQuick.Dialogs import QtQuick.Layouts import QGroundControl -import QGroundControl.Palette -import QGroundControl.FactSystem -import QGroundControl.FactControls import QGroundControl.Controls import QGroundControl.ScreenTools import QGroundControl.Controllers AnalyzePage { - id: geoTagPage - pageComponent: pageComponent - pageDescription: qsTr("Used to tag a set of images from a survey mission with gps coordinates. You must provide the binary log from the flight as well as the directory which contains the images to tag.") + pageComponent: pageComponent + pageDescription: qsTr("Used to tag a set of images from a survey mission with gps coordinates. You must provide the binary log from the flight as well as the directory which contains the images to tag.") - readonly property real _margin: ScreenTools.defaultFontPixelWidth * 2 - readonly property real _minWidth: ScreenTools.defaultFontPixelWidth * 20 - readonly property real _maxWidth: ScreenTools.defaultFontPixelWidth * 30 + readonly property real _margin: ScreenTools.defaultFontPixelWidth * 2 + readonly property real _minWidth: ScreenTools.defaultFontPixelWidth * 20 + readonly property real _maxWidth: ScreenTools.defaultFontPixelWidth * 30 Component { - id: pageComponent + id: pageComponent + GridLayout { - columns: 2 - columnSpacing: _margin - rowSpacing: ScreenTools.defaultFontPixelWidth * 2 - width: availableWidth + columns: 2 + columnSpacing: _margin + rowSpacing: ScreenTools.defaultFontPixelWidth * 2 + width: availableWidth + BusyIndicator { - running: geoController.progress > 0 && geoController.progress < 100 && geoController.errorMessage === "" - width: progressBar.height - height: progressBar.height - Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter + running: (geoController.progress > 0) && (geoController.progress < 100) && !geoController.errorMessage + width: progressBar.height + height: progressBar.height + Layout.alignment: Qt.AlignVCenter | Qt.AlignHCenter } - //----------------------------------------------------------------- + ProgressBar { - id: progressBar - to: 100 - value: geoController.progress - opacity: geoController.progress > 0 ? 1 : 0.25 - Layout.fillWidth: true - Layout.alignment: Qt.AlignVCenter + id: progressBar + to: 100 + value: geoController.progress + opacity: (geoController.progress > 0) ? 1 : 0.25 + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter } - //----------------------------------------------------------------- + QGCLabel { - text: geoController.errorMessage - color: "red" - font.bold: true - font.pointSize: ScreenTools.mediumFontPointSize - horizontalAlignment:Text.AlignHCenter - Layout.alignment: Qt.AlignHCenter - Layout.columnSpan: 2 + text: geoController.errorMessage + color: "red" + font.bold: true + font.pointSize: ScreenTools.mediumFontPointSize + horizontalAlignment: Text.AlignHCenter + Layout.alignment: Qt.AlignHCenter + Layout.columnSpan: 2 } - //----------------------------------------------------------------- - //-- Log File + QGCButton { - text: qsTr("Select log file") - onClicked: openLogFile.openForLoad() - Layout.minimumWidth:_minWidth - Layout.maximumWidth:_maxWidth - Layout.fillWidth: true - Layout.alignment: Qt.AlignVCenter + text: qsTr("Select log file") + Layout.minimumWidth: _minWidth + Layout.maximumWidth: _maxWidth + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + onClicked: openLogFile.openForLoad() + QGCFileDialog { - id: openLogFile - title: qsTr("Select log file") - nameFilters: [qsTr("ULog file (*.ulg)"), qsTr("PX4 log file (*.px4log)"), qsTr("All Files (*)")] - defaultSuffix: "ulg" + id: openLogFile + title: qsTr("Select log file") + nameFilters: [qsTr("ULog file (*.ulg)"), qsTr("PX4 log file (*.px4log)"), qsTr("All Files (*)")] + defaultSuffix: "ulg" onAcceptedForLoad: (file) => { - geoController.logFile = openLogFile.file + geoController.logFile = file close() } } } + QGCLabel { - text: geoController.logFile - elide: Text.ElideLeft - Layout.fillWidth: true - Layout.alignment: Qt.AlignVCenter + text: geoController.logFile + elide: Text.ElideLeft + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter } - //----------------------------------------------------------------- - //-- Image Directory + QGCButton { - text: qsTr("Select image directory") - onClicked: selectImageDir.openForLoad() - Layout.minimumWidth:_minWidth - Layout.maximumWidth:_maxWidth - Layout.fillWidth: true - Layout.alignment: Qt.AlignVCenter + text: qsTr("Select image directory") + Layout.minimumWidth: _minWidth + Layout.maximumWidth: _maxWidth + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + onClicked: selectImageDir.openForLoad() + QGCFileDialog { - id: selectImageDir - title: qsTr("Select image directory") - selectFolder: true + id: selectImageDir + title: qsTr("Select image directory") + selectFolder: true onAcceptedForLoad: (file) => { - geoController.imageDirectory = selectImageDir.file + geoController.imageDirectory = file close() } } } + QGCLabel { - text: geoController.imageDirectory - elide: Text.ElideLeft - Layout.fillWidth: true - Layout.alignment: Qt.AlignVCenter + text: geoController.imageDirectory + elide: Text.ElideLeft + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter } - //----------------------------------------------------------------- - //-- Save Directory + QGCButton { - text: qsTr("(Optionally) Select save directory") - onClicked: selectDestDir.openForLoad() - Layout.minimumWidth:_minWidth - Layout.maximumWidth:_maxWidth - Layout.fillWidth: true - Layout.alignment: Qt.AlignVCenter + text: qsTr("(Optionally) Select save directory") + Layout.minimumWidth: _minWidth + Layout.maximumWidth: _maxWidth + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + onClicked: selectDestDir.openForLoad() + QGCFileDialog { - id: selectDestDir - title: qsTr("Select save directory") - selectFolder: true + id: selectDestDir + title: qsTr("Select save directory") + selectFolder: true onAcceptedForLoad: (file) => { - geoController.saveDirectory = selectDestDir.file + geoController.saveDirectory = file close() } } } + QGCLabel { - text: geoController.saveDirectory === "" ? (geoController.imageDirectory === "" ? qsTr("/TAGGED folder in your image folder") : geoController.imageDirectory + qsTr("/TAGGED")) : geoController.saveDirectory - elide: Text.ElideLeft - Layout.fillWidth: true - Layout.alignment: Qt.AlignVCenter + text: { + if (geoController.saveDirectory) { + return geoController.saveDirectory; + } else if (geoController.imageDirectory) { + return geoController.imageDirectory + qsTr("/TAGGED"); + } else { + return qsTr("/TAGGED folder in your image folder"); + } + } + elide: Text.ElideLeft + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter } - //----------------------------------------------------------------- - //-- Execute + QGCButton { - text: geoController.inProgress ? qsTr("Cancel Tagging") : qsTr("Start Tagging") - width: ScreenTools.defaultFontPixelWidth * 30 - enabled: (geoController.imageDirectory !== "" && geoController.logFile !== "") || geoController.inProgress - Layout.alignment: Qt.AlignHCenter - Layout.columnSpan: 2 + text: geoController.inProgress ? qsTr("Cancel Tagging") : qsTr("Start Tagging") + enabled: (geoController.imageDirectory && geoController.logFile) || geoController.inProgress + Layout.minimumWidth: _minWidth + Layout.maximumWidth: _maxWidth + Layout.alignment: Qt.AlignHCenter + Layout.columnSpan: 2 onClicked: { if (geoController.inProgress) { geoController.cancelTagging() diff --git a/src/AnalyzeView/GeoTagWorker.cc b/src/AnalyzeView/GeoTagWorker.cc index 91aecb614ed..234ae6f470b 100644 --- a/src/AnalyzeView/GeoTagWorker.cc +++ b/src/AnalyzeView/GeoTagWorker.cc @@ -7,6 +7,8 @@ * ****************************************************************************/ +// TODO: https://github.com/PX4/PX4-Autopilot/blob/main/Tools/geotag_images_ulog.py + #include "GeoTagWorker.h" #include "ExifParser.h" #include "ULogParser.h" @@ -17,175 +19,234 @@ QGC_LOGGING_CATEGORY(GeoTagWorkerLog, "qgc.analyzeview.geotagworker") -GeoTagWorker::GeoTagWorker() - : _cancel(false) +GeoTagWorker::GeoTagWorker(QObject *parent) + : QObject(parent) { + // qCDebug(GeoTagWorkerLog) << Q_FUNC_INFO << this; + +#ifdef QT_DEBUG + (void) connect(this, &GeoTagWorker::error, this, [](const QString &errorMsg) { + qCDebug(GeoTagWorkerLog) << errorMsg; + }, Qt::AutoConnection); +#endif +} +GeoTagWorker::~GeoTagWorker() +{ + // qCDebug(GeoTagWorkerLog) << Q_FUNC_INFO << this; } -void GeoTagWorker::run() +bool GeoTagWorker::process() { _cancel = false; emit progressChanged(1); - double nSteps = 5; - // Load Images + using StepFunction = bool (GeoTagWorker::*)(); + const StepFunction steps[] = { + &GeoTagWorker::_loadImages, + &GeoTagWorker::_parseExif, + &GeoTagWorker::_parseLogs, + &GeoTagWorker::_calibrate, + &GeoTagWorker::_tagImages + }; + + for (StepFunction step : steps) { + if (_cancel) { + emit error(tr("Tagging cancelled")); + return false; + } + + if (!(this->*step)()) { + return false; + } + } + + emit progressChanged(100); + emit taggingComplete(); + + return true; +} + +bool GeoTagWorker::_loadImages() +{ _imageList.clear(); + QDir imageDirectory = QDir(_imageDirectory); - imageDirectory.setFilter(QDir::Files | QDir::Readable | QDir::NoSymLinks | QDir::Writable); + imageDirectory.setFilter(QDir::Files | QDir::Readable | QDir::NoSymLinks); imageDirectory.setSorting(QDir::Name); + QStringList nameFilters; nameFilters << "*.jpg" << "*.JPG"; imageDirectory.setNameFilters(nameFilters); + _imageList = imageDirectory.entryInfoList(); - if(_imageList.isEmpty()) { + if (_imageList.isEmpty()) { emit error(tr("The image directory doesn't contain images, make sure your images are of the JPG format")); - return; + return false; } - emit progressChanged((100/nSteps)); - // Parse EXIF - _imageTime.clear(); - for (int i = 0; i < _imageList.size(); ++i) { - QFile file(_imageList.at(i).absoluteFilePath()); - if (!file.open(QIODevice::ReadOnly)) { - emit error(tr("Geotagging failed. Couldn't open an image.")); - return; - } - QByteArray imageBuffer = file.readAll(); - file.close(); + emit progressChanged(100. / kSteps); - _imageTime.append(ExifParser::readTime(imageBuffer)); + return true; +} - emit progressChanged((100/nSteps) + ((100/nSteps) / _imageList.size())*i); +bool GeoTagWorker::_parseExif() +{ + _imageTimestamps.clear(); + // TODO: QtConcurrent::mapped? + for (const QFileInfo& fileInfo : _imageList) { if (_cancel) { - qCDebug(GeotaggingLog) << "Tagging cancelled"; emit error(tr("Tagging cancelled")); - return; + return false; + } + + QFile file(fileInfo.absoluteFilePath()); + if (!file.open(QIODevice::ReadOnly)) { + emit error(tr("Geotagging failed. Couldn't open image: %1").arg(fileInfo.fileName())); + return false; + } + + const QByteArray imageBuffer = file.readAll(); + file.close(); + + const QDateTime imageTime = ExifParser::readTime(imageBuffer); + if (!imageTime.isValid()) { + emit error(tr("Geotagging failed. Couldn't extract time from image: %1").arg(fileInfo.fileName())); + return false; } + + (void) _imageTimestamps.append(imageTime.toSecsSinceEpoch()); } - // Load log - bool isULog = _logFile.endsWith(".ulg", Qt::CaseSensitive); + emit progressChanged(2.0 * (100.0 / kSteps)); + + return true; +} + +bool GeoTagWorker::_parseLogs() +{ + _triggerList.clear(); + QFile file(_logFile); if (!file.open(QIODevice::ReadOnly)) { emit error(tr("Geotagging failed. Couldn't open log file.")); - return; + return false; } - QByteArray log = file.readAll(); + + const QByteArray log = file.readAll(); file.close(); - // Instantiate appropriate parser - _triggerList.clear(); bool parseComplete = false; QString errorString; - if (isULog) { + if (_logFile.endsWith(".ulg", Qt::CaseSensitive)) { parseComplete = ULogParser::getTagsFromLog(log, _triggerList, errorString); } else { parseComplete = PX4LogParser::getTagsFromLog(log, _triggerList); } if (!parseComplete) { - if (_cancel) { - qCDebug(GeotaggingLog) << "Tagging cancelled"; - emit error(tr("Tagging cancelled")); - return; - } else { - qCDebug(GeotaggingLog) << "Log parsing failed"; - errorString = tr("%1 - tagging cancelled").arg(errorString.isEmpty() ? tr("Log parsing failed") : errorString); - emit error(errorString); - return; - } + emit error(errorString.isEmpty() ? tr("Log parsing failed") : errorString); + return false; + } + + qCDebug(GeoTagWorkerLog) << "Found" << _triggerList.count() << "trigger logs."; + if (_imageList.count() > _triggerList.count()) { + qCDebug(GeoTagWorkerLog) << "Detected missing feedback packets."; + } else if (_imageList.count() < _triggerList.count()) { + qCDebug(GeoTagWorkerLog) << "Detected missing image frames."; + } + + emit progressChanged(3. * (100. / kSteps)); + + return true; +} + +bool GeoTagWorker::_calibrate() +{ + _imageIndices.clear(); + _imageOffsets.clear(); + _triggerIndices.clear(); + + if (_triggerList.isEmpty() || _imageTimestamps.isEmpty()) { + emit error(tr("Calibration failed: No triggers or images available.")); + return false; } - emit progressChanged(3*(100/nSteps)); - qCDebug(GeotaggingLog) << "Found " << _triggerList.count() << " trigger logs."; + const qint64 lastImageTimestamp = _imageTimestamps.last(); + const qint64 lastTriggerTimestamp = _triggerList.last().timestamp; - if (_cancel) { - qCDebug(GeotaggingLog) << "Tagging cancelled"; - emit error(tr("Tagging cancelled")); - return; + for (int i = 0; i < _imageTimestamps.size(); ++i) { + const qint64 offset = lastImageTimestamp - _imageTimestamps[i]; + (void) _imageOffsets.insert(offset, i); } - // Filter Trigger - if (!triggerFiltering()) { - qCDebug(GeotaggingLog) << "Geotagging failed in trigger filtering"; - emit error(tr("Geotagging failed in trigger filtering")); - return; + for (const CameraFeedbackPacket &trigger : _triggerList) { + const qint64 triggerOffset = lastTriggerTimestamp - trigger.timestamp; + if (_imageOffsets.contains(triggerOffset)) { + const int imageIndex = _imageOffsets[triggerOffset]; + (void) _imageIndices.append(imageIndex); + (void) _triggerIndices.append(&trigger - &_triggerList[0]); + } } - emit progressChanged(4*(100/nSteps)); - if (_cancel) { - qCDebug(GeotaggingLog) << "Tagging cancelled"; - emit error(tr("Tagging cancelled")); - return; + if (_imageIndices.isEmpty()) { + emit error(tr("Calibration failed: No matching triggers found for images.")); + return false; } - // Tag images - auto maxIndex = std::min(_imageIndices.count(), _triggerIndices.count()); - maxIndex = std::min(maxIndex, _imageList.count()); - for(int i = 0; i < maxIndex; i++) { - int imageIndex = _imageIndices[i]; + emit progressChanged(4. * (100. / kSteps)); + + return true; +} + +bool GeoTagWorker::_tagImages() +{ + const qsizetype maxIndex = std::min(_imageIndices.count(), _triggerIndices.count()); + for (int i = 0; i < maxIndex; i++) { + if (_cancel) { + emit error(tr("Tagging cancelled")); + return false; + } + + const int imageIndex = _imageIndices[i]; if (imageIndex >= _imageList.count()) { emit error(tr("Geotagging failed. Requesting image #%1, but only %2 images present.").arg(imageIndex).arg(_imageList.count())); - return; + return false; } - QFile fileRead(_imageList.at(_imageIndices[i]).absoluteFilePath()); + + const QFileInfo &imageInfo = _imageList.at(imageIndex); + QFile fileRead(imageInfo.absoluteFilePath()); if (!fileRead.open(QIODevice::ReadOnly)) { emit error(tr("Geotagging failed. Couldn't open an image.")); - return; + return false; } + QByteArray imageBuffer = fileRead.readAll(); fileRead.close(); - if (!ExifParser::write(imageBuffer, _triggerList[_triggerIndices[i]])) { - emit error(tr("Geotagging failed. Couldn't write to image.")); - return; - } else { - QFile fileWrite; - if(_saveDirectory == "") { - fileWrite.setFileName(_imageDirectory + "/TAGGED/" + _imageList.at(_imageIndices[i]).fileName()); - } else { - fileWrite.setFileName(_saveDirectory + "/" + _imageList.at(_imageIndices[i]).fileName()); - } - if (!fileWrite.open(QFile::WriteOnly)) { - emit error(tr("Geotagging failed. Couldn't write to an image.")); - return; - } - fileWrite.write(imageBuffer); - fileWrite.close(); + if (!ExifParser::write(imageBuffer, _triggerList[imageIndex])) { + emit error(tr("Geotagging failed. Couldn't write to image: %1").arg(imageInfo.fileName())); + return false; } - emit progressChanged(4*(100/nSteps) + ((100/nSteps) / maxIndex)*i); - if (_cancel) { - qCDebug(GeotaggingLog) << "Tagging cancelled"; - emit error(tr("Tagging cancelled")); - return; + QFile fileWrite; + if (_saveDirectory.isEmpty()) { + fileWrite.setFileName(_imageDirectory + "/TAGGED/" + imageInfo.fileName()); + } else { + fileWrite.setFileName(_saveDirectory + "/" + imageInfo.fileName()); } - } - if (_cancel) { - qCDebug(GeotaggingLog) << "Tagging cancelled"; - emit error(tr("Tagging cancelled")); - return; - } + if (!fileWrite.open(QFile::WriteOnly)) { + emit error(tr("Geotagging failed. Couldn't write to image: %1").arg(imageInfo.fileName())); + return false; + } - emit progressChanged(100); -} + fileWrite.write(imageBuffer); + fileWrite.close(); -bool GeoTagWorker::triggerFiltering() -{ - _imageIndices.clear(); - _triggerIndices.clear(); - if(_imageList.count() > _triggerList.count()) { // Logging dropouts - qCDebug(GeotaggingLog) << "Detected missing feedback packets."; - } else if (_imageList.count() < _triggerList.count()) { // Camera skipped frames - qCDebug(GeotaggingLog) << "Detected missing image frames."; - } - for(int i = 0; i < _imageList.count() && i < _triggerList.count(); i++) { - _imageIndices.append(static_cast(_triggerList[i].imageSequence)); - _triggerIndices.append(i); + emit progressChanged(4. * (100. / kSteps) + ((100. / kSteps) / maxIndex) * i); } + return true; } diff --git a/src/AnalyzeView/GeoTagWorker.h b/src/AnalyzeView/GeoTagWorker.h index 0889828edc4..602b6f57558 100644 --- a/src/AnalyzeView/GeoTagWorker.h +++ b/src/AnalyzeView/GeoTagWorker.h @@ -9,61 +9,66 @@ #pragma once -#include -#include -#include #include #include +#include +#include Q_DECLARE_LOGGING_CATEGORY(GeoTagWorkerLog) -class GeoTagWorker : public QThread +class GeoTagWorker : public QObject { Q_OBJECT public: - GeoTagWorker(); - - void setLogFile (const QString& logFile) { _logFile = logFile; } - void setImageDirectory (const QString& imageDirectory) { _imageDirectory = imageDirectory; } - void setSaveDirectory (const QString& saveDirectory) { _saveDirectory = saveDirectory; } + explicit GeoTagWorker(QObject *parent = nullptr); + ~GeoTagWorker(); - QString logFile () const { return _logFile; } - QString imageDirectory () const { return _imageDirectory; } - QString saveDirectory () const { return _saveDirectory; } + QString logFile() const { return _logFile; } + void setLogFile(const QString &logFile) { _logFile = logFile; } + QString imageDirectory() const { return _imageDirectory; } + void setImageDirectory(const QString &imageDirectory) { _imageDirectory = imageDirectory; } + QString saveDirectory() const { return _saveDirectory; } + void setSaveDirectory(const QString &saveDirectory) { _saveDirectory = saveDirectory; } - void cancelTagging () { _cancel = true; } - - struct cameraFeedbackPacket { - double timestamp; - double timestampUTC; - uint32_t imageSequence; - double latitude; - double longitude; - float altitude; - float groundDistance; - float attitudeQuaternion[4]; - uint8_t captureResult; + struct CameraFeedbackPacket { + double timestamp = 0.; + double timestampUTC = 0.; + uint32_t imageSequence = 0; + double latitude = 0.; + double longitude = 0.; + float altitude = 0.; + float groundDistance = 0.; + float attitudeQuaternion[4]{}; + uint8_t captureResult = 0; }; -protected: - void run() final; - signals: - void error (QString errorMsg); - void taggingComplete (); - void progressChanged (double progress); + void error(const QString &errorMsg); + void progressChanged(double progress); + void taggingComplete(); + +public slots: + bool process(); + void cancelTagging() { _cancel = true; } private: - bool triggerFiltering(); + bool _loadImages(); + bool _parseExif(); + bool _parseLogs(); + bool _calibrate(); + bool _tagImages(); + + bool _cancel = false; + QString _logFile; + QString _imageDirectory; + QString _saveDirectory; + QFileInfoList _imageList; + QList _imageTimestamps; + QList _triggerList; + QList _imageIndices; + QList _imageOffsets; + QList _triggerIndices; - bool _cancel; - QString _logFile; - QString _imageDirectory; - QString _saveDirectory; - QFileInfoList _imageList; - QList _imageTime; - QList _triggerList; - QList _imageIndices; - QList _triggerIndices; + static constexpr double kSteps = 5.; }; diff --git a/src/AnalyzeView/PX4LogParser.cc b/src/AnalyzeView/PX4LogParser.cc index 12ef498e267..21f9e240e89 100644 --- a/src/AnalyzeView/PX4LogParser.cc +++ b/src/AnalyzeView/PX4LogParser.cc @@ -35,7 +35,7 @@ static constexpr const int triggerLengths[2] = {8, 4}; namespace PX4LogParser { -bool getTagsFromLog(const QByteArray& log, QList& cameraFeedback) +bool getTagsFromLog(const QByteArray& log, QList& cameraFeedback) { // extract header information: message lengths const uint8_t* iptr = reinterpret_cast(log.mid(log.indexOf(gposHeaderHeader) + 4, 1).constData()); @@ -60,7 +60,7 @@ bool getTagsFromLog(const QByteArray& log, QList(log.mid(index + triggerOffsets[0], triggerLengths[0]).data()); diff --git a/src/AnalyzeView/PX4LogParser.h b/src/AnalyzeView/PX4LogParser.h index ede7dc1d402..17994f1a5d6 100644 --- a/src/AnalyzeView/PX4LogParser.h +++ b/src/AnalyzeView/PX4LogParser.h @@ -18,5 +18,5 @@ Q_DECLARE_LOGGING_CATEGORY(PX4LogParserLog) namespace PX4LogParser { - bool getTagsFromLog(const QByteArray &log, QList &cameraFeedback); + bool getTagsFromLog(const QByteArray &log, QList &cameraFeedback); } diff --git a/src/AnalyzeView/ULogParser.cc b/src/AnalyzeView/ULogParser.cc index fde59160397..4897f2c0731 100644 --- a/src/AnalyzeView/ULogParser.cc +++ b/src/AnalyzeView/ULogParser.cc @@ -22,7 +22,7 @@ QGC_LOGGING_CATEGORY(ULogParserLog, "qgc.analyzeview.ulogparser") namespace ULogParser { -bool getTagsFromLog(const QByteArray &log, QList &cameraFeedback, QString &errorMessage) +bool getTagsFromLog(const QByteArray &log, QList &cameraFeedback, QString &errorMessage) { errorMessage.clear(); @@ -51,7 +51,7 @@ bool getTagsFromLog(const QByteArray &log, QList subscription = data->subscription("camera_capture"); for (const TypedDataView &sample : *subscription) { - GeoTagWorker::cameraFeedbackPacket feedback = {0}; + GeoTagWorker::CameraFeedbackPacket feedback = {0}; try { feedback.timestamp = sample.at("timestamp").as() / 1.0e6; // to seconds diff --git a/src/AnalyzeView/ULogParser.h b/src/AnalyzeView/ULogParser.h index 5006e88f164..c813a49a45a 100644 --- a/src/AnalyzeView/ULogParser.h +++ b/src/AnalyzeView/ULogParser.h @@ -22,5 +22,5 @@ Q_DECLARE_LOGGING_CATEGORY(ULogParserLog) namespace ULogParser { /// Get GeoTags from a ULog /// @return true if failed, errorMessage set - bool getTagsFromLog(const QByteArray &log, QList &cameraFeedback, QString &errorMessage); + bool getTagsFromLog(const QByteArray &log, QList &cameraFeedback, QString &errorMessage); } // namespace ULogParser diff --git a/src/Utilities/QGCLoggingCategory.cc b/src/Utilities/QGCLoggingCategory.cc index fb514ed23d1..fc144b505e2 100644 --- a/src/Utilities/QGCLoggingCategory.cc +++ b/src/Utilities/QGCLoggingCategory.cc @@ -23,7 +23,6 @@ QGC_LOGGING_CATEGORY(FirmwareUpgradeVerboseLog, "FirmwareUpgradeVerboseLog") QGC_LOGGING_CATEGORY(MissionCommandsLog, "MissionCommandsLog") QGC_LOGGING_CATEGORY(MissionItemLog, "MissionItemLog") QGC_LOGGING_CATEGORY(ParameterManagerLog, "ParameterManagerLog") -QGC_LOGGING_CATEGORY(GeotaggingLog, "GeotaggingLog") QGC_LOGGING_CATEGORY(RTKGPSLog, "RTKGPSLog") QGC_LOGGING_CATEGORY(GuidedActionsControllerLog, "GuidedActionsControllerLog") QGC_LOGGING_CATEGORY(LocalizationLog, "LocalizationLog") diff --git a/src/Utilities/QGCLoggingCategory.h b/src/Utilities/QGCLoggingCategory.h index d42652c46a3..0c74f5d47cf 100644 --- a/src/Utilities/QGCLoggingCategory.h +++ b/src/Utilities/QGCLoggingCategory.h @@ -19,7 +19,6 @@ Q_DECLARE_LOGGING_CATEGORY(FirmwareUpgradeVerboseLog) Q_DECLARE_LOGGING_CATEGORY(MissionCommandsLog) Q_DECLARE_LOGGING_CATEGORY(MissionItemLog) Q_DECLARE_LOGGING_CATEGORY(ParameterManagerLog) -Q_DECLARE_LOGGING_CATEGORY(GeotaggingLog) Q_DECLARE_LOGGING_CATEGORY(RTKGPSLog) Q_DECLARE_LOGGING_CATEGORY(GuidedActionsControllerLog) Q_DECLARE_LOGGING_CATEGORY(LocalizationLog) diff --git a/test/AnalyzeView/CMakeLists.txt b/test/AnalyzeView/CMakeLists.txt index 449f8b887be..5f1451e46b5 100644 --- a/test/AnalyzeView/CMakeLists.txt +++ b/test/AnalyzeView/CMakeLists.txt @@ -4,6 +4,8 @@ qt_add_library(AnalyzeViewTest STATIC ExifParserTest.cc ExifParserTest.h + GeoTagControllerTest.cc + GeoTagControllerTest.h LogDownloadTest.cc LogDownloadTest.h MavlinkLogTest.cc diff --git a/test/AnalyzeView/ExifParserTest.cc b/test/AnalyzeView/ExifParserTest.cc index f5fc35e70f3..40ab270ede7 100644 --- a/test/AnalyzeView/ExifParserTest.cc +++ b/test/AnalyzeView/ExifParserTest.cc @@ -12,13 +12,13 @@ void ExifParserTest::_readTimeTest() const QByteArray imageBuffer = file.readAll(); file.close(); - const double imageTime = ExifParser::readTime(imageBuffer); + const qint64 imageTime = ExifParser::readTime(imageBuffer).toSecsSinceEpoch(); const QDate date(2008, 10, 22); const QTime time(16, 28, 39); const QDateTime tagTime(date, time); - const double expectedTime = (tagTime.toMSecsSinceEpoch() / 1000.0); + const qint64 expectedTime = tagTime.toSecsSinceEpoch(); QCOMPARE(imageTime, expectedTime); } @@ -31,7 +31,7 @@ void ExifParserTest::_writeTest() QByteArray imageBuffer = file.readAll(); file.close(); - struct GeoTagWorker::cameraFeedbackPacket data; + struct GeoTagWorker::CameraFeedbackPacket data; data.latitude = 37.225; data.longitude = -80.425; diff --git a/test/AnalyzeView/GeoTagControllerTest.cc b/test/AnalyzeView/GeoTagControllerTest.cc new file mode 100644 index 00000000000..78fac915f6b --- /dev/null +++ b/test/AnalyzeView/GeoTagControllerTest.cc @@ -0,0 +1,98 @@ +#include "GeoTagControllerTest.h" +#include "GeoTagController.h" +#include "GeoTagWorker.h" + +#include +#include + +void GeoTagControllerTest::_geoTagControllerTest() +{ + const QDir tempDir = QDir(QDir::tempPath()); + const QString imageDirPath = QDir::tempPath() + "/QGC_GEOTAG_TEST"; + const QString subDirName = QString("QGC_GEOTAG_TEST"); + if (!tempDir.exists(subDirName)) { + QVERIFY(tempDir.mkdir(subDirName)); + } else { + QDir subDir(QDir::tempPath() + "/" + subDirName); + const QStringList files = subDir.entryList(QDir::Files); + for (const QString &fileName : files) { + QVERIFY(subDir.remove(fileName)); + } + } + + QFile file(":/DSCN0010.jpg"); + for (int i = 0; i < 58; ++i) { + QVERIFY(file.copy(imageDirPath + QStringLiteral("/geotag_temp_image_%1.jpg").arg(i))); + } + + QDir imageDir = QDir(imageDirPath); + if (!imageDir.exists("TAGGED")) { + QVERIFY(imageDir.mkdir("TAGGED")); + } else { + QDir subDir(imageDirPath + "/TAGGED"); + const QStringList files = subDir.entryList(QDir::Files); + for (const QString &fileName : files) { + QVERIFY(subDir.remove(fileName)); + } + } + + GeoTagController* const controller = new GeoTagController(this); + + const QFileInfo log = QFileInfo(":/SampleULog.ulg"); + controller->setLogFile(log.filePath()); + controller->setImageDirectory(imageDirPath + "/"); + controller->setSaveDirectory(controller->imageDirectory() + "/TAGGED"); + + QVERIFY(!controller->logFile().isEmpty()); + QVERIFY(!controller->imageDirectory().isEmpty()); + QVERIFY(!controller->saveDirectory().isEmpty()); + + QSignalSpy spyGeoTagControllerProgress(controller, &GeoTagController::progressChanged); + + controller->startTagging(); + + QCOMPARE(spyGeoTagControllerProgress.wait(1000), true); + + QVERIFY(controller->progress() > 0); + QCOMPARE(controller->errorMessage(), ""); +} + +void GeoTagControllerTest::_geoTagWorkerTest() +{ + const QDir tempDir = QDir(QDir::tempPath()); + const QString imageDirPath = QDir::tempPath() + "/QGC_GEOTAG_TEST"; + const QString subDirName = QString("QGC_GEOTAG_TEST"); + if (!tempDir.exists(subDirName)) { + QVERIFY(tempDir.mkdir(subDirName)); + } else { + QDir subDir(QDir::tempPath() + "/" + subDirName); + const QStringList files = subDir.entryList(QDir::Files); + for (const QString &fileName : files) { + QVERIFY(subDir.remove(fileName)); + } + } + + QFile file(":/DSCN0010.jpg"); + for (int i = 0; i < 58; ++i) { + QVERIFY(file.copy(imageDirPath + QStringLiteral("/geotag_temp_image_%1.jpg").arg(i))); + } + + QDir imageDir = QDir(imageDirPath); + if (!imageDir.exists("TAGGED")) { + QVERIFY(imageDir.mkdir("TAGGED")); + } else { + QDir subDir(imageDirPath + "/TAGGED"); + const QStringList files = subDir.entryList(QDir::Files); + for (const QString &fileName : files) { + QVERIFY(subDir.remove(fileName)); + } + } + + const QFileInfo log = QFileInfo(":/SampleULog.ulg"); + GeoTagWorker* const worker = new GeoTagWorker(this); + worker->setLogFile(log.filePath()); + worker->setImageDirectory(imageDirPath + "/"); + worker->setSaveDirectory(worker->imageDirectory() + "/TAGGED"); + + QVERIFY(worker->process()); +} diff --git a/test/AnalyzeView/GeoTagControllerTest.h b/test/AnalyzeView/GeoTagControllerTest.h new file mode 100644 index 00000000000..e51b646bb18 --- /dev/null +++ b/test/AnalyzeView/GeoTagControllerTest.h @@ -0,0 +1,12 @@ +#pragma once + +#include "UnitTest.h" + +class GeoTagControllerTest : public UnitTest +{ + Q_OBJECT + +private slots: + void _geoTagControllerTest(); + void _geoTagWorkerTest(); +}; diff --git a/test/AnalyzeView/PX4LogParserTest.cc b/test/AnalyzeView/PX4LogParserTest.cc index 5a9416fffff..70eb2709df7 100644 --- a/test/AnalyzeView/PX4LogParserTest.cc +++ b/test/AnalyzeView/PX4LogParserTest.cc @@ -12,11 +12,11 @@ void PX4LogParserTest::_getTagsFromLogTest() const QByteArray logBuffer = file.readAll(); file.close(); - QList cameraFeedback; + QList cameraFeedback; QVERIFY(PX4LogParser::getTagsFromLog(logBuffer, cameraFeedback)); QVERIFY(!cameraFeedback.isEmpty()); - GeoTagWorker::cameraFeedbackPacket firstCameraFeedback = cameraFeedback.first(); + GeoTagWorker::CameraFeedbackPacket firstCameraFeedback = cameraFeedback.first(); QVERIFY(!qFuzzyIsNull(firstCameraFeedback.timestamp)); QVERIFY(firstCameraFeedback.imageSequence != 0);*/ } diff --git a/test/AnalyzeView/ULogParserTest.cc b/test/AnalyzeView/ULogParserTest.cc index 06531333922..b83bcdc9e3d 100644 --- a/test/AnalyzeView/ULogParserTest.cc +++ b/test/AnalyzeView/ULogParserTest.cc @@ -12,13 +12,13 @@ void ULogParserTest::_getTagsFromLogTest() const QByteArray logBuffer = file.readAll(); file.close(); - QList cameraFeedback; + QList cameraFeedback; QString errorMessage; QVERIFY(ULogParser::getTagsFromLog(logBuffer, cameraFeedback, errorMessage)); QVERIFY(errorMessage.isEmpty()); QVERIFY(!cameraFeedback.isEmpty()); - const GeoTagWorker::cameraFeedbackPacket firstCameraFeedback = cameraFeedback.constFirst(); + const GeoTagWorker::CameraFeedbackPacket firstCameraFeedback = cameraFeedback.constFirst(); // QVERIFY(!qFuzzyIsNull(firstCameraFeedback.timestamp)); QVERIFY(firstCameraFeedback.imageSequence != 0); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 24f743bcd45..ef75ce1f526 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -29,6 +29,7 @@ add_qgc_test(ADSBTest) add_subdirectory(AnalyzeView) add_qgc_test(ExifParserTest) +add_qgc_test(GeoTagControllerTest) # add_qgc_test(LogDownloadTest) # add_qgc_test(MavlinkLogTest) add_qgc_test(PX4LogParserTest) diff --git a/test/UnitTestList.cc b/test/UnitTestList.cc index 62476b9e8f9..7a408f67211 100644 --- a/test/UnitTestList.cc +++ b/test/UnitTestList.cc @@ -17,6 +17,7 @@ // AnalyzeView #include "ExifParserTest.h" +#include "GeoTagControllerTest.h" // #include "MavlinkLogTest.h" // #include "LogDownloadTest.h" #include "PX4LogParserTest.h" @@ -113,6 +114,7 @@ int runTests(bool stress, QStringView unitTestOptions) // AnalyzeView UT_REGISTER_TEST(ExifParserTest) + UT_REGISTER_TEST(GeoTagControllerTest) // UT_REGISTER_TEST(MavlinkLogTest) // UT_REGISTER_TEST(LogDownloadTest) UT_REGISTER_TEST(PX4LogParserTest) From 6407b70395ef5b2afaa650edf280e537e50e662d Mon Sep 17 00:00:00 2001 From: Tihran Katolikian Date: Fri, 18 Oct 2024 10:41:42 +0300 Subject: [PATCH 11/27] add 'cancelPending' flag and use it for proper download cancelation --- src/QtLocationPlugin/QGCCachedTileSet.cpp | 11 +++++++++++ src/QtLocationPlugin/QGCCachedTileSet.h | 3 ++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/QtLocationPlugin/QGCCachedTileSet.cpp b/src/QtLocationPlugin/QGCCachedTileSet.cpp index c6eab704ac2..94fb84019bd 100644 --- a/src/QtLocationPlugin/QGCCachedTileSet.cpp +++ b/src/QtLocationPlugin/QGCCachedTileSet.cpp @@ -61,6 +61,11 @@ QString QGCCachedTileSet::downloadStatus() const void QGCCachedTileSet::createDownloadTask() { + if (_cancelPending) { + setDownloading(false); + return; + } + if (!_downloading) { setErrorCount(0); setDownloading(true); @@ -82,11 +87,17 @@ void QGCCachedTileSet::createDownloadTask() void QGCCachedTileSet::resumeDownloadTask() { + _cancelPending = false; QGCUpdateTileDownloadStateTask* const task = new QGCUpdateTileDownloadStateTask(_id, QGCTile::StatePending, "*"); getQGCMapEngine()->addTask(task); createDownloadTask(); } +void QGCCachedTileSet::cancelDownloadTask() +{ + _cancelPending = true; +} + void QGCCachedTileSet::_tileListFetched(const QQueue &tiles) { _batchRequested = false; diff --git a/src/QtLocationPlugin/QGCCachedTileSet.h b/src/QtLocationPlugin/QGCCachedTileSet.h index 73ce1cbccbb..7f1d5cb8838 100644 --- a/src/QtLocationPlugin/QGCCachedTileSet.h +++ b/src/QtLocationPlugin/QGCCachedTileSet.h @@ -74,7 +74,7 @@ class QGCCachedTileSet : public QObject Q_INVOKABLE void createDownloadTask(); Q_INVOKABLE void resumeDownloadTask(); - Q_INVOKABLE void cancelDownloadTask() { setDownloading(false); } + Q_INVOKABLE void cancelDownloadTask(); const QString &name() const { return _name; } const QString &mapTypeStr() const { return _mapTypeStr; } @@ -184,6 +184,7 @@ private slots: bool _noMoreTiles = false; bool _batchRequested = false; bool _selected = false; + bool _cancelPending = false; QDateTime _creationDate; QHash _replies; From d915ef0d1e83a172524137e7ab431a63ab3cfc14 Mon Sep 17 00:00:00 2001 From: Holden Date: Sat, 19 Oct 2024 10:34:33 -0400 Subject: [PATCH 12/27] CI: Build GStreamer for Linux --- .github/actions/gstreamer/action.yml | 34 ++++++++++++++++------------ .github/workflows/linux.yml | 15 ++++++++---- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/.github/actions/gstreamer/action.yml b/.github/actions/gstreamer/action.yml index 58cfc117c3c..6855f0c73e3 100644 --- a/.github/actions/gstreamer/action.yml +++ b/.github/actions/gstreamer/action.yml @@ -4,7 +4,7 @@ inputs: gst_version: description: Version of GStreamer to Build required: true - default: 1.22.12 + default: 1.24.8 build_type: description: Build Type "release" or "debug" required: true @@ -34,24 +34,27 @@ runs: run: meson setup --prefix=${{ inputs.install_directory }} --buildtype=${{ inputs.build_type }} - --default-library=static - --wrap-mode=forcefallback - --strip - -Dauto_features=disabled - -Dgst-full-libraries=gstreamer,base,video,gl + --wrap_mode=forcefallback + -Dgst-full-libraries=base,video,gl -Dgpl=enabled -Dlibav=enabled -Dorc=enabled -Dbase=enabled + -Dvaapi=enabled + -Dqt6=enabled -Dgst-plugins-base:gl=enabled - -Dgst-plugins-base:gl_platform=glx - -Dgst-plugins-base:gl_winsys=x11 + -Dgst-plugins-base:gl_platform=glx,egl + -Dgst-plugins-base:gl_winsys=x11,egl,wayland + -Dgst-plugins-base:gl_api=opengl,gles2 -Dgst-plugins-base:x11=enabled -Dgst-plugins-base:playback=enabled -Dgst-plugins-base:tcp=enabled + -Dgst-plugins-base:app=enabled -Dgood=enabled -Dgst-plugins-good:qt6=enabled -Dgst-plugins-good:qt-x11=enabled + -Dgst-plugins-good:qt-egl=enabled + -Dgst-plugins-good:qt-wayland=enabled -Dgst-plugins-good:qt-method=auto -Dgst-plugins-good:isomp4=enabled -Dgst-plugins-good:matroska=enabled @@ -66,17 +69,18 @@ runs: -Dgst-plugins-bad:videoparsers=enabled -Dgst-plugins-bad:sdp=enabled -Dgst-plugins-bad:x11=enabled + -Dgst-plugins-bad:wayland=enabled + -Dgst-plugins-bad:va=enabled + -Dgst-plugins-bad:x265=enabled -Dugly=enabled -Dgst-plugins-ugly:x264=enabled builddir - # -Dqt6=enabled + # --strip=true + # --default-library=static + # --prefer_static=true + # -Dauto_features=disabled # -Dgst-full-target-type=static_library - # -Dgst-plugins-base:gl_platform=glx,egl - # -Dgst-plugins-base:gl_winsys=x11,egl,wayland - # -Dgst-plugins-good:qt-wayland=enabled - # -Dgst-plugins-good:qt-egl=enabled - # -Dgst-plugins-bad:xshm=enabled - # -Dgst-plugins-bad:wayland=enabled + # -Dgstreamer:gstreamer-static-full=true shell: bash - name: Compile GStreamer diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 59a79602712..f10476ebb64 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -36,7 +36,7 @@ jobs: env: ARTIFACT: QGroundControl-x86_64.AppImage QT_VERSION: 6.6.3 - # GST_VERSION: 1.22.12 + GST_VERSION: 1.22.12 steps: - name: Free Disk Space (Ubuntu) @@ -58,8 +58,6 @@ jobs: sudo ./tools/setup/install-dependencies-debian.sh python3 -m pip install --user ninja cmake - - uses: lukka/get-cmake@latest - - name: Install Vulkan run: | wget -qO - http://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add - @@ -81,8 +79,6 @@ jobs: cd ccache-*-linux-x86_64 sudo make install - - uses: seanmiddleditch/gha-setup-ninja@v5 - - name: Set Up Cache uses: hendrikmuhs/ccache-action@v1.2 with: @@ -108,6 +104,15 @@ jobs: # - name: Build GStreamer # uses: ./.github/actions/gstreamer + - name: Install GStreamer + uses: blinemedical/setup-gstreamer@v1 + with: + version: ${{ env.GST_VERSION }} + + - uses: lukka/get-cmake@latest + + - uses: seanmiddleditch/gha-setup-ninja@v5 + - run: mkdir ${{ runner.temp }}/shadow_build_dir - name: Configure From 0df702ad1786ef232758a8c2a6df5474c30ead12 Mon Sep 17 00:00:00 2001 From: PX4BuildBot Date: Wed, 23 Oct 2024 03:01:25 +0000 Subject: [PATCH 13/27] Update PX4 Firmware metadata Wed Oct 23 03:01:25 UTC 2024 --- src/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml b/src/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml index c4303ebcbd3..4a4c63a2da8 100644 --- a/src/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml +++ b/src/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml @@ -11010,7 +11010,7 @@ Minimum speed for the throw start - When the throw launch is enabled, the drone will only arm after this speed is exceeded before detecting the freefall. This is a safety feature to ensure the drone does not turn on after accidental drop or a rapid movement before the throw. Set to 0 to disable. + When the throw launch is enabled, the drone will only allow motors to spin after this speed is exceeded before detecting the freefall. This is a safety feature to ensure the drone does not turn on after accidental drop or a rapid movement before the throw. Set to 0 to disable. 0 m/s 1 From 78f8fca88c7e2d0f5996343e0765e39d23899794 Mon Sep 17 00:00:00 2001 From: Holden Date: Sun, 6 Oct 2024 23:50:58 -0400 Subject: [PATCH 14/27] Joystick: Convert Manager from Tool To Singleton --- CMakeLists.txt | 2 + src/API/QGCCorePlugin.cc | 2 +- src/Camera/QGCCameraManager.cc | 5 +- src/Joystick/CMakeLists.txt | 9 ++ src/Joystick/Joystick.cc | 1 + src/Joystick/Joystick.h | 6 + src/Joystick/JoystickAndroid.cc | 12 +- src/Joystick/JoystickAndroid.h | 4 +- src/Joystick/JoystickManager.cc | 127 +++++++++---------- src/Joystick/JoystickManager.h | 76 +++++------ src/Joystick/JoystickSDL.h | 2 +- src/QGCApplication.cc | 2 +- src/QGCToolbox.cc | 3 - src/QGCToolbox.h | 3 - src/Vehicle/MultiVehicleManager.cc | 4 +- src/Vehicle/MultiVehicleManager.h | 2 - src/Vehicle/Vehicle.cc | 10 +- src/Vehicle/Vehicle.h | 3 - src/VehicleSetup/JoystickConfigController.cc | 11 +- src/VehicleSetup/JoystickConfigController.h | 3 - 20 files changed, 126 insertions(+), 161 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bf5201b90d..6d14223e75e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -156,6 +156,8 @@ if(ANDROID) endif() endif() +set(QT_SILENCE_MISSING_DEPENDENCY_TARGET_WARNING ON) + find_package(Qt6 REQUIRED COMPONENTS diff --git a/src/API/QGCCorePlugin.cc b/src/API/QGCCorePlugin.cc index a338135f1fe..29bfc2db1d8 100644 --- a/src/API/QGCCorePlugin.cc +++ b/src/API/QGCCorePlugin.cc @@ -301,7 +301,7 @@ QQmlApplicationEngine* QGCCorePlugin::createQmlApplicationEngine(QObject* parent { "eventMonitor", QVariant::fromValue(&eventMonitor) } }); */ qmlEngine->addImportPath("qrc:/qml"); - qmlEngine->rootContext()->setContextProperty("joystickManager", qgcApp()->toolbox()->joystickManager()); + qmlEngine->rootContext()->setContextProperty("joystickManager", JoystickManager::instance()); qmlEngine->rootContext()->setContextProperty("debugMessageModel", AppMessages::getModel()); return qmlEngine; } diff --git a/src/Camera/QGCCameraManager.cc b/src/Camera/QGCCameraManager.cc index 3934997ff0f..c1cf3be3af2 100644 --- a/src/Camera/QGCCameraManager.cc +++ b/src/Camera/QGCCameraManager.cc @@ -69,9 +69,8 @@ void QGCCameraManager::_vehicleReady(bool ready) if(ready) { if(qgcApp()->toolbox()->multiVehicleManager()->activeVehicle() == _vehicle) { _vehicleReadyState = true; - JoystickManager *pJoyMgr = qgcApp()->toolbox()->joystickManager(); - _activeJoystickChanged(pJoyMgr->activeJoystick()); - connect(pJoyMgr, &JoystickManager::activeJoystickChanged, this, &QGCCameraManager::_activeJoystickChanged); + _activeJoystickChanged(JoystickManager::instance()->activeJoystick()); + connect(JoystickManager::instance(), &JoystickManager::activeJoystickChanged, this, &QGCCameraManager::_activeJoystickChanged); } } } diff --git a/src/Joystick/CMakeLists.txt b/src/Joystick/CMakeLists.txt index 1cebdecdbd0..e809509d17f 100644 --- a/src/Joystick/CMakeLists.txt +++ b/src/Joystick/CMakeLists.txt @@ -60,6 +60,15 @@ qt_add_resources(Joystick "gamecontrollerdb.txt" FILES ${sdl_gamecontrollerdb_SOURCE_DIR}/gamecontrollerdb.txt ) +qt_add_qml_module(Joystick + URI QGroundControl.JoystickManager + VERSION 1.0 + OUTPUT_TARGETS Joystick_targets + IMPORT_PATH ${QT_QML_OUTPUT_DIRECTORY} +) + +cmake_print_variables(Joystick_targets) + set(MINIMUM_SDL2_VERSION 2.30.0) if(NOT QGC_BUILD_DEPENDENCIES) diff --git a/src/Joystick/Joystick.cc b/src/Joystick/Joystick.cc index c8f411967d7..1e7e7257490 100644 --- a/src/Joystick/Joystick.cc +++ b/src/Joystick/Joystick.cc @@ -19,6 +19,7 @@ #include "FirmwarePlugin.h" #include "QGCLoggingCategory.h" #include "GimbalController.h" +#include "QmlObjectListModel.h" #include diff --git a/src/Joystick/Joystick.h b/src/Joystick/Joystick.h index 4d0b8bfffac..63b39e3817b 100644 --- a/src/Joystick/Joystick.h +++ b/src/Joystick/Joystick.h @@ -18,6 +18,7 @@ #include #include #include +#include // JoystickLog Category declaration moved to QGCLoggingCategory.cc to allow access in Vehicle Q_DECLARE_LOGGING_CATEGORY(JoystickValuesLog) @@ -25,6 +26,7 @@ Q_DECLARE_METATYPE(GRIPPER_ACTIONS) class MultiVehicleManager; class Vehicle; +class QmlObjectListModel; /// Action assigned to button class AssignedButtonAction : public QObject { @@ -54,6 +56,10 @@ class AssignableButtonAction : public QObject { class Joystick : public QThread { Q_OBJECT + QML_ELEMENT + QML_UNCREATABLE("") + Q_MOC_INCLUDE("QmlObjectListModel.h") + Q_MOC_INCLUDE("Vehicle.h") public: Joystick(const QString& name, int axisCount, int buttonCount, int hatCount, MultiVehicleManager* multiVehicleManager); diff --git a/src/Joystick/JoystickAndroid.cc b/src/Joystick/JoystickAndroid.cc index e0cc24f7f2a..f574be03bf8 100644 --- a/src/Joystick/JoystickAndroid.cc +++ b/src/Joystick/JoystickAndroid.cc @@ -242,12 +242,8 @@ bool JoystickAndroid::_getHat(int hat,int i) { } } -static JoystickManager *_manager = nullptr; - //helper method -bool JoystickAndroid::init(JoystickManager *manager) { - _manager = manager; - +bool JoystickAndroid::init() { //this gets list of all possible buttons - this is needed to check how many buttons our gamepad supports //instead of the whole logic below we could have just a simple array of hardcoded int values as these 'should' not change @@ -296,10 +292,8 @@ static void jniUpdateAvailableJoysticks(JNIEnv *envA, jobject thizA) Q_UNUSED(envA); Q_UNUSED(thizA); - if (_manager != nullptr) { - qCDebug(JoystickLog) << "jniUpdateAvailableJoysticks triggered"; - emit _manager->updateAvailableJoysticksSignal(); - } + qCDebug(JoystickLog) << "jniUpdateAvailableJoysticks triggered"; + emit JoystickManager::instance()->updateAvailableJoysticksSignal(); } void JoystickAndroid::setNativeMethods() diff --git a/src/Joystick/JoystickAndroid.h b/src/Joystick/JoystickAndroid.h index 8209d7751e8..bf96ebebc1d 100644 --- a/src/Joystick/JoystickAndroid.h +++ b/src/Joystick/JoystickAndroid.h @@ -13,16 +13,14 @@ #include class MultiVehicleManager; -class JoystickManager; class JoystickAndroid : public Joystick, public QtAndroidPrivate::GenericMotionEventListener, public QtAndroidPrivate::KeyEventListener { public: JoystickAndroid(const QString& name, int axisCount, int buttonCount, int id, MultiVehicleManager* multiVehicleManager); - ~JoystickAndroid(); - static bool init(JoystickManager *manager); + static bool init(); static void setNativeMethods(); diff --git a/src/Joystick/JoystickManager.cc b/src/Joystick/JoystickManager.cc index c2ae0893c42..f2562540369 100644 --- a/src/Joystick/JoystickManager.cc +++ b/src/Joystick/JoystickManager.cc @@ -7,80 +7,82 @@ * ****************************************************************************/ - #include "JoystickManager.h" -#include "MultiVehicleManager.h" #include "Joystick.h" #if defined(QGC_SDL_JOYSTICK) #include "JoystickSDL.h" #elif defined(Q_OS_ANDROID) #include "JoystickAndroid.h" #endif +#include "QGCApplication.h" +#include "MultiVehicleManager.h" #include "QGCLoggingCategory.h" +#include #include +#include #include #include -QGC_LOGGING_CATEGORY(JoystickManagerLog, "JoystickManagerLog") +QGC_LOGGING_CATEGORY(JoystickManagerLog, "qgc.joystick.joystickmanager") -JoystickManager::JoystickManager(QGCApplication* app, QGCToolbox* toolbox) - : QGCTool(app, toolbox) - , _activeJoystick(nullptr) - , _multiVehicleManager(nullptr) +Q_APPLICATION_STATIC(JoystickManager, _joystickManager); + +JoystickManager::JoystickManager(QObject *parent) + : QObject(parent) + , _joystickCheckTimer(new QTimer(this)) { // qCDebug(JoystickManagerLog) << Q_FUNC_INFO << this; + + _joystickCheckTimer->setInterval(1000); + _joystickCheckTimer->setSingleShot(false); } JoystickManager::~JoystickManager() { - QMap::iterator i; - for (i = _name2JoystickMap.begin(); i != _name2JoystickMap.end(); ++i) { - qCDebug(JoystickManagerLog) << "Releasing joystick:" << i.key(); - i.value()->stop(); - delete i.value(); + for (QMap::key_value_iterator it = _name2JoystickMap.keyValueBegin(); it != _name2JoystickMap.keyValueEnd(); ++it) { + qCDebug(JoystickManagerLog) << "Releasing joystick:" << it->first; + it->second->stop(); + delete it->second; } // qCDebug(JoystickManagerLog) << Q_FUNC_INFO << this; } -void JoystickManager::setToolbox(QGCToolbox *toolbox) +JoystickManager *JoystickManager::instance() { - QGCTool::setToolbox(toolbox); - - _multiVehicleManager = _toolbox->multiVehicleManager(); - - QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); - qmlRegisterUncreatableType("QGroundControl.JoystickManager", 1, 0, "JoystickManager", "Reference only"); - qmlRegisterUncreatableType ("QGroundControl.JoystickManager", 1, 0, "Joystick", "Reference only"); + return _joystickManager(); } -void JoystickManager::init() { +void JoystickManager::init() +{ #ifdef QGC_SDL_JOYSTICK if (!JoystickSDL::init()) { return; } _setActiveJoystickFromSettings(); #elif defined(Q_OS_ANDROID) - if (!JoystickAndroid::init(this)) { + if (!JoystickAndroid::init()) { return; } - connect(this, &JoystickManager::updateAvailableJoysticksSignal, this, &JoystickManager::restartJoystickCheckTimer); + (void) connect(this, &JoystickManager::updateAvailableJoysticksSignal, this, [this]() { + _joystickCheckTimerCounter = 5; + _joystickCheckTimer->start(); + }); #endif - connect(&_joystickCheckTimer, &QTimer::timeout, this, &JoystickManager::_updateAvailableJoysticks); + (void) connect(_joystickCheckTimer, &QTimer::timeout, this, &JoystickManager::_updateAvailableJoysticks); _joystickCheckTimerCounter = 5; - _joystickCheckTimer.start(1000); + _joystickCheckTimer->start(); } -void JoystickManager::_setActiveJoystickFromSettings(void) +void JoystickManager::_setActiveJoystickFromSettings() { QMap newMap; #ifdef QGC_SDL_JOYSTICK - // Get the latest joystick mapping - newMap = JoystickSDL::discover(_multiVehicleManager); + newMap = JoystickSDL::discover(qgcApp()->toolbox()->multiVehicleManager()); #elif defined(Q_OS_ANDROID) - newMap = JoystickAndroid::discover(_multiVehicleManager); + newMap = JoystickAndroid::discover(qgcApp()->toolbox()->multiVehicleManager()); #endif if (_activeJoystick && !newMap.contains(_activeJoystick->name())) { @@ -90,47 +92,45 @@ void JoystickManager::_setActiveJoystickFromSettings(void) // Check to see if our current mapping contains any joysticks that are not in the new mapping // If so, those joysticks have been unplugged, and need to be cleaned up - QMap::iterator i; - for (i = _name2JoystickMap.begin(); i != _name2JoystickMap.end(); ++i) { - if (!newMap.contains(i.key())) { - qCDebug(JoystickManagerLog) << "Releasing joystick:" << i.key(); - i.value()->stopPolling(); - i.value()->wait(1000); - i.value()->deleteLater(); + for (QMap::key_value_iterator it = _name2JoystickMap.keyValueBegin(); it != _name2JoystickMap.keyValueEnd(); ++it) { + if (!newMap.contains(it->first)) { + qCDebug(JoystickManagerLog) << "Releasing joystick:" << it->first; + it->second->stopPolling(); + it->second->wait(1000); + it->second->deleteLater(); } } _name2JoystickMap = newMap; emit availableJoysticksChanged(); - if (!_name2JoystickMap.count()) { + if (_name2JoystickMap.isEmpty()) { setActiveJoystick(nullptr); return; } QSettings settings; - settings.beginGroup(_settingsGroup); - QString name = settings.value(_settingsKeyActiveJoystick).toString(); + QString name = settings.value(_settingsKeyActiveJoystick).toString(); if (name.isEmpty()) { name = _name2JoystickMap.first()->name(); } setActiveJoystick(_name2JoystickMap.value(name, _name2JoystickMap.first())); settings.setValue(_settingsKeyActiveJoystick, _activeJoystick->name()); + + settings.endGroup(); } -Joystick* JoystickManager::activeJoystick(void) +Joystick *JoystickManager::activeJoystick() { return _activeJoystick; } -void JoystickManager::setActiveJoystick(Joystick* joystick) +void JoystickManager::setActiveJoystick(Joystick *joystick) { - QSettings settings; - - if (joystick != nullptr && !_name2JoystickMap.contains(joystick->name())) { + if (joystick && !_name2JoystickMap.contains(joystick->name())) { qCWarning(JoystickManagerLog) << "Set active not in map" << joystick->name(); return; } @@ -148,49 +148,42 @@ void JoystickManager::setActiveJoystick(Joystick* joystick) if (_activeJoystick != nullptr) { qCDebug(JoystickManagerLog) << "Set active:" << _activeJoystick->name(); + QSettings settings; settings.beginGroup(_settingsGroup); settings.setValue(_settingsKeyActiveJoystick, _activeJoystick->name()); + settings.endGroup(); } emit activeJoystickChanged(_activeJoystick); - emit activeJoystickNameChanged(_activeJoystick?_activeJoystick->name():""); + emit activeJoystickNameChanged(_activeJoystick ? _activeJoystick->name() : ""); } -QVariantList JoystickManager::joysticks(void) +QVariantList JoystickManager::joysticks() { QVariantList list; - - for (const QString &name: _name2JoystickMap.keys()) { - list += QVariant::fromValue(_name2JoystickMap[name]); + for (auto it = _name2JoystickMap.constBegin(); it != _name2JoystickMap.constEnd(); ++it) { + list += QVariant::fromValue(it.value()); } return list; } -QStringList JoystickManager::joystickNames(void) -{ - return _name2JoystickMap.keys(); -} - -QString JoystickManager::activeJoystickName(void) +QString JoystickManager::activeJoystickName() const { - return _activeJoystick ? _activeJoystick->name() : QString(); + return (_activeJoystick ? _activeJoystick->name() : QString()); } -bool JoystickManager::setActiveJoystickName(const QString& name) +bool JoystickManager::setActiveJoystickName(const QString &name) { if (_name2JoystickMap.contains(name)) { setActiveJoystick(_name2JoystickMap[name]); return true; - } else { - qCWarning(JoystickManagerLog) << "Set active not in map" << name; - return false; } + + qCWarning(JoystickManagerLog) << "Set active not in map" << name; + return false; } -/* - * TODO: move this to the right place: JoystickSDL.cc and JoystickAndroid.cc respectively and call through Joystick.cc - */ void JoystickManager::_updateAvailableJoysticks() { #ifdef QGC_SDL_JOYSTICK @@ -216,13 +209,7 @@ void JoystickManager::_updateAvailableJoysticks() _joystickCheckTimerCounter--; _setActiveJoystickFromSettings(); if (_joystickCheckTimerCounter <= 0) { - _joystickCheckTimer.stop(); + _joystickCheckTimer->stop(); } #endif } - -void JoystickManager::restartJoystickCheckTimer() -{ - _joystickCheckTimerCounter = 5; - _joystickCheckTimer.start(1000); -} diff --git a/src/Joystick/JoystickManager.h b/src/Joystick/JoystickManager.h index b36a0510bd0..b549c5d0df4 100644 --- a/src/Joystick/JoystickManager.h +++ b/src/Joystick/JoystickManager.h @@ -7,79 +7,67 @@ * ****************************************************************************/ -/// @file -/// @brief Joystick Manager - #pragma once -#include "QGCToolbox.h" - +#include #include -#include #include +#include Q_DECLARE_LOGGING_CATEGORY(JoystickManagerLog) -class MultiVehicleManager; class Joystick; +class MultiVehicleManager; +class QTimer; -/// Joystick Manager -class JoystickManager : public QGCTool +class JoystickManager : public QObject { Q_OBJECT + QML_ELEMENT + QML_UNCREATABLE("") Q_MOC_INCLUDE("Joystick.h") + Q_PROPERTY(QVariantList joysticks READ joysticks NOTIFY availableJoysticksChanged) + Q_PROPERTY(QStringList joystickNames READ joystickNames NOTIFY availableJoysticksChanged) + Q_PROPERTY(Joystick *activeJoystick READ activeJoystick WRITE setActiveJoystick NOTIFY activeJoystickChanged) + Q_PROPERTY(QString activeJoystickName READ activeJoystickName WRITE setActiveJoystickName NOTIFY activeJoystickNameChanged) public: - JoystickManager(QGCApplication* app, QGCToolbox* toolbox); + explicit JoystickManager(QObject *parent = nullptr); ~JoystickManager(); - Q_PROPERTY(QVariantList joysticks READ joysticks NOTIFY availableJoysticksChanged) - Q_PROPERTY(QStringList joystickNames READ joystickNames NOTIFY availableJoysticksChanged) - - Q_PROPERTY(Joystick* activeJoystick READ activeJoystick WRITE setActiveJoystick NOTIFY activeJoystickChanged) - Q_PROPERTY(QString activeJoystickName READ activeJoystickName WRITE setActiveJoystickName NOTIFY activeJoystickNameChanged) + static JoystickManager *instance(); - /// List of available joysticks QVariantList joysticks(); - /// List of available joystick names - QStringList joystickNames(void); - - /// Get active joystick - Joystick* activeJoystick(void); - /// Set active joystick - void setActiveJoystick(Joystick* joystick); + QStringList joystickNames() const { return _name2JoystickMap.keys(); } - QString activeJoystickName(void); - bool setActiveJoystickName(const QString& name); + Joystick *activeJoystick(); + void setActiveJoystick(Joystick *joystick); - void restartJoystickCheckTimer(void); + QString activeJoystickName() const; + bool setActiveJoystickName(const QString &name); - // Override from QGCTool - virtual void setToolbox(QGCToolbox *toolbox); +signals: + void activeJoystickChanged(Joystick *joystick); + void activeJoystickNameChanged(const QString &name); + void availableJoysticksChanged(); + void updateAvailableJoysticksSignal(); public slots: void init(); -signals: - void activeJoystickChanged(Joystick* joystick); - void activeJoystickNameChanged(const QString& name); - void availableJoysticksChanged(void); - void updateAvailableJoysticksSignal(); - private slots: - void _updateAvailableJoysticks(void); + // TODO: move this to the right place: JoystickSDL.cc and JoystickAndroid.cc respectively and call through Joystick.cc + void _updateAvailableJoysticks(); private: - void _setActiveJoystickFromSettings(void); + void _setActiveJoystickFromSettings(); -private: - Joystick* _activeJoystick; - QMap _name2JoystickMap; - MultiVehicleManager* _multiVehicleManager; + Joystick *_activeJoystick = nullptr; + QMap _name2JoystickMap; - int _joystickCheckTimerCounter; - QTimer _joystickCheckTimer; + int _joystickCheckTimerCounter = 0;; + QTimer *_joystickCheckTimer = nullptr; - static constexpr const char * _settingsGroup = "JoystickManager"; - static constexpr const char * _settingsKeyActiveJoystick = "ActiveJoystick"; + static constexpr const char *_settingsGroup = "JoystickManager"; + static constexpr const char *_settingsKeyActiveJoystick = "ActiveJoystick"; }; diff --git a/src/Joystick/JoystickSDL.h b/src/Joystick/JoystickSDL.h index bb56b2851f4..6cf81c28454 100644 --- a/src/Joystick/JoystickSDL.h +++ b/src/Joystick/JoystickSDL.h @@ -27,7 +27,7 @@ class JoystickSDL : public Joystick JoystickSDL(const QString& name, int axisCount, int buttonCount, int hatCount, int index, bool isGameController, MultiVehicleManager* multiVehicleManager); ~JoystickSDL(); - static QMap discover(MultiVehicleManager* _multiVehicleManager); + static QMap discover(MultiVehicleManager* _multiVehicleManager); static bool init(void); int index(void) const { return _index; } diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index fbdb956f99d..79a40a7a218 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -467,7 +467,7 @@ void QGCApplication::_initForNormalAppBoot() _toolbox->linkManager()->loadLinkConfigurationList(); // Probe for joysticks - _toolbox->joystickManager()->init(); + JoystickManager::instance()->init(); if (_settingsUpgraded) { showAppMessage(QString(tr("The format for %1 saved settings has been modified. " diff --git a/src/QGCToolbox.cc b/src/QGCToolbox.cc index 09e933c7212..05989093171 100644 --- a/src/QGCToolbox.cc +++ b/src/QGCToolbox.cc @@ -12,7 +12,6 @@ #ifndef NO_SERIAL_LINK #include "GPSManager.h" #endif -#include "JoystickManager.h" #include "LinkManager.h" #include "MAVLinkProtocol.h" #include "MissionCommandTree.h" @@ -47,7 +46,6 @@ QGCToolbox::QGCToolbox(QGCApplication* app) #ifndef NO_SERIAL_LINK _gpsManager = new GPSManager (app, this); #endif - _joystickManager = new JoystickManager (app, this); _linkManager = new LinkManager (app, this); _mavlinkProtocol = new MAVLinkProtocol (app, this); _missionCommandTree = new MissionCommandTree (app, this); @@ -74,7 +72,6 @@ void QGCToolbox::setChildToolboxes(void) #ifndef NO_SERIAL_LINK _gpsManager->setToolbox(this); #endif - _joystickManager->setToolbox(this); _linkManager->setToolbox(this); _mavlinkProtocol->setToolbox(this); _missionCommandTree->setToolbox(this); diff --git a/src/QGCToolbox.h b/src/QGCToolbox.h index 634ee38d213..ba93df9c5d8 100644 --- a/src/QGCToolbox.h +++ b/src/QGCToolbox.h @@ -15,7 +15,6 @@ class FirmwarePluginManager; class GPSManager; -class JoystickManager; class LinkManager; class MAVLinkProtocol; class MissionCommandTree; @@ -41,7 +40,6 @@ class QGCToolbox : public QObject { QGCToolbox(QGCApplication* app); FirmwarePluginManager* firmwarePluginManager () { return _firmwarePluginManager; } - JoystickManager* joystickManager () { return _joystickManager; } LinkManager* linkManager () { return _linkManager; } MAVLinkProtocol* mavlinkProtocol () { return _mavlinkProtocol; } MissionCommandTree* missionCommandTree () { return _missionCommandTree; } @@ -69,7 +67,6 @@ class QGCToolbox : public QObject { #ifndef NO_SERIAL_LINK GPSManager* _gpsManager = nullptr; #endif - JoystickManager* _joystickManager = nullptr; LinkManager* _linkManager = nullptr; MAVLinkProtocol* _mavlinkProtocol = nullptr; MissionCommandTree* _missionCommandTree = nullptr; diff --git a/src/Vehicle/MultiVehicleManager.cc b/src/Vehicle/MultiVehicleManager.cc index d5bbb22046d..48231e1ba75 100644 --- a/src/Vehicle/MultiVehicleManager.cc +++ b/src/Vehicle/MultiVehicleManager.cc @@ -32,7 +32,6 @@ MultiVehicleManager::MultiVehicleManager(QGCApplication* app, QGCToolbox* toolbo , _activeVehicle(nullptr) , _offlineEditingVehicle(nullptr) , _firmwarePluginManager(nullptr) - , _joystickManager(nullptr) , _mavlinkProtocol(nullptr) , _gcsHeartbeatEnabled(true) { @@ -47,7 +46,6 @@ void MultiVehicleManager::setToolbox(QGCToolbox *toolbox) QGCTool::setToolbox(toolbox); _firmwarePluginManager = _toolbox->firmwarePluginManager(); - _joystickManager = _toolbox->joystickManager(); _mavlinkProtocol = _toolbox->mavlinkProtocol(); QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); @@ -119,7 +117,7 @@ void MultiVehicleManager::_vehicleHeartbeatInfo(LinkInterface* link, int vehicle _app->showAppMessage(tr("Warning: A vehicle is using the same system id as %1: %2").arg(QCoreApplication::applicationName()).arg(vehicleId)); } - Vehicle* vehicle = new Vehicle(link, vehicleId, componentId, (MAV_AUTOPILOT)vehicleFirmwareType, (MAV_TYPE)vehicleType, _firmwarePluginManager, _joystickManager, this); + Vehicle* vehicle = new Vehicle(link, vehicleId, componentId, (MAV_AUTOPILOT)vehicleFirmwareType, (MAV_TYPE)vehicleType, _firmwarePluginManager, this); connect(vehicle, &Vehicle::requestProtocolVersion, this, &MultiVehicleManager::_requestProtocolVersion); connect(vehicle->vehicleLinkManager(), &VehicleLinkManager::allLinksRemoved, this, &MultiVehicleManager::_deleteVehiclePhase1); connect(vehicle->parameterManager(), &ParameterManager::parametersReadyChanged, this, &MultiVehicleManager::_vehicleParametersReadyChanged); diff --git a/src/Vehicle/MultiVehicleManager.h b/src/Vehicle/MultiVehicleManager.h index 70d66c6061b..be09b4b6298 100644 --- a/src/Vehicle/MultiVehicleManager.h +++ b/src/Vehicle/MultiVehicleManager.h @@ -21,7 +21,6 @@ #include "QmlObjectListModel.h" class FirmwarePluginManager; -class JoystickManager; class QGCApplication; class MAVLinkProtocol; class LinkInterface; @@ -111,7 +110,6 @@ private slots: QmlObjectListModel _vehicles; FirmwarePluginManager* _firmwarePluginManager; - JoystickManager* _joystickManager; MAVLinkProtocol* _mavlinkProtocol; QGeoCoordinate _lastKnownLocation; diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 9fbf14621e6..4ae6fc92763 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -80,7 +80,6 @@ Vehicle::Vehicle(LinkInterface* link, MAV_AUTOPILOT firmwareType, MAV_TYPE vehicleType, FirmwarePluginManager* firmwarePluginManager, - JoystickManager* joystickManager, QObject* parent) : VehicleFactGroup (parent) , _id (vehicleId) @@ -92,7 +91,6 @@ Vehicle::Vehicle(LinkInterface* link, , _defaultCruiseSpeed (_settingsManager->appSettings()->offlineEditingCruiseSpeed()->rawValue().toDouble()) , _defaultHoverSpeed (_settingsManager->appSettings()->offlineEditingHoverSpeed()->rawValue().toDouble()) , _firmwarePluginManager (firmwarePluginManager) - , _joystickManager (joystickManager) , _trajectoryPoints (new TrajectoryPoints(this, this)) , _mavlinkStreamConfig (std::bind(&Vehicle::_setMessageInterval, this, std::placeholders::_1, std::placeholders::_2)) , _vehicleFactGroup (this) @@ -116,7 +114,7 @@ Vehicle::Vehicle(LinkInterface* link, { _linkManager = _toolbox->linkManager(); - connect(_joystickManager, &JoystickManager::activeJoystickChanged, this, &Vehicle::_loadJoystickSettings); + connect(JoystickManager::instance(), &JoystickManager::activeJoystickChanged, this, &Vehicle::_loadJoystickSettings); connect(qgcApp()->toolbox()->multiVehicleManager(), &MultiVehicleManager::activeVehicleChanged, this, &Vehicle::_activeVehicleChanged); _mavlink = _toolbox->mavlinkProtocol(); @@ -1515,7 +1513,7 @@ void Vehicle::_loadJoystickSettings() QSettings settings; settings.beginGroup(QString(_settingsGroup).arg(_id)); - if (_toolbox->joystickManager()->activeJoystick()) { + if (JoystickManager::instance()->activeJoystick()) { qCDebug(JoystickLog) << "Vehicle " << this->id() << " Notified of an active joystick. Loading setting joystickenabled: " << settings.value(_joystickEnabledSettingsKey, false).toBool(); setJoystickEnabled(settings.value(_joystickEnabledSettingsKey, false).toBool()); } else { @@ -1533,7 +1531,7 @@ void Vehicle::saveJoystickSettings() // The joystick enabled setting should only be changed if a joystick is present // since the checkbox can only be clicked if one is present - if (_toolbox->joystickManager()->joysticks().count()) { + if (JoystickManager::instance()->joysticks().count()) { qCDebug(JoystickLog) << "Vehicle " << this->id() << " Saving setting joystickenabled: " << _joystickEnabled; settings.setValue(_joystickEnabledSettingsKey, _joystickEnabled); } @@ -1582,7 +1580,7 @@ void Vehicle::_activeVehicleChanged(Vehicle *newActiveVehicle) // tells the active joystick where to send data void Vehicle::_captureJoystick() { - Joystick* joystick = _joystickManager->activeJoystick(); + Joystick* joystick = JoystickManager::instance()->activeJoystick(); if(joystick){ qCDebug(JoystickLog) << "Vehicle " << this->id() << " Capture Joystick" << joystick->name(); diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index 9f96661f9da..03495d9fb94 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -58,7 +58,6 @@ class ImageProtocolManager; class StatusTextHandler; class InitialConnectStateMachine; class Joystick; -class JoystickManager; class LinkInterface; class LinkManager; class MAVLinkProtocol; @@ -118,7 +117,6 @@ class Vehicle : public VehicleFactGroup MAV_AUTOPILOT firmwareType, MAV_TYPE vehicleType, FirmwarePluginManager* firmwarePluginManager, - JoystickManager* joystickManager, QObject* parent = nullptr); // Pass these into the offline constructor to create an offline vehicle which tracks the offline vehicle settings @@ -1060,7 +1058,6 @@ private slots: LinkManager* _linkManager = nullptr; ParameterManager* _parameterManager = nullptr; FirmwarePluginManager* _firmwarePluginManager = nullptr; - JoystickManager* _joystickManager = nullptr; ComponentInformationManager* _componentInformationManager = nullptr; VehicleObjectAvoidance* _objectAvoidance = nullptr; Autotune* _autotune = nullptr; diff --git a/src/VehicleSetup/JoystickConfigController.cc b/src/VehicleSetup/JoystickConfigController.cc index c7a5c3c846f..90dd6e4e357 100644 --- a/src/VehicleSetup/JoystickConfigController.cc +++ b/src/VehicleSetup/JoystickConfigController.cc @@ -18,11 +18,10 @@ QGC_LOGGING_CATEGORY(JoystickConfigControllerLog, "JoystickConfigControllerLog") JoystickConfigController::JoystickConfigController(void) - : _joystickManager(qgcApp()->toolbox()->joystickManager()) { - connect(_joystickManager, &JoystickManager::activeJoystickChanged, this, &JoystickConfigController::_activeJoystickChanged); - _activeJoystickChanged(_joystickManager->activeJoystick()); + connect(JoystickManager::instance(), &JoystickManager::activeJoystickChanged, this, &JoystickConfigController::_activeJoystickChanged); + _activeJoystickChanged(JoystickManager::instance()->activeJoystick()); _setStickPositions(); _resetInternalCalibrationValues(); _currentStickPositions << _sticksCentered.leftX << _sticksCentered.leftY << _sticksCentered.rightX << _sticksCentered.rightY; @@ -36,7 +35,7 @@ void JoystickConfigController::start(void) void JoystickConfigController::setDeadbandValue(int axis, int value) { _axisDeadbandChanged(axis,value); - Joystick* joystick = _joystickManager->activeJoystick(); + Joystick* joystick = JoystickManager::instance()->activeJoystick(); Joystick::Calibration_t calibration = joystick->getCalibration(axis); calibration.deadband = value; joystick->setCalibration(axis,calibration); @@ -394,7 +393,7 @@ void JoystickConfigController::_resetInternalCalibrationValues() /// @brief Sets internal calibration values from the stored settings void JoystickConfigController::_setInternalCalibrationValuesFromSettings() { - Joystick* joystick = _joystickManager->activeJoystick(); + Joystick* joystick = JoystickManager::instance()->activeJoystick(); // Initialize all function mappings to not set for (int i = 0; i < _axisCount; i++) { struct AxisInfo* info = &_rgAxisInfo[i]; @@ -468,7 +467,7 @@ void JoystickConfigController::_validateCalibration() /// @brief Saves the rc calibration values to the board parameters. void JoystickConfigController::_writeCalibration() { - Joystick* joystick = _joystickManager->activeJoystick(); + Joystick* joystick = JoystickManager::instance()->activeJoystick(); _validateCalibration(); for (int axis = 0; axis < _axisCount; axis++) { diff --git a/src/VehicleSetup/JoystickConfigController.h b/src/VehicleSetup/JoystickConfigController.h index 3e9c6060cee..29f9943a744 100644 --- a/src/VehicleSetup/JoystickConfigController.h +++ b/src/VehicleSetup/JoystickConfigController.h @@ -21,8 +21,6 @@ Q_DECLARE_LOGGING_CATEGORY(JoystickConfigControllerLog) -class JoystickManager; - class JoystickConfigController : public FactPanelController { Q_OBJECT @@ -233,7 +231,6 @@ private slots: QElapsedTimer _stickDetectSettleElapsed; QString _statusText; - JoystickManager* _joystickManager; static constexpr int _calCenterPoint = 0; static constexpr int _calValidMinValue = -32768; ///< Largest valid minimum axis value From 34cdb6bcdda33855e174e4865a769c8033fc266d Mon Sep 17 00:00:00 2001 From: Sam Gillam Date: Thu, 24 Oct 2024 10:42:44 -0600 Subject: [PATCH 15/27] Use correct aspectRation for gst video --- src/FlightDisplay/FlightDisplayViewVideo.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/FlightDisplay/FlightDisplayViewVideo.qml b/src/FlightDisplay/FlightDisplayViewVideo.qml index d1c84c1e375..66701676606 100644 --- a/src/FlightDisplay/FlightDisplayViewVideo.qml +++ b/src/FlightDisplay/FlightDisplayViewVideo.qml @@ -26,7 +26,9 @@ Item { property bool useSmallFont: true - property double _ar: QGroundControl.videoManager.aspectRatio + property double _ar: QGroundControl.videoManager.gstreamerEnabled + ? QGroundControl.videoManager.videoSize.width / QGroundControl.videoManager.videoSize.height + : QGroundControl.videoManager.aspectRatio property bool _showGrid: QGroundControl.settingsManager.videoSettings.gridLines.rawValue property var _dynamicCameras: globals.activeVehicle ? globals.activeVehicle.cameraManager : null property bool _connected: globals.activeVehicle ? !globals.activeVehicle.communicationLost : false From 077c72cd317c2046406476bcf21fc85d2493997c Mon Sep 17 00:00:00 2001 From: Holden Date: Tue, 8 Oct 2024 02:26:13 -0400 Subject: [PATCH 16/27] Utilities: Cleanup File Classes --- src/Utilities/QGCCachedFileDownload.cc | 37 ++-- src/Utilities/QGCCachedFileDownload.h | 21 +-- src/Utilities/QGCFileDownload.cc | 137 ++++++++------- src/Utilities/QGCFileDownload.h | 47 +++-- .../Components/ComponentInformationManager.cc | 2 +- test/CMakeLists.txt | 1 + test/UnitTestList.cc | 161 +++++++++--------- test/Utilities/CMakeLists.txt | 2 + test/Utilities/QGCFileDownloadTest.cc | 15 ++ test/Utilities/QGCFileDownloadTest.h | 12 ++ .../ComponentInformationTranslationTest.cc | 2 +- 11 files changed, 253 insertions(+), 184 deletions(-) create mode 100644 test/Utilities/QGCFileDownloadTest.cc create mode 100644 test/Utilities/QGCFileDownloadTest.h diff --git a/src/Utilities/QGCCachedFileDownload.cc b/src/Utilities/QGCCachedFileDownload.cc index 381584bf10f..e894a87607a 100644 --- a/src/Utilities/QGCCachedFileDownload.cc +++ b/src/Utilities/QGCCachedFileDownload.cc @@ -7,57 +7,58 @@ * ****************************************************************************/ - #include "QGCCachedFileDownload.h" #include "QGCFileDownload.h" +#include #include -QGCCachedFileDownload::QGCCachedFileDownload(QObject* parent, const QString& cacheDirectory) - : QObject(parent), _fileDownload(new QGCFileDownload(this)), _diskCache(new QNetworkDiskCache(this)) +QGCCachedFileDownload::QGCCachedFileDownload(const QString &cacheDirectory, QObject *parent) + : QObject(parent) + , _fileDownload(new QGCFileDownload(this)) + , _diskCache(new QNetworkDiskCache(this)) { _diskCache->setCacheDirectory(cacheDirectory); _fileDownload->setCache(_diskCache); - connect(_fileDownload, &QGCFileDownload::downloadProgress, this, &QGCCachedFileDownload::downloadProgress); - connect(_fileDownload, &QGCFileDownload::downloadComplete, this, &QGCCachedFileDownload::onDownloadCompleted); + (void) connect(_fileDownload, &QGCFileDownload::downloadProgress, this, &QGCCachedFileDownload::downloadProgress); + (void) connect(_fileDownload, &QGCFileDownload::downloadComplete, this, &QGCCachedFileDownload::_onDownloadCompleted); } -bool QGCCachedFileDownload::download(const QString& url, int maxCacheAgeSec) +bool QGCCachedFileDownload::download(const QString &url, int maxCacheAgeSec) { _downloadFromNetwork = false; - // Check cache - QNetworkCacheMetaData metadata = _diskCache->metaData(url); - if (metadata.isValid() && metadata.attributes().contains(QNetworkRequest::Attribute::User)) { + const QNetworkCacheMetaData metadata = _diskCache->metaData(url); + if (metadata.isValid() && metadata.attributes().contains(QNetworkRequest::Attribute::User)) { // We want the following behavior: // - Use the cached file if not older than maxCacheAgeSec // - Otherwise try to download the file, but still use the cached file if there's no connection - QDateTime creationTime = metadata.attributes().find(QNetworkRequest::Attribute::User)->toDateTime(); - bool expired = creationTime.addSecs(maxCacheAgeSec) < QDateTime::currentDateTime(); + const auto &metaDataAttributes = metadata.attributes(); + const QDateTime creationTime = metaDataAttributes.find(QNetworkRequest::Attribute::User)->toDateTime(); + const bool expired = creationTime.addSecs(maxCacheAgeSec) < QDateTime::currentDateTime(); if (expired) { // Force network download, as Qt would still use the cache otherwise (w/o checking the remote) - auto attributes = QVector>{qMakePair(QNetworkRequest::CacheLoadControlAttribute, QVariant{QNetworkRequest::AlwaysNetwork})}; + const auto attributes = QList>{qMakePair(QNetworkRequest::CacheLoadControlAttribute, QVariant{QNetworkRequest::AlwaysNetwork})}; _downloadFromNetwork = true; return _fileDownload->download(url, attributes); } - auto attributes = QVector>{qMakePair(QNetworkRequest::CacheLoadControlAttribute, QVariant{QNetworkRequest::PreferCache})}; + const auto attributes = QList>{qMakePair(QNetworkRequest::CacheLoadControlAttribute, QVariant{QNetworkRequest::PreferCache})}; return _fileDownload->download(url, attributes); - - } else { - return _fileDownload->download(url); } + + return _fileDownload->download(url); } -void QGCCachedFileDownload::onDownloadCompleted(QString remoteFile, QString localFile, QString errorMsg) +void QGCCachedFileDownload::_onDownloadCompleted(const QString &remoteFile, const QString &localFile, const QString &errorMsg) { // Set cache creation time if not set already (the Qt docs mention there's a creation time, but I could not find any API) QNetworkCacheMetaData metadata = _diskCache->metaData(remoteFile); if (metadata.isValid() && !metadata.attributes().contains(QNetworkRequest::Attribute::User)) { QNetworkCacheMetaData::AttributesMap attributes = metadata.attributes(); - attributes.insert(QNetworkRequest::Attribute::User, QDateTime::currentDateTime()); + (void) attributes.insert(QNetworkRequest::Attribute::User, QDateTime::currentDateTime()); metadata.setAttributes(attributes); _diskCache->updateMetaData(metadata); } diff --git a/src/Utilities/QGCCachedFileDownload.h b/src/Utilities/QGCCachedFileDownload.h index 83b13e01e4b..ca98fc0a0bb 100644 --- a/src/Utilities/QGCCachedFileDownload.h +++ b/src/Utilities/QGCCachedFileDownload.h @@ -9,8 +9,8 @@ #pragma once -#include #include +#include class QGCFileDownload; class QNetworkDiskCache; @@ -18,24 +18,25 @@ class QNetworkDiskCache; class QGCCachedFileDownload : public QObject { Q_OBJECT - + public: - QGCCachedFileDownload(QObject* parent, const QString& cacheDirectory); + QGCCachedFileDownload(const QString &cacheDirectory, QObject *parent = nullptr); /// Download the specified remote file. /// @param url File to download /// @param maxCacheAgeSec Maximum age of cached item in seconds /// @return true: Asynchronous download has started, false: Download initialization failed - bool download(const QString& url, int maxCacheAgeSec); + bool download(const QString &url, int maxCacheAgeSec); signals: void downloadProgress(qint64 curr, qint64 total); - void downloadComplete(QString remoteFile, QString localFile, QString errorMsg); + void downloadComplete(const QString &remoteFile, const QString &localFile, const QString &errorMsg); -private: - void onDownloadCompleted(QString remoteFile, QString localFile, QString errorMsg); +private slots: + void _onDownloadCompleted(const QString &remoteFile, const QString &localFile, const QString &errorMsg); - QGCFileDownload* _fileDownload; - QNetworkDiskCache* _diskCache; - bool _downloadFromNetwork{false}; +private: + QGCFileDownload* _fileDownload = nullptr; + QNetworkDiskCache* _diskCache = nullptr; + bool _downloadFromNetwork = false; }; diff --git a/src/Utilities/QGCFileDownload.cc b/src/Utilities/QGCFileDownload.cc index 10903e232b3..41b0842dfec 100644 --- a/src/Utilities/QGCFileDownload.cc +++ b/src/Utilities/QGCFileDownload.cc @@ -7,20 +7,45 @@ * ****************************************************************************/ - #include "QGCFileDownload.h" +#include "QGCLoggingCategory.h" #include #include #include -QGCFileDownload::QGCFileDownload(QObject* parent) - : QNetworkAccessManager(parent) +QGC_LOGGING_CATEGORY(QGCFileDownloadLog, "qgc.utilities.qgcfiledownload"); + +QGCFileDownload::QGCFileDownload(QObject *parent) + : QObject(parent) + , _networkManager(new QNetworkAccessManager(this)) +{ + // qCDebug(QGCFileDownloadLog) << Q_FUNC_INFO << this; +} + +QGCFileDownload::~QGCFileDownload() { + // qCDebug(QGCFileDownloadLog) << Q_FUNC_INFO << this; +} +void QGCFileDownload::setCache(QAbstractNetworkCache *cache) +{ + _networkManager->setCache(cache); +} + +void QGCFileDownload::setIgnoreSSLErrorsIfNeeded(QNetworkReply &networkReply) +{ + const bool sslLibraryBuildIs1x = ((QSslSocket::sslLibraryBuildVersionNumber() & 0xf0000000) == 0x10000000); + const bool sslLibraryIs3x = ((QSslSocket::sslLibraryVersionNumber() & 0xf0000000) == 0x30000000); + if (sslLibraryBuildIs1x && sslLibraryIs3x) { + qCWarning(QGCFileDownloadLog) << "Ignoring ssl certificates due to OpenSSL version mismatch"; + QList errorsThatCanBeIgnored; + errorsThatCanBeIgnored << QSslError(QSslError::NoPeerCertificate); + networkReply.ignoreSslErrors(errorsThatCanBeIgnored); + } } -bool QGCFileDownload::download(const QString& remoteFile, const QVector>& requestAttributes, bool redirect) +bool QGCFileDownload::download(const QString &remoteFile, const QList> &requestAttributes, bool redirect) { if (!redirect) { _requestAttributes = requestAttributes; @@ -28,10 +53,9 @@ bool QGCFileDownload::download(const QString& remoteFile, const QVector &attribute : requestAttributes) { networkRequest.setAttribute(attribute.first, attribute.second); } - QNetworkProxy tProxy; +#if !defined(Q_OS_IOS) && !defined(Q_OS_ANDROID) + QNetworkProxy tProxy = _networkManager->proxy(); tProxy.setType(QNetworkProxy::DefaultProxy); - setProxy(tProxy); - - QNetworkReply* networkReply = get(networkRequest); + _networkManager->setProxy(tProxy); +#endif + + QNetworkReply *const networkReply = _networkManager->get(networkRequest); if (!networkReply) { - qWarning() << "QNetworkAccessManager::get failed"; + qCWarning(QGCFileDownloadLog) << "QNetworkAccessManager::get failed"; return false; } setIgnoreSSLErrorsIfNeeded(*networkReply); - connect(networkReply, &QNetworkReply::downloadProgress, this, &QGCFileDownload::downloadProgress); - connect(networkReply, &QNetworkReply::finished, this, &QGCFileDownload::_downloadFinished); - connect(networkReply, &QNetworkReply::errorOccurred, this, &QGCFileDownload::_downloadError); + (void) connect(networkReply, &QNetworkReply::downloadProgress, this, &QGCFileDownload::downloadProgress); + (void) connect(networkReply, &QNetworkReply::finished, this, &QGCFileDownload::_downloadFinished); + (void) connect(networkReply, &QNetworkReply::errorOccurred, this, &QGCFileDownload::_downloadError); + return true; } -void QGCFileDownload::_downloadFinished(void) +void QGCFileDownload::_downloadFinished() { - QNetworkReply* reply = qobject_cast(QObject::sender()); + QNetworkReply *const reply = qobject_cast(QObject::sender()); + if (!reply) { + return; + } + reply->deleteLater(); - // When an error occurs or the user cancels the download, we still end up here. So bail out in - // those cases. if (reply->error() != QNetworkReply::NoError) { - reply->deleteLater(); return; } - // Check for redirection + if (!reply->isOpen()) { + return; + } + + const int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + if ((statusCode < HTTP_Response::SUCCESS_OK) || (statusCode >= HTTP_Response::REDIRECTION_MULTIPLE_CHOICES)) { + return; + } + QVariant redirectionTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute); if (!redirectionTarget.isNull()) { - QUrl redirectUrl = reply->url().resolved(redirectionTarget.toUrl()); - download(redirectUrl.toString(), _requestAttributes, true /* redirect */); - reply->deleteLater(); + const QUrl redirectUrl = reply->url().resolved(redirectionTarget.toUrl()); + (void) download(redirectUrl.toString(), _requestAttributes, true); return; } // Split out filename from path QString remoteFileName = QFileInfo(reply->url().toString()).fileName(); if (remoteFileName.isEmpty()) { - qWarning() << "Unabled to parse filename from remote url" << reply->url().toString(); + qCWarning(QGCFileDownloadLog) << "Unabled to parse filename from remote url" << reply->url().toString(); remoteFileName = "DownloadedFile"; } // Strip out http parameters from remote filename - int parameterIndex = remoteFileName.indexOf("?"); + const int parameterIndex = remoteFileName.indexOf("?"); if (parameterIndex != -1) { - remoteFileName = remoteFileName.left(parameterIndex); + remoteFileName = remoteFileName.left(parameterIndex); } - // Determine location to download file to QString downloadFilename = QStandardPaths::writableLocation(QStandardPaths::TempLocation); if (downloadFilename.isEmpty()) { downloadFilename = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); @@ -110,13 +144,13 @@ void QGCFileDownload::_downloadFinished(void) return; } } - downloadFilename += "/" + remoteFileName; + downloadFilename += "/" + remoteFileName; if (!downloadFilename.isEmpty()) { // Store downloaded file in download location QFile file(downloadFilename); if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { - emit downloadComplete(_originalRemoteFile, downloadFilename, tr("Could not save downloaded file to %1. Error: %2").arg(downloadFilename).arg(file.errorString())); + emit downloadComplete(_originalRemoteFile, downloadFilename, tr("Could not save downloaded file to %1. Error: %2").arg(downloadFilename, file.errorString())); return; } @@ -125,44 +159,27 @@ void QGCFileDownload::_downloadFinished(void) emit downloadComplete(_originalRemoteFile, downloadFilename, QString()); } else { - QString errorMsg = "Internal error"; - qWarning() << errorMsg; + const QString errorMsg = "Internal error"; + qCWarning(QGCFileDownloadLog) << errorMsg; emit downloadComplete(_originalRemoteFile, downloadFilename, errorMsg); } - - reply->deleteLater(); } -/// @brief Called when an error occurs during download void QGCFileDownload::_downloadError(QNetworkReply::NetworkError code) { QString errorMsg; - - if (code == QNetworkReply::OperationCanceledError) { - errorMsg = tr("Download cancelled"); - } else if (code == QNetworkReply::ContentNotFoundError) { + switch (code) { + case QNetworkReply::OperationCanceledError: + errorMsg = tr("Download cancelled"); + break; + case QNetworkReply::ContentNotFoundError: errorMsg = tr("Error: File Not Found"); - - } else { + break; + default: errorMsg = tr("Error during download. Error: %1").arg(code); + break; } emit downloadComplete(_originalRemoteFile, QString(), errorMsg); } - -void QGCFileDownload::setIgnoreSSLErrorsIfNeeded(QNetworkReply& networkReply) -{ - // Some systems (like Ubuntu 22.04) only ship with OpenSSL 3.x, however Qt 5.15.2 links against OpenSSL 1.x. - // This results in unresolved symbols for EVP_PKEY_base_id and SSL_get_peer_certificate. - // To still get a connection we have to ignore certificate verification (connection is still encrypted but open to MITM attacks) - // See https://bugreports.qt.io/browse/QTBUG-115146 - const bool sslLibraryBuildIs1x = (QSslSocket::sslLibraryBuildVersionNumber() & 0xf0000000) == 0x10000000; - const bool sslLibraryIs3x = (QSslSocket::sslLibraryVersionNumber() & 0xf0000000) == 0x30000000; - if (sslLibraryBuildIs1x && sslLibraryIs3x) { - qWarning() << "Ignoring ssl certificates due to OpenSSL version mismatch"; - QList errorsThatCanBeIgnored; - errorsThatCanBeIgnored << QSslError(QSslError::NoPeerCertificate); - networkReply.ignoreSslErrors(errorsThatCanBeIgnored); - } -} diff --git a/src/Utilities/QGCFileDownload.h b/src/Utilities/QGCFileDownload.h index 984a36117c0..0013cce250f 100644 --- a/src/Utilities/QGCFileDownload.h +++ b/src/Utilities/QGCFileDownload.h @@ -9,33 +9,52 @@ #pragma once +#include +#include #include #include -class QGCFileDownload : public QNetworkAccessManager +class QNetworkAccessManager; +class QAbstractNetworkCache; + +Q_DECLARE_LOGGING_CATEGORY(QGCFileDownloadLog) + +class QGCFileDownload : public QObject { Q_OBJECT - + + enum HTTP_Response { + SUCCESS_OK = 200, + REDIRECTION_MULTIPLE_CHOICES = 300 + }; + public: - QGCFileDownload(QObject* parent = nullptr); - + QGCFileDownload(QObject *parent = nullptr); + ~QGCFileDownload(); + /// Download the specified remote file. - /// @param remoteFile File to download. Can be http address or file system path. - /// @param requestAttributes Optional request attributes to set - /// @param redirect true: call is internal due to redirect - /// @return true: Asynchronous download has started, false: Download initialization failed - bool download(const QString& remoteFile, const QVector>& requestAttributes={}, bool redirect = false); + /// @param remoteFile File to download. Can be http address or file system path. + /// @param requestAttributes Optional request attributes to set + /// @param redirect true: call is internal due to redirect + /// @return true: Asynchronous download has started, false: Download initialization failed + bool download(const QString& remoteFile, const QList> &requestAttributes = {}, bool redirect = false); - static void setIgnoreSSLErrorsIfNeeded(QNetworkReply& networkReply); + void setCache(QAbstractNetworkCache *cache); + + static void setIgnoreSSLErrorsIfNeeded(QNetworkReply &networkReply); signals: void downloadProgress(qint64 curr, qint64 total); - void downloadComplete(QString remoteFile, QString localFile, QString errorMsg); + void downloadComplete(const QString &remoteFile, const QString &localFile, const QString &errorMsg); -private: - void _downloadFinished(void); +private slots: + void _downloadFinished(); + + /// Called when an error occurs during download void _downloadError(QNetworkReply::NetworkError code); +private: + QNetworkAccessManager *_networkManager = nullptr; QString _originalRemoteFile; - QVector> _requestAttributes; + QList> _requestAttributes; }; diff --git a/src/Vehicle/Components/ComponentInformationManager.cc b/src/Vehicle/Components/ComponentInformationManager.cc index 40999d782c9..86a416c2ab6 100644 --- a/src/Vehicle/Components/ComponentInformationManager.cc +++ b/src/Vehicle/Components/ComponentInformationManager.cc @@ -28,7 +28,7 @@ QGC_LOGGING_CATEGORY(ComponentInformationManagerLog, "ComponentInformationManage ComponentInformationManager::ComponentInformationManager(Vehicle* vehicle) : _vehicle (vehicle) , _requestTypeStateMachine (this) - , _cachedFileDownload(new QGCCachedFileDownload(this, QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/QGCCompInfoFileDownloadCache"))) + , _cachedFileDownload(new QGCCachedFileDownload(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/QGCCompInfoFileDownloadCache"), this)) , _fileCache(ComponentInformationCache::defaultInstance()) , _translation(new ComponentInformationTranslation(this, _cachedFileDownload)) { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ef75ce1f526..b363c2f0a23 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -98,6 +98,7 @@ add_subdirectory(UI) add_subdirectory(Utilities) # Compression add_qgc_test(DecompressionTest) +add_qgc_test(UtilitiesTest) add_subdirectory(Vehicle) # Components diff --git a/test/UnitTestList.cc b/test/UnitTestList.cc index 7a408f67211..7b487a7adca 100644 --- a/test/UnitTestList.cc +++ b/test/UnitTestList.cc @@ -9,7 +9,6 @@ #include "UnitTestList.h" #include "UnitTest.h" -#include "QGCApplication.h" #include "QGCLoggingCategory.h" // ADSB @@ -88,6 +87,7 @@ // Utilities // Compression #include "DecompressionTest.h" +#include "QGCFileDownloadTest.h" // Vehicle // Components @@ -109,98 +109,99 @@ QGC_LOGGING_CATEGORY(UnitTestsLog, "qgc.test.unittestlist") int runTests(bool stress, QStringView unitTestOptions) { - // ADSB - UT_REGISTER_TEST(ADSBTest) + // ADSB + UT_REGISTER_TEST(ADSBTest) - // AnalyzeView - UT_REGISTER_TEST(ExifParserTest) - UT_REGISTER_TEST(GeoTagControllerTest) - // UT_REGISTER_TEST(MavlinkLogTest) - // UT_REGISTER_TEST(LogDownloadTest) - UT_REGISTER_TEST(PX4LogParserTest) - UT_REGISTER_TEST(ULogParserTest) + // AnalyzeView + UT_REGISTER_TEST(ExifParserTest) + UT_REGISTER_TEST(GeoTagControllerTest) + // UT_REGISTER_TEST(MavlinkLogTest) + // UT_REGISTER_TEST(LogDownloadTest) + UT_REGISTER_TEST(PX4LogParserTest) + UT_REGISTER_TEST(ULogParserTest) - // Audio - UT_REGISTER_TEST(AudioOutputTest) + // Audio + UT_REGISTER_TEST(AudioOutputTest) - // AutoPilotPlugins - // UT_REGISTER_TEST(RadioConfigTest) + // AutoPilotPlugins + // UT_REGISTER_TEST(RadioConfigTest) - // Comms - UT_REGISTER_TEST(QGCSerialPortInfoTest) + // Comms + UT_REGISTER_TEST(QGCSerialPortInfoTest) - // FactSystem - UT_REGISTER_TEST(FactSystemTestGeneric) - UT_REGISTER_TEST(FactSystemTestPX4) - UT_REGISTER_TEST(ParameterManagerTest) + // FactSystem + UT_REGISTER_TEST(FactSystemTestGeneric) + UT_REGISTER_TEST(FactSystemTestPX4) + UT_REGISTER_TEST(ParameterManagerTest) - // FollowMe - UT_REGISTER_TEST(FollowMeTest) + // FollowMe + UT_REGISTER_TEST(FollowMeTest) - // Geo + // Geo UT_REGISTER_TEST(GeoTest) // MAVLink UT_REGISTER_TEST(StatusTextHandlerTest) UT_REGISTER_TEST(SigningTest) - // MissionManager - UT_REGISTER_TEST(CameraCalcTest) - UT_REGISTER_TEST(CameraSectionTest) - UT_REGISTER_TEST(CorridorScanComplexItemTest) - // UT_REGISTER_TEST(FWLandingPatternTest) - // UT_REGISTER_TEST(LandingComplexItemTest) - // UT_REGISTER_TEST_STANDALONE(MissionCommandTreeEditorTest) - UT_REGISTER_TEST(MissionCommandTreeTest) - UT_REGISTER_TEST(MissionControllerManagerTest) - UT_REGISTER_TEST(MissionControllerTest) - UT_REGISTER_TEST(MissionItemTest) - UT_REGISTER_TEST(MissionManagerTest) - UT_REGISTER_TEST(MissionSettingsTest) - UT_REGISTER_TEST(PlanMasterControllerTest) - UT_REGISTER_TEST(QGCMapPolygonTest) - UT_REGISTER_TEST(QGCMapPolylineTest) - // UT_REGISTER_TEST(SectionTest) - UT_REGISTER_TEST(SimpleMissionItemTest) - UT_REGISTER_TEST(SpeedSectionTest) - UT_REGISTER_TEST(StructureScanComplexItemTest) - UT_REGISTER_TEST(SurveyComplexItemTest) - UT_REGISTER_TEST(TransectStyleComplexItemTest) - // UT_REGISTER_TEST(VisualMissionItemTest) - - // qgcunittest - // UT_REGISTER_TEST(FileDialogTest) - // UT_REGISTER_TEST(MainWindowTest) - // UT_REGISTER_TEST(MessageBoxTest) - - // QmlControls - - // Terrain - UT_REGISTER_TEST(TerrainQueryTest) - - // UI - - // Utilities - // Compression - UT_REGISTER_TEST(DecompressionTest) - - // Vehicle - // Components - UT_REGISTER_TEST(ComponentInformationCacheTest) - UT_REGISTER_TEST(ComponentInformationTranslationTest) - UT_REGISTER_TEST(FTPManagerTest) - // UT_REGISTER_TEST(InitialConnectTest) - // UT_REGISTER_TEST(RequestMessageTest) - // UT_REGISTER_TEST(SendMavCommandWithHandlerTest) - // UT_REGISTER_TEST(SendMavCommandWithSignalingTest) - - // Missing - // UT_REGISTER_TEST(FlightGearUnitTest) - // UT_REGISTER_TEST(LinkManagerTest) - // UT_REGISTER_TEST(SendMavCommandTest) - // UT_REGISTER_TEST(TCPLinkTest) - - int result = 0; + // MissionManager + UT_REGISTER_TEST(CameraCalcTest) + UT_REGISTER_TEST(CameraSectionTest) + UT_REGISTER_TEST(CorridorScanComplexItemTest) + // UT_REGISTER_TEST(FWLandingPatternTest) + // UT_REGISTER_TEST(LandingComplexItemTest) + // UT_REGISTER_TEST_STANDALONE(MissionCommandTreeEditorTest) + UT_REGISTER_TEST(MissionCommandTreeTest) + UT_REGISTER_TEST(MissionControllerManagerTest) + UT_REGISTER_TEST(MissionControllerTest) + UT_REGISTER_TEST(MissionItemTest) + UT_REGISTER_TEST(MissionManagerTest) + UT_REGISTER_TEST(MissionSettingsTest) + UT_REGISTER_TEST(PlanMasterControllerTest) + UT_REGISTER_TEST(QGCMapPolygonTest) + UT_REGISTER_TEST(QGCMapPolylineTest) + // UT_REGISTER_TEST(SectionTest) + UT_REGISTER_TEST(SimpleMissionItemTest) + UT_REGISTER_TEST(SpeedSectionTest) + UT_REGISTER_TEST(StructureScanComplexItemTest) + UT_REGISTER_TEST(SurveyComplexItemTest) + UT_REGISTER_TEST(TransectStyleComplexItemTest) + // UT_REGISTER_TEST(VisualMissionItemTest) + + // qgcunittest + // UT_REGISTER_TEST(FileDialogTest) + // UT_REGISTER_TEST(MainWindowTest) + // UT_REGISTER_TEST(MessageBoxTest) + + // QmlControls + + // Terrain + UT_REGISTER_TEST(TerrainQueryTest) + + // UI + + // Utilities + // Compression + UT_REGISTER_TEST(DecompressionTest) + // UT_REGISTER_TEST(QGCFileDownloadTest) + + // Vehicle + // Components + UT_REGISTER_TEST(ComponentInformationCacheTest) + UT_REGISTER_TEST(ComponentInformationTranslationTest) + UT_REGISTER_TEST(FTPManagerTest) + // UT_REGISTER_TEST(InitialConnectTest) + // UT_REGISTER_TEST(RequestMessageTest) + // UT_REGISTER_TEST(SendMavCommandWithHandlerTest) + // UT_REGISTER_TEST(SendMavCommandWithSignalingTest) + + // Missing + // UT_REGISTER_TEST(FlightGearUnitTest) + // UT_REGISTER_TEST(LinkManagerTest) + // UT_REGISTER_TEST(SendMavCommandTest) + // UT_REGISTER_TEST(TCPLinkTest) + + int result = 0; for (int i=0; i < (stress ? 20 : 1); i++) { // Run the test diff --git a/test/Utilities/CMakeLists.txt b/test/Utilities/CMakeLists.txt index ff0354538b4..22b813e255b 100644 --- a/test/Utilities/CMakeLists.txt +++ b/test/Utilities/CMakeLists.txt @@ -3,6 +3,8 @@ add_subdirectory(Compression) find_package(Qt6 REQUIRED COMPONENTS Core) qt_add_library(UtilitiesTest STATIC + QGCFileDownloadTest.cc + QGCFileDownloadTest.h ) target_link_libraries(UtilitiesTest diff --git a/test/Utilities/QGCFileDownloadTest.cc b/test/Utilities/QGCFileDownloadTest.cc new file mode 100644 index 00000000000..5a10ab2c2e8 --- /dev/null +++ b/test/Utilities/QGCFileDownloadTest.cc @@ -0,0 +1,15 @@ +#include "QGCFileDownloadTest.h" +#include "QGCCachedFileDownload.h" +#include "QGCFileDownload.h" + +#include + +void QGCFileDownloadTest::_testFileDownload() +{ + QGCFileDownload file; +} + +void QGCFileDownloadTest::_testCachedFileDownload() +{ + QGCCachedFileDownload file(""); +} diff --git a/test/Utilities/QGCFileDownloadTest.h b/test/Utilities/QGCFileDownloadTest.h new file mode 100644 index 00000000000..d294e6bcf67 --- /dev/null +++ b/test/Utilities/QGCFileDownloadTest.h @@ -0,0 +1,12 @@ +#pragma once + +#include "UnitTest.h" + +class QGCFileDownloadTest : public UnitTest +{ + Q_OBJECT + +private slots: + void _testFileDownload(); + void _testCachedFileDownload(); +}; diff --git a/test/Vehicle/Components/ComponentInformationTranslationTest.cc b/test/Vehicle/Components/ComponentInformationTranslationTest.cc index a462718044e..b5e78a23788 100644 --- a/test/Vehicle/Components/ComponentInformationTranslationTest.cc +++ b/test/Vehicle/Components/ComponentInformationTranslationTest.cc @@ -18,7 +18,7 @@ void ComponentInformationTranslationTest::_basic_test() { QString translationJson = ":/unittest/TranslationTest.json"; QString translationTs = ":/unittest/TranslationTest_de_DE.ts"; - ComponentInformationTranslation* translation = new ComponentInformationTranslation(this, new QGCCachedFileDownload(this, "")); + ComponentInformationTranslation* translation = new ComponentInformationTranslation(this, new QGCCachedFileDownload("", this)); QString tempFilename = translation->translateJsonUsingTS(translationJson, translationTs); QVERIFY(!tempFilename.isEmpty()); From 056d25a9c48d9f10cf810782a10cb54c23b31541 Mon Sep 17 00:00:00 2001 From: Holden Date: Fri, 18 Oct 2024 12:31:56 -0400 Subject: [PATCH 17/27] CMake: Improve Dependencies --- .github/actions/gstreamer/action.yml | 34 ++-- .github/workflows/linux.yml | 50 ++++++ CMakeLists.txt | 16 +- cmake/find-modules/FindAVFoundation.cmake | 14 ++ cmake/find-modules/FindBlueZ.cmake | 12 ++ cmake/find-modules/FindFFmpeg.cmake | 18 +- cmake/find-modules/FindFlite.cmake | 97 +++++++++++ cmake/find-modules/FindGStreamer.cmake | 134 ++++++++++----- .../FindGeographicLib.cmake | 0 cmake/find-modules/FindLibdrm.cmake | 134 +++++++++++++++ cmake/find-modules/FindLibudev.cmake | 10 ++ cmake/find-modules/FindMMRenderer.cmake | 29 ++++ cmake/find-modules/FindMMRendererCore.cmake | 12 ++ cmake/find-modules/FindPipeWire.cmake | 123 +++++++++++++ cmake/{ => find-modules}/FindSDL2.cmake | 0 cmake/find-modules/FindSpeechDispatcher.cmake | 17 ++ cmake/find-modules/FindWMF.cmake | 52 ++++++ cmake/find-modules/FindWrapPulseAudio.cmake | 37 ++++ cmake/find-modules/FindXRender.cmake | 14 ++ cmake/find-modules/Findgbm.cmake | 125 ++++++++++++++ src/Audio/CMakeLists.txt | 12 ++ src/Joystick/CMakeLists.txt | 4 +- .../VideoReceiver/GStreamer/GStreamer.cc | 4 +- .../GStreamer/gstqml6gl/CMakeLists.txt | 162 +++++++++--------- tools/setup/install-dependencies-debian.sh | 82 +++++---- 25 files changed, 1013 insertions(+), 179 deletions(-) create mode 100644 cmake/find-modules/FindAVFoundation.cmake create mode 100644 cmake/find-modules/FindBlueZ.cmake create mode 100644 cmake/find-modules/FindFlite.cmake rename cmake/{ => find-modules}/FindGeographicLib.cmake (100%) create mode 100644 cmake/find-modules/FindLibdrm.cmake create mode 100644 cmake/find-modules/FindLibudev.cmake create mode 100644 cmake/find-modules/FindMMRenderer.cmake create mode 100644 cmake/find-modules/FindMMRendererCore.cmake create mode 100644 cmake/find-modules/FindPipeWire.cmake rename cmake/{ => find-modules}/FindSDL2.cmake (100%) create mode 100644 cmake/find-modules/FindSpeechDispatcher.cmake create mode 100644 cmake/find-modules/FindWMF.cmake create mode 100644 cmake/find-modules/FindWrapPulseAudio.cmake create mode 100644 cmake/find-modules/FindXRender.cmake create mode 100644 cmake/find-modules/Findgbm.cmake diff --git a/.github/actions/gstreamer/action.yml b/.github/actions/gstreamer/action.yml index 6855f0c73e3..0eb486c6bef 100644 --- a/.github/actions/gstreamer/action.yml +++ b/.github/actions/gstreamer/action.yml @@ -34,30 +34,32 @@ runs: run: meson setup --prefix=${{ inputs.install_directory }} --buildtype=${{ inputs.build_type }} - --wrap_mode=forcefallback - -Dgst-full-libraries=base,video,gl + --wrap-mode=forcefallback + --strip + -Dauto_features=disabled + -Dgst-full-libraries=video,gl -Dgpl=enabled -Dlibav=enabled -Dorc=enabled - -Dbase=enabled - -Dvaapi=enabled -Dqt6=enabled + -Dvaapi=enabled + -Dbase=enabled + -Dgst-plugins-base:app=enabled -Dgst-plugins-base:gl=enabled + -Dgst-plugins-base:gl_api=opengl,gles2 -Dgst-plugins-base:gl_platform=glx,egl -Dgst-plugins-base:gl_winsys=x11,egl,wayland - -Dgst-plugins-base:gl_api=opengl,gles2 - -Dgst-plugins-base:x11=enabled -Dgst-plugins-base:playback=enabled -Dgst-plugins-base:tcp=enabled - -Dgst-plugins-base:app=enabled + -Dgst-plugins-base:x11=enabled -Dgood=enabled - -Dgst-plugins-good:qt6=enabled - -Dgst-plugins-good:qt-x11=enabled - -Dgst-plugins-good:qt-egl=enabled - -Dgst-plugins-good:qt-wayland=enabled - -Dgst-plugins-good:qt-method=auto -Dgst-plugins-good:isomp4=enabled -Dgst-plugins-good:matroska=enabled + -Dgst-plugins-good:qt-egl=enabled + -Dgst-plugins-good:qt-method=auto + -Dgst-plugins-good:qt-wayland=enabled + -Dgst-plugins-good:qt-x11=enabled + -Dgst-plugins-good:qt6=enabled -Dgst-plugins-good:rtp=enabled -Dgst-plugins-good:rtpmanager=enabled -Dgst-plugins-good:rtsp=enabled @@ -66,19 +68,17 @@ runs: -Dgst-plugins-bad:gl=enabled -Dgst-plugins-bad:mpegtsdemux=enabled -Dgst-plugins-bad:rtp=enabled - -Dgst-plugins-bad:videoparsers=enabled -Dgst-plugins-bad:sdp=enabled - -Dgst-plugins-bad:x11=enabled - -Dgst-plugins-bad:wayland=enabled -Dgst-plugins-bad:va=enabled + -Dgst-plugins-bad:videoparsers=enabled + -Dgst-plugins-bad:wayland=enabled + -Dgst-plugins-bad:x11=enabled -Dgst-plugins-bad:x265=enabled -Dugly=enabled -Dgst-plugins-ugly:x264=enabled builddir - # --strip=true # --default-library=static # --prefer_static=true - # -Dauto_features=disabled # -Dgst-full-target-type=static_library # -Dgstreamer:gstreamer-static-full=true shell: bash diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index f10476ebb64..1e33d35f8b0 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -108,6 +108,56 @@ jobs: uses: blinemedical/setup-gstreamer@v1 with: version: ${{ env.GST_VERSION }} + # gstreamerOptions: | + # --buildtype=release + # --wrap-mode=forcefallback + # --strip + # -Dauto_features=disabled + # -Dbase=enabled + # -Ddoc=disabled + # -Dexamples=disabled + # -Dges=disabled + # -Dgpl=enabled + # -Dgst-examples=disabled + # -Dgtk_doc=disabled + # -Dlibav=enabled + # -Dlibnice=disabled + # -Dnls=disabled + # -Dorc=enabled + # -Dpython=disabled + # -Dqt5=disabled + # -Drtsp_server=disabled + # -Dtests=disabled + # -Dgst-plugins-base:app=enabled + # -Dgst-plugins-base:gl=enabled + # -Dgst-plugins-base:gl_api=opengl,gles2 + # -Dgst-plugins-base:gl_platform=glx,egl + # -Dgst-plugins-base:gl_winsys=x11,egl,wayland + # -Dgst-plugins-base:playback=enabled + # -Dgst-plugins-base:tcp=enabled + # -Dgst-plugins-base:x11=enabled + # -Dgood=enabled + # -Dgst-plugins-good:isomp4=enabled + # -Dgst-plugins-good:matroska=enabled + # -Dgst-plugins-good:qt-egl=enabled + # -Dgst-plugins-good:qt-method=auto + # -Dgst-plugins-good:qt-wayland=enabled + # -Dgst-plugins-good:qt-x11=enabled + # -Dgst-plugins-good:rtp=enabled + # -Dgst-plugins-good:rtpmanager=enabled + # -Dgst-plugins-good:rtsp=enabled + # -Dgst-plugins-good:udp=enabled + # -Dbad=enabled + # -Dgst-plugins-bad:gl=enabled + # -Dgst-plugins-bad:mpegtsdemux=enabled + # -Dgst-plugins-bad:rtp=enabled + # -Dgst-plugins-bad:sdp=enabled + # -Dgst-plugins-bad:videoparsers=enabled + # -Dgst-plugins-bad:wayland=enabled + # -Dgst-plugins-bad:x11=enabled + # -Dgst-plugins-bad:x265=enabled + # -Dugly=enabled + # -Dgst-plugins-ugly:x264=enabled - uses: lukka/get-cmake@latest diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d14223e75e..3aaabc5be92 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -150,10 +150,6 @@ if(ANDROID) message(STATUS "QT_ANDROID_KEYSTORE_ALIAS $ENV{QT_ANDROID_KEYSTORE_ALIAS}") # QT_ANDROID_KEYSTORE_STORE_PASS, QT_ANDROID_KEYSTORE_KEY_PASS endif() - - if(NOT Qt6LinguistTools_DIR) - set(Qt6LinguistTools_DIR ${QT_HOST_PATH}/lib/cmake/Qt6LinguistTools) - endif() endif() set(QT_SILENCE_MISSING_DEPENDENCY_TARGET_WARNING ON) @@ -161,19 +157,19 @@ set(QT_SILENCE_MISSING_DEPENDENCY_TARGET_WARNING ON) find_package(Qt6 REQUIRED COMPONENTS - Bluetooth - Charts Concurrent Core Core5Compat Location Multimedia Network + OpenGL Positioning + Qml + QmlIntegration Quick QuickControls2 QuickWidgets - OpenGL Sensors Sql Svg @@ -182,6 +178,8 @@ find_package(Qt6 Widgets Xml OPTIONAL_COMPONENTS + Bluetooth + Charts LinguistTools MultimediaQuickPrivate Quick3D @@ -191,6 +189,10 @@ find_package(Qt6 ${QT_LIBRARY_HINTS} ) +if(NOT Qt6LinguistTools_DIR) + set(Qt6LinguistTools_DIR ${QT_HOST_PATH}/lib/cmake/Qt6LinguistTools) +endif() + # Require 6.6.3 because otherwise libQt6QuickControls2Basic.so.6 & # libQt6QuickControls2BasicStyleImpl.so.6 are missing. qt_standard_project_setup(REQUIRES 6.6.3) diff --git a/cmake/find-modules/FindAVFoundation.cmake b/cmake/find-modules/FindAVFoundation.cmake new file mode 100644 index 00000000000..3dad5d67745 --- /dev/null +++ b/cmake/find-modules/FindAVFoundation.cmake @@ -0,0 +1,14 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +find_library(AVFoundation_LIBRARY NAMES AVFoundation) +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(AVFoundation DEFAULT_MSG AVFoundation_LIBRARY) + +if(AVFoundation_FOUND AND NOT TARGET AVFoundation::AVFoundation) + add_library(AVFoundation::AVFoundation INTERFACE IMPORTED) + set_target_properties(AVFoundation::AVFoundation PROPERTIES + INTERFACE_LINK_LIBRARIES "${AVFoundation_LIBRARY}") +endif() + +mark_as_advanced(AVFoundation_LIBRARY) diff --git a/cmake/find-modules/FindBlueZ.cmake b/cmake/find-modules/FindBlueZ.cmake new file mode 100644 index 00000000000..b1d30122e66 --- /dev/null +++ b/cmake/find-modules/FindBlueZ.cmake @@ -0,0 +1,12 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +set(BlueZ_FOUND 0) + +find_package(PkgConfig QUIET) + +pkg_check_modules(BLUEZ bluez IMPORTED_TARGET) + +if(TARGET PkgConfig::BLUEZ) + set(BlueZ_FOUND 1) +endif() diff --git a/cmake/find-modules/FindFFmpeg.cmake b/cmake/find-modules/FindFFmpeg.cmake index ecf9b07cb16..1cd87badc25 100644 --- a/cmake/find-modules/FindFFmpeg.cmake +++ b/cmake/find-modules/FindFFmpeg.cmake @@ -253,7 +253,11 @@ endfunction() # and adds them to the target library. function(__ffmpeg_internal_set_dependencies _component) string(TOLOWER ${_component} lib) - set(PC_FILE ${${_component}_LIBRARY_DIR}/pkgconfig/lib${lib}.pc) + + # The pkgconfig directory is always in lib/pkgconfig/, even on Windows + # where libs and dlls are in bin/ + set(PC_FILE ${${_component}_LIBRARY_DIR}/../lib/pkgconfig/lib${lib}.pc) + if(EXISTS ${PC_FILE}) file(READ ${PC_FILE} pcfile) @@ -370,8 +374,20 @@ foreach (_component ${FFmpeg_FIND_COMPONENTS}) list(APPEND _FFmpeg_REQUIRED_VARS ${_component}_LIBRARY ${_component}_INCLUDE_DIR) endforeach () +set(FIND_FFMPEG_HELP_STRING +[=[FFMPEG_DIR CMake variable is not correct. + Make sure that the FFMPEG_DIR CMake variable is set to a path that + contains FFmpeg 'lib' and 'include' directories and that the FFmpeg + installation is built with the avformat, avcodec, swresample, + swscale, and avutil libraries. To resolve the issue, please delete + CMakeCache.txt and run configure again with the correct FFMPEG_DIR + CMake variable set. +]=]) + # Give a nice error message if some of the required vars are missing. find_package_handle_standard_args(FFmpeg REQUIRED_VARS ${_FFmpeg_REQUIRED_VARS} HANDLE_COMPONENTS + REASON_FAILURE_MESSAGE + ${FIND_FFMPEG_HELP_STRING} ) diff --git a/cmake/find-modules/FindFlite.cmake b/cmake/find-modules/FindFlite.cmake new file mode 100644 index 00000000000..ef1f27211c6 --- /dev/null +++ b/cmake/find-modules/FindFlite.cmake @@ -0,0 +1,97 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +# We can't create the same interface imported target multiple times, CMake will complain if we do +# that. This can happen if the find_package call is done in multiple different subdirectories. +if(TARGET Flite::Flite) + set(Flite_FOUND 1) + return() +endif() + +find_path(Flite_INCLUDE_DIR + NAMES + flite/flite.h +) +find_library(Flite_LIBRARY + NAMES + flite +) + +if(NOT Flite_INCLUDE_DIR OR NOT Flite_LIBRARY) + set(Flite_FOUND 0) + return() +endif() + +include(CMakePushCheckState) +include(CheckCXXSourceCompiles) + +# Flite can be built with ALSA support, +# in which case we need to link ALSA as well +find_package(ALSA QUIET) + +cmake_push_check_state(RESET) + +set(CMAKE_REQUIRED_INCLUDES "${Flite_INCLUDE_DIR}") +set(CMAKE_REQUIRED_LIBRARIES "${Flite_LIBRARY}") + +if(ALSA_FOUND) + list(APPEND CMAKE_REQUIRED_LIBRARIES "${ALSA_LIBRARIES}") +endif() + +check_cxx_source_compiles(" +#include + +static int fliteAudioCb(const cst_wave *w, int start, int size, + int last, cst_audio_streaming_info *asi) +{ + (void)w; + (void)start; + (void)size; + (void)last; + (void)asi; + return CST_AUDIO_STREAM_STOP; +} + +int main() +{ + cst_audio_streaming_info *asi = new_audio_streaming_info(); + asi->asc = fliteAudioCb; // This fails for old Flite + new_audio_streaming_info(); + return 0; +} +" HAVE_FLITE) + +cmake_pop_check_state() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Flite + FOUND_VAR + Flite_FOUND + REQUIRED_VARS + Flite_LIBRARY + Flite_INCLUDE_DIR + HAVE_FLITE +) + +if(Flite_FOUND) + add_library(Flite::Flite UNKNOWN IMPORTED) + set_target_properties(Flite::Flite PROPERTIES + IMPORTED_LOCATION "${Flite_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${Flite_INCLUDE_DIR}" + ) + if(ALSA_FOUND) + set_target_properties(Flite::Flite PROPERTIES + INTERFACE_LINK_LIBRARIES "${ALSA_LIBRARIES}" + ) + endif() +endif() + +mark_as_advanced(Flite_LIBRARY Flite_INCLUDE_DIR HAVE_FLITE) + + +if(HAVE_FLITE) + set(Flite_FOUND 1) +else() + message("Flite was found, but the version is too old (<2.0.0)") + set(Flite_FOUND 0) +endif() diff --git a/cmake/find-modules/FindGStreamer.cmake b/cmake/find-modules/FindGStreamer.cmake index c1102892bab..984ded80e05 100644 --- a/cmake/find-modules/FindGStreamer.cmake +++ b/cmake/find-modules/FindGStreamer.cmake @@ -214,12 +214,14 @@ function(find_gstreamer_component component) set(target GStreamer::${component}) if(NOT TARGET ${target}) - string(TOUPPER ${component} upper) - pkg_check_modules(PC_GSTREAMER_${upper} IMPORTED_TARGET ${pkgconfig_name}) - if(TARGET PkgConfig::PC_GSTREAMER_${upper}) - add_library(GStreamer::${component} INTERFACE IMPORTED) - target_link_libraries(GStreamer::${component} INTERFACE PkgConfig::PC_GSTREAMER_${upper}) - set_target_properties(GStreamer::${component} PROPERTIES VERSION ${PC_GSTREAMER_${upper}_VERSION}) + if(PkgConfig_FOUND) + string(TOUPPER ${component} upper) + pkg_check_modules(PC_GSTREAMER_${upper} IMPORTED_TARGET ${pkgconfig_name}) + if(TARGET PkgConfig::PC_GSTREAMER_${upper}) + add_library(GStreamer::${component} INTERFACE IMPORTED) + target_link_libraries(GStreamer::${component} INTERFACE PkgConfig::PC_GSTREAMER_${upper}) + set_target_properties(GStreamer::${component} PROPERTIES VERSION ${PC_GSTREAMER_${upper}_VERSION}) + endif() else() foreach(dependency IN LISTS ARGS_DEPENDENCIES) if (NOT TARGET ${dependency}) @@ -295,20 +297,73 @@ find_gstreamer_component(Gl ################################################################################ -set(GST_DEPENDENCIES) -find_package(GLESv2 QUIET) -list(APPEND GST_DEPENDENCIES GLESv2::GLESv2) -find_package(OpenGL QUIET OPTIONAL_COMPONENTS EGL GLX OpenGL) # GLES2 GLES3 -list(APPEND GST_DEPENDENCIES OpenGL::GL OpenGL::EGL OpenGL::GLX OpenGL::OpenGL) -find_package(X11_XCB QUIET) -list(APPEND GST_DEPENDENCIES X11::XCB) -find_package(EGL QUIET) -list(APPEND GST_DEPENDENCIES EGL::EGL) -pkg_check_modules(PC_libdrm IMPORTED_TARGET libdrm) -list(APPEND GST_DEPENDENCIES PkgConfig::PC_libdrm) -pkg_check_modules(PC_gudev IMPORTED_TARGET gudev-1.0) -list(APPEND GST_DEPENDENCIES PkgConfig::PC_gudev) -cmake_print_variables(GST_DEPENDENCIES) +# set(GST_DEPENDENCIES) +# list(APPEND GST_DEPENDENCIES GLIB2::GLIB2 GLIB2::GOBJECT GLIB2::GIO GObject::GObject) +# find_package(GLESv2 QUIET) +# list(APPEND GST_DEPENDENCIES GLESv2::GLESv2) +# find_package(OpenGL QUIET OPTIONAL_COMPONENTS EGL GLX OpenGL) # GLES2 GLES3 +# list(APPEND GST_DEPENDENCIES OpenGL::GL OpenGL::EGL OpenGL::GLX OpenGL::OpenGL) +# find_package(X11 QUIET) +# list(APPEND GST_DEPENDENCIES X11::X11) +# find_package(XRender QUIET) +# list(APPEND GST_DEPENDENCIES PkgConfig::XRender) +# find_package(XCB QUIET) +# list(APPEND GST_DEPENDENCIES XCB::XCB) +# find_package(X11_XCB QUIET) +# list(APPEND GST_DEPENDENCIES X11::XCB) +# find_package(XKB QUIET) +# list(APPEND GST_DEPENDENCIES XKB::XKB) +# find_package(XKB_COMMON_X11 QUIET) +# list(APPEND GST_DEPENDENCIES PkgConfig::XKB_COMMON_X11) +# find_package(EGL QUIET) +# list(APPEND GST_DEPENDENCIES EGL::EGL) +# find_package(Wayland QUIET) +# list(APPEND GST_DEPENDENCIES Wayland::Client Wayland::Cursor Wayland::Egl) +# find_package(Libdrm QUIET) +# list(APPEND GST_DEPENDENCIES Libdrm::Libdrm) +# find_package(gbm QUIET) +# list(APPEND GST_DEPENDENCIES gbm::gbm) +# find_package(FFmpeg QUIET) +# list(APPEND GST_DEPENDENCIES FFmpeg::FFmpeg) +# find_package(Fontconfig QUIET) +# list(APPEND GST_DEPENDENCIES Fontconfig::Fontconfig) +# find_package(Freetype QUIET) +# list(APPEND GST_DEPENDENCIES Freetype::Freetype) +# find_package(Iconv QUIET) +# list(APPEND GST_DEPENDENCIES Iconv::Iconv) +# find_package(Intl QUIET) +# list(APPEND GST_DEPENDENCIES Intl::Intl) +# find_package(JPEG QUIET) +# list(APPEND GST_DEPENDENCIES JPEG::JPEG) +# find_package(PNG QUIET) +# list(APPEND GST_DEPENDENCIES PNG::PNG) +# find_package(BZip2 QUIET) +# list(APPEND GST_DEPENDENCIES BZip2::BZip2) +# find_package(EXPAT QUIET) +# list(APPEND GST_DEPENDENCIES EXPAT::EXPAT) +# find_package(LibXml2 QUIET) +# list(APPEND GST_DEPENDENCIES LibXml2::LibXml2) +# find_package(LibLZMA QUIET) +# list(APPEND GST_DEPENDENCIES LibLZMA::LibLZMA) +# find_package(TIFF QUIET) +# list(APPEND GST_DEPENDENCIES TIFF::TIFF) +# find_package(ZLIB QUIET) +# list(APPEND GST_DEPENDENCIES ZLIB::ZLIB) +# find_package(Libudev) +# list(APPEND GST_DEPENDENCIES PkgConfig::Libudev) +# find_package(VAAPI QUIET) +# list(APPEND GST_DEPENDENCIES VAAPI::VAAPI) +# find_package(Vulkan QUIET) +# list(APPEND GST_DEPENDENCIES Vulkan::Vulkan) +# find_package(OpenCL) +# list(APPEND GST_DEPENDENCIES OpenCL::OpenCL) +# find_package(Threads) +# list(APPEND GST_DEPENDENCIES Threads::Threads) +# pkg_check_modules(PC_gudev IMPORTED_TARGET gudev-1.0) +# list(APPEND GST_DEPENDENCIES PkgConfig::PC_gudev) +# find_package(Qt6 QUIET COMPONENTS OPTIONAL_COMPONENTS Network OpenGL) # WaylandClient +# list(APPEND GST_DEPENDENCIES Qt6::Network Qt6::OpenGL) # Qt6::WaylandClient +# cmake_print_variables(GST_DEPENDENCIES) ################################################################################ @@ -433,12 +488,9 @@ if(GlX11 IN_LIST GStreamer_FIND_COMPONENTS) # find_package(X11 QUIET) # find_package(XCB QUIET) find_package(X11_XCB QUIET) - set(GlX11_DEPENDENCIES X11::XCB) - foreach(dependency IN LISTS GlX11_DEPENDENCIES) - if(TARGET ${dependency}) - target_link_libraries(GStreamer::GlX11 INTERFACE ${dependency}) - endif() - endforeach() + if(TARGET X11::XCB) + target_link_libraries(GStreamer::GlX11 INTERFACE X11::XCB) + endif() endif() endif() @@ -544,10 +596,6 @@ if(Va IN_LIST GStreamer_FIND_COMPONENTS) HEADER gst/va/gstva.h LIBRARY gstva-1.0 DEPENDENCIES GStreamer::Core GStreamer::Video) - - if(TARGET GSreamer::Va) - find_package(VAAPI QUIET) - endif() endif() ################################################################################ @@ -595,12 +643,12 @@ if(QGC_GST_STATIC_BUILD) target_compile_definitions(GStreamer::GStreamer INTERFACE QGC_GST_STATIC_BUILD) endif() -foreach(dependency IN LISTS GST_DEPENDENCIES) - if(TARGET ${dependency}) - cmake_print_variables(dependency) - target_link_libraries(GStreamer::GStreamer INTERFACE ${dependency}) - endif() -endforeach() +# foreach(dependency IN LISTS GST_DEPENDENCIES) +# if(TARGET ${dependency}) +# cmake_print_variables(dependency) +# target_link_libraries(GStreamer::GStreamer INTERFACE ${dependency}) +# endif() +# endforeach() target_include_directories(GStreamer::GStreamer INTERFACE @@ -616,6 +664,8 @@ target_link_directories(GStreamer::GStreamer INTERFACE ${GSTREAMER_LIB_PATH}) ################################################################################ +# TODO: https://gstreamer.freedesktop.org/documentation/qt6d3d11/index.html#qml6d3d11sink-page + add_library(GStreamer::Plugins INTERFACE IMPORTED) set(GSTREAMER_PLUGIN_PATH ${GSTREAMER_LIB_PATH}/gstreamer-1.0) @@ -639,8 +689,9 @@ set(GST_PLUGINS gstudp gstvideoparsersbad gstx264 - gstasf gstva + gstapp + gstvaapi # gstqml6 ) if(ANDROID) @@ -651,7 +702,7 @@ endif() foreach(plugin IN LISTS GST_PLUGINS) if(PkgConfig_FOUND) - pkg_check_modules(GST_PLUGIN_${plugin} IMPORTED_TARGET ${plugin}) + pkg_check_modules(GST_PLUGIN_${plugin} QUIET IMPORTED_TARGET ${plugin}) if(GST_PLUGIN_${plugin}_FOUND) target_link_libraries(GStreamer::Plugins INTERFACE PkgConfig::GST_PLUGIN_${plugin}) endif() @@ -665,15 +716,14 @@ foreach(plugin IN LISTS GST_PLUGINS) ${GSTREAMER_PLUGIN_PATH} ) if(GST_PLUGIN_${plugin}_LIBRARY) - cmake_print_variables(plugin) target_link_libraries(GStreamer::Plugins INTERFACE ${GST_PLUGIN_${plugin}_LIBRARY}) set(GST_PLUGIN_${plugin}_FOUND TRUE) endif() endif() - # if(GST_PLUGIN_${plugin}_FOUND) - # cmake_print_variables(plugin) - # endif() + if(GST_PLUGIN_${plugin}_FOUND) + cmake_print_variables(plugin) + endif() endforeach() if(NOT MACOS) diff --git a/cmake/FindGeographicLib.cmake b/cmake/find-modules/FindGeographicLib.cmake similarity index 100% rename from cmake/FindGeographicLib.cmake rename to cmake/find-modules/FindGeographicLib.cmake diff --git a/cmake/find-modules/FindLibdrm.cmake b/cmake/find-modules/FindLibdrm.cmake new file mode 100644 index 00000000000..a94e7fbcb60 --- /dev/null +++ b/cmake/find-modules/FindLibdrm.cmake @@ -0,0 +1,134 @@ +#.rst: +# FindLibdrm +# ------- +# +# Try to find libdrm on a Unix system. +# +# This will define the following variables: +# +# ``Libdrm_FOUND`` +# True if (the requested version of) libdrm is available +# ``Libdrm_VERSION`` +# The version of libdrm +# ``Libdrm_LIBRARIES`` +# This can be passed to target_link_libraries() instead of the ``Libdrm::Libdrm`` +# target +# ``Libdrm_INCLUDE_DIRS`` +# This should be passed to target_include_directories() if the target is not +# used for linking +# ``Libdrm_DEFINITIONS`` +# This should be passed to target_compile_options() if the target is not +# used for linking +# +# If ``Libdrm_FOUND`` is TRUE, it will also define the following imported target: +# +# ``Libdrm::Libdrm`` +# The libdrm library +# +# In general we recommend using the imported target, as it is easier to use. +# Bear in mind, however, that if the target is in the link interface of an +# exported library, it must be made available by the package config file. + +#============================================================================= +# Copyright 2014 Alex Merry +# Copyright 2014 Martin Gräßlin +# +# 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 copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +#============================================================================= + +if(CMAKE_VERSION VERSION_LESS 2.8.12) + message(FATAL_ERROR "CMake 2.8.12 is required by FindLibdrm.cmake") +endif() +if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.12) + message(AUTHOR_WARNING "Your project should require at least CMake 2.8.12 to use FindLibdrm.cmake") +endif() + +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 QUIET) + pkg_check_modules(PKG_Libdrm QUIET libdrm) + + set(Libdrm_DEFINITIONS ${PKG_Libdrm_CFLAGS_OTHER}) + set(Libdrm_VERSION ${PKG_Libdrm_VERSION}) + + find_path(Libdrm_INCLUDE_DIR + NAMES + xf86drm.h + HINTS + ${PKG_Libdrm_INCLUDE_DIRS} + PATH_SUFFIXES + libdrm + ) + find_library(Libdrm_LIBRARY + NAMES + drm + HINTS + ${PKG_Libdrm_LIBRARY_DIRS} + ) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(Libdrm + FOUND_VAR + Libdrm_FOUND + REQUIRED_VARS + Libdrm_LIBRARY + Libdrm_INCLUDE_DIR + VERSION_VAR + Libdrm_VERSION + ) + + if(Libdrm_FOUND AND NOT TARGET Libdrm::Libdrm) + add_library(Libdrm::Libdrm UNKNOWN IMPORTED) + set_target_properties(Libdrm::Libdrm PROPERTIES + IMPORTED_LOCATION "${Libdrm_LIBRARY}" + INTERFACE_COMPILE_OPTIONS "${Libdrm_DEFINITIONS}" + INTERFACE_INCLUDE_DIRECTORIES "${Libdrm_INCLUDE_DIR}" + ) + foreach(suffix libdrm drm) + if(EXISTS "${Libdrm_INCLUDE_DIR}/${suffix}") + set_property(TARGET Libdrm::Libdrm APPEND PROPERTY + INTERFACE_INCLUDE_DIRECTORIES "${Libdrm_INCLUDE_DIR}/${suffix}" + ) + endif() + endforeach() + endif() + + mark_as_advanced(Libdrm_LIBRARY Libdrm_INCLUDE_DIR) + + # compatibility variables + set(Libdrm_LIBRARIES ${Libdrm_LIBRARY}) + set(Libdrm_INCLUDE_DIRS ${Libdrm_INCLUDE_DIR} "${Libdrm_INCLUDE_DIR}/libdrm" "${Libdrm_INCLUDE_DIR}/drm") + set(Libdrm_VERSION_STRING ${Libdrm_VERSION}) + +else() + message(STATUS "FindLibdrm.cmake cannot find libdrm on Windows systems.") + set(Libdrm_FOUND FALSE) +endif() + +include(FeatureSummary) +set_package_properties(Libdrm PROPERTIES + URL "https://wiki.freedesktop.org/dri/" + DESCRIPTION "Userspace interface to kernel DRM services." +) diff --git a/cmake/find-modules/FindLibudev.cmake b/cmake/find-modules/FindLibudev.cmake new file mode 100644 index 00000000000..57e34659d1f --- /dev/null +++ b/cmake/find-modules/FindLibudev.cmake @@ -0,0 +1,10 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +find_package(PkgConfig QUIET) + +pkg_check_modules(Libudev IMPORTED_TARGET "libudev") + +if (NOT TARGET PkgConfig::Libudev) + set(Libudev_FOUND 0) +endif() diff --git a/cmake/find-modules/FindMMRenderer.cmake b/cmake/find-modules/FindMMRenderer.cmake new file mode 100644 index 00000000000..43dcf3f0052 --- /dev/null +++ b/cmake/find-modules/FindMMRenderer.cmake @@ -0,0 +1,29 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +# FindMMRenderer +# --------- +# +# Try to locate the mm-renderer library. +# If found, this will define the following variables: +# +# ``MMRenderer_FOUND`` +# True if the mm-renderer library is available +# ``MMRenderer_LIBRARY`` +# The mm-renderer library +# +# If ``MMRenderer_FOUND`` is TRUE, it will also define the following +# imported target: +# +# ``MMRenderer::MMRenderer`` +# The mm-renderer library to link to + +find_library(MMRenderer_LIBRARY NAMES mmrndclient) +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(MMRenderer DEFAULT_MSG MMRenderer_LIBRARY) +if(MMRenderer_FOUND AND NOT TARGET MMRenderer::MMRenderer) + add_library(MMRenderer::MMRenderer INTERFACE IMPORTED) + target_link_libraries(MMRenderer::MMRenderer + INTERFACE "${MMRenderer_LIBRARY}") +endif() +mark_as_advanced(MMRenderer_LIBRARY) diff --git a/cmake/find-modules/FindMMRendererCore.cmake b/cmake/find-modules/FindMMRendererCore.cmake new file mode 100644 index 00000000000..3f07e3720f0 --- /dev/null +++ b/cmake/find-modules/FindMMRendererCore.cmake @@ -0,0 +1,12 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +find_library(MMRendererCore_LIBRARY NAMES mmrndcore) +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(MMRendererCore DEFAULT_MSG MMRendererCore_LIBRARY) +if(MMRendererCore_FOUND AND NOT TARGET MMRendererCore::MMRendererCore) + add_library(MMRendererCore::MMRendererCore INTERFACE IMPORTED) + target_link_libraries(MMRendererCore::MMRendererCore + INTERFACE "${MMRendererCore_LIBRARY}") +endif() +mark_as_advanced(MMRendererCore_LIBRARY) diff --git a/cmake/find-modules/FindPipeWire.cmake b/cmake/find-modules/FindPipeWire.cmake new file mode 100644 index 00000000000..5812bac727b --- /dev/null +++ b/cmake/find-modules/FindPipeWire.cmake @@ -0,0 +1,123 @@ +#.rst: +# FindPipeWire +# ------- +# +# Try to find PipeWire on a Unix system. +# +# This will define the following variables: +# +# ``PipeWire_FOUND`` +# True if (the requested version of) PipeWire is available +# ``PipeWire_VERSION`` +# The version of PipeWire +# ``PipeWire_LIBRARIES`` +# This can be passed to target_link_libraries() instead of the ``PipeWire::PipeWire`` +# target +# ``PipeWire_INCLUDE_DIRS`` +# This should be passed to target_include_directories() if the target is not +# used for linking +# ``PipeWire_DEFINITIONS`` +# This should be passed to target_compile_options() if the target is not +# used for linking +# +# If ``PipeWire_FOUND`` is TRUE, it will also define the following imported target: +# +# ``PipeWire::PipeWire`` +# The PipeWire library +# +# In general we recommend using the imported target, as it is easier to use. +# Bear in mind, however, that if the target is in the link interface of an +# exported library, it must be made available by the package config file. + +#============================================================================= +# Copyright 2014 Alex Merry +# Copyright 2014 Martin Gräßlin +# Copyright 2018-2020 Jan Grulich +# +# 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 copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +#============================================================================= + +# Use pkg-config to get the directories and then use these values +# in the FIND_PATH() and FIND_LIBRARY() calls +find_package(PkgConfig QUIET) + +if(PkgConfig_FOUND) + pkg_search_module(PKG_PipeWire QUIET libpipewire-0.3) + pkg_search_module(PKG_Spa QUIET libspa-0.2) +endif() + +set(PipeWire_DEFINITIONS "${PKG_PipeWire_CFLAGS}" "${PKG_Spa_CFLAGS}") +set(PipeWire_VERSION "${PKG_PipeWire_VERSION}") + +find_path(PipeWire_INCLUDE_DIRS + NAMES + pipewire/pipewire.h + HINTS + ${PKG_PipeWire_INCLUDE_DIRS} + ${PKG_PipeWire_INCLUDE_DIRS}/pipewire-0.3 +) + +find_path(Spa_INCLUDE_DIRS + NAMES + spa/param/props.h + HINTS + ${PKG_Spa_INCLUDE_DIRS} + ${PKG_Spa_INCLUDE_DIRS}/spa-0.2 +) + +find_library(PipeWire_LIBRARIES + NAMES + pipewire-0.3 + HINTS + ${PKG_PipeWire_LIBRARY_DIRS} +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(PipeWire + FOUND_VAR + PipeWire_FOUND + REQUIRED_VARS + PipeWire_LIBRARIES + PipeWire_INCLUDE_DIRS + Spa_INCLUDE_DIRS + VERSION_VAR + PipeWire_VERSION +) + +if(PipeWire_FOUND AND NOT TARGET PipeWire::PipeWire) + add_library(PipeWire::PipeWire UNKNOWN IMPORTED) + set_target_properties(PipeWire::PipeWire PROPERTIES + IMPORTED_LOCATION "${PipeWire_LIBRARIES}" + INTERFACE_COMPILE_OPTIONS "${PipeWire_DEFINITIONS}" + INTERFACE_INCLUDE_DIRECTORIES "${PipeWire_INCLUDE_DIRS};${Spa_INCLUDE_DIRS}" + ) +endif() + +mark_as_advanced(PipeWire_LIBRARIES PipeWire_INCLUDE_DIRS) + +include(FeatureSummary) +set_package_properties(PipeWire PROPERTIES + URL "https://www.pipewire.org" + DESCRIPTION "PipeWire - multimedia processing" +) diff --git a/cmake/FindSDL2.cmake b/cmake/find-modules/FindSDL2.cmake similarity index 100% rename from cmake/FindSDL2.cmake rename to cmake/find-modules/FindSDL2.cmake diff --git a/cmake/find-modules/FindSpeechDispatcher.cmake b/cmake/find-modules/FindSpeechDispatcher.cmake new file mode 100644 index 00000000000..d1453196e00 --- /dev/null +++ b/cmake/find-modules/FindSpeechDispatcher.cmake @@ -0,0 +1,17 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +# We can't create the same interface imported target multiple times, CMake will complain if we do +# that. This can happen if the find_package call is done in multiple different subdirectories. +if(TARGET SpeechDispatcher::SpeechDispatcher) + set(SpeechDispatcher_FOUND 1) + return() +endif() + +find_package(PkgConfig QUIET) + +pkg_check_modules(SpeechDispatcher "speech-dispatcher" IMPORTED_TARGET GLOBAL) + +if (TARGET PkgConfig::SpeechDispatcher) + add_library(SpeechDispatcher::SpeechDispatcher ALIAS PkgConfig::SpeechDispatcher) +endif() diff --git a/cmake/find-modules/FindWMF.cmake b/cmake/find-modules/FindWMF.cmake new file mode 100644 index 00000000000..79306a350ef --- /dev/null +++ b/cmake/find-modules/FindWMF.cmake @@ -0,0 +1,52 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +# FindWMF +# --------- +# +# Try to locate the Windows Media Foundation library. +# If found, this will define the following variables: +# +# ``WMF_FOUND`` +# True if Windows Media Foundation is available +# ``WMF_LIBRARIES`` +# The Windows Media Foundation set of libraries +# +# If ``WMF_FOUND`` is TRUE, it will also define the following +# imported target: +# +# ``WMF::WMF`` +# The Windows Media Foundation library to link to + +find_library(WMF_STRMIIDS_LIBRARY strmiids HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES}) +find_library(WMF_AMSTRMID_LIBRARY amstrmid HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES}) +find_library(WMF_DMOGUIDS_LIBRARY dmoguids HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES}) +find_library(WMF_UUID_LIBRARY uuid HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES}) +find_library(WMF_MSDMO_LIBRARY msdmo HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES}) +find_library(WMF_OLE32_LIBRARY ole32 HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES}) +find_library(WMF_OLEAUT32_LIBRARY oleaut32 HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES}) +find_library(WMF_MF_LIBRARY mf HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES}) +find_library(WMF_MFUUID_LIBRARY mfuuid HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES}) +find_library(WMF_MFPLAT_LIBRARY mfplat HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES}) +find_library(WMF_MFCORE_LIBRARY mfcore HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES}) +find_library(WMF_PROPSYS_LIBRARY propsys HINTS ${CMAKE_C_IMPLICIT_LINK_DIRECTORIES}) + + +set(WMF_LIBRARIES ${WMF_STRMIIDS_LIBRARY} ${WMF_AMSTRMID_LIBRARY} ${WMF_DMOGUIDS_LIBRARY} ${WMF_UUID_LIBRARY} + ${WMF_MSDMO_LIBRARY} ${WMF_OLE32_LIBRARY} ${WMF_OLEAUT32_LIBRARY} ${WMF_MF_LIBRARY} + ${WMF_MFUUID_LIBRARY} ${WMF_MFPLAT_LIBRARY} ${WMF_MFCORE_LIBRARY} ${WMF_PROPSYS_LIBRARY}) +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(WMF REQUIRED_VARS + WMF_STRMIIDS_LIBRARY WMF_AMSTRMID_LIBRARY WMF_DMOGUIDS_LIBRARY WMF_UUID_LIBRARY + WMF_MSDMO_LIBRARY WMF_OLE32_LIBRARY WMF_OLEAUT32_LIBRARY WMF_MF_LIBRARY + WMF_MFUUID_LIBRARY WMF_MFPLAT_LIBRARY WMF_MFCORE_LIBRARY WMF_PROPSYS_LIBRARY) + +if(WMF_FOUND AND NOT TARGET WMF::WMF) + add_library(WMF::WMF INTERFACE IMPORTED) + set_target_properties(WMF::WMF PROPERTIES + INTERFACE_LINK_LIBRARIES "${WMF_LIBRARIES}") +endif() + +mark_as_advanced(WMF_LIBRARIES WMF_STRMIIDS_LIBRARY WMF_AMSTRMID_LIBRARY WMF_DMOGUIDS_LIBRARY WMF_UUID_LIBRARY + WMF_MSDMO_LIBRARY WMF_OLE32_LIBRARY WMF_OLEAUT32_LIBRARY WMF_MF_LIBRARY WMF_MFUUID_LIBRARY WMF_MFPLAT_LIBRARY + WMF_MFCORE_LIBRARY WMF_PROPSYS_LIBRARY) diff --git a/cmake/find-modules/FindWrapPulseAudio.cmake b/cmake/find-modules/FindWrapPulseAudio.cmake new file mode 100644 index 00000000000..ce170da632a --- /dev/null +++ b/cmake/find-modules/FindWrapPulseAudio.cmake @@ -0,0 +1,37 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +# FindWrapPulseAudio +# --------- +# +# Try to locate the pulseaudio library. +# If found, this will define the following variables: +# +# ``WrapPulseAudio_FOUND`` +# True if the pulseaudio library is available +# +# If ``WrapPulseAduio_FOUND`` is TRUE, it will also define the following +# imported target: +# +# ``WrapPulseAudio::WrapPulseAudio`` +# The pulseaudio library to link to + +if(TARGET WrapPulseAudio::WrapPulseAudio) + set(WrapPulseAudio_FOUND ON) + return() +endif() +find_package(PulseAudio QUIET) +if(PulseAudio_FOUND) + set(WrapPulseAudio_FOUND 1) +endif() +if(WrapPulseAudio_FOUND AND NOT TARGET WrapPulseAudio::WrapPulseAudio) + add_library(WrapPulseAudio::WrapPulseAudio INTERFACE IMPORTED) + target_include_directories(WrapPulseAudio::WrapPulseAudio INTERFACE "${PULSEAUDIO_INCLUDE_DIR}") + target_link_libraries(WrapPulseAudio::WrapPulseAudio + INTERFACE "${PULSEAUDIO_LIBRARY}") +endif() +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(WrapPulseAudio REQUIRED_VARS + PULSEAUDIO_LIBRARY PULSEAUDIO_INCLUDE_DIR WrapPulseAudio_FOUND) + +mark_as_advanced(PULSEAUDIO_LIBRARY PULSEAUDIO_INCLUDE_DIR) diff --git a/cmake/find-modules/FindXRender.cmake b/cmake/find-modules/FindXRender.cmake new file mode 100644 index 00000000000..6908cc45fbc --- /dev/null +++ b/cmake/find-modules/FindXRender.cmake @@ -0,0 +1,14 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +find_package(PkgConfig QUIET) + +if(NOT TARGET PkgConfig::XRender) + pkg_check_modules(XRender IMPORTED_TARGET "xrender") + + if (NOT TARGET PkgConfig::XRender) + set(XRender_FOUND 0) + endif() +else() + set(XRender_FOUND 1) +endif() diff --git a/cmake/find-modules/Findgbm.cmake b/cmake/find-modules/Findgbm.cmake new file mode 100644 index 00000000000..a77f7e86dbe --- /dev/null +++ b/cmake/find-modules/Findgbm.cmake @@ -0,0 +1,125 @@ +#.rst: +# Findgbm +# ------- +# +# Try to find gbm on a Unix system. +# +# This will define the following variables: +# +# ``gbm_FOUND`` +# True if (the requested version of) gbm is available +# ``gbm_VERSION`` +# The version of gbm +# ``gbm_LIBRARIES`` +# This can be passed to target_link_libraries() instead of the ``gbm::gbm`` +# target +# ``gbm_INCLUDE_DIRS`` +# This should be passed to target_include_directories() if the target is not +# used for linking +# ``gbm_DEFINITIONS`` +# This should be passed to target_compile_options() if the target is not +# used for linking +# +# If ``gbm_FOUND`` is TRUE, it will also define the following imported target: +# +# ``gbm::gbm`` +# The gbm library +# +# In general we recommend using the imported target, as it is easier to use. +# Bear in mind, however, that if the target is in the link interface of an +# exported library, it must be made available by the package config file. + +#============================================================================= +# Copyright 2014 Alex Merry +# Copyright 2014 Martin Gräßlin +# +# 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 copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +#============================================================================= + +if(CMAKE_VERSION VERSION_LESS 2.8.12) + message(FATAL_ERROR "CMake 2.8.12 is required by Findgbm.cmake") +endif() +if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.12) + message(AUTHOR_WARNING "Your project should require at least CMake 2.8.12 to use Findgbm.cmake") +endif() + +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 QUIET) + pkg_check_modules(PKG_gbm QUIET gbm) + + set(gbm_DEFINITIONS ${PKG_gbm_CFLAGS_OTHER}) + set(gbm_VERSION ${PKG_gbm_VERSION}) + + find_path(gbm_INCLUDE_DIR + NAMES + gbm.h + HINTS + ${PKG_gbm_INCLUDE_DIRS} + ) + find_library(gbm_LIBRARY + NAMES + gbm + HINTS + ${PKG_gbm_LIBRARY_DIRS} + ) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(gbm + FOUND_VAR + gbm_FOUND + REQUIRED_VARS + gbm_LIBRARY + gbm_INCLUDE_DIR + VERSION_VAR + gbm_VERSION + ) + + if(gbm_FOUND AND NOT TARGET gbm::gbm) + add_library(gbm::gbm UNKNOWN IMPORTED) + set_target_properties(gbm::gbm PROPERTIES + IMPORTED_LOCATION "${gbm_LIBRARY}" + INTERFACE_COMPILE_OPTIONS "${gbm_DEFINITIONS}" + INTERFACE_INCLUDE_DIRECTORIES "${gbm_INCLUDE_DIR}" + ) + endif() + + mark_as_advanced(gbm_LIBRARY gbm_INCLUDE_DIR) + + # compatibility variables + set(gbm_LIBRARIES ${gbm_LIBRARY}) + set(gbm_INCLUDE_DIRS ${gbm_INCLUDE_DIR}) + set(gbm_VERSION_STRING ${gbm_VERSION}) + +else() + message(STATUS "Findgbm.cmake cannot find gbm on Windows systems.") + set(gbm_FOUND FALSE) +endif() + +include(FeatureSummary) +set_package_properties(gbm PROPERTIES + URL "http://www.mesa3d.org" + DESCRIPTION "Mesa gbm library." +) diff --git a/src/Audio/CMakeLists.txt b/src/Audio/CMakeLists.txt index d4dc52556f8..edf0c5b9e20 100644 --- a/src/Audio/CMakeLists.txt +++ b/src/Audio/CMakeLists.txt @@ -15,3 +15,15 @@ target_link_libraries(Audio ) target_include_directories(Audio PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + +if(LINUX) + find_package(SpeechDispatcher) + if(TARGET SpeechDispatcher::SpeechDispatcher) + target_link_libraries(Audio PRIVATE SpeechDispatcher::SpeechDispatcher) + endif() + + find_package(Flite) + if(TARGET Flite::Flite) + target_link_libraries(Audio PRIVATE Flite::Flite) + endif() +endif() diff --git a/src/Joystick/CMakeLists.txt b/src/Joystick/CMakeLists.txt index e809509d17f..e1eb4aedaec 100644 --- a/src/Joystick/CMakeLists.txt +++ b/src/Joystick/CMakeLists.txt @@ -73,7 +73,7 @@ set(MINIMUM_SDL2_VERSION 2.30.0) if(NOT QGC_BUILD_DEPENDENCIES) find_package(SDL2 ${MINIMUM_SDL2_VERSION}) - if(SDL2_FOUND) + if(TARGET SDL2::SDL2) message(STATUS "Found JoystickSDL ${SDL2_VERSION_STRING}") target_link_libraries(Joystick PRIVATE SDL2::SDL2) return() @@ -81,7 +81,7 @@ if(NOT QGC_BUILD_DEPENDENCIES) find_package(PkgConfig) if(PkgConfig_FOUND) pkg_check_modules(SDL2 IMPORTED_TARGET sdl2>=${MINIMUM_SDL2_VERSION}) - if(SDL2_FOUND) + if(TARGET PkgConfig::SDL2) message(STATUS "Found JoystickSDL ${SDL2_VERSION}") target_link_libraries(Joystick PRIVATE PkgConfig::SDL2) return() diff --git a/src/VideoManager/VideoReceiver/GStreamer/GStreamer.cc b/src/VideoManager/VideoReceiver/GStreamer/GStreamer.cc index 6ef042f4810..f89235ed353 100644 --- a/src/VideoManager/VideoReceiver/GStreamer/GStreamer.cc +++ b/src/VideoManager/VideoReceiver/GStreamer/GStreamer.cc @@ -38,7 +38,7 @@ GST_PLUGIN_STATIC_DECLARE(matroska); GST_PLUGIN_STATIC_DECLARE(mpegtsdemux); GST_PLUGIN_STATIC_DECLARE(opengl); GST_PLUGIN_STATIC_DECLARE(tcp); -// GST_PLUGIN_STATIC_DECLARE(asf); +GST_PLUGIN_STATIC_DECLARE(app); // #ifndef Q_OS_ANDROID // GST_PLUGIN_STATIC_DECLARE(va); // #endif @@ -145,7 +145,7 @@ static void _registerPlugins() GST_PLUGIN_STATIC_REGISTER(mpegtsdemux); GST_PLUGIN_STATIC_REGISTER(opengl); GST_PLUGIN_STATIC_REGISTER(tcp); - // GST_PLUGIN_STATIC_REGISTER(asf); + GST_PLUGIN_STATIC_REGISTER(app); // #ifndef Q_OS_ANDROID // GST_PLUGIN_STATIC_REGISTER(va); // #endif diff --git a/src/VideoManager/VideoReceiver/GStreamer/gstqml6gl/CMakeLists.txt b/src/VideoManager/VideoReceiver/GStreamer/gstqml6gl/CMakeLists.txt index 67b7b03ca0d..1b344505284 100644 --- a/src/VideoManager/VideoReceiver/GStreamer/gstqml6gl/CMakeLists.txt +++ b/src/VideoManager/VideoReceiver/GStreamer/gstqml6gl/CMakeLists.txt @@ -1,6 +1,6 @@ find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick) -set(GST_COMPONENTS Allocators Audio Codecparsers Controller Fft GlEGL GlPrototypes GlWayland GlX11 Mpegts Net Pbutils Photography Riff Rtp Rtsp Sdp Tag Va) +set(GST_COMPONENTS Allocators App Audio Codecparsers Controller Fft GlEGL GlPrototypes GlWayland GlX11 Mpegts Net Pbutils Photography Riff Rtp Rtsp Sdp Tag Va) find_package(GStreamer REQUIRED COMPONENTS OPTIONAL_COMPONENTS ${GST_COMPONENTS}) qt_add_library(gstqml6gl STATIC) @@ -18,13 +18,94 @@ target_link_libraries(gstqml6gl foreach(component IN LISTS GST_COMPONENTS) if(GStreamer_${component}_FOUND) - target_link_libraries(gstqml6gl PUBLIC GStreamer::${component}) + target_link_libraries(gstqml6gl INTERFACE GStreamer::${component}) endif() endforeach() ################################################################################ -# TODO: Don't Download & Build if gstreamer1.0-qt6 was found +if(LINUX) + # if(NOT QGC_GST_STATIC_BUILD) + install(DIRECTORY ${GSTREAMER_LIB_PATH}/gstreamer1.0 DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install(CODE "execute_process(COMMAND chmod +x \"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/gstreamer1.0/gstreamer-1.0/gst-plugin-scanner\")") + install(CODE "execute_process(COMMAND chmod +x \"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/gstreamer1.0/gstreamer-1.0/gst-ptp-helper\")") + install(DIRECTORY ${GSTREAMER_LIB_PATH}/gio DESTINATION ${CMAKE_INSTALL_LIBDIR}) + get_target_property(LINKED_PLUGINS GStreamer::Plugins INTERFACE_LINK_LIBRARIES) + install(FILES ${LINKED_PLUGINS} DESTINATION ${CMAKE_INSTALL_LIBDIR}/gstreamer-1.0) + # endif() +elseif(WIN32) + cmake_path(CONVERT "${GSTREAMER_PREFIX}/bin/*.dll" TO_CMAKE_PATH_LIST GST_WIN_BINS_PATH) + file(GLOB GST_WIN_BINS ${GST_WIN_BINS_PATH}) + install(FILES ${GST_WIN_BINS} DESTINATION ${CMAKE_INSTALL_BINDIR}) + + cmake_path(CONVERT "${GSTREAMER_LIB_PATH}/gio/modules/*.dll" TO_CMAKE_PATH_LIST GST_GIO_MODULES_PATH) + file(GLOB GST_GIO_MODULES ${GST_GIO_MODULES_PATH}) + install(FILES ${GST_GIO_MODULES} DESTINATION ${CMAKE_INSTALL_LIBDIR}/gio/modules) + + cmake_path(CONVERT "${GSTREAMER_PLUGIN_PATH}/*.dll" TO_CMAKE_PATH_LIST GST_WIN_PLUGINS_PATH) + file(GLOB GST_WIN_PLUGINS ${GST_WIN_PLUGINS_PATH}) + install(FILES ${GST_WIN_PLUGINS} DESTINATION ${CMAKE_INSTALL_LIBDIR}/gstreamer-1.0) + + cmake_path(CONVERT "${GSTREAMER_PREFIX}/libexec/gstreamer-1.0/*.exe" TO_CMAKE_PATH_LIST GST_HELPER_BINS_PATH) + file(GLOB GST_HELPER_BINS ${GST_HELPER_BINS_PATH}) + install(FILES ${GST_HELPER_BINS} DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/gstreamer-1.0) +elseif(MACOS) + # cmake_path(CONVERT "${GSTREAMER_PREFIX}/bin/*.dylib" TO_CMAKE_PATH_LIST GST_MACOS_BINS_PATH) + # file(GLOB GST_MACOS_BINS ${GST_MACOS_BINS_PATH}) + # install(FILES ${GST_MACOS_BINS} DESTINATION ${CMAKE_INSTALL_BINDIR}) + + # cmake_path(CONVERT "${GSTREAMER_LIB_PATH}/gio/modules/*.dylib" TO_CMAKE_PATH_LIST GST_GIO_MODULES_PATH) + # file(GLOB GST_GIO_MODULES ${GST_GIO_MODULES_PATH}) + # install(FILES ${GST_GIO_MODULES} DESTINATION ${CMAKE_INSTALL_LIBDIR}/gio/modules) + + # cmake_path(CONVERT "${GSTREAMER_PLUGIN_PATH}/*.dylib" TO_CMAKE_PATH_LIST GST_MACOS_PLUGINS_PATH) + # file(GLOB GST_MACOS_PLUGINS ${GST_MACOS_PLUGINS_PATH}) + # install(FILES ${GST_MACOS_PLUGINS} DESTINATION ${CMAKE_INSTALL_LIBDIR}/gstreamer-1.0) + + # cmake_path(CONVERT "${GSTREAMER_PREFIX}/libexec/gstreamer-1.0/*" TO_CMAKE_PATH_LIST GST_HELPER_BINS_PATH) + # file(GLOB GST_HELPER_BINS ${GST_HELPER_BINS_PATH}) + # install(FILES ${GST_HELPER_BINS} DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/gstreamer-1.0) +elseif(ANDROID) + if(CMAKE_HOST_WIN32) + cmake_path(CONVERT "${GSTREAMER_PREFIX}/share/gst-android/ndk-build/tools/windows/*.dll" TO_CMAKE_PATH_LIST GST_WIN_TOOLS_PATH) + file(GLOB GST_WIN_TOOLS ${GST_WIN_TOOLS_PATH}) + install(FILES ${GST_WIN_TOOLS} DESTINATION ${CMAKE_INSTALL_BINDIR}) + endif() +endif() + +################################################################################ + +# DYLD_LIBRARY_PATH + +# LSEnvironment +# +# GST_REGISTRY_REUSE_PLUGIN_SCANNER +# no +# GST_PLUGIN_SCANNER +# @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/libexec/gstreamer-1.0/gst-plugin-scanner +# GST_PTP_HELPER_1_0 +# @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/libexec/gstreamer-1.0/gst-ptp-helper +# GIO_EXTRA_MODULES +# @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/lib/gio/modules +# GST_PLUGIN_SYSTEM_PATH_1_0 +# @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0 +# GST_PLUGIN_SYSTEM_PATH +# @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0 +# GST_PLUGIN_PATH_1_0 +# @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0 +# GST_PLUGIN_PATH +# @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0 +# + +################################################################################ + +# Don't Download & Build if gstreamer1.0-qt6 was found +if(GST_PLUGIN_gstqml6_FOUND) + return() +endif() + +################################################################################ + if(GStreamer_VERSION VERSION_GREATER_EQUAL 1.22) FetchContent_Declare(gstreamer_good_plugins # URL https://gitlab.freedesktop.org/gstreamer/gstreamer/-/archive/${GST_PLUGINS_VERSION}/gstreamer-${GST_PLUGINS_VERSION}.zip?path=subprojects/gst-plugins-good/ext/qt6 @@ -38,8 +119,6 @@ else() endif() cmake_print_variables(QGC_GST_QT6_PLUGIN_PATH) -# TODO: https://gstreamer.freedesktop.org/documentation/qt6d3d11/index.html#qml6d3d11sink-page - ################################################################################ file(READ ${QGC_GST_QT6_PLUGIN_PATH}/qt6glitem.h FILE_CONTENTS) @@ -146,76 +225,3 @@ if(UNIX) endif() ################################################################################ - -if(LINUX) - # if(NOT QGC_GST_STATIC_BUILD) - install(DIRECTORY ${GSTREAMER_LIB_PATH}/gstreamer1.0 DESTINATION ${CMAKE_INSTALL_LIBDIR}) - install(CODE "execute_process(COMMAND chmod +x \"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/gstreamer1.0/gstreamer-1.0/gst-plugin-scanner\")") - install(CODE "execute_process(COMMAND chmod +x \"${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/gstreamer1.0/gstreamer-1.0/gst-ptp-helper\")") - install(DIRECTORY ${GSTREAMER_LIB_PATH}/gio DESTINATION ${CMAKE_INSTALL_LIBDIR}) - get_target_property(LINKED_PLUGINS GStreamer::Plugins INTERFACE_LINK_LIBRARIES) - install(FILES ${LINKED_PLUGINS} DESTINATION ${CMAKE_INSTALL_LIBDIR}/gstreamer-1.0) - # endif() -elseif(WIN32) - cmake_path(CONVERT "${GSTREAMER_PREFIX}/bin/*.dll" TO_CMAKE_PATH_LIST GST_WIN_BINS_PATH) - file(GLOB GST_WIN_BINS ${GST_WIN_BINS_PATH}) - install(FILES ${GST_WIN_BINS} DESTINATION ${CMAKE_INSTALL_BINDIR}) - - cmake_path(CONVERT "${GSTREAMER_LIB_PATH}/gio/modules/*.dll" TO_CMAKE_PATH_LIST GST_GIO_MODULES_PATH) - file(GLOB GST_GIO_MODULES ${GST_GIO_MODULES_PATH}) - install(FILES ${GST_GIO_MODULES} DESTINATION ${CMAKE_INSTALL_LIBDIR}/gio/modules) - - cmake_path(CONVERT "${GSTREAMER_PLUGIN_PATH}/*.dll" TO_CMAKE_PATH_LIST GST_WIN_PLUGINS_PATH) - file(GLOB GST_WIN_PLUGINS ${GST_WIN_PLUGINS_PATH}) - install(FILES ${GST_WIN_PLUGINS} DESTINATION ${CMAKE_INSTALL_LIBDIR}/gstreamer-1.0) - - cmake_path(CONVERT "${GSTREAMER_PREFIX}/libexec/gstreamer-1.0/*.exe" TO_CMAKE_PATH_LIST GST_HELPER_BINS_PATH) - file(GLOB GST_HELPER_BINS ${GST_HELPER_BINS_PATH}) - install(FILES ${GST_HELPER_BINS} DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/gstreamer-1.0) -elseif(MACOS) - # cmake_path(CONVERT "${GSTREAMER_PREFIX}/bin/*.dylib" TO_CMAKE_PATH_LIST GST_MACOS_BINS_PATH) - # file(GLOB GST_MACOS_BINS ${GST_MACOS_BINS_PATH}) - # install(FILES ${GST_MACOS_BINS} DESTINATION ${CMAKE_INSTALL_BINDIR}) - - # cmake_path(CONVERT "${GSTREAMER_LIB_PATH}/gio/modules/*.dylib" TO_CMAKE_PATH_LIST GST_GIO_MODULES_PATH) - # file(GLOB GST_GIO_MODULES ${GST_GIO_MODULES_PATH}) - # install(FILES ${GST_GIO_MODULES} DESTINATION ${CMAKE_INSTALL_LIBDIR}/gio/modules) - - # cmake_path(CONVERT "${GSTREAMER_PLUGIN_PATH}/*.dylib" TO_CMAKE_PATH_LIST GST_MACOS_PLUGINS_PATH) - # file(GLOB GST_MACOS_PLUGINS ${GST_MACOS_PLUGINS_PATH}) - # install(FILES ${GST_MACOS_PLUGINS} DESTINATION ${CMAKE_INSTALL_LIBDIR}/gstreamer-1.0) - - # cmake_path(CONVERT "${GSTREAMER_PREFIX}/libexec/gstreamer-1.0/*" TO_CMAKE_PATH_LIST GST_HELPER_BINS_PATH) - # file(GLOB GST_HELPER_BINS ${GST_HELPER_BINS_PATH}) - # install(FILES ${GST_HELPER_BINS} DESTINATION ${CMAKE_INSTALL_LIBEXECDIR}/gstreamer-1.0) -elseif(ANDROID) - if(CMAKE_HOST_WIN32) - cmake_path(CONVERT "${GSTREAMER_PREFIX}/share/gst-android/ndk-build/tools/windows/*.dll" TO_CMAKE_PATH_LIST GST_WIN_TOOLS_PATH) - file(GLOB GST_WIN_TOOLS ${GST_WIN_TOOLS_PATH}) - install(FILES ${GST_WIN_TOOLS} DESTINATION ${CMAKE_INSTALL_BINDIR}) - endif() -endif() - -################################################################################ - -# DYLD_LIBRARY_PATH - -# LSEnvironment -# -# GST_REGISTRY_REUSE_PLUGIN_SCANNER -# no -# GST_PLUGIN_SCANNER -# @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/libexec/gstreamer-1.0/gst-plugin-scanner -# GST_PTP_HELPER_1_0 -# @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/libexec/gstreamer-1.0/gst-ptp-helper -# GIO_EXTRA_MODULES -# @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/lib/gio/modules -# GST_PLUGIN_SYSTEM_PATH_1_0 -# @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0 -# GST_PLUGIN_SYSTEM_PATH -# @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0 -# GST_PLUGIN_PATH_1_0 -# @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0 -# GST_PLUGIN_PATH -# @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/lib/gstreamer-1.0 -# diff --git a/tools/setup/install-dependencies-debian.sh b/tools/setup/install-dependencies-debian.sh index 225de8958b4..187851b561d 100755 --- a/tools/setup/install-dependencies-debian.sh +++ b/tools/setup/install-dependencies-debian.sh @@ -34,33 +34,36 @@ DEBIAN_FRONTEND=noninteractive apt-get -y --quiet install \ # Qt Required - https://doc.qt.io/qt-6/linux-requirements.html DEBIAN_FRONTEND=noninteractive apt-get -y --quiet install \ - libfontconfig1 \ - libfreetype6 \ - libx11-6 \ - libx11-xcb1 \ - libxcb-cursor0 \ - libxcb-glx0 \ - libxcb-icccm4 \ - libxcb-image0 \ - libxcb-keysyms1 \ - libxcb-present0 \ - libxcb-randr0 \ - libxcb-render-util0 \ - libxcb-render0 \ - libxcb-shape0 \ - libxcb-shm0 \ - libxcb-sync1 \ - libxcb-util1 \ - libxcb-xfixes0 \ - libxcb-xinerama0 \ - libxcb-xkb1 \ - libxcb1 \ - libxext6 \ - libxfixes3 \ - libxi6 \ - libxkbcommon-x11-0 \ - libxkbcommon0 \ - libxrender1 + libatspi2.0-dev \ + libfontconfig1-dev \ + libfreetype-dev \ + libglib2.0-dev \ + libsm-dev \ + libx11-dev \ + libx11-xcb-dev \ + libxcb-cursor-dev \ + libxcb-glx0-dev \ + libxcb-icccm4-dev \ + libxcb-image0-dev \ + libxcb-keysyms1-dev \ + libxcb-present-dev \ + libxcb-randr0-dev \ + libxcb-render-util0-dev \ + libxcb-render0-dev \ + libxcb-shape0-dev \ + libxcb-shm0-dev \ + libxcb-sync-dev \ + libxcb-util-dev \ + libxcb-xfixes0-dev \ + libxcb-xinerama0-dev \ + libxcb-xkb-dev \ + libxcb1-dev \ + libxext-dev \ + libxfixes-dev \ + libxi-dev \ + libxkbcommon-dev \ + libxkbcommon-x11-dev \ + libxrender-dev DEBIAN_FRONTEND=noninteractive apt-get -y --quiet install \ libunwind-dev @@ -101,15 +104,21 @@ DEBIAN_FRONTEND=noninteractive apt-get -y --quiet install \ # Speech DEBIAN_FRONTEND=noninteractive apt-get -y --quiet install \ + flite \ flite1-dev \ + libflite1 \ libspeechd-dev \ speech-dispatcher \ + speech-dispatcher-audio-plugins \ speech-dispatcher-espeak \ speech-dispatcher-espeak-ng \ speech-dispatcher-flite \ # Additional DEBIAN_FRONTEND=noninteractive apt-get -y --quiet install \ + bison \ + flex \ + gobject-introspection \ gvfs \ intel-media-va-driver \ libasound2-dev \ @@ -125,13 +134,14 @@ DEBIAN_FRONTEND=noninteractive apt-get -y --quiet install \ libgles2-mesa-dev \ libglew-dev \ libglfw3-dev \ - libglib2.0-dev \ libglu1-mesa-dev \ libglvnd-dev \ libglx-dev \ libglx-mesa0 \ libgudev-1.0-dev \ libgraphene-1.0-dev \ + libharfbuzz-dev \ + libmp3lame-dev \ libmjpegtools-dev \ libjpeg-dev \ libjson-glib-1.0-0 \ @@ -139,6 +149,7 @@ DEBIAN_FRONTEND=noninteractive apt-get -y --quiet install \ libopenal-dev \ libopenjp2-7-dev \ libopus-dev \ + liborc-0.4-dev \ libpng-dev \ libpulse-dev \ libsdl2-dev \ @@ -148,10 +159,15 @@ DEBIAN_FRONTEND=noninteractive apt-get -y --quiet install \ libtheora-dev \ libva-dev \ libvdpau-dev \ + libvorbis-dev \ libvpx-dev \ libwayland-dev \ libwxgtk3.*-dev \ - libx11-dev \ + libx264-dev \ + libx265-dev \ + libxcb-dri2-0-dev \ + libxcb-dri3-dev \ + libxcb-xf86dri0-dev \ libxml2-dev \ libzstd-dev \ mesa-common-dev \ @@ -159,7 +175,13 @@ DEBIAN_FRONTEND=noninteractive apt-get -y --quiet install \ mesa-va-drivers \ mesa-vdpau-drivers \ mesa-vulkan-drivers \ - vainfo + va-driver-all \ + vainfo \ + wayland-protocols + +if apt-cache show libdav1d-dev >/dev/null 2>&1 && apt-cache show libdav1d-dev 2>/dev/null | grep -q "^Package: libdav1d-dev"; then + DEBIAN_FRONTEND=noninteractive apt-get install -y --quiet libdav1d-dev +fi if apt-cache show libvpl-dev >/dev/null 2>&1 && apt-cache show libvpl-dev 2>/dev/null | grep -q "^Package: libvpl-dev"; then DEBIAN_FRONTEND=noninteractive apt-get install -y --quiet libvpl-dev From 61d37c0d23ff0cc9a885679a4d1c8c7caa481f21 Mon Sep 17 00:00:00 2001 From: Holden Date: Mon, 13 May 2024 23:54:40 -0400 Subject: [PATCH 18/27] GPS: Prepare for new gps sources & threading changes --- .gitmodules | 2 +- src/GPS/Drivers => libs/PX4-GPSDrivers | 0 src/Comms/LinkManager.cc | 7 +- src/GPS/CMakeLists.txt | 19 +- src/GPS/GPSManager.cc | 137 +------- src/GPS/GPSManager.h | 49 +-- src/GPS/GPSPositionMessage.h | 32 -- src/GPS/GPSProvider.cc | 357 +++++++++++---------- src/GPS/GPSProvider.h | 108 ++++--- src/GPS/GPSRtk.cc | 152 +++++++++ src/GPS/GPSRtk.h | 56 ++++ src/GPS/RTCMMavlink.cc | 118 ++++--- src/GPS/RTCMMavlink.h | 29 +- src/GPS/definitions.h | 58 ++-- src/GPS/satellite_info.h | 18 +- src/GPS/sensor_gnss_relative.h | 24 +- src/GPS/sensor_gps.h | 64 ++-- src/QGCToolbox.cc | 9 - src/QGCToolbox.h | 7 - src/QmlControls/QGroundControlQmlGlobal.cc | 3 +- src/Utilities/QGCLoggingCategory.cc | 1 - src/Utilities/QGCLoggingCategory.h | 1 - test/CMakeLists.txt | 4 + test/GPS/CMakeLists.txt | 17 + test/GPS/GpsTest.cc | 19 ++ test/GPS/GpsTest.h | 20 ++ test/UnitTestList.cc | 6 + 27 files changed, 725 insertions(+), 592 deletions(-) rename src/GPS/Drivers => libs/PX4-GPSDrivers (100%) delete mode 100644 src/GPS/GPSPositionMessage.h create mode 100644 src/GPS/GPSRtk.cc create mode 100644 src/GPS/GPSRtk.h create mode 100644 test/GPS/CMakeLists.txt create mode 100644 test/GPS/GpsTest.cc create mode 100644 test/GPS/GpsTest.h diff --git a/.gitmodules b/.gitmodules index 2e3cf6a5200..e37ef969513 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,5 +1,5 @@ [submodule "src/GPS/Drivers"] - path = src/GPS/Drivers + path = libs/PX4-GPSDrivers url = https://github.com/PX4/GpsDrivers.git [submodule "libs/mavlink/include/mavlink/v2.0"] path = libs/mavlink/include/mavlink/v2.0 diff --git a/src/GPS/Drivers b/libs/PX4-GPSDrivers similarity index 100% rename from src/GPS/Drivers rename to libs/PX4-GPSDrivers diff --git a/src/Comms/LinkManager.cc b/src/Comms/LinkManager.cc index 43df54e8d38..790913f68d1 100644 --- a/src/Comms/LinkManager.cc +++ b/src/Comms/LinkManager.cc @@ -28,6 +28,7 @@ #include "GPSManager.h" #include "PositionManager.h" #include "UdpIODevice.h" +#include "GPSRtk.h" #endif #ifdef QT_DEBUG @@ -852,7 +853,7 @@ void LinkManager::_addSerialAutoConnectLink() case QGCSerialPortInfo::BoardTypeRTKGPS: qCDebug(LinkManagerLog) << "RTK GPS auto-connected" << portInfo.portName().trimmed(); _autoConnectRTKPort = portInfo.systemLocation(); - _toolbox->gpsManager()->connectGPS(portInfo.systemLocation(), boardName); + GPSManager::instance()->gpsRtk()->connectGPS(portInfo.systemLocation(), boardName); break; default: qCWarning(LinkManagerLog) << "Internal error: Unknown board type" << boardType; @@ -876,7 +877,7 @@ void LinkManager::_addSerialAutoConnectLink() // Check for RTK GPS connection gone if (!_autoConnectRTKPort.isEmpty() && !currentPorts.contains(_autoConnectRTKPort)) { qCDebug(LinkManagerLog) << "RTK GPS disconnected" << _autoConnectRTKPort; - _toolbox->gpsManager()->disconnectGPS(); + GPSManager::instance()->gpsRtk()->disconnectGPS(); _autoConnectRTKPort.clear(); } } @@ -900,7 +901,7 @@ bool LinkManager::_allowAutoConnectToBoard(QGCSerialPortInfo::BoardType_t boardT } break; case QGCSerialPortInfo::BoardTypeRTKGPS: - if (_autoConnectSettings->autoConnectRTKGPS()->rawValue().toBool() && !_toolbox->gpsManager()->connected()) { + if (_autoConnectSettings->autoConnectRTKGPS()->rawValue().toBool() && !GPSManager::instance()->gpsRtk()->connected()) { return true; } break; diff --git a/src/GPS/CMakeLists.txt b/src/GPS/CMakeLists.txt index eef62e187ec..3f59ed72855 100644 --- a/src/GPS/CMakeLists.txt +++ b/src/GPS/CMakeLists.txt @@ -42,23 +42,20 @@ qt_add_library(GPSDrivers STATIC ${px4-gpsdrivers_SOURCE_DIR}/src/unicore.h ) -target_link_libraries(GPSDrivers - PUBLIC - Qt6::Core - Utilities -) - -target_include_directories(GPSDrivers PUBLIC ${px4-gpsdrivers_SOURCE_DIR}) +target_link_libraries(GPSDrivers PUBLIC Qt6::Core) target_compile_definitions(GPSDrivers PUBLIC GPS_DEFINITIONS_HEADER=<${CMAKE_CURRENT_SOURCE_DIR}/definitions.h>) +target_include_directories(GPSDrivers PUBLIC ${px4-gpsdrivers_SOURCE_DIR}/src) + target_sources(GPS PRIVATE GPSManager.cc GPSManager.h - GPSPositionMessage.h GPSProvider.cc GPSProvider.h + GPSRtk.cc + GPSRtk.h RTCMMavlink.cc RTCMMavlink.h satellite_info.h @@ -69,14 +66,14 @@ target_sources(GPS target_link_libraries(GPS PRIVATE Comms - GPSDrivers + MAVLink + QGC Settings Utilities Vehicle PUBLIC Qt6::Core - MAVLink - QGC + GPSDrivers ) target_include_directories(GPS PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/src/GPS/GPSManager.cc b/src/GPS/GPSManager.cc index 486a2d9e073..e3e32a98944 100644 --- a/src/GPS/GPSManager.cc +++ b/src/GPS/GPSManager.cc @@ -7,142 +7,29 @@ * ****************************************************************************/ - #include "GPSManager.h" -#include "GPSProvider.h" -#include "QGCApplication.h" -#include "SettingsManager.h" -#include "RTKSettings.h" -#include "GPSRTKFactGroup.h" -#include "RTCMMavlink.h" +#include "GPSRtk.h" #include "QGCLoggingCategory.h" +#include -GPSManager::GPSManager(QGCApplication* app, QGCToolbox* toolbox) - : QGCTool(app, toolbox) -{ - qRegisterMetaType(); - qRegisterMetaType(); -} - -GPSManager::~GPSManager() -{ - disconnectGPS(); - - delete _gpsRtkFactGroup; - _gpsRtkFactGroup = nullptr; -} - -void GPSManager::setToolbox(QGCToolbox *toolbox) -{ - QGCTool::setToolbox(toolbox); - - _gpsRtkFactGroup = new GPSRTKFactGroup(this); +QGC_LOGGING_CATEGORY(GPSManagerLog, "qgc.gps.gpsmanager") - connect(this, &GPSManager::onConnect, this, &GPSManager::_onGPSConnect); - connect(this, &GPSManager::onDisconnect, this, &GPSManager::_onGPSDisconnect); - connect(this, &GPSManager::surveyInStatus, this, &GPSManager::_gpsSurveyInStatus); - connect(this, &GPSManager::satelliteUpdate, this, &GPSManager::_gpsNumSatellites); -} - -void GPSManager::_onGPSConnect() -{ - _gpsRtkFactGroup->connected()->setRawValue(true); -} - -void GPSManager::_onGPSDisconnect() -{ - _gpsRtkFactGroup->connected()->setRawValue(false); -} +Q_APPLICATION_STATIC(GPSManager, _gpsManager); -void GPSManager::_gpsSurveyInStatus(float duration, float accuracyMM, double latitude, double longitude, float altitude, bool valid, bool active) +GPSManager::GPSManager(QObject *parent) + : QObject(parent) + , _gpsRtk(new GPSRtk(this)) { - _gpsRtkFactGroup->currentDuration()->setRawValue(duration); - _gpsRtkFactGroup->currentAccuracy()->setRawValue(static_cast(accuracyMM) / 1000.0); - _gpsRtkFactGroup->currentLatitude()->setRawValue(latitude); - _gpsRtkFactGroup->currentLongitude()->setRawValue(longitude); - _gpsRtkFactGroup->currentAltitude()->setRawValue(altitude); - _gpsRtkFactGroup->valid()->setRawValue(valid); - _gpsRtkFactGroup->active()->setRawValue(active); + // qCDebug(GPSManagerLog) << Q_FUNC_INFO << this; } -void GPSManager::_gpsNumSatellites(int numSatellites) -{ - _gpsRtkFactGroup->numSatellites()->setRawValue(numSatellites); -} - -void GPSManager::connectGPS(const QString& device, const QString& gps_type) -{ - RTKSettings* rtkSettings = qgcApp()->toolbox()->settingsManager()->rtkSettings(); - - GPSProvider::GPSType type; - if (gps_type.contains("trimble", Qt::CaseInsensitive)) { - type = GPSProvider::GPSType::trimble; - qCDebug(RTKGPSLog) << "Connecting Trimble device"; - } else if (gps_type.contains("septentrio", Qt::CaseInsensitive)) { - type = GPSProvider::GPSType::septentrio; - qCDebug(RTKGPSLog) << "Connecting Septentrio device"; - } else { - type = GPSProvider::GPSType::u_blox; - qCDebug(RTKGPSLog) << "Connecting U-blox device"; - } - - disconnectGPS(); - _requestGpsStop = false; - _gpsProvider = new GPSProvider(device, - type, - true, /* enableSatInfo */ - rtkSettings->surveyInAccuracyLimit()->rawValue().toDouble(), - rtkSettings->surveyInMinObservationDuration()->rawValue().toInt(), - rtkSettings->useFixedBasePosition()->rawValue().toBool(), - rtkSettings->fixedBasePositionLatitude()->rawValue().toDouble(), - rtkSettings->fixedBasePositionLongitude()->rawValue().toDouble(), - rtkSettings->fixedBasePositionAltitude()->rawValue().toFloat(), - rtkSettings->fixedBasePositionAccuracy()->rawValue().toFloat(), - _requestGpsStop); - _gpsProvider->start(); - - //create RTCM device - _rtcmMavlink = new RTCMMavlink(*_toolbox); - - connect(_gpsProvider, &GPSProvider::RTCMDataUpdate, _rtcmMavlink, &RTCMMavlink::RTCMDataUpdate); - - //test: connect to position update - connect(_gpsProvider, &GPSProvider::positionUpdate, this, &GPSManager::GPSPositionUpdate); - connect(_gpsProvider, &GPSProvider::satelliteInfoUpdate, this, &GPSManager::GPSSatelliteUpdate); - connect(_gpsProvider, &GPSProvider::finished, this, &GPSManager::onDisconnect); - connect(_gpsProvider, &GPSProvider::surveyInStatus, this, &GPSManager::surveyInStatus); - - emit onConnect(); -} - -void GPSManager::disconnectGPS(void) +GPSManager::~GPSManager() { - if (_gpsProvider) { - _requestGpsStop = true; - //Note that we need a relatively high timeout to be sure the GPS thread finished. - if (!_gpsProvider->wait(2000)) { - qWarning() << "Failed to wait for GPS thread exit. Consider increasing the timeout"; - } - delete(_gpsProvider); - } - if (_rtcmMavlink) { - delete(_rtcmMavlink); - } - _gpsProvider = nullptr; - _rtcmMavlink = nullptr; + // qCDebug(GPSManagerLog) << Q_FUNC_INFO << this; } -bool GPSManager::connected() const { return _gpsProvider && _gpsProvider->isRunning(); } - -FactGroup* GPSManager::gpsRtkFactGroup(void) { return _gpsRtkFactGroup; } - -void GPSManager::GPSPositionUpdate(GPSPositionMessage msg) -{ - qCDebug(RTKGPSLog) << QString("GPS: got position update: alt=%1, long=%2, lat=%3").arg(msg.position_data.altitude_msl_m).arg(msg.position_data.longitude_deg).arg(msg.position_data.latitude_deg); -} -void GPSManager::GPSSatelliteUpdate(GPSSatelliteMessage msg) +GPSManager *GPSManager::instance() { - qCDebug(RTKGPSLog) << QString("GPS: got satellite info update, %1 satellites").arg((int)msg.satellite_data.count); - emit satelliteUpdate(msg.satellite_data.count); + return _gpsManager(); } diff --git a/src/GPS/GPSManager.h b/src/GPS/GPSManager.h index 00a1232f944..028fe76613d 100644 --- a/src/GPS/GPSManager.h +++ b/src/GPS/GPSManager.h @@ -7,56 +7,27 @@ * ****************************************************************************/ - #pragma once -#include "QGCToolbox.h" -#include "GPSPositionMessage.h" - -#include +#include #include -class GPSRTKFactGroup; -class FactGroup; -class RTCMMavlink; -class GPSProvider; +Q_DECLARE_LOGGING_CATEGORY(GPSManagerLog) -/** - ** class GPSManager - * handles a GPS provider and RTK - */ -class GPSManager : public QGCTool +class GPSRtk; + +class GPSManager : public QObject { Q_OBJECT + public: - GPSManager(QGCApplication* app, QGCToolbox* toolbox); + GPSManager(QObject *parent = nullptr); ~GPSManager(); - void setToolbox(QGCToolbox* toolbox) override; + static GPSManager *instance(); - void connectGPS (const QString& device, const QString& gps_type); - void disconnectGPS (void); - bool connected (void) const; - FactGroup* gpsRtkFactGroup(void); - -signals: - void onConnect(); - void onDisconnect(); - void surveyInStatus(float duration, float accuracyMM, double latitude, double longitude, float altitude, bool valid, bool active); - void satelliteUpdate(int numSats); - -private slots: - void GPSPositionUpdate(GPSPositionMessage msg); - void GPSSatelliteUpdate(GPSSatelliteMessage msg); - void _onGPSConnect(void); - void _onGPSDisconnect(void); - void _gpsSurveyInStatus(float duration, float accuracyMM, double latitude, double longitude, float altitude, bool valid, bool active); - void _gpsNumSatellites(int numSatellites); + GPSRtk *gpsRtk() { return _gpsRtk; } private: - GPSProvider* _gpsProvider = nullptr; - RTCMMavlink* _rtcmMavlink = nullptr; - GPSRTKFactGroup* _gpsRtkFactGroup = nullptr; - - std::atomic_bool _requestGpsStop; ///< signals the thread to quit + GPSRtk *_gpsRtk = nullptr; }; diff --git a/src/GPS/GPSPositionMessage.h b/src/GPS/GPSPositionMessage.h deleted file mode 100644 index 272fcdbe47d..00000000000 --- a/src/GPS/GPSPositionMessage.h +++ /dev/null @@ -1,32 +0,0 @@ -/**************************************************************************** - * - * (c) 2009-2024 QGROUNDCONTROL PROJECT - * - * QGroundControl is licensed according to the terms in the file - * COPYING.md in the root of the source code directory. - * - ****************************************************************************/ - - -#pragma once - -#include "sensor_gps.h" -#include "satellite_info.h" -#include - -/** - ** struct GPSPositionMessage - * wrapper that can be used for Qt signal/slots - */ -struct GPSPositionMessage -{ - sensor_gps_s position_data; -}; -Q_DECLARE_METATYPE(GPSPositionMessage); - - -struct GPSSatelliteMessage -{ - satellite_info_s satellite_data; -}; -Q_DECLARE_METATYPE(GPSSatelliteMessage); diff --git a/src/GPS/GPSProvider.cc b/src/GPS/GPSProvider.cc index 01c5f9d8b4e..4948455947a 100644 --- a/src/GPS/GPSProvider.cc +++ b/src/GPS/GPSProvider.cc @@ -7,233 +7,256 @@ * ****************************************************************************/ - #include "GPSProvider.h" #include "QGCLoggingCategory.h" -#include "Drivers/src/ubx.h" -#include "Drivers/src/sbf.h" -#include "Drivers/src/ashtech.h" -#include "Drivers/src/base_station.h" -#include "definitions.h" +#include "RTCMMavlink.h" + +#include +#include +#include +#include +#include +#include #ifdef Q_OS_ANDROID #include "qserialport.h" #else -#include +#include #endif -#define GPS_RECEIVE_TIMEOUT 1200 +QGC_LOGGING_CATEGORY(GPSProviderLog, "qgc.gps.gpsprovider") +QGC_LOGGING_CATEGORY(GPSDriversLog, "qgc.gps.drivers") -//#define SIMULATE_RTCM_OUTPUT //if defined, generate simulated RTCM messages - //additionally make sure to call connectGPS(""), eg. from QGCToolbox.cc +GPSProvider::GPSProvider(const QString &device, GPSType type, const rtk_data_s &rtkData, const std::atomic_bool &requestStop, QObject *parent) + : QThread(parent) + , _device(device) + , _type(type) + , _requestStop(requestStop) + , _rtkData(rtkData) + , _serial(new QSerialPort(this)) +{ + // qCDebug(GPSProviderLog) << Q_FUNC_INFO << this; + qCDebug(GPSProviderLog) << QString("Survey in accuracy: %1 | duration: %2").arg(_rtkData.surveyInAccMeters).arg(_rtkData.surveyInDurationSecs); -void GPSProvider::run() + _serial->setPortName(_device); +} + +GPSProvider::~GPSProvider() { -#ifdef SIMULATE_RTCM_OUTPUT - const int fakeMsgLengths[3] = { 30, 170, 240 }; - uint8_t* fakeData = new uint8_t[fakeMsgLengths[2]]; - while (!_requestStop) { - for (int i = 0; i < 3; ++i) { - gotRTCMData((uint8_t*) fakeData, fakeMsgLengths[i]); - msleep(4); - } - msleep(100); - } - delete[] fakeData; -#endif /* SIMULATE_RTCM_OUTPUT */ + // qCDebug(GPSProviderLog) << Q_FUNC_INFO << this; +} + +void GPSProvider::_publishSatelliteInfo() +{ + emit satelliteInfoUpdate(_satelliteInfo); +} - if (_serial) delete _serial; +void GPSProvider::_publishSensorGNSSRelative() +{ + emit sensorGnssRelativeUpdate(_sensorGnssRelative); +} - _serial = new QSerialPort(); - _serial->setPortName(_device); - if (!_serial->open(QIODevice::ReadWrite)) { - int retries = 60; - // Give the device some time to come up. In some cases the device is not - // immediately accessible right after startup for some reason. This can take 10-20s. - while (retries-- > 0 && _serial->error() == QSerialPort::PermissionError) { - qCDebug(RTKGPSLog) << "Cannot open device... retrying"; - msleep(500); - if (_serial->open(QIODevice::ReadWrite)) { - _serial->clearError(); - break; +void GPSProvider::_publishSensorGPS() +{ + emit sensorGpsUpdate(_sensorGps); +} + +void GPSProvider::_gotRTCMData(const uint8_t *data, size_t len) +{ + const QByteArray message(reinterpret_cast(data), len); + emit RTCMDataUpdate(message); +} + +int GPSProvider::_callbackEntry(GPSCallbackType type, void *data1, int data2, void *user) +{ + GPSProvider* const gps = reinterpret_cast(user); + return gps->callback(type, data1, data2); +} + +int GPSProvider::callback(GPSCallbackType type, void *data1, int data2) +{ + switch (type) { + case GPSCallbackType::readDeviceData: + if (_serial->bytesAvailable() == 0) { + const int timeout = *(reinterpret_cast(data1)); + if (!_serial->waitForReadyRead(timeout)) { + return 0; } } - if (_serial->error() != QSerialPort::NoError) { - qWarning() << "GPS: Failed to open Serial Device" << _device << _serial->errorString(); - return; + return _serial->read(reinterpret_cast(data1), data2); + case GPSCallbackType::writeDeviceData: + if (_serial->write(reinterpret_cast(data1), data2) >= 0) { + if (_serial->waitForBytesWritten(-1)) { + return data2; + } } + return -1; + case GPSCallbackType::setBaudrate: + return (_serial->setBaudRate(data2) ? 0 : -1); + case GPSCallbackType::gotRTCMMessage: + _gotRTCMData(reinterpret_cast(data1), data2); + break; + case GPSCallbackType::surveyInStatus: + { + const SurveyInStatus* const status = reinterpret_cast(data1); + qCDebug(GPSProviderLog) << "Position:" << status->latitude << status->longitude << status->altitude; + + const bool valid = status->flags & 1; + const bool active = (status->flags >> 1) & 1; + + qCDebug(GPSProviderLog) << QString("Survey-in status: %1s cur accuracy: %2mm valid: %3 active: %4").arg(status->duration).arg(status->mean_accuracy).arg(valid).arg(active); + emit surveyInStatus(status->duration, status->mean_accuracy, status->latitude, status->longitude, status->altitude, valid, active); + break; + } + case GPSCallbackType::setClock: + case GPSCallbackType::gotRelativePositionMessage: + // _sensorGnssRelative + default: + break; } - _serial->setBaudRate(QSerialPort::Baud9600); - _serial->setDataBits(QSerialPort::Data8); - _serial->setParity(QSerialPort::NoParity); - _serial->setStopBits(QSerialPort::OneStop); - _serial->setFlowControl(QSerialPort::NoFlowControl); - unsigned int baudrate; - GPSBaseStationSupport* gpsDriver = nullptr; + return 0; +} - while (!_requestStop) { +void GPSProvider::run() +{ +#ifdef SIMULATE_RTCM_OUTPUT + _sendRTCMData(); +#endif + + _connectSerial(); + + GPSBaseStationSupport *gpsDriver = nullptr; + while (!_requestStop) { if (gpsDriver) { delete gpsDriver; gpsDriver = nullptr; } - if (_type == GPSType::trimble) { - gpsDriver = new GPSDriverAshtech(&callbackEntry, this, &_reportGpsPos, _pReportSatInfo); - baudrate = 115200; - } else if (_type == GPSType::septentrio) { - gpsDriver = new GPSDriverSBF(&callbackEntry, this, &_reportGpsPos, _pReportSatInfo, 5); - baudrate = 0; // auto-configure - } else { - gpsDriver = new GPSDriverUBX(GPSDriverUBX::Interface::UART, &callbackEntry, this, &_reportGpsPos, _pReportSatInfo); - baudrate = 0; // auto-configure - } - gpsDriver->setSurveyInSpecs(_surveyInAccMeters * 10000.0f, _surveryInDurationSecs); - - if (_useFixedBaseLoction) { - gpsDriver->setBasePosition(_fixedBaseLatitude, _fixedBaseLongitude, _fixedBaseAltitudeMeters, _fixedBaseAccuracyMeters * 1000.0f); - } - - _gpsConfig.output_mode = GPSHelper::OutputMode::RTCM; - if (gpsDriver->configure(baudrate, _gpsConfig) == 0) { - - /* reset report */ - memset(&_reportGpsPos, 0, sizeof(_reportGpsPos)); - - //In rare cases it can happen that we get an error from the driver (eg. checksum failure) due to - //bus errors or buggy firmware. In this case we want to try multiple times before giving up. - int numTries = 0; + gpsDriver = _connectGPS(); + if (gpsDriver) { + (void) memset(&_sensorGps, 0, sizeof(_sensorGps)); - while (!_requestStop && numTries < 3) { - int helperRet = gpsDriver->receive(GPS_RECEIVE_TIMEOUT); + uint8_t numTries = 0; + while (!_requestStop && (numTries < 3)) { + const int helperRet = gpsDriver->receive(kGPSReceiveTimeout); if (helperRet > 0) { numTries = 0; - if (helperRet & 1) { - publishGPSPosition(); + if (helperRet & GPSReceiveType::Position) { + _publishSensorGPS(); numTries = 0; } - if (_pReportSatInfo && (helperRet & 2)) { - publishGPSSatellite(); + if (helperRet & GPSReceiveType::Satellite) { + _publishSatelliteInfo(); numTries = 0; } } else { ++numTries; } } - if (_serial->error() != QSerialPort::NoError && _serial->error() != QSerialPort::TimeoutError) { + + if ((_serial->error() != QSerialPort::NoError) && (_serial->error() != QSerialPort::TimeoutError)) { break; } } } - qCDebug(RTKGPSLog) << "Exiting GPS thread"; -} -GPSProvider::GPSProvider(const QString& device, - GPSType type, - bool enableSatInfo, - double surveyInAccMeters, - int surveryInDurationSecs, - bool useFixedBaseLocation, - double fixedBaseLatitude, - double fixedBaseLongitude, - float fixedBaseAltitudeMeters, - float fixedBaseAccuracyMeters, - const std::atomic_bool& requestStop) - : _device (device) - , _type (type) - , _requestStop (requestStop) - , _surveyInAccMeters (surveyInAccMeters) - , _surveryInDurationSecs (surveryInDurationSecs) - , _useFixedBaseLoction (useFixedBaseLocation) - , _fixedBaseLatitude (fixedBaseLatitude) - , _fixedBaseLongitude (fixedBaseLongitude) - , _fixedBaseAltitudeMeters (fixedBaseAltitudeMeters) - , _fixedBaseAccuracyMeters (fixedBaseAccuracyMeters) -{ - qCDebug(RTKGPSLog) << "Survey in accuracy:duration" << surveyInAccMeters << surveryInDurationSecs; - if (enableSatInfo) _pReportSatInfo = new satellite_info_s(); -} + delete gpsDriver; + gpsDriver = nullptr; -GPSProvider::~GPSProvider() -{ - if (_pReportSatInfo) delete(_pReportSatInfo); - if (_serial) delete _serial; + qCDebug(GPSProviderLog) << Q_FUNC_INFO << "Exiting GPS thread"; } -void GPSProvider::publishGPSPosition() +bool GPSProvider::_connectSerial() { - GPSPositionMessage msg; - msg.position_data = _reportGpsPos; - emit positionUpdate(msg); -} + if (!_serial->open(QIODevice::ReadWrite)) { + // Give the device some time to come up. In some cases the device is not + // immediately accessible right after startup for some reason. This can take 10-20s. + uint32_t retries = 60; + while ((retries-- > 0) && (_serial->error() == QSerialPort::PermissionError)) { + qCDebug(GPSProviderLog) << "Cannot open device... retrying"; + msleep(500); + if (_serial->open(QIODevice::ReadWrite)) { + _serial->clearError(); + break; + } + } -void GPSProvider::publishGPSSatellite() -{ - GPSSatelliteMessage msg; - msg.satellite_data = *_pReportSatInfo; - emit satelliteInfoUpdate(msg); -} + if (_serial->error() != QSerialPort::NoError) { + qCWarning(GPSProviderLog) << "GPS: Failed to open Serial Device" << _device << _serial->errorString(); + return false; + } + } -void GPSProvider::gotRTCMData(uint8_t* data, size_t len) -{ - QByteArray message((char*)data, static_cast(len)); - emit RTCMDataUpdate(message); -} + (void) _serial->setBaudRate(QSerialPort::Baud9600); + (void) _serial->setDataBits(QSerialPort::Data8); + (void) _serial->setParity(QSerialPort::NoParity); + (void) _serial->setStopBits(QSerialPort::OneStop); + (void) _serial->setFlowControl(QSerialPort::NoFlowControl); -int GPSProvider::callbackEntry(GPSCallbackType type, void *data1, int data2, void *user) -{ - GPSProvider *gps = (GPSProvider *)user; - return gps->callback(type, data1, data2); + return true; } -int GPSProvider::callback(GPSCallbackType type, void *data1, int data2) +GPSBaseStationSupport *GPSProvider::_connectGPS() { - switch (type) { - case GPSCallbackType::readDeviceData: { - if (_serial->bytesAvailable() == 0) { - int timeout = *((int *) data1); - if (!_serial->waitForReadyRead(timeout)) - return 0; //timeout - } - return (int)_serial->read((char*) data1, data2); - } - case GPSCallbackType::writeDeviceData: - if (_serial->write((char*) data1, data2) >= 0) { - if (_serial->waitForBytesWritten(-1)) - return data2; - } - return -1; + GPSBaseStationSupport *gpsDriver = nullptr; + uint32_t baudrate = 0; + switch(_type) { + case GPSType::trimble: + gpsDriver = new GPSDriverAshtech(&_callbackEntry, this, &_sensorGps, &_satelliteInfo); + baudrate = 115200; + break; + case GPSType::septentrio: + gpsDriver = new GPSDriverSBF(&_callbackEntry, this, &_sensorGps, &_satelliteInfo, kGPSHeadingOffset); + baudrate = 0; + break; + case GPSType::u_blox: + gpsDriver = new GPSDriverUBX(GPSDriverUBX::Interface::UART, &_callbackEntry, this, &_sensorGps, &_satelliteInfo); + baudrate = 0; + break; + case GPSType::femto: + gpsDriver = new GPSDriverFemto(&_callbackEntry, this, &_sensorGps, &_satelliteInfo); + baudrate = 0; + break; + default: + // GPSDriverEmlidReach, GPSDriverMTK, GPSDriverNMEA + qCWarning(GPSProviderLog) << "Unsupported GPS Type:" << static_cast(_type); + return nullptr; + } + + gpsDriver->setSurveyInSpecs(_rtkData.surveyInAccMeters * 10000.f, _rtkData.surveyInDurationSecs); - case GPSCallbackType::setBaudrate: - return _serial->setBaudRate(data2) ? 0 : -1; + if (_rtkData.useFixedBaseLoction) { + gpsDriver->setBasePosition(_rtkData.fixedBaseLatitude, _rtkData.fixedBaseLongitude, _rtkData.fixedBaseAltitudeMeters, _rtkData.fixedBaseAccuracyMeters * 1000.0f); + } - case GPSCallbackType::gotRTCMMessage: - gotRTCMData((uint8_t*) data1, data2); - break; + _gpsConfig.output_mode = GPSHelper::OutputMode::RTCM; - case GPSCallbackType::surveyInStatus: - { - SurveyInStatus* status = (SurveyInStatus*)data1; - qCDebug(RTKGPSLog) << "Position: " << status->latitude << status->longitude << status->altitude; + if (gpsDriver->configure(baudrate, _gpsConfig) != 0) { + return nullptr; + } - qCDebug(RTKGPSLog) << QString("Survey-in status: %1s cur accuracy: %2mm valid: %3 active: %4").arg(status->duration).arg(status->mean_accuracy).arg((int)(status->flags & 1)).arg((int)((status->flags>>1) & 1)); - emit surveyInStatus(status->duration, status->mean_accuracy, status->latitude, status->longitude, status->altitude, (int)(status->flags & 1), (int)((status->flags>>1) & 1)); - } - break; + return gpsDriver; +} - case GPSCallbackType::setClock: - /* do nothing */ - break; +void GPSProvider::_sendRTCMData() +{ + RTCMMavlink *const rtcm = new RTCMMavlink(this); - case GPSCallbackType::gotRelativePositionMessage: - /* do nothing */ - break; + const int fakeMsgLengths[3] = { 30, 170, 240 }; + const uint8_t* const fakeData = new uint8_t[fakeMsgLengths[2]]; + while (!_requestStop) { + for (int i = 0; i < 3; ++i) { + const QByteArray message(reinterpret_cast(fakeData), fakeMsgLengths[i]); + rtcm->RTCMDataUpdate(message); + msleep(4); + } + msleep(100); } - - return 0; + delete[] fakeData; } diff --git a/src/GPS/GPSProvider.h b/src/GPS/GPSProvider.h index 1098b12aa27..233c205a50d 100644 --- a/src/GPS/GPSProvider.h +++ b/src/GPS/GPSProvider.h @@ -7,87 +7,91 @@ * ****************************************************************************/ - #pragma once - -#include "GPSPositionMessage.h" -#include "Drivers/src/gps_helper.h" - +#include +#include +#include +#include #include #include -#include -class QSerialPort; +#include + +#include "satellite_info.h" +#include "sensor_gnss_relative.h" +#include "sensor_gps.h" +Q_DECLARE_LOGGING_CATEGORY(GPSProviderLog) + +class QSerialPort; +class GPSBaseStationSupport; -/** - ** class GPSProvider - * opens a GPS device and handles the protocol - */ class GPSProvider : public QThread { Q_OBJECT -public: +public: enum class GPSType { u_blox, trimble, - septentrio + septentrio, + femto }; - GPSProvider(const QString& device, - GPSType type, - bool enableSatInfo, - double surveyInAccMeters, - int surveryInDurationSecs, - bool useFixedBaseLocation, - double fixedBaseLatitude, - double fixedBaseLongitude, - float fixedBaseAltitudeMeters, - float fixedBaseAccuracyMeters, - const std::atomic_bool& requestStop); + struct rtk_data_s { + double surveyInAccMeters = 0; + int surveyInDurationSecs = 0; + bool useFixedBaseLoction = false; + double fixedBaseLatitude = 0.; + double fixedBaseLongitude = 0.; + float fixedBaseAltitudeMeters = 0.f; + float fixedBaseAccuracyMeters = 0.f; + }; + + GPSProvider(const QString &device, GPSType type, const rtk_data_s &rtkData, const std::atomic_bool &requestStop, QObject *parent = nullptr); ~GPSProvider(); - /** - * this is called by the callback method - */ - void gotRTCMData(uint8_t *data, size_t len); + int callback(GPSCallbackType type, void *data1, int data2); signals: - void positionUpdate(GPSPositionMessage message); - void satelliteInfoUpdate(GPSSatelliteMessage message); - void RTCMDataUpdate(QByteArray message); + void satelliteInfoUpdate(const satellite_info_s &message); + void sensorGnssRelativeUpdate(const sensor_gnss_relative_s &message); + void sensorGpsUpdate(const sensor_gps_s &message); + void RTCMDataUpdate(const QByteArray &message); void surveyInStatus(float duration, float accuracyMM, double latitude, double longitude, float altitude, bool valid, bool active); -protected: - void run(); - private: - void publishGPSPosition(); - void publishGPSSatellite(); + void run() final; - /** - * callback from the driver for the platform specific stuff - */ - static int callbackEntry(GPSCallbackType type, void *data1, int data2, void *user); + bool _connectSerial(); + GPSBaseStationSupport *_connectGPS(); + void _publishSensorGPS(); + void _publishSatelliteInfo(); + void _publishSensorGNSSRelative(); - int callback(GPSCallbackType type, void *data1, int data2); + void _gotRTCMData(const uint8_t *data, size_t len); + void _sendRTCMData(); + + static int _callbackEntry(GPSCallbackType type, void *data1, int data2, void *user); QString _device; GPSType _type; - const std::atomic_bool& _requestStop; - double _surveyInAccMeters; - int _surveryInDurationSecs; - bool _useFixedBaseLoction; - double _fixedBaseLatitude; - double _fixedBaseLongitude; - float _fixedBaseAltitudeMeters; - float _fixedBaseAccuracyMeters; + const std::atomic_bool &_requestStop; + rtk_data_s _rtkData{}; GPSHelper::GPSConfig _gpsConfig{}; - struct sensor_gps_s _reportGpsPos; - struct satellite_info_s *_pReportSatInfo = nullptr; + struct satellite_info_s _satelliteInfo{}; + struct sensor_gnss_relative_s _sensorGnssRelative{}; + struct sensor_gps_s _sensorGps{}; + + QSerialPort *_serial = nullptr; + + enum GPSReceiveType { + Position = 1, + Satellite = 2 + }; - QSerialPort *_serial = nullptr; + static constexpr uint32_t kGPSReceiveTimeout = 1200; + static constexpr float kGPSHeadingOffset = 5.f; }; diff --git a/src/GPS/GPSRtk.cc b/src/GPS/GPSRtk.cc new file mode 100644 index 00000000000..153c4d8cd65 --- /dev/null +++ b/src/GPS/GPSRtk.cc @@ -0,0 +1,152 @@ +/**************************************************************************** + * + * (c) 2009-2020 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "GPSRtk.h" +#include "GPSProvider.h" +#include "GPSRTKFactGroup.h" +#include "QGCApplication.h" +#include "QGCLoggingCategory.h" +#include "RTCMMavlink.h" +#include "RTKSettings.h" +#include "SettingsManager.h" + +QGC_LOGGING_CATEGORY(GPSRtkLog, "qgc.gps.gpsrtk") + +GPSRtk::GPSRtk(QObject *parent) + : QObject(parent) + , _gpsRtkFactGroup(new GPSRTKFactGroup(this)) +{ + // qCDebug(GPSRtkLog) << Q_FUNC_INFO << this; + + qRegisterMetaType("satellite_info_s"); + qRegisterMetaType("sensor_gnss_relative_s"); + qRegisterMetaType("sensor_gps_s"); +} + +GPSRtk::~GPSRtk() +{ + disconnectGPS(); + + // qCDebug(GPSRtkLog) << Q_FUNC_INFO << this; +} + +void GPSRtk::_onGPSConnect() +{ + _gpsRtkFactGroup->connected()->setRawValue(true); +} + +void GPSRtk::_onGPSDisconnect() +{ + _gpsRtkFactGroup->connected()->setRawValue(false); +} + +void GPSRtk::_onGPSSurveyInStatus(float duration, float accuracyMM, double latitude, double longitude, float altitude, bool valid, bool active) +{ + _gpsRtkFactGroup->currentDuration()->setRawValue(duration); + _gpsRtkFactGroup->currentAccuracy()->setRawValue(static_cast(accuracyMM) / 1000.0); + _gpsRtkFactGroup->currentLatitude()->setRawValue(latitude); + _gpsRtkFactGroup->currentLongitude()->setRawValue(longitude); + _gpsRtkFactGroup->currentAltitude()->setRawValue(altitude); + _gpsRtkFactGroup->valid()->setRawValue(valid); + _gpsRtkFactGroup->active()->setRawValue(active); +} + +void GPSRtk::connectGPS(const QString &device, QStringView gps_type) +{ + GPSProvider::GPSType type; + if (gps_type.contains(QStringLiteral("trimble"), Qt::CaseInsensitive)) { + type = GPSProvider::GPSType::trimble; + qCDebug(GPSRtkLog) << "Connecting Trimble device"; + } else if (gps_type.contains(QStringLiteral("septentrio"), Qt::CaseInsensitive)) { + type = GPSProvider::GPSType::septentrio; + qCDebug(GPSRtkLog) << "Connecting Septentrio device"; + } else if (gps_type.contains(QStringLiteral("femtomes"), Qt::CaseInsensitive)) { + type = GPSProvider::GPSType::femto; + qCDebug(GPSRtkLog) << "Connecting Femtomes device"; + } else { + type = GPSProvider::GPSType::u_blox; + qCDebug(GPSRtkLog) << "Connecting U-blox device"; + } + + disconnectGPS(); + + RTKSettings* const rtkSettings = qgcApp()->toolbox()->settingsManager()->rtkSettings(); + _requestGpsStop = false; + const GPSProvider::rtk_data_s rtkData = { + rtkSettings->surveyInAccuracyLimit()->rawValue().toDouble(), + rtkSettings->surveyInMinObservationDuration()->rawValue().toInt(), + rtkSettings->useFixedBasePosition()->rawValue().toBool(), + rtkSettings->fixedBasePositionLatitude()->rawValue().toDouble(), + rtkSettings->fixedBasePositionLongitude()->rawValue().toDouble(), + rtkSettings->fixedBasePositionAltitude()->rawValue().toFloat(), + rtkSettings->fixedBasePositionAccuracy()->rawValue().toFloat() + }; + _gpsProvider = new GPSProvider( + device, + type, + rtkData, + _requestGpsStop, + this + ); + (void) QMetaObject::invokeMethod(_gpsProvider, "start", Qt::AutoConnection); + + _rtcmMavlink = new RTCMMavlink(this); + (void) connect(_gpsProvider, &GPSProvider::RTCMDataUpdate, _rtcmMavlink, &RTCMMavlink::RTCMDataUpdate); + + (void) connect(_gpsProvider, &GPSProvider::satelliteInfoUpdate, this, &GPSRtk::_satelliteInfoUpdate); + (void) connect(_gpsProvider, &GPSProvider::sensorGpsUpdate, this, &GPSRtk::_sensorGpsUpdate); + (void) connect(_gpsProvider, &GPSProvider::surveyInStatus, this, &GPSRtk::_onGPSSurveyInStatus); + (void) connect(_gpsProvider, &GPSProvider::finished, this, &GPSRtk::_onGPSDisconnect); + + (void) QMetaObject::invokeMethod(this, "_onGPSConnect", Qt::AutoConnection); +} + +void GPSRtk::disconnectGPS() +{ + if (_gpsProvider) { + _requestGpsStop = true; + if (!_gpsProvider->wait(kGPSThreadDisconnectTimeout)) { + qCWarning(GPSRtkLog) << "Failed to wait for GPS thread exit. Consider increasing the timeout"; + } + + _gpsProvider->deleteLater(); + _gpsProvider = nullptr; + } + + if (_rtcmMavlink) { + _rtcmMavlink->deleteLater(); + _rtcmMavlink = nullptr; + } +} + +bool GPSRtk::connected() const +{ + return (_gpsProvider ? _gpsProvider->isRunning() : false); +} + +FactGroup *GPSRtk::gpsRtkFactGroup() +{ + return _gpsRtkFactGroup; +} + +void GPSRtk::_satelliteInfoUpdate(const satellite_info_s &msg) +{ + qCDebug(GPSRtkLog) << Q_FUNC_INFO << QStringLiteral("%1 satellites").arg(msg.count); + _gpsRtkFactGroup->numSatellites()->setRawValue(msg.count); +} + +void GPSRtk::_sensorGnssRelativeUpdate(const sensor_gnss_relative_s &msg) +{ + qCDebug(GPSRtkLog) << Q_FUNC_INFO; +} + +void GPSRtk::_sensorGpsUpdate(const sensor_gps_s &msg) +{ + qCDebug(GPSRtkLog) << Q_FUNC_INFO << QStringLiteral("alt=%1, long=%2, lat=%3").arg(msg.altitude_msl_m).arg(msg.longitude_deg).arg(msg.latitude_deg); +} diff --git a/src/GPS/GPSRtk.h b/src/GPS/GPSRtk.h new file mode 100644 index 00000000000..68d9fae14d2 --- /dev/null +++ b/src/GPS/GPSRtk.h @@ -0,0 +1,56 @@ +/**************************************************************************** + * + * (c) 2009-2020 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#pragma once + +#include +#include +#include + +#include "satellite_info.h" +#include "sensor_gnss_relative.h" +#include "sensor_gps.h" + +Q_DECLARE_LOGGING_CATEGORY(GPSRtkLog) + +class GPSRTKFactGroup; +class FactGroup; +class RTCMMavlink; +class GPSProvider; + +class GPSRtk : public QObject +{ + Q_OBJECT + +public: + GPSRtk(QObject *parent = nullptr); + ~GPSRtk(); + + void connectGPS(const QString &device, QStringView gps_type); + void disconnectGPS(); + bool connected() const; + FactGroup *gpsRtkFactGroup(); + +private slots: + void _satelliteInfoUpdate(const satellite_info_s &msg); + void _sensorGnssRelativeUpdate(const sensor_gnss_relative_s &msg); + void _sensorGpsUpdate(const sensor_gps_s &msg); + void _onGPSConnect(); + void _onGPSDisconnect(); + void _onGPSSurveyInStatus(float duration, float accuracyMM, double latitude, double longitude, float altitude, bool valid, bool active); + +private: + GPSProvider *_gpsProvider = nullptr; + RTCMMavlink *_rtcmMavlink = nullptr; + GPSRTKFactGroup *_gpsRtkFactGroup = nullptr; + + std::atomic_bool _requestGpsStop = false; + + static constexpr uint32_t kGPSThreadDisconnectTimeout = 2000; +}; diff --git a/src/GPS/RTCMMavlink.cc b/src/GPS/RTCMMavlink.cc index 53272c8b836..b9ae04dd54c 100644 --- a/src/GPS/RTCMMavlink.cc +++ b/src/GPS/RTCMMavlink.cc @@ -7,74 +7,96 @@ * ****************************************************************************/ - #include "RTCMMavlink.h" +#include "MAVLinkProtocol.h" #include "MultiVehicleManager.h" +#include "QGCApplication.h" +#include "QGCLoggingCategory.h" #include "Vehicle.h" -#include "MAVLinkProtocol.h" -RTCMMavlink::RTCMMavlink(QGCToolbox& toolbox) - : _toolbox(toolbox) +QGC_LOGGING_CATEGORY(RTCMMavlinkLog, "qgc.gps.rtcmmavlink") + +RTCMMavlink::RTCMMavlink(QObject *parent) + : QObject(parent) { + // qCDebug(RTCMMavlinkLog) << Q_FUNC_INFO << this; + _bandwidthTimer.start(); } -void RTCMMavlink::RTCMDataUpdate(QByteArray message) +RTCMMavlink::~RTCMMavlink() { - /* statistics */ - _bandwidthByteCounter += message.size(); - qint64 elapsed = _bandwidthTimer.elapsed(); - if (elapsed > 1000) { - qDebug() << QStringLiteral("RTCM bandwidth: %1 kB/s\n").arg(_bandwidthByteCounter / elapsed * 1000.f / 1024.f); - _bandwidthTimer.restart(); - _bandwidthByteCounter = 0; - } + // qCDebug(RTCMMavlinkLog) << Q_FUNC_INFO << this; +} + +void RTCMMavlink::RTCMDataUpdate(QByteArrayView data) +{ +#ifdef QT_DEBUG + _calculateBandwith(data.size()); +#endif - const qsizetype maxMessageLength = MAVLINK_MSG_GPS_RTCM_DATA_FIELD_DATA_LEN; - mavlink_gps_rtcm_data_t mavlinkRtcmData; - memset(&mavlinkRtcmData, 0, sizeof(mavlink_gps_rtcm_data_t)); + mavlink_gps_rtcm_data_t gpsRtcmData{}; - if (message.size() < maxMessageLength) { - mavlinkRtcmData.len = message.size(); - mavlinkRtcmData.flags = (_sequenceId & 0x1F) << 3; - memcpy(&mavlinkRtcmData.data, message.data(), message.size()); - sendMessageToVehicle(mavlinkRtcmData); + static constexpr qsizetype maxMessageLength = MAVLINK_MSG_GPS_RTCM_DATA_FIELD_DATA_LEN; + if (data.size() < maxMessageLength) { + gpsRtcmData.len = data.size(); + gpsRtcmData.flags = (_sequenceId & 0x1FU) << 3; + (void) memcpy(&gpsRtcmData.data, data.data(), data.size()); + _sendMessageToVehicle(gpsRtcmData); } else { - // We need to fragment - - uint8_t fragmentId = 0; // Fragment id indicates the fragment within a set - int start = 0; - while (start < message.size()) { - int length = std::min(message.size() - start, maxMessageLength); - mavlinkRtcmData.flags = 1; // LSB set indicates message is fragmented - mavlinkRtcmData.flags |= fragmentId++ << 1; // Next 2 bits are fragment id - mavlinkRtcmData.flags |= (_sequenceId & 0x1F) << 3; // Next 5 bits are sequence id - mavlinkRtcmData.len = length; - memcpy(&mavlinkRtcmData.data, message.data() + start, length); - sendMessageToVehicle(mavlinkRtcmData); + uint8_t fragmentId = 0; + qsizetype start = 0; + while (start < data.size()) { + gpsRtcmData.flags = 0x01U; // LSB set indicates message is fragmented + gpsRtcmData.flags |= fragmentId++ << 1; // Next 2 bits are fragment id + gpsRtcmData.flags |= (_sequenceId & 0x1FU) << 3; // Next 5 bits are sequence id + + const qsizetype length = std::min(data.size() - start, maxMessageLength); + gpsRtcmData.len = length; + + (void) memcpy(gpsRtcmData.data, data.constData() + start, length); + _sendMessageToVehicle(gpsRtcmData); + start += length; } } + ++_sequenceId; } -void RTCMMavlink::sendMessageToVehicle(const mavlink_gps_rtcm_data_t& msg) +void RTCMMavlink::_sendMessageToVehicle(const mavlink_gps_rtcm_data_t &data) { - QmlObjectListModel& vehicles = *_toolbox.multiVehicleManager()->vehicles(); - MAVLinkProtocol* mavlinkProtocol = _toolbox.mavlinkProtocol(); - for (int i = 0; i < vehicles.count(); i++) { - Vehicle* vehicle = qobject_cast(vehicles[i]); - SharedLinkInterfacePtr sharedLink = vehicle->vehicleLinkManager()->primaryLink().lock(); - + QmlObjectListModel* const vehicles = qgcApp()->toolbox()->multiVehicleManager()->vehicles(); + for (qsizetype i = 0; i < vehicles->count(); i++) { + Vehicle* const vehicle = qobject_cast(vehicles->get(i)); + const SharedLinkInterfacePtr sharedLink = vehicle->vehicleLinkManager()->primaryLink().lock(); if (sharedLink) { - mavlink_message_t message; - - mavlink_msg_gps_rtcm_data_encode_chan(mavlinkProtocol->getSystemId(), - mavlinkProtocol->getComponentId(), - sharedLink->mavlinkChannel(), - &message, - &msg); - vehicle->sendMessageOnLinkThreadSafe(sharedLink.get(), message); + const MAVLinkProtocol* const mavlinkProtocol = qgcApp()->toolbox()->mavlinkProtocol(); + mavlink_message_t message; + (void) mavlink_msg_gps_rtcm_data_encode_chan( + mavlinkProtocol->getSystemId(), + mavlinkProtocol->getComponentId(), + sharedLink->mavlinkChannel(), + &message, + &data + ); + (void) vehicle->sendMessageOnLinkThreadSafe(sharedLink.get(), message); } } } + +void RTCMMavlink::_calculateBandwith(qsizetype bytes) +{ + if (!_bandwidthTimer.isValid()) { + return; + } + + _bandwidthByteCounter += bytes; + + const qint64 elapsed = _bandwidthTimer.elapsed(); + if (elapsed > 1000) { + qCDebug(RTCMMavlinkLog) << QStringLiteral("RTCM bandwidth: %1 kB/s").arg(((_bandwidthByteCounter / elapsed) * 1000.f) / 1024.f); + (void) _bandwidthTimer.restart(); + _bandwidthByteCounter = 0; + } +} diff --git a/src/GPS/RTCMMavlink.h b/src/GPS/RTCMMavlink.h index c527b8eaf0d..31498a98c1c 100644 --- a/src/GPS/RTCMMavlink.h +++ b/src/GPS/RTCMMavlink.h @@ -10,31 +10,30 @@ #pragma once -#include "QGCToolbox.h" -#include "MAVLinkLib.h" - -#include #include +#include +#include + +typedef struct __mavlink_gps_rtcm_data_t mavlink_gps_rtcm_data_t; + +Q_DECLARE_LOGGING_CATEGORY(RTCMMavlinkLog) -/** - ** class RTCMMavlink - * Receives RTCM updates and sends them via MAVLINK to the device - */ class RTCMMavlink : public QObject { Q_OBJECT + public: - RTCMMavlink(QGCToolbox& toolbox); - //TODO: API to select device(s)? + RTCMMavlink(QObject *parent = nullptr); + ~RTCMMavlink(); public slots: - void RTCMDataUpdate(QByteArray message); + void RTCMDataUpdate(QByteArrayView data); private: - void sendMessageToVehicle(const mavlink_gps_rtcm_data_t& msg); + void _calculateBandwith(qsizetype bytes); + static void _sendMessageToVehicle(const mavlink_gps_rtcm_data_t &data); - QGCToolbox& _toolbox; - QElapsedTimer _bandwidthTimer; - int _bandwidthByteCounter = 0; uint8_t _sequenceId = 0; + qsizetype _bandwidthByteCounter = 0; + QElapsedTimer _bandwidthTimer; }; diff --git a/src/GPS/definitions.h b/src/GPS/definitions.h index 6a4c287bff5..366e95f7f96 100644 --- a/src/GPS/definitions.h +++ b/src/GPS/definitions.h @@ -49,51 +49,29 @@ #pragma once #include - -#define GPS_READ_BUFFER_SIZE 1024 - -#define GPS_INFO(...) qInfo(__VA_ARGS__) -#define GPS_WARN(...) qWarning(__VA_ARGS__) -#define GPS_ERR(...) qCritical(__VA_ARGS__) +#include +#include +#include #include "sensor_gps.h" #include "sensor_gnss_relative.h" #include "satellite_info.h" -#define M_DEG_TO_RAD (M_PI / 180.0) -#define M_RAD_TO_DEG (180.0 / M_PI) -#define M_DEG_TO_RAD_F 0.01745329251994f -#define M_RAD_TO_DEG_F 57.2957795130823f - -#define M_PI_2_F M_PI +Q_DECLARE_LOGGING_CATEGORY(GPSDriversLog) -#include - -class Sleeper : public QThread -{ -public: - static void usleep(unsigned long usecs) { QThread::usleep(usecs); } -}; - -static inline void gps_usleep(unsigned long usecs) { - Sleeper::usleep(usecs); -} +#define GPS_INFO(...) qCInfo(GPSDriversLog, __VA_ARGS__) +#define GPS_WARN(...) qCWarning(GPSDriversLog, __VA_ARGS__) +#define GPS_ERR(...) qCCritical(GPSDriversLog, __VA_ARGS__) -typedef uint64_t gps_abstime; +#define M_DEG_TO_RAD (M_PI / 180.0) +#define M_RAD_TO_DEG (180.0 / M_PI) +#define M_DEG_TO_RAD_F 0.0174532925f +#define M_RAD_TO_DEG_F 57.2957795f -#include -/** - * Get the current time in us. Function signature: - * uint64_t hrt_absolute_time() - */ -static inline gps_abstime gps_absolute_time() { - //FIXME: is there something with microsecond accuracy? - return QDateTime::currentMSecsSinceEpoch() * 1000; -} +#define M_PI_2_F 0.63661977f -//timespec is UNIX-specific #ifdef _WIN32 -#if _MSC_VER < 1900 +#if (_MSC_VER < 1900) struct timespec { time_t tv_sec; @@ -104,3 +82,13 @@ struct timespec #endif #endif +static inline void gps_usleep(unsigned long usecs) +{ + QThread::usleep(usecs); +} + +typedef uint64_t gps_abstime; +static inline gps_abstime gps_absolute_time() +{ + return (QDateTime::currentMSecsSinceEpoch() * 1000); +} diff --git a/src/GPS/satellite_info.h b/src/GPS/satellite_info.h index 33974c31467..8a03709a27c 100644 --- a/src/GPS/satellite_info.h +++ b/src/GPS/satellite_info.h @@ -40,16 +40,19 @@ * ****************************************************************************/ +/* https://github.com/PX4/Firmware/blob/master/msg/SatelliteInfo.msg */ + #pragma once + #include -/* - * This file is auto-generated from https://github.com/PX4/Firmware/blob/master/msg/satellite_info.msg - * and was manually copied here. - */ +#include -struct satellite_info_s { +struct satellite_info_s +{ uint64_t timestamp; + static constexpr uint8_t SAT_INFO_MAX_SATELLITES = 20; + uint8_t count; uint8_t svid[20]; uint8_t used[20]; @@ -57,8 +60,5 @@ struct satellite_info_s { uint8_t azimuth[20]; uint8_t snr[20]; uint8_t prn[20]; -#ifdef __cplusplus - static const uint8_t SAT_INFO_MAX_SATELLITES = 20; - -#endif }; +Q_DECLARE_METATYPE(satellite_info_s); diff --git a/src/GPS/sensor_gnss_relative.h b/src/GPS/sensor_gnss_relative.h index e1a39706f35..2de28ba3aa0 100644 --- a/src/GPS/sensor_gnss_relative.h +++ b/src/GPS/sensor_gnss_relative.h @@ -40,29 +40,34 @@ * ****************************************************************************/ -/* Auto-generated by genmsg_cpp from file /home/julianoes/src/upstream/PX4-Autopilot/msg/SensorGnssRelative.msg */ - +/* https://github.com/PX4/Firmware/blob/master/msg/SensorGnssRelative.msg */ #pragma once + #include -/* - * This file is auto-generated from https://github.com/PX4/Firmware/blob/master/msg/SensorGnssRelative.msg - * and was manually copied here. - */ +#include -struct sensor_gnss_relative_s { +struct sensor_gnss_relative_s +{ uint64_t timestamp; uint64_t timestamp_sample; - uint64_t time_utc_usec; + uint32_t device_id; + + uint64_t time_utc_usec; + + uint16_t reference_station_id; + float position[3]; float position_accuracy[3]; + float heading; float heading_accuracy; + float position_length; float accuracy_length; - uint16_t reference_station_id; + bool gnss_fix_ok; bool differential_solution; bool relative_position_valid; @@ -74,3 +79,4 @@ struct sensor_gnss_relative_s { bool heading_valid; bool relative_position_normalized; }; +Q_DECLARE_METATYPE(sensor_gnss_relative_s); diff --git a/src/GPS/sensor_gps.h b/src/GPS/sensor_gps.h index 2be1599bb53..6f0a68394c3 100644 --- a/src/GPS/sensor_gps.h +++ b/src/GPS/sensor_gps.h @@ -40,65 +40,75 @@ * ****************************************************************************/ -/* Auto-generated by genmsg_cpp from file /home/julianoes/src/upstream/PX4-Autopilot/msg/SensorGps.msg */ - +/* https://github.com/PX4/Firmware/blob/master/msg/SensorGps.msg */ #pragma once + #include -/* - * This file is auto-generated from https://github.com/PX4/Firmware/blob/master/msg/SensorGps.msg - * and was manually copied here. - */ +#include -struct sensor_gps_s { +struct sensor_gps_s +{ uint64_t timestamp; uint64_t timestamp_sample; + + uint32_t device_id; + double latitude_deg; double longitude_deg; double altitude_msl_m; double altitude_ellipsoid_m; - uint64_t time_utc_usec; - uint32_t device_id; + float s_variance_m_s; float c_variance_rad; + uint8_t fix_type; + float eph; float epv; + float hdop; float vdop; + int32_t noise_per_ms; + uint16_t automatic_gain_control; + + static constexpr uint8_t JAMMING_STATE_UNKNOWN = 0; + static constexpr uint8_t JAMMING_STATE_OK = 1; + static constexpr uint8_t JAMMING_STATE_WARNING = 2; + static constexpr uint8_t JAMMING_STATE_CRITICAL = 3; + uint8_t jamming_state; int32_t jamming_indicator; + + static constexpr uint8_t SPOOFING_STATE_UNKNOWN = 0; + static constexpr uint8_t SPOOFING_STATE_NONE = 1; + static constexpr uint8_t SPOOFING_STATE_INDICATED = 2; + static constexpr uint8_t SPOOFING_STATE_MULTIPLE = 3; + uint8_t spoofing_state; + float vel_m_s; float vel_n_m_s; float vel_e_m_s; float vel_d_m_s; float cog_rad; + bool vel_ned_valid; + int32_t timestamp_time_relative; + uint64_t time_utc_usec; + + uint8_t satellites_used; + float heading; float heading_offset; float heading_accuracy; + float rtcm_injection_rate; - uint16_t automatic_gain_control; - uint8_t fix_type; - uint8_t jamming_state; - uint8_t spoofing_state; - bool vel_ned_valid; - uint8_t satellites_used; uint8_t selected_rtcm_instance; - bool rtcm_crc_failed; - uint8_t rtcm_msg_used; - uint8_t _padding0[2]; // required for logger - - static constexpr uint8_t JAMMING_STATE_UNKNOWN = 0; - static constexpr uint8_t JAMMING_STATE_OK = 1; - static constexpr uint8_t JAMMING_STATE_WARNING = 2; - static constexpr uint8_t JAMMING_STATE_CRITICAL = 3; - static constexpr uint8_t SPOOFING_STATE_UNKNOWN = 0; - static constexpr uint8_t SPOOFING_STATE_NONE = 1; - static constexpr uint8_t SPOOFING_STATE_INDICATED = 2; - static constexpr uint8_t SPOOFING_STATE_MULTIPLE = 3; + bool rtcm_crc_failed; static constexpr uint8_t RTCM_MSG_USED_UNKNOWN = 0; static constexpr uint8_t RTCM_MSG_USED_NOT_USED = 1; static constexpr uint8_t RTCM_MSG_USED_USED = 2; + uint8_t rtcm_msg_used; }; +Q_DECLARE_METATYPE(sensor_gps_s); diff --git a/src/QGCToolbox.cc b/src/QGCToolbox.cc index 05989093171..1614d88c802 100644 --- a/src/QGCToolbox.cc +++ b/src/QGCToolbox.cc @@ -9,9 +9,6 @@ #include "FirmwarePluginManager.h" -#ifndef NO_SERIAL_LINK -#include "GPSManager.h" -#endif #include "LinkManager.h" #include "MAVLinkProtocol.h" #include "MissionCommandTree.h" @@ -43,9 +40,6 @@ QGCToolbox::QGCToolbox(QGCApplication* app) //-- Scan and load plugins _scanAndLoadPlugins(app); _firmwarePluginManager = new FirmwarePluginManager (app, this); -#ifndef NO_SERIAL_LINK - _gpsManager = new GPSManager (app, this); -#endif _linkManager = new LinkManager (app, this); _mavlinkProtocol = new MAVLinkProtocol (app, this); _missionCommandTree = new MissionCommandTree (app, this); @@ -69,9 +63,6 @@ void QGCToolbox::setChildToolboxes(void) _corePlugin->setToolbox(this); _firmwarePluginManager->setToolbox(this); -#ifndef NO_SERIAL_LINK - _gpsManager->setToolbox(this); -#endif _linkManager->setToolbox(this); _mavlinkProtocol->setToolbox(this); _missionCommandTree->setToolbox(this); diff --git a/src/QGCToolbox.h b/src/QGCToolbox.h index ba93df9c5d8..acef3379d6a 100644 --- a/src/QGCToolbox.h +++ b/src/QGCToolbox.h @@ -14,7 +14,6 @@ #include class FirmwarePluginManager; -class GPSManager; class LinkManager; class MAVLinkProtocol; class MissionCommandTree; @@ -49,9 +48,6 @@ class QGCToolbox : public QObject { MAVLinkLogManager* mavlinkLogManager () { return _mavlinkLogManager; } QGCCorePlugin* corePlugin () { return _corePlugin; } SettingsManager* settingsManager () { return _settingsManager; } -#ifndef NO_SERIAL_LINK - GPSManager* gpsManager () { return _gpsManager; } -#endif #ifndef QGC_AIRLINK_DISABLED AirLinkManager* airlinkManager () { return _airlinkManager; } #endif @@ -64,9 +60,6 @@ class QGCToolbox : public QObject { void _scanAndLoadPlugins(QGCApplication *app); FirmwarePluginManager* _firmwarePluginManager = nullptr; -#ifndef NO_SERIAL_LINK - GPSManager* _gpsManager = nullptr; -#endif LinkManager* _linkManager = nullptr; MAVLinkProtocol* _mavlinkProtocol = nullptr; MissionCommandTree* _missionCommandTree = nullptr; diff --git a/src/QmlControls/QGroundControlQmlGlobal.cc b/src/QmlControls/QGroundControlQmlGlobal.cc index ffd7923a57b..89d4238f310 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.cc +++ b/src/QmlControls/QGroundControlQmlGlobal.cc @@ -19,6 +19,7 @@ #include "ADSBVehicleManager.h" #ifndef NO_SERIAL_LINK #include "GPSManager.h" +#include "GPSRtk.h" #endif #include "QGCPalette.h" #ifdef QT_DEBUG @@ -85,7 +86,7 @@ void QGroundControlQmlGlobal::setToolbox(QGCToolbox* toolbox) _firmwarePluginManager = toolbox->firmwarePluginManager(); _settingsManager = toolbox->settingsManager(); #ifndef NO_SERIAL_LINK - _gpsRtkFactGroup = toolbox->gpsManager()->gpsRtkFactGroup(); + _gpsRtkFactGroup = GPSManager::instance()->gpsRtk()->gpsRtkFactGroup(); #endif _globalPalette = new QGCPalette(this); #ifndef QGC_AIRLINK_DISABLED diff --git a/src/Utilities/QGCLoggingCategory.cc b/src/Utilities/QGCLoggingCategory.cc index fc144b505e2..ec3580dc76e 100644 --- a/src/Utilities/QGCLoggingCategory.cc +++ b/src/Utilities/QGCLoggingCategory.cc @@ -23,7 +23,6 @@ QGC_LOGGING_CATEGORY(FirmwareUpgradeVerboseLog, "FirmwareUpgradeVerboseLog") QGC_LOGGING_CATEGORY(MissionCommandsLog, "MissionCommandsLog") QGC_LOGGING_CATEGORY(MissionItemLog, "MissionItemLog") QGC_LOGGING_CATEGORY(ParameterManagerLog, "ParameterManagerLog") -QGC_LOGGING_CATEGORY(RTKGPSLog, "RTKGPSLog") QGC_LOGGING_CATEGORY(GuidedActionsControllerLog, "GuidedActionsControllerLog") QGC_LOGGING_CATEGORY(LocalizationLog, "LocalizationLog") QGC_LOGGING_CATEGORY(VideoAllLog, kVideoAllLogCategory) diff --git a/src/Utilities/QGCLoggingCategory.h b/src/Utilities/QGCLoggingCategory.h index 0c74f5d47cf..bd22a3f3426 100644 --- a/src/Utilities/QGCLoggingCategory.h +++ b/src/Utilities/QGCLoggingCategory.h @@ -19,7 +19,6 @@ Q_DECLARE_LOGGING_CATEGORY(FirmwareUpgradeVerboseLog) Q_DECLARE_LOGGING_CATEGORY(MissionCommandsLog) Q_DECLARE_LOGGING_CATEGORY(MissionItemLog) Q_DECLARE_LOGGING_CATEGORY(ParameterManagerLog) -Q_DECLARE_LOGGING_CATEGORY(RTKGPSLog) Q_DECLARE_LOGGING_CATEGORY(GuidedActionsControllerLog) Q_DECLARE_LOGGING_CATEGORY(LocalizationLog) Q_DECLARE_LOGGING_CATEGORY(VideoAllLog) // turns on all individual QGC video logs diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index b363c2f0a23..e943e7bfd85 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -55,6 +55,9 @@ add_qgc_test(FollowMeTest) add_subdirectory(Geo) add_qgc_test(GeoTest) +add_subdirectory(GPS) +add_qgc_test(GpsTest) + add_subdirectory(MAVLink) add_qgc_test(StatusTextHandlerTest) add_qgc_test(SigningTest) @@ -134,6 +137,7 @@ target_link_libraries(qgctest FactSystemTest FollowMeTest GeoTest + GpsTest MAVLinkTest MissionManagerTest QmlControlsTest diff --git a/test/GPS/CMakeLists.txt b/test/GPS/CMakeLists.txt new file mode 100644 index 00000000000..54c2adb86c6 --- /dev/null +++ b/test/GPS/CMakeLists.txt @@ -0,0 +1,17 @@ +find_package(Qt6 REQUIRED COMPONENTS Core Positioning Test) + +qt_add_library(GpsTest + STATIC + GpsTest.cc + GpsTest.h +) + +target_link_libraries(GpsTest + PRIVATE + Qt6::Test + GPS + PUBLIC + qgcunittest +) + +target_include_directories(GpsTest PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/test/GPS/GpsTest.cc b/test/GPS/GpsTest.cc new file mode 100644 index 00000000000..a6a3fe5ae17 --- /dev/null +++ b/test/GPS/GpsTest.cc @@ -0,0 +1,19 @@ +/**************************************************************************** + * + * (c) 2009-2020 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "GpsTest.h" +#include "GPSProvider.h" +#include "RTCMMavlink.h" + +#include + +void GpsTest::_testGpsRTCM() +{ + +} diff --git a/test/GPS/GpsTest.h b/test/GPS/GpsTest.h new file mode 100644 index 00000000000..3da3e44ebd0 --- /dev/null +++ b/test/GPS/GpsTest.h @@ -0,0 +1,20 @@ +/**************************************************************************** + * + * (c) 2009-2020 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#pragma once + +#include "UnitTest.h" + +class GpsTest : public UnitTest +{ + Q_OBJECT + +private slots: + void _testGpsRTCM(); +}; diff --git a/test/UnitTestList.cc b/test/UnitTestList.cc index 7b487a7adca..604041f9325 100644 --- a/test/UnitTestList.cc +++ b/test/UnitTestList.cc @@ -42,6 +42,9 @@ // Geo #include "GeoTest.h" +// GPS +#include "GpsTest.h" + // MAVLink #include "StatusTextHandlerTest.h" #include "SigningTest.h" @@ -140,6 +143,9 @@ int runTests(bool stress, QStringView unitTestOptions) // Geo UT_REGISTER_TEST(GeoTest) + // GPS + // UT_REGISTER_TEST(GpsTest) + // MAVLink UT_REGISTER_TEST(StatusTextHandlerTest) UT_REGISTER_TEST(SigningTest) From 20953448578dfe431b915728a6336210e8a9a50e Mon Sep 17 00:00:00 2001 From: Holden Date: Fri, 25 Oct 2024 12:30:32 -0400 Subject: [PATCH 19/27] GPS: Threading Fixes --- src/GPS/GPSProvider.cc | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/GPS/GPSProvider.cc b/src/GPS/GPSProvider.cc index 4948455947a..b3614aeb3c9 100644 --- a/src/GPS/GPSProvider.cc +++ b/src/GPS/GPSProvider.cc @@ -33,17 +33,16 @@ GPSProvider::GPSProvider(const QString &device, GPSType type, const rtk_data_s & , _type(type) , _requestStop(requestStop) , _rtkData(rtkData) - , _serial(new QSerialPort(this)) { // qCDebug(GPSProviderLog) << Q_FUNC_INFO << this; qCDebug(GPSProviderLog) << QString("Survey in accuracy: %1 | duration: %2").arg(_rtkData.surveyInAccMeters).arg(_rtkData.surveyInDurationSecs); - - _serial->setPortName(_device); } GPSProvider::~GPSProvider() { + delete _serial; + // qCDebug(GPSProviderLog) << Q_FUNC_INFO << this; } @@ -169,11 +168,16 @@ void GPSProvider::run() delete gpsDriver; gpsDriver = nullptr; + delete _serial; + _serial = nullptr; + qCDebug(GPSProviderLog) << Q_FUNC_INFO << "Exiting GPS thread"; } bool GPSProvider::_connectSerial() { + _serial = new QSerialPort(); + _serial->setPortName(_device); if (!_serial->open(QIODevice::ReadWrite)) { // Give the device some time to come up. In some cases the device is not // immediately accessible right after startup for some reason. This can take 10-20s. From 8202e60ab665401c54283e355a8c87ba35ce307c Mon Sep 17 00:00:00 2001 From: Holden Date: Fri, 25 Oct 2024 06:16:06 -0400 Subject: [PATCH 20/27] QmlControls: Fix Parenting of Several Classes --- src/Joystick/Joystick.h | 1 + src/QmlControls/CustomAction.cc | 40 +++++- src/QmlControls/CustomAction.h | 66 +++++----- src/QmlControls/CustomActionManager.cc | 98 ++++++++------- src/QmlControls/CustomActionManager.h | 35 +++--- .../EditPositionDialogController.cc | 87 +++++++------ .../EditPositionDialogController.h | 85 +++++++------ src/QmlControls/ParameterEditorController.cc | 12 +- src/QmlControls/ParameterEditorController.h | 30 +++-- src/QmlControls/QGCFileDialogController.cc | 51 ++++---- src/QmlControls/QGCFileDialogController.h | 22 ++-- src/QmlControls/RCChannelMonitorController.cc | 20 ++- src/QmlControls/RCChannelMonitorController.h | 16 ++- src/QmlControls/RCToParamDialogController.cc | 44 ++++--- src/QmlControls/RCToParamDialogController.h | 71 ++++++----- src/QmlControls/ScreenToolsController.cc | 59 ++++++--- src/QmlControls/ScreenToolsController.h | 114 +++++++++--------- 17 files changed, 503 insertions(+), 348 deletions(-) diff --git a/src/Joystick/Joystick.h b/src/Joystick/Joystick.h index 63b39e3817b..2221b18e942 100644 --- a/src/Joystick/Joystick.h +++ b/src/Joystick/Joystick.h @@ -14,6 +14,7 @@ #include "QGCMAVLink.h" #include "CustomActionManager.h" +#include "QmlObjectListModel.h" #include #include diff --git a/src/QmlControls/CustomAction.cc b/src/QmlControls/CustomAction.cc index 849cef3054c..0da331f5da6 100644 --- a/src/QmlControls/CustomAction.cc +++ b/src/QmlControls/CustomAction.cc @@ -9,8 +9,46 @@ #include "CustomAction.h" #include "Vehicle.h" +#include "QGCLoggingCategory.h" -void CustomAction::sendTo(Vehicle* vehicle) { +QGC_LOGGING_CATEGORY(CustomActionLog, "qgc.qmlcontrols.customaction") + +CustomAction::CustomAction(QObject *parent) + : QObject(parent) +{ + // qCDebug(CustomActionLog) << Q_FUNC_INFO << this; +} + +CustomAction::CustomAction( + const QString &label, + const QString &description, + MAV_CMD mavCmd, + MAV_COMPONENT compId, + float param1, + float param2, + float param3, + float param4, + float param5, + float param6, + float param7, + QObject *parent +) : QObject(parent) + , _label(label) + , _description(description) + , _mavCmd(mavCmd) + , _compId(compId) + , _params{ param1, param2, param3, param4, param5, param6, param7 } +{ + // qCDebug(CustomActionLog) << Q_FUNC_INFO << this; +}; + +CustomAction::~CustomAction() +{ + // qCDebug(CustomActionLog) << Q_FUNC_INFO << this; +} + +void CustomAction::sendTo(Vehicle *vehicle) +{ if (vehicle) { const bool showError = true; vehicle->sendMavCommand(_compId, _mavCmd, showError, _params[0], _params[1], _params[2], _params[3], _params[4], _params[5], _params[6]); diff --git a/src/QmlControls/CustomAction.h b/src/QmlControls/CustomAction.h index 6ccb1c370bc..9166c41b5a8 100644 --- a/src/QmlControls/CustomAction.h +++ b/src/QmlControls/CustomAction.h @@ -11,49 +11,49 @@ #include "MAVLinkLib.h" +#include #include +#include + +Q_DECLARE_LOGGING_CATEGORY(CustomActionLog) class Vehicle; class CustomAction: public QObject { Q_OBJECT - - Q_PROPERTY(QString label READ label CONSTANT) - Q_PROPERTY(QString description READ description CONSTANT) + QML_ELEMENT + Q_MOC_INCLUDE("Vehicle.h") + Q_PROPERTY(QString label READ label CONSTANT) + Q_PROPERTY(QString description READ description CONSTANT) public: - CustomAction() { CustomAction(QString(), QString(), MAV_CMD(0), MAV_COMPONENT(0), 0, 0, 0, 0, 0, 0, 0); } // this is required for QML reflection + explicit CustomAction(QObject *parent = nullptr); CustomAction( - QString label, - QString description, - MAV_CMD mavCmd, - MAV_COMPONENT compId, - float param1, - float param2, - float param3, - float param4, - float param5, - float param6, - float param7, - QObject* parent = nullptr) - : QObject (parent) - , _label (label) - , _description (description) - , _mavCmd (mavCmd) - , _compId (compId) - , _params { param1, param2, param3, param4, param5, param6, param7 } - {}; - - Q_INVOKABLE void sendTo(Vehicle* vehicle); - - QString label () const { return _label; } - QString description() const { return _description; } + const QString &label, + const QString &description, + MAV_CMD mavCmd, + MAV_COMPONENT compId, + float param1, + float param2, + float param3, + float param4, + float param5, + float param6, + float param7, + QObject *parent = nullptr + ); + ~CustomAction(); + + Q_INVOKABLE void sendTo(Vehicle *vehicle); + + const QString &label() const { return _label; } + const QString &description() const { return _description; } private: - QString _label; - QString _description; - MAV_CMD _mavCmd; - MAV_COMPONENT _compId; - float _params[7]; + const QString _label; + const QString _description; + const MAV_CMD _mavCmd = MAV_CMD_ENUM_END; + const MAV_COMPONENT _compId = MAV_COMPONENT_ENUM_END; + const float _params[7]{}; }; diff --git a/src/QmlControls/CustomActionManager.cc b/src/QmlControls/CustomActionManager.cc index e4e7ca1911c..654afe6da80 100644 --- a/src/QmlControls/CustomActionManager.cc +++ b/src/QmlControls/CustomActionManager.cc @@ -7,70 +7,82 @@ * ****************************************************************************/ -#include -#include -#include - #include "CustomActionManager.h" #include "CustomAction.h" +#include "Fact.h" #include "JsonHelper.h" #include "QGCApplication.h" #include "SettingsManager.h" +#include "QGCLoggingCategory.h" +#include "QmlObjectListModel.h" + +#include +#include +#include -CustomActionManager::CustomActionManager(QObject* parent) +QGC_LOGGING_CATEGORY(CustomActionManagerLog, "qgc.qmlcontrols.customactionmanager") + +CustomActionManager::CustomActionManager(QObject *parent) : QObject(parent) + , _actions(new QmlObjectListModel(this)) { - + // qCDebug(CustomActionManagerLog) << Q_FUNC_INFO << this; } -CustomActionManager::CustomActionManager(Fact* actionFileNameFact, QObject* parent) +CustomActionManager::CustomActionManager(Fact *actionFileNameFact, QObject *parent) : QObject(parent) + , _actions(new QmlObjectListModel(this)) { setActionFileNameFact(actionFileNameFact); } -void CustomActionManager::setActionFileNameFact(Fact* actionFileNameFact) +CustomActionManager::~CustomActionManager() +{ + // qCDebug(CustomActionManagerLog) << Q_FUNC_INFO << this; +} + +void CustomActionManager::setActionFileNameFact(Fact *actionFileNameFact) { _actionFileNameFact = actionFileNameFact; emit actionFileNameFactChanged(); - connect(_actionFileNameFact, &Fact::rawValueChanged, this, &CustomActionManager::_loadActionsFile); + (void) connect(_actionFileNameFact, &Fact::rawValueChanged, this, &CustomActionManager::_loadActionsFile); _loadActionsFile(); } -void CustomActionManager::_loadActionsFile() +void CustomActionManager::_loadActionsFile() { - _actions.clearAndDeleteContents(); - QString actionFileName = _actionFileNameFact->rawValue().toString(); + _actions->clearAndDeleteContents(); + const QString actionFileName = _actionFileNameFact->rawValue().toString(); if (actionFileName.isEmpty()) { return; } // Custom actions are always loaded from the custom actions save path - QString savePath = qgcApp()->toolbox()->settingsManager()->appSettings()->customActionsSavePath(); - QDir saveDir(savePath); - QString fullPath = saveDir.absoluteFilePath(actionFileName); + const QString savePath = qgcApp()->toolbox()->settingsManager()->appSettings()->customActionsSavePath(); + const QDir saveDir = QDir(savePath); + const QString fullPath = saveDir.absoluteFilePath(actionFileName); // It's ok for the file to not exist - QFileInfo fileInfo(fullPath); + const QFileInfo fileInfo = QFileInfo(fullPath); if (!fileInfo.exists()) { return; } - const char* kQgcFileType = "CustomActions"; - const char* kActionListKey = "actions"; + constexpr const char *kQgcFileType = "CustomActions"; + constexpr const char *kActionListKey = "actions"; - _actions.clearAndDeleteContents(); + _actions->clearAndDeleteContents(); QString errorString; int version; - QJsonObject jsonObject = JsonHelper::openInternalQGCJsonFile(fullPath, kQgcFileType, 1, 1, version, errorString); + const QJsonObject jsonObject = JsonHelper::openInternalQGCJsonFile(fullPath, kQgcFileType, 1, 1, version, errorString); if (!errorString.isEmpty()) { - qgcApp()->showAppMessage(tr("Failed to load custom actions file: `%1` error: `%2`").arg(fullPath).arg(errorString)); + qgcApp()->showAppMessage(tr("Failed to load custom actions file: `%1` error: `%2`").arg(fullPath, errorString)); return; } - QList keyInfoList = { + const QList keyInfoList = { { kActionListKey, QJsonValue::Array, /* required= */ true }, }; if (!JsonHelper::validateKeys(jsonObject, keyInfoList, errorString)) { @@ -78,17 +90,15 @@ void CustomActionManager::_loadActionsFile() return; } - QJsonArray actionList = jsonObject[kActionListKey].toArray(); - for (auto actionJson: actionList) { + const QJsonArray actionList = jsonObject[kActionListKey].toArray(); + for (const auto &actionJson: actionList) { if (!actionJson.isObject()) { qgcApp()->showAppMessage(tr("Custom actions file - incorrect format: JsonValue not an object")); - _actions.clearAndDeleteContents(); + _actions->clearAndDeleteContents(); return; } - auto actionObj = actionJson.toObject(); - - QList actionKeyInfoList = { + const QList actionKeyInfoList = { { "label", QJsonValue::String, /* required= */ true }, { "description", QJsonValue::String, /* required= */ true }, { "mavCmd", QJsonValue::Double, /* required= */ true }, @@ -102,26 +112,28 @@ void CustomActionManager::_loadActionsFile() { "param6", QJsonValue::Double, /* required= */ false }, { "param7", QJsonValue::Double, /* required= */ false }, }; + + const auto actionObj = actionJson.toObject(); if (!JsonHelper::validateKeys(actionObj, actionKeyInfoList, errorString)) { qgcApp()->showAppMessage(tr("Custom actions file - incorrect format: %1").arg(errorString)); - _actions.clearAndDeleteContents(); + _actions->clearAndDeleteContents(); return; } - auto label = actionObj["label"].toString(); - auto description = actionObj["description"].toString(); - auto mavCmd = (MAV_CMD)actionObj["mavCmd"].toInt(); - auto compId = (MAV_COMPONENT)actionObj["compId"].toInt(MAV_COMP_ID_AUTOPILOT1); - auto param1 = actionObj["param1"].toDouble(0.0); - auto param2 = actionObj["param2"].toDouble(0.0); - auto param3 = actionObj["param3"].toDouble(0.0); - auto param4 = actionObj["param4"].toDouble(0.0); - auto param5 = actionObj["param5"].toDouble(0.0); - auto param6 = actionObj["param6"].toDouble(0.0); - auto param7 = actionObj["param7"].toDouble(0.0); - - CustomAction* action = new CustomAction(label, description, mavCmd, compId, param1, param2, param3, param4, param5, param6, param7, this); + const auto label = actionObj["label"].toString(); + const auto description = actionObj["description"].toString(); + const auto mavCmd = (MAV_CMD)actionObj["mavCmd"].toInt(); + const auto compId = (MAV_COMPONENT)actionObj["compId"].toInt(MAV_COMP_ID_AUTOPILOT1); + const auto param1 = actionObj["param1"].toDouble(0.0); + const auto param2 = actionObj["param2"].toDouble(0.0); + const auto param3 = actionObj["param3"].toDouble(0.0); + const auto param4 = actionObj["param4"].toDouble(0.0); + const auto param5 = actionObj["param5"].toDouble(0.0); + const auto param6 = actionObj["param6"].toDouble(0.0); + const auto param7 = actionObj["param7"].toDouble(0.0); + + CustomAction *const action = new CustomAction(label, description, mavCmd, compId, param1, param2, param3, param4, param5, param6, param7, this); QQmlEngine::setObjectOwnership(action, QQmlEngine::CppOwnership); - _actions.append(action); + (void) _actions->append(action); } } diff --git a/src/QmlControls/CustomActionManager.h b/src/QmlControls/CustomActionManager.h index 3d4d736bfa6..26e2ebbfb86 100644 --- a/src/QmlControls/CustomActionManager.h +++ b/src/QmlControls/CustomActionManager.h @@ -9,10 +9,14 @@ #pragma once -#include "Fact.h" -#include "QmlObjectListModel.h" - +#include #include +#include + +Q_DECLARE_LOGGING_CATEGORY(CustomActionManagerLog) + +class Fact; +class QmlObjectListModel; /// Loads the specified action file and provides access to the actions it contains. /// Action files are loaded from the default CustomActions directory. @@ -20,25 +24,28 @@ class CustomActionManager : public QObject { Q_OBJECT - - Q_PROPERTY(Fact* actionFileNameFact READ actionFileNameFact WRITE setActionFileNameFact NOTIFY actionFileNameFactChanged) - Q_PROPERTY(QmlObjectListModel* actions READ actions CONSTANT) + QML_ELEMENT + Q_MOC_INCLUDE("Fact.h") + Q_MOC_INCLUDE("QmlObjectListModel.h") + Q_PROPERTY(Fact* actionFileNameFact READ actionFileNameFact WRITE setActionFileNameFact NOTIFY actionFileNameFactChanged) + Q_PROPERTY(QmlObjectListModel* actions READ actions CONSTANT) public: - CustomActionManager(QObject* parent = nullptr); - CustomActionManager(Fact* actionFileNameFact, QObject* parent = nullptr); + explicit CustomActionManager(QObject *parent = nullptr); + explicit CustomActionManager(Fact *actionFileNameFact, QObject *parent = nullptr); + ~CustomActionManager(); - Fact* actionFileNameFact (void) { return _actionFileNameFact; } - void setActionFileNameFact (Fact* actionFileNameFact); - QmlObjectListModel* actions (void) { return &_actions; } + Fact *actionFileNameFact() { return _actionFileNameFact; } + void setActionFileNameFact(Fact *actionFileNameFact); + QmlObjectListModel *actions() { return _actions; } signals: void actionFileNameFactChanged(); private slots: - void _loadActionsFile(void); + void _loadActionsFile(); private: - Fact* _actionFileNameFact; - QmlObjectListModel _actions; + Fact *_actionFileNameFact = nullptr; + QmlObjectListModel *_actions = nullptr; }; diff --git a/src/QmlControls/EditPositionDialogController.cc b/src/QmlControls/EditPositionDialogController.cc index fbdaa041973..245581bd378 100644 --- a/src/QmlControls/EditPositionDialogController.cc +++ b/src/QmlControls/EditPositionDialogController.cc @@ -13,29 +13,40 @@ #include "QGCToolbox.h" #include "MultiVehicleManager.h" #include "Vehicle.h" +#include "QGCLoggingCategory.h" + +QGC_LOGGING_CATEGORY(EditPositionDialogControllerLog, "qgc.qmlcontrols.editpositiondialogcontroller") QMap EditPositionDialogController::_metaDataMap; -EditPositionDialogController::EditPositionDialogController(void) - : _latitudeFact (0, _latitudeFactName, FactMetaData::valueTypeDouble) - , _longitudeFact (0, _longitudeFactName, FactMetaData::valueTypeDouble) - , _zoneFact (0, _zoneFactName, FactMetaData::valueTypeUint8) - , _hemisphereFact (0, _hemisphereFactName, FactMetaData::valueTypeUint8) - , _eastingFact (0, _eastingFactName, FactMetaData::valueTypeDouble) - , _northingFact (0, _northingFactName, FactMetaData::valueTypeDouble) - , _mgrsFact (0, _mgrsFactName, FactMetaData::valueTypeString) +EditPositionDialogController::EditPositionDialogController(QObject *parent) + : QObject(parent) + , _latitudeFact(new Fact(0, _latitudeFactName, FactMetaData::valueTypeDouble, this)) + , _longitudeFact(new Fact(0, _longitudeFactName, FactMetaData::valueTypeDouble, this)) + , _zoneFact(new Fact(0, _zoneFactName, FactMetaData::valueTypeUint8, this)) + , _hemisphereFact(new Fact(0, _hemisphereFactName, FactMetaData::valueTypeUint8, this)) + , _eastingFact(new Fact(0, _eastingFactName, FactMetaData::valueTypeDouble, this)) + , _northingFact(new Fact(0, _northingFactName, FactMetaData::valueTypeDouble, this)) + , _mgrsFact(new Fact(0, _mgrsFactName, FactMetaData::valueTypeString, this)) { + // qCDebug(EditPositionDialogControllerLog) << Q_FUNC_INFO << this; + if (_metaDataMap.isEmpty()) { _metaDataMap = FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/EditPositionDialog.FactMetaData.json"), nullptr /* QObject parent */); } - _latitudeFact.setMetaData (_metaDataMap[_latitudeFactName]); - _longitudeFact.setMetaData (_metaDataMap[_longitudeFactName]); - _zoneFact.setMetaData (_metaDataMap[_zoneFactName]); - _hemisphereFact.setMetaData (_metaDataMap[_hemisphereFactName]); - _eastingFact.setMetaData (_metaDataMap[_eastingFactName]); - _northingFact.setMetaData (_metaDataMap[_northingFactName]); - _mgrsFact.setMetaData (_metaDataMap[_mgrsFactName]); + _latitudeFact->setMetaData(_metaDataMap[_latitudeFactName]); + _longitudeFact->setMetaData(_metaDataMap[_longitudeFactName]); + _zoneFact->setMetaData(_metaDataMap[_zoneFactName]); + _hemisphereFact->setMetaData(_metaDataMap[_hemisphereFactName]); + _eastingFact->setMetaData(_metaDataMap[_eastingFactName]); + _northingFact->setMetaData(_metaDataMap[_northingFactName]); + _mgrsFact->setMetaData(_metaDataMap[_mgrsFactName]); +} + +EditPositionDialogController::~EditPositionDialogController() +{ + // qCDebug(EditPositionDialogControllerLog) << Q_FUNC_INFO << this; } void EditPositionDialogController::setCoordinate(QGeoCoordinate coordinate) @@ -46,55 +57,55 @@ void EditPositionDialogController::setCoordinate(QGeoCoordinate coordinate) } } -void EditPositionDialogController::initValues(void) +void EditPositionDialogController::initValues() { - _latitudeFact.setRawValue(_coordinate.latitude()); - _longitudeFact.setRawValue(_coordinate.longitude()); + _latitudeFact->setRawValue(_coordinate.latitude()); + _longitudeFact->setRawValue(_coordinate.longitude()); double easting, northing; - int zone = QGCGeo::convertGeoToUTM(_coordinate, easting, northing); - if (zone >= 1 && zone <= 60) { - _zoneFact.setRawValue(zone); - _hemisphereFact.setRawValue(_coordinate.latitude() < 0); - _eastingFact.setRawValue(easting); - _northingFact.setRawValue(northing); + const int zone = QGCGeo::convertGeoToUTM(_coordinate, easting, northing); + if ((zone >= 1) && (zone <= 60)) { + _zoneFact->setRawValue(zone); + _hemisphereFact->setRawValue(_coordinate.latitude() < 0); + _eastingFact->setRawValue(easting); + _northingFact->setRawValue(northing); } - QString mgrs = QGCGeo::convertGeoToMGRS(_coordinate); + + const QString mgrs = QGCGeo::convertGeoToMGRS(_coordinate); if (!mgrs.isEmpty()) { - _mgrsFact.setRawValue(mgrs); + _mgrsFact->setRawValue(mgrs); } } -void EditPositionDialogController::setFromGeo(void) +void EditPositionDialogController::setFromGeo() { - _coordinate.setLatitude(_latitudeFact.rawValue().toDouble()); - _coordinate.setLongitude(_longitudeFact.rawValue().toDouble()); + _coordinate.setLatitude(_latitudeFact->rawValue().toDouble()); + _coordinate.setLongitude(_longitudeFact->rawValue().toDouble()); emit coordinateChanged(_coordinate); } -void EditPositionDialogController::setFromUTM(void) +void EditPositionDialogController::setFromUTM() { - qDebug() << _eastingFact.rawValue().toDouble() << _northingFact.rawValue().toDouble() << _zoneFact.rawValue().toInt() << (_hemisphereFact.rawValue().toInt() == 1); - if (QGCGeo::convertUTMToGeo(_eastingFact.rawValue().toDouble(), _northingFact.rawValue().toDouble(), _zoneFact.rawValue().toInt(), _hemisphereFact.rawValue().toInt() == 1, _coordinate)) { - qDebug() << _eastingFact.rawValue().toDouble() << _northingFact.rawValue().toDouble() << _zoneFact.rawValue().toInt() << (_hemisphereFact.rawValue().toInt() == 1) << _coordinate; + qCDebug(EditPositionDialogControllerLog) << _eastingFact->rawValue().toDouble() << _northingFact->rawValue().toDouble() << _zoneFact->rawValue().toInt() << (_hemisphereFact->rawValue().toInt() == 1); + if (QGCGeo::convertUTMToGeo(_eastingFact->rawValue().toDouble(), _northingFact->rawValue().toDouble(), _zoneFact->rawValue().toInt(), _hemisphereFact->rawValue().toInt() == 1, _coordinate)) { + qCDebug(EditPositionDialogControllerLog) << _eastingFact->rawValue().toDouble() << _northingFact->rawValue().toDouble() << _zoneFact->rawValue().toInt() << (_hemisphereFact->rawValue().toInt() == 1) << _coordinate; emit coordinateChanged(_coordinate); } else { initValues(); } } -void EditPositionDialogController::setFromMGRS(void) +void EditPositionDialogController::setFromMGRS() { - if (QGCGeo::convertMGRSToGeo(_mgrsFact.rawValue().toString(), _coordinate)) { + if (QGCGeo::convertMGRSToGeo(_mgrsFact->rawValue().toString(), _coordinate)) { emit coordinateChanged(_coordinate); } else { initValues(); } } -void EditPositionDialogController::setFromVehicle(void) +void EditPositionDialogController::setFromVehicle() { - _coordinate = qgcApp()->toolbox()->multiVehicleManager()->activeVehicle()->coordinate(); - emit coordinateChanged(_coordinate); + setCoordinate(qgcApp()->toolbox()->multiVehicleManager()->activeVehicle()->coordinate()); } diff --git a/src/QmlControls/EditPositionDialogController.h b/src/QmlControls/EditPositionDialogController.h index 2b41856b5f3..21efc159eb6 100644 --- a/src/QmlControls/EditPositionDialogController.h +++ b/src/QmlControls/EditPositionDialogController.h @@ -12,62 +12,67 @@ #include "Fact.h" #include +#include #include +#include + +Q_DECLARE_LOGGING_CATEGORY(EditPositionDialogControllerLog) class EditPositionDialogController : public QObject { Q_OBJECT - -public: - EditPositionDialogController(void); - + QML_ELEMENT Q_PROPERTY(QGeoCoordinate coordinate READ coordinate WRITE setCoordinate NOTIFY coordinateChanged) - Q_PROPERTY(Fact* latitude READ latitude CONSTANT) - Q_PROPERTY(Fact* longitude READ longitude CONSTANT) - Q_PROPERTY(Fact* zone READ zone CONSTANT) - Q_PROPERTY(Fact* hemisphere READ hemisphere CONSTANT) - Q_PROPERTY(Fact* easting READ easting CONSTANT) - Q_PROPERTY(Fact* northing READ northing CONSTANT) - Q_PROPERTY(Fact* mgrs READ mgrs CONSTANT) + Q_PROPERTY(Fact *latitude READ latitude CONSTANT) + Q_PROPERTY(Fact *longitude READ longitude CONSTANT) + Q_PROPERTY(Fact *zone READ zone CONSTANT) + Q_PROPERTY(Fact *hemisphere READ hemisphere CONSTANT) + Q_PROPERTY(Fact *easting READ easting CONSTANT) + Q_PROPERTY(Fact *northing READ northing CONSTANT) + Q_PROPERTY(Fact *mgrs READ mgrs CONSTANT) + +public: + explicit EditPositionDialogController(QObject *parent = nullptr); + ~EditPositionDialogController(); - QGeoCoordinate coordinate(void) const { return _coordinate; } - Fact* latitude (void) { return &_latitudeFact; } - Fact* longitude (void) { return &_longitudeFact; } - Fact* zone (void) { return &_zoneFact; } - Fact* hemisphere(void) { return &_hemisphereFact; } - Fact* easting (void) { return &_eastingFact; } - Fact* northing (void) { return &_northingFact; } - Fact* mgrs (void) { return &_mgrsFact; } + Q_INVOKABLE void initValues(); + Q_INVOKABLE void setFromGeo(); + Q_INVOKABLE void setFromUTM(); + Q_INVOKABLE void setFromMGRS(); + Q_INVOKABLE void setFromVehicle(); void setCoordinate(QGeoCoordinate coordinate); + QGeoCoordinate coordinate() const { return _coordinate; } - Q_INVOKABLE void initValues(void); - Q_INVOKABLE void setFromGeo(void); - Q_INVOKABLE void setFromUTM(void); - Q_INVOKABLE void setFromMGRS(void); - Q_INVOKABLE void setFromVehicle(void); + Fact *latitude() { return _latitudeFact; } + Fact *longitude() { return _longitudeFact; } + Fact *zone() { return _zoneFact; } + Fact *hemisphere() { return _hemisphereFact; } + Fact *easting() { return _eastingFact; } + Fact *northing() { return _northingFact; } + Fact *mgrs() { return _mgrsFact; } signals: void coordinateChanged(QGeoCoordinate coordinate); private: - static QMap _metaDataMap; - QGeoCoordinate _coordinate; - Fact _latitudeFact; - Fact _longitudeFact; - Fact _zoneFact; - Fact _hemisphereFact; - Fact _eastingFact; - Fact _northingFact; - Fact _mgrsFact; + Fact *_latitudeFact = nullptr; + Fact *_longitudeFact = nullptr; + Fact *_zoneFact = nullptr; + Fact *_hemisphereFact = nullptr; + Fact *_eastingFact = nullptr; + Fact *_northingFact = nullptr; + Fact *_mgrsFact = nullptr; + + static QMap _metaDataMap; - static constexpr const char* _latitudeFactName = "Latitude"; - static constexpr const char* _longitudeFactName = "Longitude"; - static constexpr const char* _zoneFactName = "Zone"; - static constexpr const char* _hemisphereFactName = "Hemisphere"; - static constexpr const char* _eastingFactName = "Easting"; - static constexpr const char* _northingFactName = "Northing"; - static constexpr const char* _mgrsFactName = "MGRS"; + static constexpr const char *_latitudeFactName = "Latitude"; + static constexpr const char *_longitudeFactName = "Longitude"; + static constexpr const char *_zoneFactName = "Zone"; + static constexpr const char *_hemisphereFactName = "Hemisphere"; + static constexpr const char *_eastingFactName = "Easting"; + static constexpr const char *_northingFactName = "Northing"; + static constexpr const char *_mgrsFactName = "MGRS"; }; diff --git a/src/QmlControls/ParameterEditorController.cc b/src/QmlControls/ParameterEditorController.cc index a2863f40288..12347b1bddb 100644 --- a/src/QmlControls/ParameterEditorController.cc +++ b/src/QmlControls/ParameterEditorController.cc @@ -12,10 +12,16 @@ #include "ParameterManager.h" #include "AppSettings.h" #include "Vehicle.h" +#include "QGCLoggingCategory.h" -ParameterEditorController::ParameterEditorController(void) - : _parameterMgr(_vehicle->parameterManager()) +QGC_LOGGING_CATEGORY(ParameterEditorControllerLog, "qgc.qmlcontrols.parametereditorcontroller") + +ParameterEditorController::ParameterEditorController(QObject *parent) + : FactPanelController(parent) + , _parameterMgr(_vehicle->parameterManager()) { + // qCDebug(ParameterEditorControllerLog) << Q_FUNC_INFO << this; + _buildLists(); connect(this, &ParameterEditorController::currentCategoryChanged, this, &ParameterEditorController::_currentCategoryChanged); @@ -31,7 +37,7 @@ ParameterEditorController::ParameterEditorController(void) ParameterEditorController::~ParameterEditorController() { - + // qCDebug(ParameterEditorControllerLog) << Q_FUNC_INFO << this; } void ParameterEditorController::_buildListsForComponent(int compId) diff --git a/src/QmlControls/ParameterEditorController.h b/src/QmlControls/ParameterEditorController.h index fd860b227fe..9895525b747 100644 --- a/src/QmlControls/ParameterEditorController.h +++ b/src/QmlControls/ParameterEditorController.h @@ -13,7 +13,11 @@ #include "QmlObjectListModel.h" #include "FactMetaData.h" +#include #include +#include + +Q_DECLARE_LOGGING_CATEGORY(ParameterEditorControllerLog) class ParameterManager; @@ -83,23 +87,23 @@ class ParameterEditorDiff : public QObject class ParameterEditorController : public FactPanelController { Q_OBJECT + QML_ELEMENT + Q_PROPERTY(QString searchText MEMBER _searchText NOTIFY searchTextChanged) + Q_PROPERTY(QmlObjectListModel* categories READ categories CONSTANT) + Q_PROPERTY(QObject* currentCategory READ currentCategory WRITE setCurrentCategory NOTIFY currentCategoryChanged) + Q_PROPERTY(QObject* currentGroup READ currentGroup WRITE setCurrentGroup NOTIFY currentGroupChanged) + Q_PROPERTY(QmlObjectListModel* parameters MEMBER _parameters NOTIFY parametersChanged) + Q_PROPERTY(bool showModifiedOnly MEMBER _showModifiedOnly NOTIFY showModifiedOnlyChanged) + + // These property are related to the diff associated with a load from file + Q_PROPERTY(bool diffOtherVehicle MEMBER _diffOtherVehicle NOTIFY diffOtherVehicleChanged) + Q_PROPERTY(bool diffMultipleComponents MEMBER _diffMultipleComponents NOTIFY diffMultipleComponentsChanged) + Q_PROPERTY(QmlObjectListModel* diffList READ diffList CONSTANT) public: - ParameterEditorController(void); + explicit ParameterEditorController(QObject *parent = nullptr); ~ParameterEditorController(); - Q_PROPERTY(QString searchText MEMBER _searchText NOTIFY searchTextChanged) - Q_PROPERTY(QmlObjectListModel* categories READ categories CONSTANT) - Q_PROPERTY(QObject* currentCategory READ currentCategory WRITE setCurrentCategory NOTIFY currentCategoryChanged) - Q_PROPERTY(QObject* currentGroup READ currentGroup WRITE setCurrentGroup NOTIFY currentGroupChanged) - Q_PROPERTY(QmlObjectListModel* parameters MEMBER _parameters NOTIFY parametersChanged) - Q_PROPERTY(bool showModifiedOnly MEMBER _showModifiedOnly NOTIFY showModifiedOnlyChanged) - - // These property are related to the diff associated with a load from file - Q_PROPERTY(bool diffOtherVehicle MEMBER _diffOtherVehicle NOTIFY diffOtherVehicleChanged) - Q_PROPERTY(bool diffMultipleComponents MEMBER _diffMultipleComponents NOTIFY diffMultipleComponentsChanged) - Q_PROPERTY(QmlObjectListModel* diffList READ diffList CONSTANT) - Q_INVOKABLE QStringList searchParameters(const QString& searchText, bool searchInName=true, bool searchInDescriptions=true); Q_INVOKABLE void saveToFile (const QString& filename); diff --git a/src/QmlControls/QGCFileDialogController.cc b/src/QmlControls/QGCFileDialogController.cc index c590ff08150..f52393537b6 100644 --- a/src/QmlControls/QGCFileDialogController.cc +++ b/src/QmlControls/QGCFileDialogController.cc @@ -12,20 +12,31 @@ #include "QGCLoggingCategory.h" #include "QGCApplication.h" #include "SettingsManager.h" + #include -QGC_LOGGING_CATEGORY(QGCFileDialogControllerLog, "QGCFileDialogControllerLog") +QGC_LOGGING_CATEGORY(QGCFileDialogControllerLog, "qgc.qmlcontrols.qgcfiledialogcontroller") + +QGCFileDialogController::QGCFileDialogController(QObject *parent) + : QObject(parent) +{ + // qCDebug(QGCFileDialogControllerLog) << Q_FUNC_INFO << this; +} + +QGCFileDialogController::~QGCFileDialogController() +{ + // qCDebug(QGCFileDialogControllerLog) << Q_FUNC_INFO << this; +} -QStringList QGCFileDialogController::getFiles(const QString& directoryPath, const QStringList& nameFilters) +QStringList QGCFileDialogController::getFiles(const QString &directoryPath, const QStringList &nameFilters) { qCDebug(QGCFileDialogControllerLog) << "getFiles" << directoryPath << nameFilters; - QStringList files; QDir fileDir(directoryPath); + const QFileInfoList fileInfoList = fileDir.entryInfoList(nameFilters, QDir::Files, QDir::Name); - QFileInfoList fileInfoList = fileDir.entryInfoList(nameFilters, QDir::Files, QDir::Name); - - for (const QFileInfo& fileInfo: fileInfoList) { + QStringList files; + for (const QFileInfo &fileInfo: fileInfoList) { qCDebug(QGCFileDialogControllerLog) << "getFiles found" << fileInfo.fileName(); files << fileInfo.fileName(); } @@ -33,7 +44,7 @@ QStringList QGCFileDialogController::getFiles(const QString& directoryPath, cons return files; } -bool QGCFileDialogController::fileExists(const QString& filename) +bool QGCFileDialogController::fileExists(const QString &filename) { return QFile(filename).exists(); } @@ -49,7 +60,7 @@ QString QGCFileDialogController::fullyQualifiedFilename(const QString& directory extensionFound = false; for (const QString& nameFilter: nameFilters) { if (nameFilter.startsWith("*.")) { - QString fileExtension = nameFilter.right(nameFilter.length() - 2); + const QString fileExtension = nameFilter.right(nameFilter.length() - 2); if (fileExtension != "*") { if (firstFileExtention.isEmpty()) { firstFileExtention = fileExtension; @@ -71,28 +82,26 @@ QString QGCFileDialogController::fullyQualifiedFilename(const QString& directory filenameWithExtension = QStringLiteral("%1.%2").arg(filename).arg(firstFileExtention); } - return directoryPath + QStringLiteral("/") + filenameWithExtension; + return (directoryPath + QStringLiteral("/") + filenameWithExtension); } -void QGCFileDialogController::deleteFile(const QString& filename) +void QGCFileDialogController::deleteFile(const QString &filename) { QFile::remove(filename); } -QString QGCFileDialogController::fullFolderPathToShortMobilePath(const QString& fullFolderPath) +QString QGCFileDialogController::fullFolderPathToShortMobilePath(const QString &fullFolderPath) { -#ifdef __mobile__ - QString defaultSavePath = qgcApp()->toolbox()->settingsManager()->appSettings()->savePath()->rawValueString(); +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) + const QString defaultSavePath = qgcApp()->toolbox()->settingsManager()->appSettings()->savePath()->rawValueString(); if (fullFolderPath.startsWith(defaultSavePath)) { - int lastDirSepIndex = fullFolderPath.lastIndexOf(QStringLiteral("/")); - return QCoreApplication::applicationName() + QStringLiteral("/") + fullFolderPath.right(fullFolderPath.length() - lastDirSepIndex); - } else { - return fullFolderPath; + const int lastDirSepIndex = fullFolderPath.lastIndexOf(QStringLiteral("/")); + return (QCoreApplication::applicationName() + QStringLiteral("/") + fullFolderPath.right(fullFolderPath.length() - lastDirSepIndex)); } #else - qWarning() << "QGCFileDialogController::fullFolderPathToShortMobilePath should only be used in mobile builds"; - return fullFolderPath; + qCWarning(QGCFileDialogControllerLog) << Q_FUNC_INFO << "should only be used in mobile builds"; #endif + return fullFolderPath; } QString QGCFileDialogController::urlToLocalFile(QUrl url) @@ -101,7 +110,7 @@ QString QGCFileDialogController::urlToLocalFile(QUrl url) // Seems to be new behavior with Qt6. if (url.isLocalFile()) { return url.toLocalFile(); - } else { - return url.toString(); } + + return url.toString(); } diff --git a/src/QmlControls/QGCFileDialogController.h b/src/QmlControls/QGCFileDialogController.h index 36383b64f82..8be185d9d72 100644 --- a/src/QmlControls/QGCFileDialogController.h +++ b/src/QmlControls/QGCFileDialogController.h @@ -10,34 +10,40 @@ #pragma once +#include #include #include -#include +#include Q_DECLARE_LOGGING_CATEGORY(QGCFileDialogControllerLog) class QGCFileDialogController : public QObject { Q_OBJECT + // TODO: Q_NAMESPACE + QML_ELEMENT public: + explicit QGCFileDialogController(QObject *parent = nullptr); + ~QGCFileDialogController(); + /// Return all file in the specified path which match the specified extension - Q_INVOKABLE QStringList getFiles(const QString& directoryPath, const QStringList& nameFilters); + Q_INVOKABLE static QStringList getFiles(const QString &directoryPath, const QStringList &nameFilters); /// Returns the fully qualified file name from the specified parts. /// If filename has no file extension the first file extension is nameFilters is added to the filename. - Q_INVOKABLE QString fullyQualifiedFilename(const QString& directoryPath, const QString& filename, const QStringList& nameFilters = QStringList()); + Q_INVOKABLE static QString fullyQualifiedFilename(const QString &directoryPath, const QString &filename, const QStringList &nameFilters = QStringList()); /// Check for file existence of specified fully qualified file name - Q_INVOKABLE bool fileExists(const QString& filename); - + Q_INVOKABLE static bool fileExists(const QString &filename); + /// Deletes the file specified by the fully qualified file name - Q_INVOKABLE void deleteFile(const QString& filename); + Q_INVOKABLE static void deleteFile(const QString &filename); - Q_INVOKABLE QString urlToLocalFile(QUrl url); + Q_INVOKABLE static QString urlToLocalFile(QUrl url); /// Important: Should only be used in mobile builds where default save location cannot be changed. /// Returns the standard QGC location portion of a fully qualified folder path. /// Example: "/Users/Don/Document/QGroundControl/Missions" returns "QGroundControl/Missions" - Q_INVOKABLE QString fullFolderPathToShortMobilePath(const QString& fullFolderPath); + Q_INVOKABLE static QString fullFolderPathToShortMobilePath(const QString &fullFolderPath); }; diff --git a/src/QmlControls/RCChannelMonitorController.cc b/src/QmlControls/RCChannelMonitorController.cc index df686dcf405..7ed3402f32e 100644 --- a/src/QmlControls/RCChannelMonitorController.cc +++ b/src/QmlControls/RCChannelMonitorController.cc @@ -10,17 +10,27 @@ #include "RCChannelMonitorController.h" #include "Vehicle.h" +#include "QGCLoggingCategory.h" -RCChannelMonitorController::RCChannelMonitorController(void) - : _chanCount(0) +QGC_LOGGING_CATEGORY(RCChannelMonitorControllerLog, "qgc.qmlcontrols.rcchannelmonitorcontroller") + +RCChannelMonitorController::RCChannelMonitorController(QObject *parent) + : FactPanelController(parent) +{ + // qCDebug(RCChannelMonitorControllerLog) << Q_FUNC_INFO << this; + + (void) connect(_vehicle, &Vehicle::rcChannelsChanged, this, &RCChannelMonitorController::_rcChannelsChanged); +} + +RCChannelMonitorController::~RCChannelMonitorController() { - connect(_vehicle, &Vehicle::rcChannelsChanged, this, &RCChannelMonitorController::_rcChannelsChanged); + // qCDebug(RCChannelMonitorControllerLog) << Q_FUNC_INFO << this; } void RCChannelMonitorController::_rcChannelsChanged(int channelCount, int pwmValues[QGCMAVLink::maxRcChannels]) { - for (int channel=0; channel +#include + +Q_DECLARE_LOGGING_CATEGORY(RCChannelMonitorControllerLog) + class RCChannelMonitorController : public FactPanelController { Q_OBJECT + QML_ELEMENT + Q_PROPERTY(int channelCount READ channelCount NOTIFY channelCountChanged) public: - RCChannelMonitorController(void); - - Q_PROPERTY(int channelCount READ channelCount NOTIFY channelCountChanged) + explicit RCChannelMonitorController(QObject *parent = nullptr); + ~RCChannelMonitorController(); - int channelCount(void) const{ return _chanCount; } + int channelCount() const { return _chanCount; } signals: void channelCountChanged(int channelCount); @@ -32,5 +38,5 @@ private slots: void _rcChannelsChanged(int channelCount, int pwmValues[QGCMAVLink::maxRcChannels]); private: - int _chanCount; + int _chanCount = 0; }; diff --git a/src/QmlControls/RCToParamDialogController.cc b/src/QmlControls/RCToParamDialogController.cc index 2028568c733..3b5f16c5d63 100644 --- a/src/QmlControls/RCToParamDialogController.cc +++ b/src/QmlControls/RCToParamDialogController.cc @@ -13,40 +13,52 @@ #include "ParameterManager.h" #include "MultiVehicleManager.h" #include "Vehicle.h" +#include "QGCLoggingCategory.h" +#include "Fact.h" + +QGC_LOGGING_CATEGORY(RCToParamDialogControllerLog, "qgc.qmlcontrols.rctoparamdialogcontroller") QMap RCToParamDialogController::_metaDataMap; -RCToParamDialogController::RCToParamDialogController(void) - : _scaleFact (0, _scaleFactName, FactMetaData::valueTypeDouble) - , _centerFact (0, _centerFactName, FactMetaData::valueTypeDouble) - , _minFact (0, _minFactName, FactMetaData::valueTypeDouble) - , _maxFact (0, _maxFactName, FactMetaData::valueTypeDouble) +RCToParamDialogController::RCToParamDialogController(QObject *parent) + : QObject(parent) + , _scaleFact(new Fact(0, _scaleFactName, FactMetaData::valueTypeDouble, this)) + , _centerFact(new Fact(0, _centerFactName, FactMetaData::valueTypeDouble, this)) + , _minFact(new Fact(0, _minFactName, FactMetaData::valueTypeDouble, this)) + , _maxFact(new Fact(0, _maxFactName, FactMetaData::valueTypeDouble, this)) { + // qCDebug(RCToParamDialogControllerLog) << Q_FUNC_INFO << this; + if (_metaDataMap.isEmpty()) { _metaDataMap = FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/RCToParamDialog.FactMetaData.json"), nullptr /* QObject parent */); } - _scaleFact.setMetaData (_metaDataMap[_scaleFactName], true /* setDefaultFromMetaData */); - _centerFact.setMetaData (_metaDataMap[_centerFactName]); - _minFact.setMetaData (_metaDataMap[_minFactName]); - _maxFact.setMetaData (_metaDataMap[_maxFactName]); + _scaleFact->setMetaData(_metaDataMap[_scaleFactName], true /* setDefaultFromMetaData */); + _centerFact->setMetaData(_metaDataMap[_centerFactName]); + _minFact->setMetaData(_metaDataMap[_minFactName]); + _maxFact->setMetaData(_metaDataMap[_maxFactName]); +} + +RCToParamDialogController::~RCToParamDialogController() +{ + // qCDebug(RCToParamDialogControllerLog) << Q_FUNC_INFO << this;` } -void RCToParamDialogController::setTuningFact(Fact* tuningFact) +void RCToParamDialogController::setTuningFact(Fact *tuningFact) { _tuningFact = tuningFact; emit tuningFactChanged(tuningFact); - _centerFact.setRawValue(_tuningFact->rawValue().toDouble()); - _minFact.setRawValue(_tuningFact->rawMin().toDouble()); - _maxFact.setRawValue(_tuningFact->rawMax().toDouble()); + _centerFact->setRawValue(_tuningFact->rawValue().toDouble()); + _minFact->setRawValue(_tuningFact->rawMin().toDouble()); + _maxFact->setRawValue(_tuningFact->rawMax().toDouble()); - connect(_tuningFact, &Fact::vehicleUpdated, this, &RCToParamDialogController::_parameterUpdated); + (void) connect(_tuningFact, &Fact::vehicleUpdated, this, &RCToParamDialogController::_parameterUpdated); qgcApp()->toolbox()->multiVehicleManager()->activeVehicle()->parameterManager()->refreshParameter(ParameterManager::defaultComponentId, _tuningFact->name()); } -void RCToParamDialogController::_parameterUpdated(void) +void RCToParamDialogController::_parameterUpdated() { _ready = true; - emit readyChanged(true); + emit readyChanged(_ready); } diff --git a/src/QmlControls/RCToParamDialogController.h b/src/QmlControls/RCToParamDialogController.h index 4d9318185f4..9e4ddb1dabb 100644 --- a/src/QmlControls/RCToParamDialogController.h +++ b/src/QmlControls/RCToParamDialogController.h @@ -9,50 +9,57 @@ #pragma once -#include "Fact.h" - +#include #include +#include + +Q_DECLARE_LOGGING_CATEGORY(RCToParamDialogControllerLog) + +class Fact; +class FactMetaData; class RCToParamDialogController : public QObject { Q_OBJECT - + QML_ELEMENT + Q_MOC_INCLUDE("Fact.h") + Q_PROPERTY(Fact *tuningFact READ tuningFact WRITE setTuningFact NOTIFY tuningFactChanged) + Q_PROPERTY(Fact *scale READ scale CONSTANT) + Q_PROPERTY(Fact *center READ center CONSTANT) + Q_PROPERTY(Fact *min READ min CONSTANT) + Q_PROPERTY(Fact *max READ max CONSTANT) + Q_PROPERTY(bool ready MEMBER _ready NOTIFY readyChanged) // true: editing can begin, false: still waiting for param update from vehicle + public: - RCToParamDialogController(void); - - Q_PROPERTY(Fact* tuningFact READ tuningFact WRITE setTuningFact NOTIFY tuningFactChanged) - Q_PROPERTY(bool ready MEMBER _ready NOTIFY readyChanged) // true: editing can begin, false: still waiting for param update from vehicle - Q_PROPERTY(Fact* scale READ scale CONSTANT) - Q_PROPERTY(Fact* center READ center CONSTANT) - Q_PROPERTY(Fact* min READ min CONSTANT) - Q_PROPERTY(Fact* max READ max CONSTANT) - - Fact* tuningFact (void) { return _tuningFact; } - Fact* scale (void) { return &_scaleFact; } - Fact* center (void) { return &_centerFact; } - Fact* min (void) { return &_minFact; } - Fact* max (void) { return &_maxFact; } - void setTuningFact (Fact* tuningFact); + explicit RCToParamDialogController(QObject *parent = nullptr); + ~RCToParamDialogController(); + + Fact *tuningFact() { return _tuningFact; } + Fact *scale() { return _scaleFact; } + Fact *center() { return _centerFact; } + Fact *min() { return _minFact; } + Fact *max() { return _maxFact; } + void setTuningFact(Fact *tuningFact); signals: - void tuningFactChanged (Fact* fact); - void readyChanged (bool ready); + void tuningFactChanged(Fact *fact); + void readyChanged(bool ready); private slots: - void _parameterUpdated(void); + void _parameterUpdated(); private: + Fact *_tuningFact = nullptr; + Fact *_scaleFact = nullptr; + Fact *_centerFact = nullptr; + Fact *_minFact = nullptr; + Fact *_maxFact = nullptr; + bool _ready = false; + static QMap _metaDataMap; - Fact* _tuningFact = nullptr; - bool _ready = false; - Fact _scaleFact; - Fact _centerFact; - Fact _minFact; - Fact _maxFact; - - static constexpr const char* _scaleFactName = "Scale"; - static constexpr const char* _centerFactName = "CenterValue"; - static constexpr const char* _minFactName = "MinValue"; - static constexpr const char* _maxFactName = "MaxValue"; + static constexpr const char *_scaleFactName = "Scale"; + static constexpr const char *_centerFactName = "CenterValue"; + static constexpr const char *_minFactName = "MinValue"; + static constexpr const char *_maxFactName = "MaxValue"; }; diff --git a/src/QmlControls/ScreenToolsController.cc b/src/QmlControls/ScreenToolsController.cc index f33c571854f..ccc25c4c1d5 100644 --- a/src/QmlControls/ScreenToolsController.cc +++ b/src/QmlControls/ScreenToolsController.cc @@ -13,25 +13,44 @@ #include "ScreenToolsController.h" #include "QGCApplication.h" +#include "QGCLoggingCategory.h" +#include "SettingsManager.h" + +#include #include #include #include -#include "SettingsManager.h" - #if defined(Q_OS_IOS) #include #endif -ScreenToolsController::ScreenToolsController() +QGC_LOGGING_CATEGORY(ScreenToolsControllerLog, "qgc.qmlcontrols.screentoolscontroller") + +ScreenToolsController::ScreenToolsController(QObject *parent) + : QObject(parent) +{ + // qCDebug(ScreenToolsControllerLog) << Q_FUNC_INFO << this; +} + +ScreenToolsController::~ScreenToolsController() { + // qCDebug(ScreenToolsControllerLog) << Q_FUNC_INFO << this; +} + +int ScreenToolsController::mouseX() +{ + return QCursor::pos().x(); +} +int ScreenToolsController::mouseY() +{ + return QCursor::pos().y(); } -bool -ScreenToolsController::hasTouch() const +bool ScreenToolsController::hasTouch() { - for (const auto& inputDevice: QInputDevice::devices()) { + for (const auto &inputDevice: QInputDevice::devices()) { if (inputDevice->type() == QInputDevice::DeviceType::TouchScreen) { return true; } @@ -39,8 +58,7 @@ ScreenToolsController::hasTouch() const return false; } -QString -ScreenToolsController::iOSDevice() const +QString ScreenToolsController::iOSDevice() { #if defined(Q_OS_IOS) struct utsname systemInfo; @@ -51,29 +69,30 @@ ScreenToolsController::iOSDevice() const #endif } -QString -ScreenToolsController::fixedFontFamily() const +QString ScreenToolsController::fixedFontFamily() { return QFontDatabase::systemFont(QFontDatabase::FixedFont).family(); } -QString -ScreenToolsController::normalFontFamily() const +QString ScreenToolsController::normalFontFamily() { //-- See App.SettinsGroup.json for index - int langID = qgcApp()->toolbox()->settingsManager()->appSettings()->qLocaleLanguage()->rawValue().toInt(); - if(langID == QLocale::Korean) { - return QString("NanumGothic"); - } else { - return QString("Open Sans"); + const int langID = qgcApp()->toolbox()->settingsManager()->appSettings()->qLocaleLanguage()->rawValue().toInt(); + if (langID == QLocale::Korean) { + return QStringLiteral("NanumGothic"); } + + return QStringLiteral("Open Sans"); } -double ScreenToolsController::defaultFontDescent(int pointSize) const +double ScreenToolsController::defaultFontDescent(int pointSize) { return QFontMetrics(QFont(normalFontFamily(), pointSize)).descent(); } -#ifndef __mobile__ -bool ScreenToolsController::fakeMobile() const { return qgcApp()->fakeMobile(); } +#if !defined(Q_OS_ANDROID) && !defined(Q_OS_IOS) +bool ScreenToolsController::fakeMobile() +{ + return qgcApp()->fakeMobile(); +} #endif diff --git a/src/QmlControls/ScreenToolsController.h b/src/QmlControls/ScreenToolsController.h index 53979bdded6..3b8a5bf742c 100644 --- a/src/QmlControls/ScreenToolsController.h +++ b/src/QmlControls/ScreenToolsController.h @@ -13,20 +13,18 @@ #pragma once -#include -#include +#include +#include +#include -/*! - @brief Screen helper tools for QML widgets -*/ +Q_DECLARE_LOGGING_CATEGORY(ScreenToolsControllerLog) /// This Qml control is used to return screen parameters -class ScreenToolsController : public QQuickItem +class ScreenToolsController : public QObject { Q_OBJECT -public: - ScreenToolsController(); - + QML_ELEMENT + // TODO: Q_NAMESPACE Q_PROPERTY(bool isAndroid READ isAndroid CONSTANT) Q_PROPERTY(bool isiOS READ isiOS CONSTANT) Q_PROPERTY(bool isMobile READ isMobile CONSTANT) @@ -41,73 +39,77 @@ class ScreenToolsController : public QQuickItem Q_PROPERTY(QString fixedFontFamily READ fixedFontFamily CONSTANT) Q_PROPERTY(QString normalFontFamily READ normalFontFamily CONSTANT) - // Returns current mouse position - Q_INVOKABLE int mouseX(void) { return QCursor::pos().x(); } - Q_INVOKABLE int mouseY(void) { return QCursor::pos().y(); } +public: + explicit ScreenToolsController(QObject *parent = nullptr); + ~ScreenToolsController(); + + /// Returns current mouse position + Q_INVOKABLE static int mouseX(); + Q_INVOKABLE static int mouseY(); // QFontMetrics::descent for default font - Q_INVOKABLE double defaultFontDescent(int pointSize) const; + Q_INVOKABLE static double defaultFontDescent(int pointSize); -#if defined(__mobile__) - bool isMobile () const { return true; } - bool fakeMobile () const { return false; } +#if defined(Q_OS_ANDROID) || defined(Q_OS_IOS) + static bool isMobile() { return true; } + static bool fakeMobile() { return false; } #else - bool isMobile () const { return fakeMobile(); } - bool fakeMobile () const; + static bool isMobile() { return fakeMobile(); } + static bool fakeMobile(); #endif #if defined (Q_OS_ANDROID) - bool isAndroid () { return true; } - bool isiOS () { return false; } - bool isLinux () { return false; } - bool isMacOS () { return false; } - bool isWindows () { return false; } + static bool isAndroid() { return true; } + static bool isiOS() { return false; } + static bool isLinux() { return false; } + static bool isMacOS() { return false; } + static bool isWindows() { return false; } #elif defined(Q_OS_IOS) - bool isAndroid () { return false; } - bool isiOS () { return true; } - bool isLinux () { return false; } - bool isMacOS () { return false; } - bool isWindows () { return false; } + static bool isAndroid() { return false; } + static bool isiOS() { return true; } + static bool isLinux() { return false; } + static bool isMacOS() { return false; } + static bool isWindows() { return false; } #elif defined(Q_OS_MAC) - bool isAndroid () { return false; } - bool isiOS () { return false; } - bool isLinux () { return false; } - bool isMacOS () { return true; } - bool isWindows () { return false; } + static bool isAndroid() { return false; } + static bool isiOS() { return false; } + static bool isLinux() { return false; } + static bool isMacOS() { return true; } + static bool isWindows() { return false; } #elif defined(Q_OS_LINUX) - bool isAndroid () { return false; } - bool isiOS () { return false; } - bool isLinux () { return true; } - bool isMacOS () { return false; } - bool isWindows () { return false; } + static bool isAndroid() { return false; } + static bool isiOS() { return false; } + static bool isLinux() { return true; } + static bool isMacOS() { return false; } + static bool isWindows() { return false; } #elif defined(Q_OS_WIN) - bool isAndroid () { return false; } - bool isiOS () { return false; } - bool isLinux () { return false; } - bool isMacOS () { return false; } - bool isWindows () { return true; } + static bool isAndroid() { return false; } + static bool isiOS() { return false; } + static bool isLinux() { return false; } + static bool isMacOS() { return false; } + static bool isWindows() { return true; } #else - bool isAndroid () { return false; } - bool isiOS () { return false; } - bool isLinux () { return false; } - bool isMacOS () { return false; } - bool isWindows () { return false; } + static bool isAndroid() { return false; } + static bool isiOS() { return false; } + static bool isLinux() { return false; } + static bool isMacOS() { return false; } + static bool isWindows() { return false; } #endif #if defined(NO_SERIAL_LINK) - bool isSerialAvailable () { return false; } + static bool isSerialAvailable() { return false; } #else - bool isSerialAvailable () { return true; } + static bool isSerialAvailable() { return true; } #endif #ifdef QT_DEBUG - bool isDebug () { return true; } + static bool isDebug() { return true; } #else - bool isDebug () { return false; } + static bool isDebug() { return false; } #endif - bool hasTouch () const; - QString iOSDevice () const; - QString fixedFontFamily () const; - QString normalFontFamily () const; + static bool hasTouch(); + static QString iOSDevice(); + static QString fixedFontFamily(); + static QString normalFontFamily(); }; From 982da35c96e31ca1f1a98d361c137b226f23e5c6 Mon Sep 17 00:00:00 2001 From: Sam Gillam Date: Thu, 24 Oct 2024 19:20:35 -0600 Subject: [PATCH 21/27] Fix disappearing loiter circles When video streaming was enabled, toggling between mini video and full screen video mode would make the circles representing loiter circles on the flight path disappear. This fixes that issue. --- src/PlanView/SimpleItemMapVisual.qml | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/src/PlanView/SimpleItemMapVisual.qml b/src/PlanView/SimpleItemMapVisual.qml index 25327f279b5..892bf105b8e 100644 --- a/src/PlanView/SimpleItemMapVisual.qml +++ b/src/PlanView/SimpleItemMapVisual.qml @@ -97,14 +97,11 @@ Item { target: _missionItem.isSimpleItem ? _missionItem : null onLoiterRadiusChanged: { - _loiterVisual.blockSignals = true - _loiterVisual.clockwiseRotation = _missionItem.loiterRadius>= 0 - _loiterVisual.blockSignals = false - _loiterVisual.radius.rawValue = Math.abs(_missionItem.loiterRadius) + _loiterVisual.handleLoiterRadiusChange() } onCoordinateChanged: { - _loiterVisual.coordinate = _missionItem.coordinate + _loiterVisual.handleCoordinateChange() } } @@ -147,6 +144,17 @@ Item { property alias radius: _mapCircle.radius property alias clockwiseRotation: _mapCircle.clockwiseRotation + function handleLoiterRadiusChange() { + blockSignals = true + clockwiseRotation = _missionItem.loiterRadius>= 0 + blockSignals = false + radius.rawValue = Math.abs(_missionItem.loiterRadius) + } + + function handleCoordinateChange() { + coordinate = _missionItem.coordinate + } + onCoordinateChanged: _mapCircle.center = coordinate sourceItem: QGCMapCircleVisuals { @@ -177,6 +185,11 @@ Item { } } } + + Component.onCompleted: { + handleLoiterRadiusChange() + handleCoordinateChange() + } } } } From f47027ced781258a2033b9dee77b1833556e9cfd Mon Sep 17 00:00:00 2001 From: Holden Date: Tue, 29 Oct 2024 11:24:01 -0400 Subject: [PATCH 22/27] CMake: Fix Finding SDL2 Version --- cmake/find-modules/FindSDL2.cmake | 360 ++++++++++-------------------- src/Joystick/CMakeLists.txt | 2 +- 2 files changed, 115 insertions(+), 247 deletions(-) diff --git a/cmake/find-modules/FindSDL2.cmake b/cmake/find-modules/FindSDL2.cmake index 7dfe9482a7a..c09d62b8942 100644 --- a/cmake/find-modules/FindSDL2.cmake +++ b/cmake/find-modules/FindSDL2.cmake @@ -1,263 +1,131 @@ -# - Find SDL2 -# Find the SDL2 headers and libraries -# -# SDL2::SDL2 - Imported target to use for building a library -# SDL2::SDL2main - Imported interface target to use if you want SDL and SDLmain. -# SDL2_FOUND - True if SDL2 was found. -# SDL2_DYNAMIC - If we found a DLL version of SDL (meaning you might want to copy a DLL from SDL2::SDL2) -# -# Original Author: -# 2015 Ryan Pavlik -# -# Copyright Sensics, Inc. 2015. -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or copy at -# http://www.boost.org/LICENSE_1_0.txt) - -# Set up architectures (for windows) and prefixes (for mingw builds) -if(WIN32) - if(MINGW) - include(MinGWSearchPathExtras OPTIONAL) - if(MINGWSEARCH_TARGET_TRIPLE) - set(SDL2_PREFIX ${MINGWSEARCH_TARGET_TRIPLE}) - endif() - endif() - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - set(SDL2_LIB_PATH_SUFFIX lib/x64) - if(NOT MSVC AND NOT SDL2_PREFIX) - set(SDL2_PREFIX x86_64-w64-mingw32) - endif() - else() - set(SDL2_LIB_PATH_SUFFIX lib/x86) - if(NOT MSVC AND NOT SDL2_PREFIX) - set(SDL2_PREFIX i686-w64-mingw32) +# Licensed under the ISC license. Please see the LICENSE.md file for details. +# Copyright (c) 2019 Sandro Stikić + +#[[============================================================================ +FindSDL2 +--------- + +Try to find SDL2. + +This module defines the following IMPORTED targets: +- SDL2::Core — Libraries should link against this. +- SDL2::SDL2 — Applications should link against this instead of SDL2::Core. + +This module defines the following variables: +- SDL2_FOUND +- SDL2main_FOUND +- SDL2_VERSION_STRING +- SDL2_LIBRARIES (deprecated) +- SDL2_INCLUDE_DIRS (deprecated) +#============================================================================]] + +# Look for SDL2. +find_path(SDL2_INCLUDE_DIR SDL.h + PATH_SUFFIXES SDL2 include/SDL2 include + PATHS ${SDL2_PATH} +) + +if (NOT SDL2_LIBRARIES) + # Determine architecture. + if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(_SDL2_PATH_SUFFIX lib/x64) + else() + set(_SDL2_PATH_SUFFIX lib/x86) endif() - endif() -endif() - -if(SDL2_PREFIX) - set(SDL2_ORIGPREFIXPATH ${CMAKE_PREFIX_PATH}) - if(SDL2_ROOT_DIR) - list(APPEND CMAKE_PREFIX_PATH "${SDL2_ROOT_DIR}") - endif() - if(CMAKE_PREFIX_PATH) - foreach(_prefix ${CMAKE_PREFIX_PATH}) - list(APPEND CMAKE_PREFIX_PATH "${_prefix}/${SDL2_PREFIX}") - endforeach() - endif() - if(MINGWSEARCH_PREFIXES) - list(APPEND CMAKE_PREFIX_PATH ${MINGWSEARCH_PREFIXES}) - endif() -endif() - -# Invoke pkgconfig for hints -find_package(PkgConfig QUIET) -set(SDL2_INCLUDE_HINTS) -set(SDL2_LIB_HINTS) -if(PKG_CONFIG_FOUND) - pkg_search_module(SDL2PC QUIET sdl2) - if(SDL2PC_INCLUDE_DIRS) - set(SDL2_INCLUDE_HINTS ${SDL2PC_INCLUDE_DIRS}) - endif() - if(SDL2PC_LIBRARY_DIRS) - set(SDL2_LIB_HINTS ${SDL2PC_LIBRARY_DIRS}) - endif() -endif() - -include(FindPackageHandleStandardArgs) -find_library(SDL2_LIBRARY - NAMES - SDL2 - HINTS - ${SDL2_LIB_HINTS} - PATHS - ${SDL2_ROOT_DIR} - ENV SDL2DIR - PATH_SUFFIXES lib SDL2 ${SDL2_LIB_PATH_SUFFIX}) - -set(_sdl2_framework FALSE) -# Some special-casing if we've found/been given a framework. -# Handles whether we're given the library inside the framework or the framework itself. -if(APPLE AND "${SDL2_LIBRARY}" MATCHES "(/[^/]+)*.framework(/.*)?$") - set(_sdl2_framework TRUE) - set(SDL2_FRAMEWORK "${SDL2_LIBRARY}") - # Move up in the directory tree as required to get the framework directory. - while("${SDL2_FRAMEWORK}" MATCHES "(/[^/]+)*.framework(/.*)$" AND NOT "${SDL2_FRAMEWORK}" MATCHES "(/[^/]+)*.framework$") - get_filename_component(SDL2_FRAMEWORK "${SDL2_FRAMEWORK}" DIRECTORY) - endwhile() - if("${SDL2_FRAMEWORK}" MATCHES "(/[^/]+)*.framework$") - set(SDL2_FRAMEWORK_NAME ${CMAKE_MATCH_1}) - # If we found a framework, do a search for the header ahead of time that will be more likely to get the framework header. - find_path(SDL2_INCLUDE_DIR - NAMES - SDL_haptic.h # this file was introduced with SDL2 - HINTS - "${SDL2_FRAMEWORK}/Headers/") - else() - # For some reason we couldn't get the framework directory itself. - # Shouldn't happen, but might if something is weird. - unset(SDL2_FRAMEWORK) - endif() + # Look for the release version of SDL2. + find_library(SDL2_LIBRARY_RELEASE + NAMES SDL2 SDL-2.0 + PATH_SUFFIXES lib ${_SDL2_PATH_SUFFIX} + PATHS ${SDL2_PATH} + ) + + # Look for the debug version of SDL2. + find_library(SDL2_LIBRARY_DEBUG + NAMES SDL2d + PATH_SUFFIXES lib ${_SDL2_PATH_SUFFIX} + PATHS ${SDL2_PATH} + ) + + # Look for SDL2main. + find_library(SDL2main_LIBRARIES + NAMES SDL2main + PATH_SUFFIXES lib ${_SDL2_PATH_SUFFIX} + PATHS ${SDL2_PATH} + ) + + include(SelectLibraryConfigurations) + select_library_configurations(SDL2) endif() -find_path(SDL2_INCLUDE_DIR - NAMES - SDL_haptic.h # this file was introduced with SDL2 - HINTS - ${SDL2_INCLUDE_HINTS} - PATHS - ${SDL2_ROOT_DIR} - ENV SDL2DIR - PATH_SUFFIXES include include/sdl2 include/SDL2 SDL2) - - +# Find the SDL2 version. if(SDL2_INCLUDE_DIR AND EXISTS "${SDL2_INCLUDE_DIR}/SDL_version.h") - file(STRINGS "${SDL2_INCLUDE_DIR}/SDL_version.h" SDL2_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL_MAJOR_VERSION[ \t]+[0-9]+$") - file(STRINGS "${SDL2_INCLUDE_DIR}/SDL_version.h" SDL2_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL_MINOR_VERSION[ \t]+[0-9]+$") - file(STRINGS "${SDL2_INCLUDE_DIR}/SDL_version.h" SDL2_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL_PATCHLEVEL[ \t]+[0-9]+$") - - string(REGEX REPLACE "^#define[ \t]+SDL_MAJOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_MAJOR "${SDL2_VERSION_MAJOR_LINE}") - string(REGEX REPLACE "^#define[ \t]+SDL_MINOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_MINOR "${SDL2_VERSION_MINOR_LINE}") - string(REGEX REPLACE "^#define[ \t]+SDL_PATCHLEVEL[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_PATCH "${SDL2_VERSION_PATCH_LINE}") - - set(SDL2_VERSION_STRING "${SDL2_VERSION_MAJOR}.${SDL2_VERSION_MINOR}.${SDL2_VERSION_PATCH}") -endif() - - -if(WIN32 AND SDL2_LIBRARY) - find_file(SDL2_RUNTIME_LIBRARY - NAMES - SDL2.dll - libSDL2.dll - HINTS - ${SDL2_LIB_HINTS} - PATHS - ${SDL2_ROOT_DIR} - ENV SDL2DIR - PATH_SUFFIXES bin lib ${SDL2_LIB_PATH_SUFFIX}) -endif() - - -if(WIN32 OR ANDROID OR IOS OR (APPLE AND NOT _sdl2_framework)) - set(SDL2_EXTRA_REQUIRED SDL2_SDLMAIN_LIBRARY) - find_library(SDL2_SDLMAIN_LIBRARY - NAMES - SDL2main - PATHS - ${SDL2_ROOT_DIR} - ENV SDL2DIR - PATH_SUFFIXES lib ${SDL2_LIB_PATH_SUFFIX}) + file(STRINGS "${SDL2_INCLUDE_DIR}/SDL_version.h" SDL2_VERSION_MAJOR_LINE REGEX "^#define[ \t]+SDL_MAJOR_VERSION[ \t]+[0-9]+$") + file(STRINGS "${SDL2_INCLUDE_DIR}/SDL_version.h" SDL2_VERSION_MINOR_LINE REGEX "^#define[ \t]+SDL_MINOR_VERSION[ \t]+[0-9]+$") + file(STRINGS "${SDL2_INCLUDE_DIR}/SDL_version.h" SDL2_VERSION_PATCH_LINE REGEX "^#define[ \t]+SDL_PATCHLEVEL[ \t]+[0-9]+$") + string(REGEX REPLACE "^#define[ \t]+SDL_MAJOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_MAJOR "${SDL2_VERSION_MAJOR_LINE}") + string(REGEX REPLACE "^#define[ \t]+SDL_MINOR_VERSION[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_MINOR "${SDL2_VERSION_MINOR_LINE}") + string(REGEX REPLACE "^#define[ \t]+SDL_PATCHLEVEL[ \t]+([0-9]+)$" "\\1" SDL2_VERSION_PATCH "${SDL2_VERSION_PATCH_LINE}") + set(SDL2_VERSION_STRING ${SDL2_VERSION_MAJOR}.${SDL2_VERSION_MINOR}.${SDL2_VERSION_PATCH}) + unset(SDL2_VERSION_MAJOR_LINE) + unset(SDL2_VERSION_MINOR_LINE) + unset(SDL2_VERSION_PATCH_LINE) + unset(SDL2_VERSION_MAJOR) + unset(SDL2_VERSION_MINOR) + unset(SDL2_VERSION_PATCH) endif() -if(MINGW AND NOT SDL2PC_FOUND) - find_library(SDL2_MINGW_LIBRARY mingw32) - find_library(SDL2_MWINDOWS_LIBRARY mwindows) -endif() - -if(SDL2_PREFIX) - # Restore things the way they used to be. - set(CMAKE_PREFIX_PATH ${SDL2_ORIGPREFIXPATH}) -endif() - -# handle the QUIETLY and REQUIRED arguments and set QUATLIB_FOUND to TRUE if -# all listed variables are TRUE include(FindPackageHandleStandardArgs) find_package_handle_standard_args(SDL2 - DEFAULT_MSG - SDL2_LIBRARY - SDL2_INCLUDE_DIR - ${SDL2_EXTRA_REQUIRED}) + REQUIRED_VARS SDL2_LIBRARIES SDL2_INCLUDE_DIR + VERSION_VAR SDL2_VERSION_STRING) -if(SDL2_FOUND) - if(NOT TARGET SDL2::SDL2) - # Create SDL2::SDL2 - if(WIN32 AND SDL2_RUNTIME_LIBRARY) - set(SDL2_DYNAMIC TRUE) - add_library(SDL2::SDL2 SHARED IMPORTED) - set_target_properties(SDL2::SDL2 - PROPERTIES - IMPORTED_IMPLIB "${SDL2_LIBRARY}" - IMPORTED_LOCATION "${SDL2_RUNTIME_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INCLUDE_DIR}" - ) - else() - add_library(SDL2::SDL2 UNKNOWN IMPORTED) - if(SDL2_FRAMEWORK AND SDL2_FRAMEWORK_NAME) - # Handle the case that SDL2 is a framework and we were able to decompose it above. - set_target_properties(SDL2::SDL2 PROPERTIES - IMPORTED_LOCATION "${SDL2_FRAMEWORK}/${SDL2_FRAMEWORK_NAME}") - elseif(_sdl2_framework AND SDL2_LIBRARY MATCHES "(/[^/]+)*.framework$") - # Handle the case that SDL2 is a framework and SDL_LIBRARY is just the framework itself. - - # This takes the basename of the framework, without the extension, - # and sets it (as a child of the framework) as the imported location for the target. - # This is the library symlink inside of the framework. - set_target_properties(SDL2::SDL2 PROPERTIES - IMPORTED_LOCATION "${SDL2_LIBRARY}/${CMAKE_MATCH_1}") - else() - # Handle non-frameworks (including non-Mac), as well as the case that we're given the library inside of the framework - set_target_properties(SDL2::SDL2 PROPERTIES - IMPORTED_LOCATION "${SDL2_LIBRARY}") - endif() - set_target_properties(SDL2::SDL2 - PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${SDL2_INCLUDE_DIR}" - ) - endif() +find_package_handle_standard_args(SDL2main + REQUIRED_VARS SDL2main_LIBRARIES SDL2_INCLUDE_DIR + VERSION_VAR SDL2_VERSION_STRING + NAME_MISMATCHED) - if(APPLE) - # Need Cocoa here, is always a framework - find_library(SDL2_COCOA_LIBRARY Cocoa) - list(APPEND SDL2_EXTRA_REQUIRED SDL2_COCOA_LIBRARY) - if(SDL2_COCOA_LIBRARY) - set_target_properties(SDL2::SDL2 PROPERTIES - IMPORTED_LINK_INTERFACE_LIBRARIES ${SDL2_COCOA_LIBRARY}) - endif() - endif() +if(SDL2_FOUND) + set(SDL2_LIBRARIES ${SDL2_LIBRARY}) + set(SDL2_INCLUDE_DIR ${SDL2_INCLUDE_DIR}) + + # Define the core SDL2 target. + if(NOT TARGET SDL2::Core) + add_library(SDL2::Core UNKNOWN IMPORTED) + set_target_properties(SDL2::Core PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES ${SDL2_INCLUDE_DIR}) + + # Link against Cocoa on macOS. + if(APPLE) + set_property(TARGET SDL2::Core APPEND PROPERTY + INTERFACE_LINK_OPTIONS -framework Cocoa) + endif() + if(SDL2_LIBRARY_RELEASE) + set_property(TARGET SDL2::Core APPEND PROPERTY + IMPORTED_CONFIGURATIONS RELEASE) + set_target_properties(SDL2::Core PROPERTIES + IMPORTED_LOCATION_RELEASE ${SDL2_LIBRARY_RELEASE}) + endif() - # Compute what to do with SDL2main - set(SDL2MAIN_LIBRARIES SDL2::SDL2) - add_library(SDL2::SDL2main INTERFACE IMPORTED) - if(SDL2_SDLMAIN_LIBRARY) - add_library(SDL2::SDL2main_real STATIC IMPORTED) - set_target_properties(SDL2::SDL2main_real - PROPERTIES - IMPORTED_LOCATION "${SDL2_SDLMAIN_LIBRARY}") - set(SDL2MAIN_LIBRARIES SDL2::SDL2main_real ${SDL2MAIN_LIBRARIES}) - endif() - if(MINGW) - # MinGW requires some additional libraries to appear earlier in the link line. - if(SDL2PC_LIBRARIES) - # Use pkgconfig-suggested extra libraries if available. - list(REMOVE_ITEM SDL2PC_LIBRARIES SDL2main SDL2) - set(SDL2MAIN_LIBRARIES ${SDL2PC_LIBRARIES} ${SDL2MAIN_LIBRARIES}) - else() - # fall back to extra libraries specified in pkg-config in - # an official binary distro of SDL2 for MinGW I downloaded - if(SDL2_MINGW_LIBRARY) - set(SDL2MAIN_LIBRARIES ${SDL2_MINGW_LIBRARY} ${SDL2MAIN_LIBRARIES}) + if(SDL2_LIBRARY_DEBUG) + set_property(TARGET SDL2::Core APPEND PROPERTY + IMPORTED_CONFIGURATIONS DEBUG) + set_target_properties(SDL2::Core PROPERTIES + IMPORTED_LOCATION_DEBUG ${SDL2_LIBRARY_DEBUG}) endif() - if(SDL2_MWINDOWS_LIBRARY) - set(SDL2MAIN_LIBRARIES ${SDL2_MWINDOWS_LIBRARY} ${SDL2MAIN_LIBRARIES}) + + if(NOT SDL2_LIBRARY_RELEASE AND NOT SDL2_LIBRARY_DEBUG) + set_property(TARGET SDL2::Core APPEND PROPERTY + IMPORTED_LOCATION ${SDL2_LIBRARY}) endif() - endif() - set_target_properties(SDL2::SDL2main - PROPERTIES - INTERFACE_COMPILE_DEFINITIONS "main=SDL_main") endif() - set_target_properties(SDL2::SDL2main - PROPERTIES - INTERFACE_LINK_LIBRARIES "${SDL2MAIN_LIBRARIES}") - endif() - mark_as_advanced(SDL2_ROOT_DIR) -endif() -mark_as_advanced(SDL2_LIBRARY - SDL2_RUNTIME_LIBRARY - SDL2_INCLUDE_DIR - SDL2_SDLMAIN_LIBRARY - SDL2_COCOA_LIBRARY - SDL2_MINGW_LIBRARY - SDL2_MWINDOWS_LIBRARY) + # Define the SDL2 target. + if(NOT TARGET SDL2::SDL2) + add_library(SDL2::SDL2 UNKNOWN IMPORTED) + set_target_properties(SDL2::SDL2 PROPERTIES + INTERFACE_LINK_LIBRARIES SDL2::Core + IMPORTED_LOCATION ${SDL2main_LIBRARIES}) + endif() +endif() diff --git a/src/Joystick/CMakeLists.txt b/src/Joystick/CMakeLists.txt index e1eb4aedaec..d1a5233211d 100644 --- a/src/Joystick/CMakeLists.txt +++ b/src/Joystick/CMakeLists.txt @@ -75,7 +75,7 @@ if(NOT QGC_BUILD_DEPENDENCIES) find_package(SDL2 ${MINIMUM_SDL2_VERSION}) if(TARGET SDL2::SDL2) message(STATUS "Found JoystickSDL ${SDL2_VERSION_STRING}") - target_link_libraries(Joystick PRIVATE SDL2::SDL2) + target_link_libraries(Joystick PRIVATE SDL2::Core) return() else() find_package(PkgConfig) From 617b651ae50047b3d89a585946893d78fa109936 Mon Sep 17 00:00:00 2001 From: Holden Date: Tue, 4 Jun 2024 00:15:46 -0400 Subject: [PATCH 23/27] Comms: Prepare TCP Link for Threading Changes --- src/Comms/TCPLink.cc | 317 ++++++++++++++++++++++++++----------------- src/Comms/TCPLink.h | 106 ++++++--------- 2 files changed, 237 insertions(+), 186 deletions(-) diff --git a/src/Comms/TCPLink.cc b/src/Comms/TCPLink.cc index 94557c30915..1626b0b26b4 100644 --- a/src/Comms/TCPLink.cc +++ b/src/Comms/TCPLink.cc @@ -9,143 +9,209 @@ #include "TCPLink.h" #include "DeviceInfo.h" +#include "QGCLoggingCategory.h" -#include +#include #include #include -TCPLink::TCPLink(SharedLinkConfigurationPtr& config) - : LinkInterface(config) - , _tcpConfig(qobject_cast(config.get())) - , _socket(nullptr) - , _socketIsConnected(false) -{ - Q_ASSERT(_tcpConfig); -} +QGC_LOGGING_CATEGORY(TCPLinkLog, "qgc.comms.tcplink") -TCPLink::~TCPLink() -{ - disconnect(); +namespace { + constexpr int SEND_BUFFER_SIZE = 1024; // >= MAVLINK_MAX_PACKET_LEN + constexpr int RECEIVE_BUFFER_SIZE = 1024; // >= MAVLINK_MAX_PACKET_LEN + constexpr int READ_BUFFER_SIZE = 1024; // >= MAVLINK_MAX_PACKET_LEN + constexpr int CONNECT_TIMEOUT_MS = 1000; + constexpr int TYPE_OF_SERVICE = 32; // Optional: Set ToS for low delay + constexpr int MAX_RECONNECTION_ATTEMPTS = 5; } -#ifdef TCPLINK_READWRITE_DEBUG -void TCPLink::_writeDebugBytes(const QByteArray data) +TCPLink::TCPLink(SharedLinkConfigurationPtr &config, QObject *parent) + : LinkInterface(config, parent) + , _tcpConfig(qobject_cast(config.get())) + , _socket(new QTcpSocket()) { - QString bytes; - QString ascii; - for (int i=0, size = data.size(); i 31 && data[i] < 127) - { - ascii.append(data[i]); - } - else - { - ascii.append(219); - } + // qCDebug(TCPLinkLog) << Q_FUNC_INFO << this; + + Q_CHECK_PTR(_tcpConfig); + if (!_tcpConfig) { + qCWarning(TCPLinkLog) << "Invalid TCPConfiguration provided."; + emit communicationError( + tr("Configuration Error"), + tr("Link %1: Invalid TCP configuration.").arg(config->name()) + ); + return; } - qDebug() << "Sent" << size << "bytes to" << _tcpConfig->host() << ":" << _tcpConfig->port() << "data:"; - qDebug() << bytes; - qDebug() << "ASCII:" << ascii; -} -#endif -void TCPLink::_writeBytes(const QByteArray &data) -{ -#ifdef TCPLINK_READWRITE_DEBUG - _writeDebugBytes(data); -#endif + _socket->setSocketOption(QAbstractSocket::SendBufferSizeSocketOption, SEND_BUFFER_SIZE); + _socket->setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption, RECEIVE_BUFFER_SIZE); + _socket->setReadBufferSize(READ_BUFFER_SIZE); + _socket->setSocketOption(QAbstractSocket::LowDelayOption, 1); + _socket->setSocketOption(QAbstractSocket::KeepAliveOption, 1); + // _socket->setSocketOption(QAbstractSocket::TypeOfServiceOption, TYPE_OF_SERVICE); - if (_socket) { - _socket->write(data); - emit bytesSent(this, data); - } + (void) QObject::connect(_socket, &QTcpSocket::connected, this, [this]() { + _isConnected = true; + _reconnectionAttempts = 0; + qCDebug(TCPLinkLog) << "TCP connected to" << _tcpConfig->host() << ":" << _tcpConfig->port(); + emit connected(); + }, Qt::AutoConnection); + + (void) QObject::connect(_socket, &QTcpSocket::disconnected, this, [this]() { + _isConnected = false; + qCDebug(TCPLinkLog) << "TCP disconnected from" << _tcpConfig->host() << ":" << _tcpConfig->port(); + emit disconnected(); + // TODO: Uncomment after threading changes + // _attemptReconnection(); + }, Qt::AutoConnection); + + (void) QObject::connect(_socket, &QTcpSocket::readyRead, this, &TCPLink::_readBytes, Qt::AutoConnection); + + (void) QObject::connect(_socket, &QTcpSocket::errorOccurred, this, [this](QTcpSocket::SocketError error) { + qCWarning(TCPLinkLog) << "TCP Link Error:" << error << _socket->errorString(); + emit communicationError( + tr("TCP Link Error"), + tr("Link %1: %2.").arg(_tcpConfig->name(), _socket->errorString()) + ); + }, Qt::AutoConnection); + +#ifdef QT_DEBUG + (void) QObject::connect(_socket, &QTcpSocket::stateChanged, this, [](QTcpSocket::SocketState state) { + qCDebug(TCPLinkLog) << "TCP State Changed:" << state; + }, Qt::AutoConnection); + + (void) QObject::connect(_socket, &QTcpSocket::hostFound, this, []() { + qCDebug(TCPLinkLog) << "TCP Host Found"; + }, Qt::AutoConnection); +#endif } -void TCPLink::_readBytes() +TCPLink::~TCPLink() { - if (_socket) { - qint64 byteCount = _socket->bytesAvailable(); - if (byteCount) - { - QByteArray buffer; - buffer.resize(byteCount); - _socket->read(buffer.data(), buffer.size()); - emit bytesReceived(this, buffer); -#ifdef TCPLINK_READWRITE_DEBUG - writeDebugBytes(buffer.data(), buffer.size()); -#endif + if (_socket->isOpen()) { + _socket->disconnectFromHost(); + if (_socket->state() != QAbstractSocket::UnconnectedState) { + _socket->waitForDisconnected(CONNECT_TIMEOUT_MS); } } + + _socket->deleteLater(); + + // qCDebug(TCPLinkLog) << Q_FUNC_INFO << this; } -void TCPLink::disconnect(void) +void TCPLink::disconnect() { - if (_socket) { - // This prevents stale signal from calling the link after it has been deleted - QObject::disconnect(_socket, &QIODevice::readyRead, this, &TCPLink::_readBytes); - _socketIsConnected = false; - _socket->disconnectFromHost(); // Disconnect tcp - _socket->deleteLater(); // Make sure delete happens on correct thread - _socket = nullptr; + if (isConnected()) { + _socket->disconnectFromHost(); + } else { emit disconnected(); } } -bool TCPLink::_connect(void) +bool TCPLink::_connect() { - if (_socket) { - qWarning() << "connect called while already connected"; + if (isConnected()) { + qCWarning(TCPLinkLog) << "Already connected to" << _tcpConfig->host() << ":" << _tcpConfig->port(); return true; } - return _hardwareConnect(); -} - -bool TCPLink::_hardwareConnect() -{ - Q_ASSERT(_socket == nullptr); - _socket = new QTcpSocket(); - QObject::connect(_socket, &QIODevice::readyRead, this, &TCPLink::_readBytes); - - QSignalSpy errorSpy(_socket, &QAbstractSocket::errorOccurred); - QObject::connect(_socket, &QAbstractSocket::errorOccurred, this, &TCPLink::_socketError); + QSignalSpy errorSpy(_socket, &QTcpSocket::errorOccurred); + qCDebug(TCPLinkLog) << "Attempting to connect to host:" << _tcpConfig->host() << "port:" << _tcpConfig->port(); _socket->connectToHost(_tcpConfig->host(), _tcpConfig->port()); - // Give the socket a second to connect to the other side otherwise error out - if (!_socket->waitForConnected(1000)) - { - // Whether a failed connection emits an error signal or not is platform specific. - // So in cases where it is not emitted, we emit one ourselves. + // TODO: Switch Blocking to Signals after Threading Changes + if (!_socket->waitForConnected(CONNECT_TIMEOUT_MS)) { + qCWarning(TCPLinkLog) << "Connection to" << _tcpConfig->host() << ":" << _tcpConfig->port() << "failed:" << _socket->errorString(); if (errorSpy.count() == 0) { - emit communicationError(tr("Link Error"), tr("Error on link %1. Connection failed").arg(_config->name())); + emit communicationError( + tr("TCP Link Connect Error"), + tr("Link %1: %2.").arg(_tcpConfig->name(), tr("Connection Failed: %1").arg(_socket->errorString())) + ); } - delete _socket; - _socket = nullptr; return false; } - _socketIsConnected = true; - emit connected(); + + qCDebug(TCPLinkLog) << "Successfully connected to" << _tcpConfig->host() << ":" << _tcpConfig->port(); return true; } -void TCPLink::_socketError(QAbstractSocket::SocketError socketError) +void TCPLink::_writeBytes(const QByteArray &bytes) { - Q_UNUSED(socketError); - emit communicationError(tr("Link Error"), tr("Error on link %1. Error on socket: %2.").arg(_config->name()).arg(_socket->errorString())); + if (!_socket->isValid()) { + return; + } + + static const QString title = tr("TCP Link Write Error"); + + if (!isConnected()) { + emit communicationError( + title, + tr("Link %1: Could Not Send Data - Link is Disconnected!").arg(_tcpConfig->name()) + ); + return; + } + + const qint64 bytesWritten = _socket->write(bytes); + if (bytesWritten < 0) { + emit communicationError( + title, + tr("Link %1: Could Not Send Data - Write Failed: %2").arg(_tcpConfig->name(), _socket->errorString()) + ); + return; + } + + if (bytesWritten < bytes.size()) { + qCWarning(TCPLinkLog) << "Wrote" << bytesWritten << "Out of" << bytes.size() << "total bytes"; + const QByteArray remainingBytes = bytes.mid(bytesWritten); + writeBytesThreadSafe(remainingBytes.constData(), remainingBytes.size()); + } + + emit bytesSent(this, bytes); } -/** - * @brief Check if connection is active. - * - * @return True if link is connected, false otherwise. - **/ -bool TCPLink::isConnected() const +void TCPLink::_readBytes() { - return _socketIsConnected; + if (!_socket->isValid()) { + return; + } + + if (!isConnected()) { + emit communicationError( + tr("TCP Link Read Error"), + tr("Link %1: Could Not Read Data - Link is Disconnected!").arg(_tcpConfig->name()) + ); + return; + } + + const QByteArray buffer = _socket->readAll(); + + if (buffer.isEmpty()) { + emit communicationError( + tr("TCP Link Read Error"), + tr("Link %1: Could Not Read Data - No Data Available!").arg(_tcpConfig->name()) + ); + return; + } + + emit bytesReceived(this, buffer); +} + +void TCPLink::_attemptReconnection() +{ + if (_reconnectionAttempts >= MAX_RECONNECTION_ATTEMPTS) { + qCWarning(TCPLinkLog) << "Max reconnection attempts reached for" << _tcpConfig->host() << ":" << _tcpConfig->port(); + emit communicationError(tr("TCP Link Reconnect Error"), tr("Link %1: Maximum reconnection attempts reached.").arg(_tcpConfig->name())); + return; + } + + _reconnectionAttempts++; + const int delay = qPow(2, _reconnectionAttempts) * 1000; // Exponential backoff + qCDebug(TCPLinkLog) << "Attempting reconnection #" << _reconnectionAttempts << "in" << delay << "ms"; + QTimer::singleShot(delay, this, [this]() { + _connect(); + }); } bool TCPLink::isSecureConnection() @@ -153,52 +219,59 @@ bool TCPLink::isSecureConnection() return QGCDeviceInfo::isNetworkWired(); } -//-------------------------------------------------------------------------- -//-- TCPConfiguration +/*===========================================================================*/ -TCPConfiguration::TCPConfiguration(const QString& name) : LinkConfiguration(name) +TCPConfiguration::TCPConfiguration(const QString &name, QObject *parent) + : LinkConfiguration(name, parent) { - _port = QGC_TCP_PORT; - _host = QLatin1String("0.0.0.0"); + // qCDebug(TCPLinkLog) << Q_FUNC_INFO << this; } -TCPConfiguration::TCPConfiguration(const TCPConfiguration* source) : LinkConfiguration(source) +TCPConfiguration::TCPConfiguration(const TCPConfiguration *copy, QObject *parent) + : LinkConfiguration(copy, parent) + , _host(copy->host()) + , _port(copy->port()) { - _port = source->port(); - _host = source->host(); -} + // qCDebug(TCPLinkLog) << Q_FUNC_INFO << this; -void TCPConfiguration::copyFrom(const LinkConfiguration *source) -{ - LinkConfiguration::copyFrom(source); - const TCPConfiguration* usource = qobject_cast(source); - Q_ASSERT(usource != nullptr); - _port = usource->port(); - _host = usource->host(); + Q_CHECK_PTR(copy); + + LinkConfiguration::copyFrom(copy); } -void TCPConfiguration::setPort(quint16 port) +TCPConfiguration::~TCPConfiguration() { - _port = port; + // qCDebug(TCPLinkLog) << Q_FUNC_INFO << this; } -void TCPConfiguration::setHost(const QString host) +void TCPConfiguration::copyFrom(const LinkConfiguration *source) { - _host = host; + Q_CHECK_PTR(source); + LinkConfiguration::copyFrom(source); + + const TCPConfiguration* const tcpSource = qobject_cast(source); + Q_CHECK_PTR(tcpSource); + + setHost(tcpSource->host()); + setPort(tcpSource->port()); } -void TCPConfiguration::saveSettings(QSettings& settings, const QString& root) +void TCPConfiguration::loadSettings(QSettings &settings, const QString &root) { settings.beginGroup(root); - settings.setValue("port", (int)_port); - settings.setValue("host", _host); + + setHost(settings.value(QStringLiteral("host"), host()).toString()); + setPort(static_cast(settings.value(QStringLiteral("port"), port()).toUInt())); + settings.endGroup(); } -void TCPConfiguration::loadSettings(QSettings& settings, const QString& root) +void TCPConfiguration::saveSettings(QSettings &settings, const QString &root) { settings.beginGroup(root); - _port = (quint16)settings.value("port", QGC_TCP_PORT).toUInt(); - _host = settings.value("host", _host).toString(); + + settings.setValue(QStringLiteral("host"), host()); + settings.setValue(QStringLiteral("port"), port()); + settings.endGroup(); } diff --git a/src/Comms/TCPLink.h b/src/Comms/TCPLink.h index 726bf632671..9c829a28000 100644 --- a/src/Comms/TCPLink.h +++ b/src/Comms/TCPLink.h @@ -9,99 +9,77 @@ #pragma once - -#include "LinkInterface.h" -#include "LinkConfiguration.h" - +#include +#include #include -#include -#include +#include -//#define TCPLINK_READWRITE_DEBUG // Use to debug data reads/writes +#include "LinkConfiguration.h" +#include "LinkInterface.h" -class TCPLinkTest; -class LinkManager; class QTcpSocket; -#define QGC_TCP_PORT 5760 +Q_DECLARE_LOGGING_CATEGORY(TCPLinkLog) class TCPConfiguration : public LinkConfiguration { Q_OBJECT -public: - - Q_PROPERTY(quint16 port READ port WRITE setPort NOTIFY portChanged) Q_PROPERTY(QString host READ host WRITE setHost NOTIFY hostChanged) + Q_PROPERTY(quint16 port READ port WRITE setPort NOTIFY portChanged) + +public: + explicit TCPConfiguration(const QString &name, QObject *parent = nullptr); + explicit TCPConfiguration(const TCPConfiguration *copy, QObject *parent = nullptr); + virtual ~TCPConfiguration(); - TCPConfiguration(const QString& name); - TCPConfiguration(const TCPConfiguration* source); + LinkType type() const override { return LinkConfiguration::TypeTcp; } + void copyFrom(const LinkConfiguration *source) override; - quint16 port (void) const { return _port; } - QString host (void) const { return _host; } - void setPort (quint16 port); - void setHost (const QString host); + QString host() const { return _host.toString(); } + void setHost(const QString &host) { if (host != _host.toString()) { _host.setAddress(host); emit hostChanged(); } } + quint16 port() const { return _port; } + void setPort(quint16 port) { if (port != _port) { _port = port; emit portChanged(); } } - //LinkConfiguration overrides - LinkType type (void) const override { return LinkConfiguration::TypeTcp; } - void copyFrom (const LinkConfiguration* source) override; - void loadSettings (QSettings& settings, const QString& root) override; - void saveSettings (QSettings& settings, const QString& root) override; - QString settingsURL (void) override { return "TcpSettings.qml"; } - QString settingsTitle (void) override { return tr("TCP Link Settings"); } + void loadSettings(QSettings &settings, const QString &root) override; + void saveSettings(QSettings &settings, const QString &root) override; + QString settingsURL() override { return QStringLiteral("TcpSettings.qml"); } + QString settingsTitle() override { return tr("TCP Link Settings"); } signals: - void portChanged(void); - void hostChanged(void); + void hostChanged(); + void portChanged(); private: - QString _host; - quint16 _port; + QHostAddress _host; + quint16 _port = 5760; }; +/*===========================================================================*/ + class TCPLink : public LinkInterface { Q_OBJECT public: - TCPLink(SharedLinkConfigurationPtr& config); + explicit TCPLink(SharedLinkConfigurationPtr &config, QObject *parent = nullptr); virtual ~TCPLink(); - QTcpSocket* getSocket (void) { return _socket; } - void signalBytesWritten (void); - - // LinkInterface overrides - bool isConnected (void) const override; - void disconnect (void) override; - bool isSecureConnection (void) override; + void run() override {}; + bool isConnected() const override { return _isConnected; } + void disconnect() override; + bool isSecureConnection() override; private slots: - void _socketError (QAbstractSocket::SocketError socketError); - void _readBytes (void); - - // LinkInterface overrides - void _writeBytes(const QByteArray &data) override; + void _writeBytes(const QByteArray &bytes) override; + void _readBytes(); private: - // LinkInterface overrides - bool _connect(void) override; - - bool _hardwareConnect (void); -#ifdef TCPLINK_READWRITE_DEBUG - void _writeDebugBytes (const QByteArray data); -#endif - - const TCPConfiguration* _tcpConfig; - QTcpSocket* _socket; - bool _socketIsConnected; - - quint64 _bitsSentTotal; - quint64 _bitsSentCurrent; - quint64 _bitsSentMax; - quint64 _bitsReceivedTotal; - quint64 _bitsReceivedCurrent; - quint64 _bitsReceivedMax; - quint64 _connectionStartTime; - QMutex _statisticsMutex; -}; + bool _connect() override; + void _attemptReconnection(); + const TCPConfiguration *_tcpConfig = nullptr; + QTcpSocket *_socket = nullptr; + bool _isConnected = false; + int _reconnectionAttempts = 0; +}; From b8d721ee84cd9abc052dd96fcc56d4921f2388d9 Mon Sep 17 00:00:00 2001 From: Holden Date: Wed, 30 Oct 2024 00:34:40 -0400 Subject: [PATCH 24/27] Comms: TCPLink threading updates --- src/Comms/LinkManager.cc | 2 + src/Comms/TCPLink.cc | 267 ++++++++++++++++++++------------------- src/Comms/TCPLink.h | 53 ++++++-- 3 files changed, 182 insertions(+), 140 deletions(-) diff --git a/src/Comms/LinkManager.cc b/src/Comms/LinkManager.cc index 790913f68d1..4abc8abd04a 100644 --- a/src/Comms/LinkManager.cc +++ b/src/Comms/LinkManager.cc @@ -229,6 +229,8 @@ void LinkManager::_linkDisconnected() for (auto it = _rgLinks.begin(); it != _rgLinks.end(); ++it) { if (it->get() == link) { qCDebug(LinkManagerLog) << "LinkManager::_linkDisconnected" << it->get()->linkConfiguration()->name() << it->use_count(); + SharedLinkConfigurationPtr config = it->get()->linkConfiguration(); + config->setLink(nullptr); (void) _rgLinks.erase(it); return; } diff --git a/src/Comms/TCPLink.cc b/src/Comms/TCPLink.cc index 1626b0b26b4..c0388f2bc1e 100644 --- a/src/Comms/TCPLink.cc +++ b/src/Comms/TCPLink.cc @@ -18,62 +18,25 @@ QGC_LOGGING_CATEGORY(TCPLinkLog, "qgc.comms.tcplink") namespace { - constexpr int SEND_BUFFER_SIZE = 1024; // >= MAVLINK_MAX_PACKET_LEN - constexpr int RECEIVE_BUFFER_SIZE = 1024; // >= MAVLINK_MAX_PACKET_LEN - constexpr int READ_BUFFER_SIZE = 1024; // >= MAVLINK_MAX_PACKET_LEN constexpr int CONNECT_TIMEOUT_MS = 1000; - constexpr int TYPE_OF_SERVICE = 32; // Optional: Set ToS for low delay - constexpr int MAX_RECONNECTION_ATTEMPTS = 5; + constexpr int TYPE_OF_SERVICE = 32; // Set ToS for low delay + constexpr int MAX_RECONNECTION_ATTEMPTS = 3; } -TCPLink::TCPLink(SharedLinkConfigurationPtr &config, QObject *parent) - : LinkInterface(config, parent) - , _tcpConfig(qobject_cast(config.get())) - , _socket(new QTcpSocket()) +TCPWorker::TCPWorker(const QString &host, quint16 port, QObject *parent) + : QObject(parent) + , _socket(new QTcpSocket(this)) + , _host(host) + , _port(port) { - // qCDebug(TCPLinkLog) << Q_FUNC_INFO << this; - - Q_CHECK_PTR(_tcpConfig); - if (!_tcpConfig) { - qCWarning(TCPLinkLog) << "Invalid TCPConfiguration provided."; - emit communicationError( - tr("Configuration Error"), - tr("Link %1: Invalid TCP configuration.").arg(config->name()) - ); - return; - } - - _socket->setSocketOption(QAbstractSocket::SendBufferSizeSocketOption, SEND_BUFFER_SIZE); - _socket->setSocketOption(QAbstractSocket::ReceiveBufferSizeSocketOption, RECEIVE_BUFFER_SIZE); - _socket->setReadBufferSize(READ_BUFFER_SIZE); _socket->setSocketOption(QAbstractSocket::LowDelayOption, 1); _socket->setSocketOption(QAbstractSocket::KeepAliveOption, 1); - // _socket->setSocketOption(QAbstractSocket::TypeOfServiceOption, TYPE_OF_SERVICE); - - (void) QObject::connect(_socket, &QTcpSocket::connected, this, [this]() { - _isConnected = true; - _reconnectionAttempts = 0; - qCDebug(TCPLinkLog) << "TCP connected to" << _tcpConfig->host() << ":" << _tcpConfig->port(); - emit connected(); - }, Qt::AutoConnection); + _socket->setSocketOption(QAbstractSocket::TypeOfServiceOption, TYPE_OF_SERVICE); - (void) QObject::connect(_socket, &QTcpSocket::disconnected, this, [this]() { - _isConnected = false; - qCDebug(TCPLinkLog) << "TCP disconnected from" << _tcpConfig->host() << ":" << _tcpConfig->port(); - emit disconnected(); - // TODO: Uncomment after threading changes - // _attemptReconnection(); - }, Qt::AutoConnection); - - (void) QObject::connect(_socket, &QTcpSocket::readyRead, this, &TCPLink::_readBytes, Qt::AutoConnection); - - (void) QObject::connect(_socket, &QTcpSocket::errorOccurred, this, [this](QTcpSocket::SocketError error) { - qCWarning(TCPLinkLog) << "TCP Link Error:" << error << _socket->errorString(); - emit communicationError( - tr("TCP Link Error"), - tr("Link %1: %2.").arg(_tcpConfig->name(), _socket->errorString()) - ); - }, Qt::AutoConnection); + (void) connect(_socket, &QTcpSocket::connected, this, &TCPWorker::_onSocketConnected); + (void) connect(_socket, &QTcpSocket::disconnected, this, &TCPWorker::_onSocketDisconnected); + (void) connect(_socket, &QTcpSocket::readyRead, this, &TCPWorker::_onSocketReadyRead); + (void) connect(_socket, &QTcpSocket::errorOccurred, this, &TCPWorker::_onSocketErrorOccurred); #ifdef QT_DEBUG (void) QObject::connect(_socket, &QTcpSocket::stateChanged, this, [](QTcpSocket::SocketState state) { @@ -86,132 +49,172 @@ TCPLink::TCPLink(SharedLinkConfigurationPtr &config, QObject *parent) #endif } -TCPLink::~TCPLink() +TCPWorker::~TCPWorker() { - if (_socket->isOpen()) { - _socket->disconnectFromHost(); - if (_socket->state() != QAbstractSocket::UnconnectedState) { - _socket->waitForDisconnected(CONNECT_TIMEOUT_MS); - } - } - - _socket->deleteLater(); - - // qCDebug(TCPLinkLog) << Q_FUNC_INFO << this; + disconnectFromHost(); } -void TCPLink::disconnect() +bool TCPWorker::isConnected() const { - if (isConnected()) { - _socket->disconnectFromHost(); - } else { - emit disconnected(); - } + return (_socket->isOpen() && (_socket->state() == QAbstractSocket::ConnectedState)); } -bool TCPLink::_connect() +void TCPWorker::connectToHost() { if (isConnected()) { - qCWarning(TCPLinkLog) << "Already connected to" << _tcpConfig->host() << ":" << _tcpConfig->port(); - return true; + qCWarning(TCPLinkLog) << "Already connected to" << _host << ":" << _port; + return; } QSignalSpy errorSpy(_socket, &QTcpSocket::errorOccurred); - qCDebug(TCPLinkLog) << "Attempting to connect to host:" << _tcpConfig->host() << "port:" << _tcpConfig->port(); - _socket->connectToHost(_tcpConfig->host(), _tcpConfig->port()); + qCDebug(TCPLinkLog) << "Attempting to connect to host:" << _host << "port:" << _port; + _socket->connectToHost(_host, _port); - // TODO: Switch Blocking to Signals after Threading Changes if (!_socket->waitForConnected(CONNECT_TIMEOUT_MS)) { - qCWarning(TCPLinkLog) << "Connection to" << _tcpConfig->host() << ":" << _tcpConfig->port() << "failed:" << _socket->errorString(); + qCWarning(TCPLinkLog) << "Connection to" << _host << ":" << _port << "failed:" << _socket->errorString(); if (errorSpy.count() == 0) { - emit communicationError( - tr("TCP Link Connect Error"), - tr("Link %1: %2.").arg(_tcpConfig->name(), tr("Connection Failed: %1").arg(_socket->errorString())) - ); + emit errorOccurred(tr("Connection Failed: %1").arg(_socket->errorString())); } - return false; + _onSocketDisconnected(); } - qCDebug(TCPLinkLog) << "Successfully connected to" << _tcpConfig->host() << ":" << _tcpConfig->port(); - return true; + qCDebug(TCPLinkLog) << "Successfully connected to" << _host.toString() << ":" << _port; } -void TCPLink::_writeBytes(const QByteArray &bytes) +void TCPWorker::disconnectFromHost() { - if (!_socket->isValid()) { - return; + if (isConnected()) { + _socket->disconnectFromHost(); } +} - static const QString title = tr("TCP Link Write Error"); +void TCPWorker::writeData(const QByteArray &data) +{ + if (isConnected()) { + qint64 totalBytesWritten = 0; + while (totalBytesWritten < data.size()) { + const qint64 bytesWritten = _socket->write(data.constData() + totalBytesWritten, data.size() - totalBytesWritten); + if (bytesWritten <= 0) { + break; + } + + totalBytesWritten += bytesWritten; + } - if (!isConnected()) { - emit communicationError( - title, - tr("Link %1: Could Not Send Data - Link is Disconnected!").arg(_tcpConfig->name()) - ); - return; + if (totalBytesWritten < 0) { + emit errorOccurred(tr("Could Not Send Data - Write Failed: %1").arg(_socket->errorString())); + } + } else { + emit errorOccurred(tr("Socket is not connected")); } +} - const qint64 bytesWritten = _socket->write(bytes); - if (bytesWritten < 0) { - emit communicationError( - title, - tr("Link %1: Could Not Send Data - Write Failed: %2").arg(_tcpConfig->name(), _socket->errorString()) - ); - return; - } +void TCPWorker::_onSocketConnected() +{ + emit connected(); +} - if (bytesWritten < bytes.size()) { - qCWarning(TCPLinkLog) << "Wrote" << bytesWritten << "Out of" << bytes.size() << "total bytes"; - const QByteArray remainingBytes = bytes.mid(bytesWritten); - writeBytesThreadSafe(remainingBytes.constData(), remainingBytes.size()); - } +void TCPWorker::_onSocketDisconnected() +{ + emit disconnected(); +} - emit bytesSent(this, bytes); +void TCPWorker::_onSocketReadyRead() +{ + const QByteArray data = _socket->readAll(); + emit dataReceived(data); } -void TCPLink::_readBytes() +void TCPWorker::_onSocketErrorOccurred(QAbstractSocket::SocketError socketError) { - if (!_socket->isValid()) { - return; - } + Q_UNUSED(socketError); + emit errorOccurred(_socket->errorString()); +} - if (!isConnected()) { +/*===========================================================================*/ + +TCPLink::TCPLink(SharedLinkConfigurationPtr &config, QObject *parent) + : LinkInterface(config, parent) + , _tcpConfig(qobject_cast(config.get())) + , _worker(nullptr) + , _workerThread(new QThread(this)) +{ + Q_CHECK_PTR(_tcpConfig); + if (!_tcpConfig) { + qCWarning(TCPLinkLog) << "Invalid TCPConfiguration provided."; emit communicationError( - tr("TCP Link Read Error"), - tr("Link %1: Could Not Read Data - Link is Disconnected!").arg(_tcpConfig->name()) + tr("Configuration Error"), + tr("Link %1: Invalid TCP configuration.").arg(config->name()) ); return; } - const QByteArray buffer = _socket->readAll(); + _worker = new TCPWorker(_tcpConfig->host(), _tcpConfig->port()); - if (buffer.isEmpty()) { - emit communicationError( - tr("TCP Link Read Error"), - tr("Link %1: Could Not Read Data - No Data Available!").arg(_tcpConfig->name()) - ); - return; - } + _worker->moveToThread(_workerThread); + + (void) connect(_workerThread, &QThread::finished, _worker, &QObject::deleteLater); + + (void) connect(_worker, &TCPWorker::connected, this, &TCPLink::_onConnected); + (void) connect(_worker, &TCPWorker::disconnected, this, &TCPLink::_onDisconnected); + (void) connect(_worker, &TCPWorker::errorOccurred, this, &TCPLink::_onErrorOccurred); + (void) connect(_worker, &TCPWorker::dataReceived, this, &TCPLink::_onDataReceived); + +#ifdef QT_DEBUG + _workerThread->setObjectName(QString("TCP_%1").arg(config->name())); +#endif + + _workerThread->start(); +} + +TCPLink::~TCPLink() +{ + disconnect(); - emit bytesReceived(this, buffer); + _workerThread->quit(); + _workerThread->wait(); } -void TCPLink::_attemptReconnection() +bool TCPLink::isConnected() const { - if (_reconnectionAttempts >= MAX_RECONNECTION_ATTEMPTS) { - qCWarning(TCPLinkLog) << "Max reconnection attempts reached for" << _tcpConfig->host() << ":" << _tcpConfig->port(); - emit communicationError(tr("TCP Link Reconnect Error"), tr("Link %1: Maximum reconnection attempts reached.").arg(_tcpConfig->name())); - return; - } + return (_worker && _worker->isConnected()); +} + +bool TCPLink::_connect() +{ + return QMetaObject::invokeMethod(_worker, "connectToHost", Qt::QueuedConnection); +} + +void TCPLink::disconnect() +{ + (void) QMetaObject::invokeMethod(_worker, "disconnectFromHost", Qt::QueuedConnection); +} + +void TCPLink::_onConnected() +{ + emit connected(); +} + +void TCPLink::_onDisconnected() +{ + emit disconnected(); +} - _reconnectionAttempts++; - const int delay = qPow(2, _reconnectionAttempts) * 1000; // Exponential backoff - qCDebug(TCPLinkLog) << "Attempting reconnection #" << _reconnectionAttempts << "in" << delay << "ms"; - QTimer::singleShot(delay, this, [this]() { - _connect(); - }); +void TCPLink::_onErrorOccurred(const QString &errorString) +{ + emit communicationError(tr("TCP Link Error"), tr("Link %1: %2").arg(_tcpConfig->name(), errorString)); +} + +void TCPLink::_onDataReceived(const QByteArray &data) +{ + // Process data or emit signal + emit bytesReceived(this, data); +} + +void TCPLink::_writeBytes(const QByteArray& bytes) +{ + (void) QMetaObject::invokeMethod(_worker, "writeData", Qt::QueuedConnection, Q_ARG(QByteArray, bytes)); } bool TCPLink::isSecureConnection() diff --git a/src/Comms/TCPLink.h b/src/Comms/TCPLink.h index 9c829a28000..a1e62fb74f5 100644 --- a/src/Comms/TCPLink.h +++ b/src/Comms/TCPLink.h @@ -21,6 +21,41 @@ class QTcpSocket; Q_DECLARE_LOGGING_CATEGORY(TCPLinkLog) +class TCPWorker : public QObject +{ + Q_OBJECT + +public: + TCPWorker(const QString &host, quint16 port, QObject *parent = nullptr); + ~TCPWorker(); + + bool isConnected() const; + +public slots: + void connectToHost(); + void disconnectFromHost(); + void writeData(const QByteArray &data); + +signals: + void connected(); + void disconnected(); + void errorOccurred(const QString &errorString); + void dataReceived(const QByteArray &data); + +private slots: + void _onSocketConnected(); + void _onSocketDisconnected(); + void _onSocketReadyRead(); + void _onSocketErrorOccurred(QAbstractSocket::SocketError socketError); + +private: + QTcpSocket *_socket = nullptr; + QHostAddress _host; + quint16 _port = 0; +}; + +/*===========================================================================*/ + class TCPConfiguration : public LinkConfiguration { Q_OBJECT @@ -65,21 +100,23 @@ class TCPLink : public LinkInterface explicit TCPLink(SharedLinkConfigurationPtr &config, QObject *parent = nullptr); virtual ~TCPLink(); - void run() override {}; - bool isConnected() const override { return _isConnected; } + bool isConnected() const override; void disconnect() override; bool isSecureConnection() override; private slots: - void _writeBytes(const QByteArray &bytes) override; - void _readBytes(); + bool _connect() override; + void _onConnected(); + void _onDisconnected(); + void _onErrorOccurred(const QString &errorString); + void _onDataReceived(const QByteArray &data); private: - bool _connect() override; + void _writeBytes(const QByteArray &bytes) override; + void _setupSocket(); void _attemptReconnection(); + TCPWorker *_worker = nullptr; + QThread *_workerThread = nullptr; const TCPConfiguration *_tcpConfig = nullptr; - QTcpSocket *_socket = nullptr; - bool _isConnected = false; - int _reconnectionAttempts = 0; }; From b6052e5f5d03f517078e768788734b98f011f086 Mon Sep 17 00:00:00 2001 From: mohan Date: Wed, 30 Oct 2024 19:45:37 +0530 Subject: [PATCH 25/27] Video Streaming URL Changed (#12052) * Update index.md link edited * Update index.md link should be open in new tab. * Update index.md using html * Update index.md link * Update index.md --- docs/en/qgc-dev-guide/getting_started/index.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/en/qgc-dev-guide/getting_started/index.md b/docs/en/qgc-dev-guide/getting_started/index.md index 9f68cbef573..4af9646c633 100644 --- a/docs/en/qgc-dev-guide/getting_started/index.md +++ b/docs/en/qgc-dev-guide/getting_started/index.md @@ -117,7 +117,8 @@ To see a complete list of all available components in the installer _Select Comp These features can be forcibly enabled/disabled by specifying additional values to qmake. ::: - - **Video Streaming/Gstreamer:** - see [Video Streaming](https://github.com/mavlink/qgroundcontrol/blob/master/src/VideoReceiver/README.md). + - **Video Streaming/Gstreamer:** - see [Video Streaming](https://github.com/mavlink/qgroundcontrol/blob/master/src/VideoManager/VideoReceiver/GStreamer/README.md) + #### Building using Qt Creator {#qt-creator} From cc914a668c09ea6a4bdc8a25d370ae2c6e392edc Mon Sep 17 00:00:00 2001 From: PX4BuildBot Date: Wed, 30 Oct 2024 16:24:44 +0000 Subject: [PATCH 26/27] Update PX4 Firmware metadata Wed Oct 30 16:24:44 UTC 2024 --- .../PX4/PX4ParameterFactMetaData.xml | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml b/src/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml index 4a4c63a2da8..304adb3a836 100644 --- a/src/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml +++ b/src/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml @@ -19538,6 +19538,32 @@ Channel 18 + + Payload Power Switch RC channel + 0 + 18 + + Unassigned + Channel 1 + Channel 2 + Channel 3 + Channel 4 + Channel 5 + Channel 6 + Channel 7 + Channel 8 + Channel 9 + Channel 10 + Channel 11 + Channel 12 + Channel 13 + Channel 14 + Channel 15 + Channel 16 + Channel 17 + Channel 18 + + Return switch channel 0 @@ -19596,6 +19622,12 @@ -1 1 + + Threshold for selecting payload power switch + 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th + -1 + 1 + Threshold for selecting return to launch mode 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th From 2b1c88b23143d5a0420ecc67718891b83c78c7a6 Mon Sep 17 00:00:00 2001 From: alexklimaj Date: Fri, 18 Oct 2024 16:45:32 -0600 Subject: [PATCH 27/27] boards: add ARK FPV --- src/Comms/USBBoardInfo.json | 1 + src/VehicleSetup/FirmwareUpgradeController.cc | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Comms/USBBoardInfo.json b/src/Comms/USBBoardInfo.json index e3b3925bd2f..90c661b986d 100644 --- a/src/Comms/USBBoardInfo.json +++ b/src/Comms/USBBoardInfo.json @@ -31,6 +31,7 @@ { "vendorID": 12643, "productID": 76, "boardClass": "Pixhawk", "name": "CUAV Flight Controller" }, { "vendorID": 1155, "productID": 55, "boardClass": "Pixhawk", "name": "PX4 FMU SmartAP AIRLink" }, { "vendorID": 12677, "productID": 57, "boardClass": "Pixhawk", "name": "ARK FMU V6X" }, + { "vendorID": 12677, "productID": 59, "boardClass": "Pixhawk", "name": "ARK FPV" }, { "vendorID": 1155, "productID": 22336, "boardClass": "Pixhawk", "name": "ArduPilot ChibiOS" }, { "vendorID": 4617, "productID": 22336, "boardClass": "Pixhawk", "name": "ArduPilot ChibiOS" }, diff --git a/src/VehicleSetup/FirmwareUpgradeController.cc b/src/VehicleSetup/FirmwareUpgradeController.cc index 72fbc50f68f..35f34f01b4b 100644 --- a/src/VehicleSetup/FirmwareUpgradeController.cc +++ b/src/VehicleSetup/FirmwareUpgradeController.cc @@ -51,6 +51,7 @@ static QMap px4_board_name_map { {54, "px4_fmu-v6u_default"}, {56, "px4_fmu-v6c_default"}, {57, "ark_fmu-v6x_default"}, + {59, "ark_fpv_default"}, {35, "px4_fmu-v6xrt_default"}, {55, "sky-drones_smartap-airlink_default"}, {88, "airmind_mindpx-v2_default"},