diff --git a/CMakeLists.txt b/CMakeLists.txt index c3a021ef9296..cfc6937a552b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,10 @@ cmake_minimum_required(VERSION 3.22.1) -list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake) +list(APPEND CMAKE_MODULE_PATH + ${CMAKE_SOURCE_DIR}/cmake + ${CMAKE_SOURCE_DIR}/cmake/modules + ${CMAKE_SOURCE_DIR}/cmake/find-modules +) ####################################################### # Custom Build Configuration @@ -60,6 +64,7 @@ set(CMAKE_AUTORCC ON) set(CMAKE_INCLUDE_CURRENT_DIR OFF) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) +# set(CMAKE_OPTIMIZE_DEPENDENCIES ON) set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug;Release") if(NOT CMAKE_BUILD_TYPE) @@ -106,6 +111,10 @@ endif() set(BUILD_SHARED_LIBS OFF) +if(CMAKE_BUILD_TYPE STREQUAL "Release") + add_compile_definitions(QGC_INSTALL_RELEASE) +endif() + ####################################################### # Qt6 Configuration ####################################################### diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 0aaefbcaf0f1..df97d72b8b91 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/android/src/org/freedesktop/gstreamer/GStreamer.java b/android/src/org/freedesktop/gstreamer/GStreamer.java new file mode 100644 index 000000000000..066b2152b499 --- /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/freedesktop/gstreamer/fontconfig/fonts.conf b/android/src/org/freedesktop/gstreamer/fontconfig/fonts.conf new file mode 100644 index 000000000000..445d8ce5d730 --- /dev/null +++ b/android/src/org/freedesktop/gstreamer/fontconfig/fonts.conf @@ -0,0 +1,126 @@ + + + + + + + + fontconfig/fonts + + + + fontconfig + + + + + mono + + + monospace + + + + + + + sans serif + + + sans-serif + + + + + + + sans + + + sans-serif + + + + + + + 0x0020 + 0x00A0 + 0x00AD + 0x034F + 0x0600 + 0x0601 + 0x0602 + 0x0603 + 0x06DD + 0x070F + 0x115F + 0x1160 + 0x1680 + 0x17B4 + 0x17B5 + 0x180E + 0x2000 + 0x2001 + 0x2002 + 0x2003 + 0x2004 + 0x2005 + 0x2006 + 0x2007 + 0x2008 + 0x2009 + 0x200A + 0x200B + 0x200C + 0x200D + 0x200E + 0x200F + 0x2028 + 0x2029 + 0x202A + 0x202B + 0x202C + 0x202D + 0x202E + 0x202F + 0x205F + 0x2060 + 0x2061 + 0x2062 + 0x2063 + 0x206A + 0x206B + 0x206C + 0x206D + 0x206E + 0x206F + 0x2800 + 0x3000 + 0x3164 + 0xFEFF + 0xFFA0 + 0xFFF9 + 0xFFFA + 0xFFFB + + + + 30 + + + + + diff --git a/android/src/org/freedesktop/gstreamer/fontconfig/fonts/Ubuntu-R.ttf b/android/src/org/freedesktop/gstreamer/fontconfig/fonts/Ubuntu-R.ttf new file mode 100644 index 000000000000..45a038bade52 Binary files /dev/null and b/android/src/org/freedesktop/gstreamer/fontconfig/fonts/Ubuntu-R.ttf differ diff --git a/cmake/Qt6QGCConfiguration.cmake b/cmake/Qt6QGCConfiguration.cmake index a170f4f43997..f00bcf3ad393 100644 --- a/cmake/Qt6QGCConfiguration.cmake +++ b/cmake/Qt6QGCConfiguration.cmake @@ -54,3 +54,7 @@ endif() include(CMakePrintHelpers) cmake_print_variables(QT_VERSION QT_MKSPEC QT_LIBRARY_HINTS) + +# if(ANDROID) + # set(ENV{PKG_CONFIG_SYSROOT_DIR} ${CMAKE_SYSROOT}) +# endif() diff --git a/cmake/find-modules/ECMFindModuleHelpersStub.cmake b/cmake/find-modules/ECMFindModuleHelpersStub.cmake new file mode 100644 index 000000000000..bb8c9a62fc6d --- /dev/null +++ b/cmake/find-modules/ECMFindModuleHelpersStub.cmake @@ -0,0 +1 @@ +include(${CMAKE_CURRENT_LIST_DIR}/../modules/ECMFindModuleHelpers.cmake) diff --git a/cmake/find-modules/FindEGL.cmake b/cmake/find-modules/FindEGL.cmake new file mode 100644 index 000000000000..747901b3a946 --- /dev/null +++ b/cmake/find-modules/FindEGL.cmake @@ -0,0 +1,151 @@ +# SPDX-FileCopyrightText: 2014 Alex Merry +# SPDX-FileCopyrightText: 2014 Martin Gräßlin +# +# 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. + +Since pre-1.0.0. +#]=======================================================================] + +include(${CMAKE_CURRENT_LIST_DIR}/ECMFindModuleHelpersStub.cmake) +include(CheckCXXSourceCompiles) +include(CMakePushCheckState) + +ecm_find_package_version_check(EGL) + +# 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_EGL QUIET egl) + +set(EGL_DEFINITIONS ${PKG_EGL_CFLAGS_OTHER}) + +find_path(EGL_INCLUDE_DIR + NAMES + EGL/egl.h + HINTS + ${PKG_EGL_INCLUDE_DIRS} +) +find_library(EGL_LIBRARY + NAMES + EGL + libEGL + HINTS + ${PKG_EGL_LIBRARY_DIRS} +) + +# NB: We do *not* use the version information from pkg-config, as that +# is the implementation version (eg: the Mesa version) +if(EGL_INCLUDE_DIR) + # egl.h has defines of the form EGL_VERSION_x_y for each supported + # version; so the header for EGL 1.1 will define EGL_VERSION_1_0 and + # EGL_VERSION_1_1. Finding the highest supported version involves + # finding all these defines and selecting the highest numbered. + file(READ "${EGL_INCLUDE_DIR}/EGL/egl.h" _EGL_header_contents) + string(REGEX MATCHALL + "[ \t]EGL_VERSION_[0-9_]+" + _EGL_version_lines + "${_EGL_header_contents}" + ) + unset(_EGL_header_contents) + foreach(_EGL_version_line ${_EGL_version_lines}) + string(REGEX REPLACE + "[ \t]EGL_VERSION_([0-9_]+)" + "\\1" + _version_candidate + "${_EGL_version_line}" + ) + string(REPLACE "_" "." _version_candidate "${_version_candidate}") + if(NOT DEFINED EGL_VERSION OR EGL_VERSION VERSION_LESS _version_candidate) + set(EGL_VERSION "${_version_candidate}") + endif() + endforeach() + unset(_EGL_version_lines) +endif() + +cmake_push_check_state(RESET) +list(APPEND CMAKE_REQUIRED_LIBRARIES "${EGL_LIBRARY}") +list(APPEND CMAKE_REQUIRED_INCLUDES "${EGL_INCLUDE_DIR}") + +check_cxx_source_compiles(" +#include + +int main(int argc, char *argv[]) { + EGLint x = 0; EGLDisplay dpy = 0; EGLContext ctx = 0; + eglDestroyContext(dpy, ctx); +}" HAVE_EGL) + +cmake_pop_check_state() + +set(required_vars EGL_INCLUDE_DIR HAVE_EGL) +if(NOT EMSCRIPTEN) + list(APPEND required_vars EGL_LIBRARY) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(EGL + FOUND_VAR + EGL_FOUND + REQUIRED_VARS + ${required_vars} + VERSION_VAR + EGL_VERSION +) + +if(EGL_FOUND AND NOT TARGET EGL::EGL) + if (EMSCRIPTEN) + add_library(EGL::EGL INTERFACE IMPORTED) + # Nothing further to be done, system include paths have headers and linkage is implicit. + else() + add_library(EGL::EGL UNKNOWN IMPORTED) + set_target_properties(EGL::EGL PROPERTIES + IMPORTED_LOCATION "${EGL_LIBRARY}" + INTERFACE_COMPILE_OPTIONS "${EGL_DEFINITIONS}" + INTERFACE_INCLUDE_DIRECTORIES "${EGL_INCLUDE_DIR}" + ) + endif() +endif() + +mark_as_advanced(EGL_LIBRARY EGL_INCLUDE_DIR HAVE_EGL) + +# compatibility variables +set(EGL_LIBRARIES ${EGL_LIBRARY}) +set(EGL_INCLUDE_DIRS ${EGL_INCLUDE_DIR}) +set(EGL_VERSION_STRING ${EGL_VERSION}) + +include(FeatureSummary) +set_package_properties(EGL PROPERTIES + URL "https://www.khronos.org/egl/" + DESCRIPTION "A platform-agnostic mechanism for creating rendering surfaces for use with other graphics libraries, such as OpenGL|ES and OpenVG." +) diff --git a/cmake/FindFFMPEG.cmake b/cmake/find-modules/FindFFMPEG.cmake similarity index 100% rename from cmake/FindFFMPEG.cmake rename to cmake/find-modules/FindFFMPEG.cmake diff --git a/cmake/find-modules/FindFFmpeg.cmake b/cmake/find-modules/FindFFmpeg.cmake new file mode 100644 index 000000000000..27b49a2a5091 --- /dev/null +++ b/cmake/find-modules/FindFFmpeg.cmake @@ -0,0 +1,384 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause +#.rst: +# FindFFmpeg +# ---------- +# +# Try to find the required ffmpeg components (default: AVFORMAT, AVUTIL, AVCODEC) +# +# Next variables can be used to hint FFmpeg libs search: +# +# :: +# +# PC__LIBRARY_DIRS +# PC_FFMPEG_LIBRARY_DIRS +# PC__INCLUDE_DIRS +# PC_FFMPEG_INCLUDE_DIRS +# +# Once done this will define +# +# :: +# +# 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. +# +# For each of the components it will additionally set. +# +# :: +# +# AVCODEC +# AVDEVICE +# AVFORMAT +# AVFILTER +# AVUTIL +# POSTPROC +# SWSCALE +# +# the following variables will be defined +# +# :: +# +# _FOUND - System has +# FFMPEG__FOUND - System has (as checked by FHSPA) +# _INCLUDE_DIRS - Include directory necessary for using the headers +# _LIBRARIES - Link these to use +# _LIBRARY_DIRS - Link directories +# _DEFINITIONS - Compiler switches required for using +# _VERSION - The components version +# +# the following import targets is created +# +# :: +# +# FFmpeg::FFmpeg - for all components +# FFmpeg:: - where in lower case (FFmpeg::avcodec) for each components +# +# Copyright (c) 2006, Matthias Kretz, +# Copyright (c) 2008, Alexander Neundorf, +# Copyright (c) 2011, Michael Jansen, +# Copyright (c) 2017, Alexander Drozdov, +# + +include(FindPackageHandleStandardArgs) + +# The default components were taken from a survey over other FindFFMPEG.cmake files +if (NOT FFmpeg_FIND_COMPONENTS) + set(FFmpeg_FIND_COMPONENTS AVCODEC AVFORMAT AVUTIL) +endif () + +if (QT_DEPLOY_FFMPEG AND BUILD_SHARED_LIBS) + set(shared_libs_desired TRUE) +endif() + +# finds FFmpeg libs, including symlinks, for the specified component. +macro(find_shared_libs_for_component _component) + # the searching pattern is pretty rough but it seems to be sufficient to gather dynamic libs + get_filename_component(name_we ${${_component}_LIBRARY} NAME_WE) + + if (WIN32) + get_filename_component(dir_name ${${_component}_LIBRARY_DIR} NAME) + if (${dir_name} STREQUAL "lib" AND EXISTS "${${_component}_LIBRARY_DIR}/../bin") + # llvm-mingv builds aux ffmpeg static libs like lib/libavutil.dll.a and cmake finds + # only them even though the folder bin/ contains proper *.dll and *.lib. + + string(REGEX REPLACE "^lib" "" name_we "${name_we}") + set(shared_lib_pattern "../bin/${name_we}*${CMAKE_SHARED_LIBRARY_SUFFIX}") + else() + set(shared_lib_pattern "${name_we}*${CMAKE_SHARED_LIBRARY_SUFFIX}") + endif() + + else() + set(shared_lib_pattern "${name_we}*${CMAKE_SHARED_LIBRARY_SUFFIX}*") + endif() + + file(GLOB ${_component}_SHARED_LIBRARIES "${${_component}_LIBRARY_DIR}/${shared_lib_pattern}") +endmacro() + +# +### Macro: set_component_found +# +# Marks the given component as found if both *_LIBRARY_NAME AND *_INCLUDE_DIRS is present. +# +macro(set_component_found _component) + if (${_component}_LIBRARY_NAME AND ${_component}_INCLUDE_DIR) + # message(STATUS " - ${_component} found.") + set(${_component}_FOUND TRUE) + set(${CMAKE_FIND_PACKAGE_NAME}_${_component}_FOUND TRUE) + else () + # message(STATUS " - ${_component} not found.") + endif () +endmacro() + +find_package(PkgConfig QUIET) +if (NOT PKG_CONFIG_FOUND AND NOT FFMPEG_DIR) + set(FFMPEG_DIR "/usr/local") +endif() +# +### Macro: find_component +# +# Checks for the given component by invoking pkgconfig and then looking up the libraries and +# include directories. +# +macro(find_component _component _pkgconfig _library _header) + + # use pkg-config to get the directories and then use these values + # in the FIND_PATH() and FIND_LIBRARY() calls + if (PKG_CONFIG_FOUND AND NOT FFMPEG_DIR) + pkg_check_modules(PC_${_component} ${_pkgconfig}) + endif () + + if (FFMPEG_DIR OR FFMPEG_ROOT) + set(__find_ffmpeg_backup_root_dir "${CMAKE_FIND_ROOT_PATH}") + endif() + + if(FFMPEG_DIR) + list(APPEND CMAKE_FIND_ROOT_PATH "${FFMPEG_DIR}") + endif() + + if(FFMPEG_ROOT) + list(APPEND CMAKE_FIND_ROOT_PATH "${FFMPEG_ROOT}") + endif() + + if (${_component}_INCLUDE_DIR AND NOT EXISTS ${${_component}_INCLUDE_DIR}) + message(STATUS "Cached include dir ${${_component}_INCLUDE_DIR} doesn't exist") + unset(${_component}_INCLUDE_DIR CACHE) + endif() + + find_path(${_component}_INCLUDE_DIR ${_header} + HINTS + ${PC_${_component}_INCLUDEDIR} + ${PC_${_component}_INCLUDE_DIRS} + ${PC_FFMPEG_INCLUDE_DIRS} + PATHS + ${FFMPEG_DIR} + PATH_SUFFIXES + ffmpeg include + ) + + if (shared_libs_desired AND NOT WIN32) + set(CMAKE_FIND_LIBRARY_SUFFIXES "${CMAKE_SHARED_LIBRARY_SUFFIX};${CMAKE_STATIC_LIBRARY_SUFFIX}") + endif() + + if (${_component}_LIBRARY AND NOT EXISTS ${${_component}_LIBRARY}) + message(STATUS "Cached library ${${_component}_LIBRARY} doesn't exist") + unset(${_component}_LIBRARY CACHE) + endif() + + find_library(${_component}_LIBRARY + NAMES ${PC_${_component}_LIBRARIES} ${_library} + HINTS + ${PC_${_component}_LIBDIR} + ${PC_${_component}_LIBRARY_DIRS} + ${PC_FFMPEG_LIBRARY_DIRS} + PATHS + ${FFMPEG_DIR} + PATH_SUFFIXES + lib bin + ) + + if(FFMPEG_DIR OR FFMPEG_ROOT) + set(CMAKE_FIND_ROOT_PATH "${__find_ffmpeg_backup_root_dir}") + endif() + + if (${_component}_LIBRARY) + get_filename_component(${_component}_LIBRARY_DIR ${${_component}_LIBRARY} DIRECTORY) + get_filename_component(${_component}_LIBRARY_NAME ${${_component}_LIBRARY} NAME) + + # On Windows, shared linking goes through 'integration' static libs, so we should look for shared ones anyway + # On Unix, we gather symlinks as well so that we could install them. + if (WIN32 OR ${${_component}_LIBRARY_NAME} MATCHES "\\${CMAKE_SHARED_LIBRARY_SUFFIX}$") + find_shared_libs_for_component(${_component}) + endif() + + endif() + + set(${_component}_DEFINITIONS ${PC_${_component}_CFLAGS_OTHER}) + set_component_found(${_component}) + + mark_as_advanced(${_component}_LIBRARY) +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) + +# Check for components. +foreach (_component ${FFmpeg_FIND_COMPONENTS}) + string(TOLOWER ${_component} library) + find_component(${_component} "lib${library}" ${library} "lib${library}/${library}.h") + + if (${_component}_FOUND) + list(APPEND FFMPEG_LIBRARIES ${${_component}_LIBRARY_NAME}) + list(APPEND FFMPEG_DEFINITIONS ${${_component}_DEFINITIONS}) + list(APPEND FFMPEG_INCLUDE_DIRS ${${_component}_INCLUDE_DIR}) + list(APPEND FFMPEG_LIBRARY_DIRS ${${_component}_LIBRARY_DIR}) + + if (${_component}_SHARED_LIBRARIES) + list(APPEND FFMPEG_SHARED_LIBRARIES ${${_component}_SHARED_LIBRARIES}) + list(APPEND FFMPEG_SHARED_COMPONENTS ${_component}) + else() + list(APPEND FFMPEG_STATIC_COMPONENTS ${_component}) + endif() + + mark_as_advanced(${_component}_LIBRARY_NAME ${_component}_DEFINITIONS ${_component}_INCLUDE_DIR + ${_component}_LIBRARY_DIR ${_component}_SHARED_LIBRARIES) + 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(__try_add_dynamic_resolve_dependency dep added) + set(added TRUE PARENT_SCOPE) + + 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) + 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) + if(EXISTS ${PC_FILE}) + file(READ ${PC_FILE} pcfile) + + set(prefix_l "(^| )\\-l") + set(suffix_lib "\\.lib($| )") + + string(REGEX REPLACE ".*Libs:([^\n\r]+).*" "\\1" out "${pcfile}") + string(REGEX MATCHALL "${prefix_l}[^ ]+" libs_dependency ${out}) + string(REGEX MATCHALL "[^ ]+${suffix_lib}" libs_dependency_lib ${out}) + + string(REGEX REPLACE ".*Libs.private:([^\n\r]+).*" "\\1" out "${pcfile}") + string(REGEX MATCHALL "${prefix_l}[^ ]+" libs_private_dependency ${out}) + string(REGEX MATCHALL "[^ ]+${suffix_lib}" libs_private_dependency_lib ${out}) + + list(APPEND deps_no_suffix ${libs_dependency} ${libs_private_dependency}) + 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) + 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() + endif() +endfunction() + +# Check for cached results. If there are skip the costly part. +#if (NOT FFMPEG_LIBRARIES) + + # Check if the required components were found and add their stuff to the FFMPEG_* vars. + + + foreach (_component ${FFmpeg_FIND_COMPONENTS}) + if (${_component}_FOUND) + string(TOLOWER ${_component} _lowerComponent) + if (NOT TARGET FFmpeg::${_lowerComponent}) + add_library(FFmpeg::${_lowerComponent} INTERFACE IMPORTED) + set_target_properties(FFmpeg::${_lowerComponent} PROPERTIES + INTERFACE_COMPILE_OPTIONS "${${_component}_DEFINITIONS}" + INTERFACE_INCLUDE_DIRECTORIES ${${_component}_INCLUDE_DIR} + INTERFACE_LINK_LIBRARIES "${${_component}_LIBRARY_NAME}" + INTERFACE_LINK_DIRECTORIES "${${_component}_LIBRARY_DIR}" + ) + if(NOT ${_component}_SHARED_LIBRARIES) + __ffmpeg_internal_set_dependencies(${_lowerComponent}) + endif() + 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}") + endif () + endif() + endif() + endforeach () + + # Build the include path with duplicates removed. + list(REMOVE_DUPLICATES FFMPEG_INCLUDE_DIRS) + list(REMOVE_DUPLICATES FFMPEG_LIBRARY_DIRS) + list(REMOVE_DUPLICATES FFMPEG_SHARED_LIBRARIES) + + message(STATUS "FFmpeg shared libs: ${FFMPEG_SHARED_LIBRARIES}") + + # 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) + + mark_as_advanced(FFMPEG_INCLUDE_DIRS + FFMPEG_LIBRARIES + FFMPEG_DEFINITIONS + FFMPEG_LIBRARY_DIRS + FFMPEG_SHARED_LIBRARIES + ) +# endif () + +list(LENGTH FFMPEG_LIBRARY_DIRS DIRS_COUNT) +if (${DIRS_COUNT} GREATER 1) + message(WARNING "One ffmpeg library dir is expected, found dirs: ${FFMPEG_LIBRARY_DIRS}") +endif() + +if(FFMPEG_SHARED_COMPONENTS AND FFMPEG_STATIC_COMPONENTS) + message(WARNING + "Only static or shared components are expected\n" + " static components: ${FFMPEG_STATIC_COMPONENTS}\n" + " shared components: ${FFMPEG_SHARED_COMPONENTS}") +endif() + +if (shared_libs_desired AND NOT FFMPEG_SHARED_COMPONENTS) + message(WARNING + "Shared FFmpeg libs are desired as QT_DEPLOY_FFMPEG=TRUE, but haven't been found!\n" + "Remove QT_DEPLOY_FFMPEG or set the proper path to shared FFmpeg via FFMPEG_DIR.") +endif() + +if (NOT TARGET FFmpeg::FFmpeg) + add_library(FFmpeg INTERFACE) + set_target_properties(FFmpeg PROPERTIES + INTERFACE_COMPILE_OPTIONS "${FFMPEG_DEFINITIONS}" + INTERFACE_INCLUDE_DIRECTORIES "${FFMPEG_INCLUDE_DIRS}" + INTERFACE_LINK_LIBRARIES "${FFMPEG_LIBRARIES}" + INTERFACE_LINK_DIRECTORIES "${FFMPEG_LIBRARY_DIRS}" + ) + add_library(FFmpeg::FFmpeg ALIAS FFmpeg) +endif() + +# Compile the list of required vars +set(_FFmpeg_REQUIRED_VARS FFMPEG_LIBRARIES FFMPEG_INCLUDE_DIRS) +foreach (_component ${FFmpeg_FIND_COMPONENTS}) + list(APPEND _FFmpeg_REQUIRED_VARS ${_component}_LIBRARY ${_component}_INCLUDE_DIR) +endforeach () + +# 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 +) diff --git a/cmake/find-modules/FindGLIB2.cmake b/cmake/find-modules/FindGLIB2.cmake new file mode 100644 index 000000000000..01d1c0cfe69a --- /dev/null +++ b/cmake/find-modules/FindGLIB2.cmake @@ -0,0 +1,151 @@ +# SPDX-FileCopyrightText: 2008 Laurent Montel +# +# SPDX-License-Identifier: BSD-3-Clause + + +#[=======================================================================[.rst: +FindGLIB2 +--------- + +Try to locate the GLib2 library. +If found, this will define the following variables: + +``GLIB2_FOUND`` + True if the GLib2 library is available +``GLIB2_INCLUDE_DIRS`` + The GLib2 include directories +``GLIB2_LIBRARIES`` + The GLib2 libraries for linking +``GLIB2_INCLUDE_DIR`` + Deprecated, use ``GLIB2_INCLUDE_DIRS`` +``GLIB2_LIBRARY`` + Deprecated, use ``GLIB2_LIBRARIES`` + +If ``GLIB2_FOUND`` is TRUE, it will also define the following +imported target: + +``GLIB2::GLIB2`` + The GLIB2 library +``GLIB2::GTHREAD2`` + The GThread2 library (since 6.7.0) +``GLIB2::GOBJECT`` + The GObject library (since 6.7.0) +``GLIB2::GIO`` + The GIO library (since 6.7.0) + +Since 5.41.0. +#]=======================================================================] + +find_package(PkgConfig QUIET) +pkg_check_modules(PC_GLIB2 QUIET glib-2.0) + +find_path(GLIB2_INCLUDE_DIRS + NAMES glib.h + HINTS ${PC_GLIB2_INCLUDEDIR} + PATH_SUFFIXES glib-2.0) + +find_library(GLIB2_LIBRARIES + NAMES glib-2.0 + HINTS ${PC_GLIB2_LIBDIR} +) + +pkg_check_modules(PC_GTHREAD2 QUIET gthread-2.0) + +find_library(GTHREAD2_LIBRARIES + NAMES gthread-2.0 + HINTS ${PC_GTHREAD2_LIBDIR} +) + +pkg_check_modules(PC_GOBJECT QUIET gobject-2.0) + +find_path(GLIB2_GOBJECT_INCLUDE_DIRS + NAMES glib-object.h + HINTS ${PC_GOBJECT_INCLUDEDIR} + PATH_SUFFIXES glib-2.0) + +find_library(GLIB2_GOBJECT_LIBRARIES + NAMES gobject-2.0 + HINTS ${PC_GOBJECT_LIBDIR} +) + +pkg_check_modules(PC_GIO QUIET gio-2.0) + +find_path(GLIB2_GIO_INCLUDE_DIRS + NAMES gio/gio.h + HINTS ${PC_GIO_INCLUDEDIR} + PATH_SUFFIXES glib-2.0) + +find_library(GLIB2_GIO_LIBRARIES + NAMES gio-2.0 + HINTS ${PC_GIO_LIBDIR} +) + +# search the glibconfig.h include dir under the same root where the library is found +get_filename_component(glib2LibDir "${GLIB2_LIBRARIES}" PATH) + +find_path(GLIB2_INTERNAL_INCLUDE_DIR glibconfig.h + PATH_SUFFIXES glib-2.0/include + HINTS ${PC_GLIB2_INCLUDEDIR} "${glib2LibDir}" ${CMAKE_SYSTEM_LIBRARY_PATH}) + +# not sure if this include dir is optional or required +# for now it is optional +if(GLIB2_INTERNAL_INCLUDE_DIR) + list(APPEND GLIB2_INCLUDE_DIRS "${GLIB2_INTERNAL_INCLUDE_DIR}") + list(APPEND GLIB2_GOBJECT_INCLUDE_DIRS "${GLIB2_INTERNAL_INCLUDE_DIR}") + list(APPEND GLIB2_GIO_INCLUDE_DIRS "${GLIB2_INTERNAL_INCLUDE_DIR}") +endif() + +# Deprecated synonyms +set(GLIB2_INCLUDE_DIR "${GLIB2_INCLUDE_DIRS}") +set(GLIB2_LIBRARY "${GLIB2_LIBRARIES}") +set(GLIB2_GOBJECT_INCLUDE_DIR "${GLIB2_GOBJECT_INCLUDE_DIRS}") +set(GLIB2_GOBJECT_LIBRARY "${GLIB2_GOBJECT_LIBRARIES}") +set(GLIB2_GIO_INCLUDE_DIR "${GLIB2_GIO_INCLUDE_DIRS}") +set(GLIB2_GIO_LIBRARY "${GLIB2_GIO_LIBRARIES}") + +if(GLIB2_GOBJECT_LIBRARIES AND GLIB2_GOBJECT_INCLUDE_DIRS) + set(GLIB2_GOBJECT_FOUND TRUE) +endif() + +if(GLIB2_GIO_LIBRARIES AND GLIB2_GIO_INCLUDE_DIRS) + set(GLIB2_GIO_FOUND TRUE) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(GLIB2 + REQUIRED_VARS GLIB2_LIBRARIES GTHREAD2_LIBRARIES GLIB2_INCLUDE_DIRS + HANDLE_COMPONENTS) + +if(GLIB2_FOUND AND NOT TARGET GLIB2::GLIB2) + add_library(GLIB2::GLIB2 UNKNOWN IMPORTED) + set_target_properties(GLIB2::GLIB2 PROPERTIES + IMPORTED_LOCATION "${GLIB2_LIBRARIES}" + INTERFACE_LINK_LIBRARIES "${GTHREAD2_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${GLIB2_INCLUDE_DIRS}") +endif() + +if(GLIB2_GOBJECT_FOUND AND NOT TARGET GLIB2::GOBJECT) + add_library(GLIB2::GOBJECT UNKNOWN IMPORTED) + set_target_properties(GLIB2::GOBJECT PROPERTIES + IMPORTED_LOCATION "${GLIB2_GOBJECT_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${GLIB2_GOBJECT_INCLUDE_DIRS}") +endif() + +if(GLIB2_GIO_FOUND AND NOT TARGET GLIB2::GIO) + add_library(GLIB2::GIO UNKNOWN IMPORTED) + set_target_properties(GLIB2::GIO PROPERTIES + IMPORTED_LOCATION "${GLIB2_GIO_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${GLIB2_GIO_INCLUDE_DIRS}") +endif() + +mark_as_advanced(GLIB2_INCLUDE_DIRS GLIB2_INCLUDE_DIR + GLIB2_LIBRARIES GLIB2_LIBRARY + GLIB2_GOBJECT_INCLUDE_DIRS GLIB2_GOBJECT_INCLUDE_DIR + GLIB2_GOBJECT_LIBRARIES GLIB2_GOBJECT_LIBRARY + GLIB2_GIO_INCLUDE_DIRS GLIB2_GIO_INCLUDE_DIR + GLIB2_GIO_LIBRARIES GLIB2_GIO_LIBRARY) + +include(FeatureSummary) +set_package_properties(GLIB2 PROPERTIES + URL "https://wiki.gnome.org/Projects/GLib" + DESCRIPTION "Event loop and utility library") diff --git a/cmake/FindGStreamer.cmake b/cmake/find-modules/FindGStreamer.cmake similarity index 61% rename from cmake/FindGStreamer.cmake rename to cmake/find-modules/FindGStreamer.cmake index ed0b86f947b7..1e9a91722f19 100644 --- a/cmake/FindGStreamer.cmake +++ b/cmake/find-modules/FindGStreamer.cmake @@ -1,35 +1,6 @@ -# Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: BSD-3-Clause - -# FindGStreamer -# --------- -# -# Locate the gstreamer-1.0 library and some of its plugins. -# Defines the following imported target: -# -# ``GStreamer::GStreamer`` -# If the gstreamer-1.0 library is available and target GStreamer::Base, -# GStreamer::Audio, GStreamer::Video, GStreamer::Pbutils and -# GStreamer::Allocators exist -# -# If target GStreamer::GStreamer exists, the following targets may be defined: -# -# ``GStreamer::App`` -# If the gstapp-1.0 library is available and target GStreamer::GStreamer exists -# ``GStreamer::Photography`` -# If the gstphotography-1.0 library is available and target GStreamer::GStreamer exists -# ``GStreamer::Gl`` -# If the gstgl-1.0 library is available and target GStreamer::GStreamer exists -# - -################################################################################ - -if(ANDROID) - set(QGC_GST_STATIC_BUILD ON) - list(APPEND PKG_CONFIG_ARGN --static) -endif() - 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() @@ -37,6 +8,10 @@ if(ANDROID OR IOS) endif() endif() +if(QGC_GST_STATIC_BUILD) + list(APPEND PKG_CONFIG_ARGN --static) +endif() + ################################################################################ # NOTE: CMP0144 in regards to GSTREAMER_ROOT @@ -52,12 +27,8 @@ if(WIN32) find_program(PKG_CONFIG_PROGRAM pkg-config PATHS ${GSTREAMER_PREFIX}/bin) if(PKG_CONFIG_PROGRAM) set(PKG_CONFIG_EXECUTABLE ${PKG_CONFIG_PROGRAM}) - cmake_print_variables(PKG_CONFIG_EXECUTABLE) endif() set(ENV{PKG_CONFIG_PATH} "${GSTREAMER_PREFIX}/lib/pkgconfig;${GSTREAMER_PREFIX}/lib/gstreamer-1.0/pkgconfig;$ENV{PKG_CONFIG_PATH}") - # cmake_path(CONVERT "${GSTREAMER_PREFIX}/lib/pkgconfig;${GSTREAMER_PREFIX}/lib/gstreamer-1.0/pkgconfig;$ENV{PKG_CONFIG_PATH}" TO_NATIVE_PATH_LIST PKG_CONFIG_PATH NORMALIZE) - # cmake_print_variables(PKG_CONFIG_PATH) - # set(ENV{PKG_CONFIG_PATH} "${PKG_CONFIG_PATH}") cmake_path(CONVERT "${GSTREAMER_PREFIX}" TO_CMAKE_PATH_LIST PREFIX_PATH NORMALIZE) cmake_path(CONVERT "${GSTREAMER_PREFIX}/lib" TO_CMAKE_PATH_LIST LIBDIR_PATH NORMALIZE) cmake_path(CONVERT "${GSTREAMER_PREFIX}/include" TO_CMAKE_PATH_LIST INCLUDE_PATH NORMALIZE) @@ -67,7 +38,6 @@ if(WIN32) --define-variable=libdir=${LIBDIR_PATH} --define-variable=includedir=${INCLUDE_PATH} ) - cmake_print_variables(PKG_CONFIG_ARGN) elseif(MACOS) set(GSTREAMER_PREFIX "/Library/Frameworks/GStreamer.framework") set(ENV{PKG_CONFIG_PATH} "${GSTREAMER_PREFIX}/Versions/Current/lib/pkgconfig:$ENV{PKG_CONFIG_PATH}") @@ -75,9 +45,10 @@ 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}") elseif(IOS) - set(GSTREAMER_PREFIX "~/Library/Developer/GStreamer/iPhone.sdk/GStreamer.framework") if(DEFINED ENV{GSTREAMER_PREFIX_IOS} AND EXISTS $ENV{GSTREAMER_PREFIX_IOS}) set(GSTREAMER_PREFIX_IOS $ENV{GSTREAMER_PREFIX_IOS}) + elseif(EXISTS "~/Library/Developer/GStreamer/iPhone.sdk/GStreamer.framework") + set(GSTREAMER_PREFIX_IOS "~/Library/Developer/GStreamer/iPhone.sdk/GStreamer.framework") else() FetchContent_Declare(gstreamer URL "https://gstreamer.freedesktop.org/data/pkg/ios/${QGC_GST_TARGET_VERSION}/gstreamer-1.0-devel-${QGC_GST_TARGET_VERSION}-ios-universal.pkg" @@ -86,18 +57,26 @@ elseif(IOS) FetchContent_MakeAvailable(gstreamer) set(GSTREAMER_PREFIX_IOS ${gstreamer_SOURCE_DIR}) endif() - # TODO: set(GSTREAMER_PREFIX ${GSTREAMER_PREFIX_IOS}/) + 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() - FetchContent_Declare(gstreamer - URL "https://gstreamer.freedesktop.org/data/pkg/android/${QGC_GST_TARGET_VERSION}/gstreamer-1.0-android-universal-${QGC_GST_TARGET_VERSION}.tar.xz" - DOWNLOAD_EXTRACT_TIMESTAMP true - ) - FetchContent_MakeAvailable(gstreamer) - set(GSTREAMER_PREFIX_ANDROID ${gstreamer_SOURCE_DIR}) + set(GSTREAMER_ARCHIVE "gstreamer-1.0-android-universal-${QGC_GST_TARGET_VERSION}.tar.xz") + set(GSTREAMER_URL "https://gstreamer.freedesktop.org/data/pkg/android/${QGC_GST_TARGET_VERSION}/${GSTREAMER_ARCHIVE}") + set(GSTREAMER_TARBALL "${CMAKE_BINARY_DIR}/_deps/gstreamer/${GSTREAMER_ARCHIVE}") + set(GSTREAMER_INSTALL_DIR "${CMAKE_BINARY_DIR}/_deps/gstreamer/install/gstreamer") + if(NOT EXISTS ${GSTREAMER_TARBALL}) + message(STATUS "Downloading GStreamer from ${GSTREAMER_URL}") + file(DOWNLOAD ${GSTREAMER_URL} ${GSTREAMER_TARBALL} SHOW_PROGRESS) + endif() + if(NOT EXISTS ${GSTREAMER_INSTALL_DIR}) + message(STATUS "Extracting GStreamer to ${GSTREAMER_INSTALL_DIR}") + file(MAKE_DIRECTORY ${GSTREAMER_INSTALL_DIR}) + file(ARCHIVE_EXTRACT INPUT ${GSTREAMER_TARBALL} DESTINATION ${GSTREAMER_INSTALL_DIR}) + endif() + set(GSTREAMER_PREFIX_ANDROID ${GSTREAMER_INSTALL_DIR}) endif() if(${CMAKE_ANDROID_ARCH_ABI} STREQUAL armeabi-v7a) set(GSTREAMER_PREFIX ${GSTREAMER_PREFIX_ANDROID}/armv7) @@ -108,24 +87,19 @@ elseif(ANDROID) elseif(${CMAKE_ANDROID_ARCH_ABI} STREQUAL x86_64) set(GSTREAMER_PREFIX ${GSTREAMER_PREFIX_ANDROID}/x86_64) endif() - set(ENV{PKG_CONFIG_PATH} "${GSTREAMER_PREFIX}/lib/pkgconfig:${GSTREAMER_PREFIX}/lib/gstreamer-1.0/pkgconfig:$ENV{PKG_CONFIG_PATH}") + set(ENV{PKG_CONFIG_PATH} "") set(ENV{PKG_CONFIG_LIBDIR} "${GSTREAMER_PREFIX}/lib/pkgconfig:${GSTREAMER_PREFIX}/lib/gstreamer-1.0/pkgconfig") - # set(ENV{PKG_CONFIG_SYSROOT_DIR} "${GSTREAMER_PREFIX}") - message(STATUS "PKG_CONFIG_PATH $ENV{PKG_CONFIG_PATH}") - # message(STATUS "PKG_CONFIG_SYSROOT_DIR $ENV{PKG_CONFIG_SYSROOT_DIR}") 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 ) - cmake_print_variables(PKG_CONFIG_ARGN) 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}) - cmake_print_variables(PKG_CONFIG_EXECUTABLE) endif() endif() endif() @@ -140,6 +114,10 @@ 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() find_file(GStreamer_VERSION_HEADER @@ -172,6 +150,8 @@ else() endif() cmake_print_variables(GStreamer_VERSION) +################################################################################ + function(find_gstreamer_component component prefix header library) if(NOT TARGET GStreamer::${component}) string(TOUPPER ${component} upper) @@ -336,18 +316,71 @@ foreach(component ${GStreamer_FIND_COMPONENTS}) 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 AND TARGET GStreamer::Gl) - target_link_libraries(GStreamer::X11 INTERFACE GStreamer::Gl) + 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 AND TARGET GStreamer::Gl) - target_link_libraries(GStreamer::EGL INTERFACE GStreamer::Gl) + 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 AND TARGET GStreamer::Gl) - target_link_libraries(GStreamer::Wayland INTERFACE GStreamer::Gl) + 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 PUBLIC 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") @@ -356,6 +389,19 @@ endforeach() ################################################################################ +if(TARGET PkgConfig::PC_GSTREAMER_GL) + get_target_property(_qt_incs PkgConfig::PC_GSTREAMER_GL INTERFACE_INCLUDE_DIRECTORIES) + set(__qt_fixed_incs) + foreach(path IN LISTS _qt_incs) + if(IS_DIRECTORY "${path}") + list(APPEND __qt_fixed_incs "${path}") + endif() + endforeach() + 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 @@ -375,19 +421,200 @@ if(GStreamer_FOUND AND NOT TARGET GStreamer::GStreamer) GStreamer::Video GStreamer::Gl ) - set_target_properties(GStreamer::GStreamer PROPERTIES VERSION ${GStreamer_Core_VERSION}) - set(GStreamer_VERSION ${GStreamer_Core_VERSION}) + set_target_properties(GStreamer::GStreamer PROPERTIES VERSION ${GStreamer_VERSION}) endif() -if(TARGET PkgConfig::PC_GSTREAMER_GL) - get_target_property(_qt_incs PkgConfig::PC_GSTREAMER_GL INTERFACE_INCLUDE_DIRECTORIES) - set(__qt_fixed_incs) - foreach(path IN LISTS _qt_incs) - if(IS_DIRECTORY "${path}") - list(APPEND __qt_fixed_incs "${path}") +################################################################################ + +set(GST_TARGET_PLUGINS + gstcoreelements + gstisomp4 + gstlibav + gstmatroska + gstmpegtsdemux + gstopengl + gstplayback + gstrtp + gstrtpmanager + gstrtsp + gstsdpelem + gsttcp + gstudp + gstvideoparsersbad + gstx264 + gstqml6 + gstasf + # gstva +) +if(ANDROID) + list(APPEND GST_TARGET_PLUGINS gstandroidmedia) +elseif(IOS) + list(APPEND GST_TARGET_PLUGINS gstapplemedia) +endif() + +find_package(PkgConfig QUIET) +if(PkgConfig_FOUND) + pkg_check_modules(GST_PLUGINS IMPORTED_TARGET ${GST_TARGET_PLUGINS}) + if(GST_PLUGINS_FOUND) + target_link_libraries(GStreamer::GStreamer INTERFACE PkgConfig::GST_PLUGINS) + if(WIN32) + # install(FILES ${GST_PLUGINS_LIBRARIES} DESTINATION ${CMAKE_INSTALL_BINDIR}) + elseif(LINUX) + # install(FILES ${GST_PLUGINS_LIBRARIES} DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif() + endif() + + pkg_check_modules(GSTREAMER_PLUGINS_BASE IMPORTED_TARGET gstreamer-plugins-base-1.0) + if(GSTREAMER_PLUGINS_BASE_FOUND) + target_link_libraries(GStreamer::GStreamer INTERFACE PkgConfig::GSTREAMER_PLUGINS_BASE) + endif() + + pkg_check_modules(GSTREAMER_PLUGINS_GOOD IMPORTED_TARGET gstreamer-plugins-good-1.0) + if(GSTREAMER_PLUGINS_GOOD) + target_link_libraries(GStreamer::GStreamer INTERFACE PkgConfig::GSTREAMER_PLUGINS_GOOD) + endif() + + pkg_check_modules(GSTREAMER_PLUGINS_BAD IMPORTED_TARGET gstreamer-plugins-bad-1.0) + if(GSTREAMER_PLUGINS_BAD) + target_link_libraries(GStreamer::GStreamer INTERFACE PkgConfig::GSTREAMER_PLUGINS_BAD) + endif() +endif() + +if(NOT GST_PLUGINS_FOUND) + set(GST_DEPENDENCIES + glib-2.0 + gio + gobject-2.0 + gmodule-no-export-2.0 + zlib + drm + graphene-1.0 + opus + ffi + dl + m + ) + list(APPEND GST_TARGET_PLUGINS ${GST_DEPENDENCIES}) + + foreach(plugin IN LISTS GST_TARGET_PLUGINS) + find_library(GStreamer_${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_PREFIX}/Versions/Current/lib + ${GSTREAMER_PREFIX}/Versions/Current/lib/gstreamer-1.0 + ) + find_path(GStreamer_${plugin}_INCLUDE_DIR + NAMES ${header} + PATH_SUFFIXES gstreamer-1.0 + PATHS ${GSTREAMER_PREFIX}/include + ) + if(GStreamer_${plugin}_LIBRARY AND GStreamer_${plugin}_INCLUDE_DIR) + cmake_print_variables(plugin) + target_link_libraries(GStreamer::GStreamer INTERFACE ${GStreamer_${plugin}_LIBRARY}) + target_include_directories(GStreamer::GStreamer INTERFACE ${GStreamer_${plugin}_INCLUDE_DIR}) + if(WIN32) + # install(FILES ${GStreamer_${plugin}_LIBRARY} DESTINATION ${CMAKE_INSTALL_BINDIR}) + elseif(LINUX) + # install(FILES ${GStreamer_${plugin}_LIBRARY} DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif() endif() endforeach() - set_property(TARGET PkgConfig::PC_GSTREAMER_GL PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${__qt_fixed_incs}") + + 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 OPTIONAL_COMPONENTS AVDEVICE AVFILTER 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() +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) + # find_path(GStreamer_${component}_INCLUDE_DIR + # NAMES ${header} + # 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 + ) 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) 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/FindLibExiv2.cmake b/cmake/find-modules/FindLibExiv2.cmake new file mode 100644 index 000000000000..83e1d6b7907f --- /dev/null +++ b/cmake/find-modules/FindLibExiv2.cmake @@ -0,0 +1,102 @@ +# SPDX-FileCopyrightText: 2018 Christophe Giboudeaux +# SPDX-FileCopyrightText: 2010 Alexander Neundorf +# SPDX-FileCopyrightText: 2008 Gilles Caulier +# +# SPDX-License-Identifier: BSD-3-Clause + +#[=======================================================================[.rst: +FindLibExiv2 +------------ + +Try to find the Exiv2 library. + +This will define the following variables: + +``LibExiv2_FOUND`` + True if (the requested version of) Exiv2 is available + +``LibExiv2_VERSION`` + The version of Exiv2 + +``LibExiv2_INCLUDE_DIRS`` + The include dirs of Exiv2 for use with target_include_directories() + +``LibExiv2_LIBRARIES`` + The Exiv2 library for use with target_link_libraries(). + This can be passed to target_link_libraries() instead of + the ``LibExiv2::LibExiv2`` target + +If ``LibExiv2_FOUND`` is TRUE, it will also define the following imported +target: + +``LibExiv2::LibExiv2`` + The Exiv2 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 5.53.0. +#]=======================================================================] + +find_package(PkgConfig QUIET) +pkg_check_modules(PC_EXIV2 QUIET exiv2) + +find_path(LibExiv2_INCLUDE_DIRS NAMES exiv2/exif.hpp + HINTS ${PC_EXIV2_INCLUDEDIR} +) + +find_library(LibExiv2_LIBRARIES NAMES exiv2 libexiv2 + HINTS ${PC_EXIV2_LIBRARY_DIRS} +) + +set(LibExiv2_VERSION ${PC_EXIV2_VERSION}) + +if(NOT LibExiv2_VERSION AND DEFINED LibExiv2_INCLUDE_DIRS) + # With exiv >= 0.27, the version #defines are in exv_conf.h instead of version.hpp + foreach(_exiv2_version_file "version.hpp" "exv_conf.h") + if(EXISTS "${LibExiv2_INCLUDE_DIRS}/exiv2/${_exiv2_version_file}") + file(READ "${LibExiv2_INCLUDE_DIRS}/exiv2/${_exiv2_version_file}" _exiv_version_file_content) + string(REGEX MATCH "#define EXIV2_MAJOR_VERSION[ ]+\\([0-9]+U?\\)" EXIV2_MAJOR_VERSION_MATCH ${_exiv_version_file_content}) + string(REGEX MATCH "#define EXIV2_MINOR_VERSION[ ]+\\([0-9]+U?\\)" EXIV2_MINOR_VERSION_MATCH ${_exiv_version_file_content}) + string(REGEX MATCH "#define EXIV2_PATCH_VERSION[ ]+\\([0-9]+U?\\)" EXIV2_PATCH_VERSION_MATCH ${_exiv_version_file_content}) + if(EXIV2_MAJOR_VERSION_MATCH) + string(REGEX REPLACE ".*_MAJOR_VERSION[ ]+\\(([0-9]*)U?\\)" "\\1" EXIV2_MAJOR_VERSION ${EXIV2_MAJOR_VERSION_MATCH}) + string(REGEX REPLACE ".*_MINOR_VERSION[ ]+\\(([0-9]*)U?\\)" "\\1" EXIV2_MINOR_VERSION ${EXIV2_MINOR_VERSION_MATCH}) + string(REGEX REPLACE ".*_PATCH_VERSION[ ]+\\(([0-9]*)U?\\)" "\\1" EXIV2_PATCH_VERSION ${EXIV2_PATCH_VERSION_MATCH}) + endif() + endif() + endforeach() + + set(LibExiv2_VERSION "${EXIV2_MAJOR_VERSION}.${EXIV2_MINOR_VERSION}.${EXIV2_PATCH_VERSION}") +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(LibExiv2 + FOUND_VAR LibExiv2_FOUND + REQUIRED_VARS LibExiv2_LIBRARIES LibExiv2_INCLUDE_DIRS + VERSION_VAR LibExiv2_VERSION +) + +mark_as_advanced(LibExiv2_INCLUDE_DIRS LibExiv2_LIBRARIES) + +if(LibExiv2_FOUND AND NOT TARGET LibExiv2::LibExiv2) + add_library(LibExiv2::LibExiv2 UNKNOWN IMPORTED) + set_target_properties(LibExiv2::LibExiv2 PROPERTIES + IMPORTED_LOCATION "${LibExiv2_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${LibExiv2_INCLUDE_DIRS}" + ) + if (LibExiv2_VERSION VERSION_LESS 0.28.0) + # exiv2 0.27 or older still uses std::auto_ptr, which is no longer available + # by default when using newer C++ versions + set_target_properties(LibExiv2::LibExiv2 PROPERTIES + INTERFACE_COMPILE_DEFINITIONS "_LIBCPP_ENABLE_CXX17_REMOVED_AUTO_PTR=1;_HAS_AUTO_PTR_ETC=1" + ) + endif() +endif() + +include(FeatureSummary) +set_package_properties(LibExiv2 PROPERTIES + URL "https://www.exiv2.org" + DESCRIPTION "Image metadata support" +) diff --git a/cmake/find-modules/FindQtWaylandScanner.cmake b/cmake/find-modules/FindQtWaylandScanner.cmake new file mode 100644 index 000000000000..1a73b80facda --- /dev/null +++ b/cmake/find-modules/FindQtWaylandScanner.cmake @@ -0,0 +1,212 @@ +# SPDX-FileCopyrightText: 2012-2014 Pier Luigi Fiorini +# +# SPDX-License-Identifier: BSD-3-Clause + +#[=======================================================================[.rst: +FindQtWaylandScanner +-------------------- + +Try to find qtwaylandscanner. + +If the qtwaylandscanner executable is not in your PATH, you can provide +an alternative name or full path location with the ``QtWaylandScanner_EXECUTABLE`` +variable. + +This will define the following variables: + +``QtWaylandScanner_FOUND`` + True if qtwaylandscanner is available + +``QtWaylandScanner_EXECUTABLE`` + The qtwaylandscanner executable. + +If ``QtWaylandScanner_FOUND`` is TRUE, it will also define the following imported +target: + +``Wayland::QtScanner`` + The qtwaylandscanner executable. + +This module provides the following functions to generate C++ protocol +implementations: + + - ``ecm_add_qtwayland_client_protocol`` + - ``ecm_add_qtwayland_server_protocol`` + +:: + + ecm_add_qtwayland_client_protocol( + PROTOCOL + BASENAME + [PREFIX ] + [PRIVATE_CODE]) + + ecm_add_qtwayland_client_protocol( + PROTOCOL + BASENAME + [PREFIX ] + [PRIVATE_CODE]) + +Generate C++ wrapper to Wayland client protocol files from ```` +XML definition for the ```` interface and append those files +to ```` or ````. Pass the ```` argument if the interface +names don't start with ``qt_`` or ``wl_``. +``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. + +WaylandScanner is required and will be searched for. + +:: + + ecm_add_qtwayland_server_protocol( + PROTOCOL + BASENAME + [PREFIX ] + [PRIVATE_CODE]) + + ecm_add_qtwayland_server_protocol( + PROTOCOL + BASENAME + [PREFIX ] + [PRIVATE_CODE]) + +Generate C++ wrapper to Wayland server protocol files from ```` +XML definition for the ```` interface and append those files +to ```` or ````. Pass the ```` argument if the interface +names don't start with ``qt_`` or ``wl_``. +``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. + +WaylandScanner is required and will be searched for. + +Since 1.4.0. +#]=======================================================================] + +include(${CMAKE_CURRENT_LIST_DIR}/ECMFindModuleHelpersStub.cmake) +include("${ECM_MODULE_DIR}/ECMQueryQt.cmake") + +ecm_find_package_version_check(QtWaylandScanner) + +if (QT_MAJOR_VERSION STREQUAL "5") + ecm_query_qt(qtwaylandscanner_dir QT_HOST_BINS) +else() + ecm_query_qt(qtwaylandscanner_dir QT_HOST_LIBEXECS) +endif() + +# Find qtwaylandscanner +find_program(QtWaylandScanner_EXECUTABLE NAMES qtwaylandscanner HINTS ${qtwaylandscanner_dir}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(QtWaylandScanner + FOUND_VAR + QtWaylandScanner_FOUND + REQUIRED_VARS + QtWaylandScanner_EXECUTABLE +) + +mark_as_advanced(QtWaylandScanner_EXECUTABLE) + +if(NOT TARGET Wayland::QtScanner AND QtWaylandScanner_FOUND) + add_executable(Wayland::QtScanner IMPORTED) + set_target_properties(Wayland::QtScanner PROPERTIES + IMPORTED_LOCATION "${QtWaylandScanner_EXECUTABLE}" + ) +endif() + +include(FeatureSummary) +set_package_properties(QtWaylandScanner PROPERTIES + URL "https://qt.io/" + DESCRIPTION "Executable that converts XML protocol files to C++ code" +) + +function(ecm_add_qtwayland_client_protocol target_or_sources_var) + # Parse arguments + set(oneValueArgs PROTOCOL BASENAME PREFIX) + set(options PRIVATE_CODE) + cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "" ${ARGN}) + + if(ARGS_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown keywords given to ecm_add_qtwayland_client_protocol(): \"${ARGS_UNPARSED_ARGUMENTS}\"") + endif() + + set(_prefix "${ARGS_PREFIX}") + if(ARGS_PRIVATE_CODE) + set(_private_code_option PRIVATE_CODE) + endif() + + find_package(WaylandScanner REQUIRED QUIET) + ecm_add_wayland_client_protocol(${target_or_sources_var} + PROTOCOL ${ARGS_PROTOCOL} + BASENAME ${ARGS_BASENAME} + ${_private_code_option}) + + get_filename_component(_infile ${ARGS_PROTOCOL} ABSOLUTE) + set(_header "${CMAKE_CURRENT_BINARY_DIR}/qwayland-${ARGS_BASENAME}.h") + set(_code "${CMAKE_CURRENT_BINARY_DIR}/qwayland-${ARGS_BASENAME}.cpp") + + set_source_files_properties(${_header} ${_code} GENERATED) + + add_custom_command(OUTPUT "${_header}" + COMMAND ${QtWaylandScanner_EXECUTABLE} client-header ${_infile} "" ${_prefix} > ${_header} + DEPENDS ${_infile} ${_cheader} VERBATIM) + + add_custom_command(OUTPUT "${_code}" + COMMAND ${QtWaylandScanner_EXECUTABLE} client-code ${_infile} "" ${_prefix} > ${_code} + DEPENDS ${_infile} ${_header} VERBATIM) + + set_property(SOURCE ${_header} ${_code} PROPERTY SKIP_AUTOMOC ON) + + if (TARGET ${target_or_sources_var}) + target_sources(${target_or_sources_var} PRIVATE "${_code}") + else() + list(APPEND ${target_or_sources_var} "${_code}") + set(${target_or_sources_var} ${${target_or_sources_var}} PARENT_SCOPE) + endif() +endfunction() + + +function(ecm_add_qtwayland_server_protocol target_or_sources_var) + # Parse arguments + set(oneValueArgs PROTOCOL BASENAME PREFIX) + set(options PRIVATE_CODE) + cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "" ${ARGN}) + + if(ARGS_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown keywords given to ecm_add_qtwayland_server_protocol(): \"${ARGS_UNPARSED_ARGUMENTS}\"") + endif() + + set(_prefix "${ARGS_PREFIX}") + if(ARGS_PRIVATE_CODE) + set(_private_code_option PRIVATE_CODE) + endif() + + find_package(WaylandScanner REQUIRED QUIET) + ecm_add_wayland_server_protocol(${target_or_sources_var} + PROTOCOL ${ARGS_PROTOCOL} + BASENAME ${ARGS_BASENAME} + ${_private_code_option}) + + get_filename_component(_infile ${ARGS_PROTOCOL} ABSOLUTE) + set(_header "${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-${ARGS_BASENAME}.h") + set(_code "${CMAKE_CURRENT_BINARY_DIR}/qwayland-server-${ARGS_BASENAME}.cpp") + + set_source_files_properties(${_header} ${_code} GENERATED) + + add_custom_command(OUTPUT "${_header}" + COMMAND ${QtWaylandScanner_EXECUTABLE} server-header ${_infile} "" ${_prefix} > ${_header} + DEPENDS ${_infile} VERBATIM) + + add_custom_command(OUTPUT "${_code}" + COMMAND ${QtWaylandScanner_EXECUTABLE} server-code ${_infile} "" ${_prefix} > ${_code} + DEPENDS ${_infile} ${_header} VERBATIM) + + set_property(SOURCE ${_header} ${_code} PROPERTY SKIP_AUTOMOC ON) + + if (TARGET ${target_or_sources_var}) + target_sources(${target_or_sources_var} PRIVATE "${_code}") + else() + list(APPEND ${target_or_sources_var} "${_code}") + set(${target_or_sources_var} ${${target_or_sources_var}} PARENT_SCOPE) + endif() +endfunction() 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/FindUDev.cmake b/cmake/find-modules/FindUDev.cmake new file mode 100644 index 000000000000..bab092f8a03f --- /dev/null +++ b/cmake/find-modules/FindUDev.cmake @@ -0,0 +1,71 @@ +# SPDX-FileCopyrightText: 2010 Rafael Fernández López +# SPDX-FileCopyrightText: 2019 Volker Krause +# +# SPDX-License-Identifier: BSD-3-Clause + +#[=======================================================================[.rst: +FindUDev +-------- + +Try to find the UDev library. + +This will define the following variables: + +``UDev_FOUND`` + System has UDev. + +``UDev_INCLUDE_DIRS`` + The libudev include directory. + +``UDev_LIBRARIES`` + The libudev libraries. + +``UDev_VERSION`` + The libudev version. + +If ``UDev_FOUND`` is TRUE, it will also define the following imported +target: + +``UDev::UDev`` + The UDev library + +Since 5.57.0. +#]=======================================================================] + +find_package(PkgConfig QUIET) +pkg_check_modules(PC_UDEV QUIET libudev) + +find_path(UDev_INCLUDE_DIRS NAMES libudev.h HINTS ${PC_UDEV_INCLUDE_DIRS}) +find_library(UDev_LIBRARIES NAMES udev HINTS ${PC_UDEV_LIBRARY_DIRS}) + +set(UDev_VERSION ${PC_UDEV_VERSION}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(UDev + FOUND_VAR UDev_FOUND + REQUIRED_VARS UDev_INCLUDE_DIRS UDev_LIBRARIES + VERSION_VAR UDev_VERSION +) + +mark_as_advanced(UDev_INCLUDE_DIRS UDev_LIBRARIES) + +if(UDev_FOUND AND NOT TARGET UDev::UDev) + add_library(UDev::UDev UNKNOWN IMPORTED) + set_target_properties(UDev::UDev PROPERTIES + IMPORTED_LOCATION "${UDev_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${UDev_INCLUDE_DIRS}" + INTERFACE_COMPILE_DEFINITIONS "${PC_UDEV_CFLAGS_OTHER}" + ) +endif() + +# backward compat variables, remove for KF6 +set(UDEV_FOUND ${UDev_FOUND}) +set(UDEV_LIBS ${UDev_LIBRARIES}) +set(UDEV_INCLUDE_DIR ${UDev_INCLUDE_DIRS}) +mark_as_advanced(UDEV_FOUND UDEV_LIBS UDEV_INCLUDE_DIR) + +include(FeatureSummary) +set_package_properties(UDev PROPERTIES + DESCRIPTION "API for enumerating and introspecting local devices (part of systemd)" + URL "https://www.freedesktop.org/wiki/Software/systemd/" +) diff --git a/cmake/find-modules/FindWayland.cmake b/cmake/find-modules/FindWayland.cmake new file mode 100644 index 000000000000..926fd4855282 --- /dev/null +++ b/cmake/find-modules/FindWayland.cmake @@ -0,0 +1,137 @@ +# SPDX-FileCopyrightText: 2014 Alex Merry +# SPDX-FileCopyrightText: 2014 Martin Gräßlin +# +# SPDX-License-Identifier: BSD-3-Clause + +#[=======================================================================[.rst: +FindWayland +----------- + +Try to find Wayland. + +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:: + + Client Server Cursor Egl + +If no components are specified, this module will act as though all components +were passed to OPTIONAL_COMPONENTS. + +This module will define the following variables, independently of the +components searched for or found: + +``Wayland_FOUND`` + TRUE if (the requested version of) Wayland is available +``Wayland_VERSION`` + Found Wayland version +``Wayland_TARGETS`` + A list of all targets imported by this module (note that there may be more + than the components that were requested) +``Wayland_LIBRARIES`` + This can be passed to target_link_libraries() instead of the imported + targets +``Wayland_INCLUDE_DIRS`` + This should be passed to target_include_directories() if the targets are + not used for linking +``Wayland_DEFINITIONS`` + This should be passed to target_compile_options() if the targets are not + used for linking +``Wayland_DATADIR`` + The core wayland protocols data directory + Since 5.73.0 + +For each searched-for components, ``Wayland__FOUND`` will be set to +TRUE if the corresponding Wayland library was found, and FALSE otherwise. If +``Wayland__FOUND`` is TRUE, the imported target +``Wayland::`` will be defined. This module will also attempt to +determine ``Wayland_*_VERSION`` variables for each imported target, although +``Wayland_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. +#]=======================================================================] + +include(${CMAKE_CURRENT_LIST_DIR}/ECMFindModuleHelpersStub.cmake) + +ecm_find_package_version_check(Wayland) + +set(Wayland_known_components + Client + Server + Cursor + Egl +) +foreach(_comp ${Wayland_known_components}) + string(TOLOWER "${_comp}" _lc_comp) + set(Wayland_${_comp}_component_deps) + set(Wayland_${_comp}_pkg_config "wayland-${_lc_comp}") + set(Wayland_${_comp}_lib "wayland-${_lc_comp}") + set(Wayland_${_comp}_header "wayland-${_lc_comp}.h") +endforeach() +set(Wayland_Egl_component_deps Client) + +ecm_find_package_parse_components(Wayland + RESULT_VAR Wayland_components + KNOWN_COMPONENTS ${Wayland_known_components} +) +ecm_find_package_handle_library_components(Wayland + COMPONENTS ${Wayland_components} +) + +# If pkg-config didn't provide us with version information, +# try to extract it from wayland-version.h +# (Note that the version from wayland-egl.pc will probably be +# the Mesa version, rather than the Wayland version, but that +# version will be ignored as we always find wayland-client.pc +# first). +if(NOT Wayland_VERSION) + find_file(Wayland_VERSION_HEADER + NAMES wayland-version.h + HINTS ${Wayland_INCLUDE_DIRS} + ) + mark_as_advanced(Wayland_VERSION_HEADER) + if(Wayland_VERSION_HEADER) + file(READ ${Wayland_VERSION_HEADER} _wayland_version_header_contents) + string(REGEX REPLACE + "^.*[ \t]+WAYLAND_VERSION[ \t]+\"([0-9.]*)\".*$" + "\\1" + Wayland_VERSION + "${_wayland_version_header_contents}" + ) + unset(_wayland_version_header_contents) + endif() +endif() + +find_package_handle_standard_args(Wayland + FOUND_VAR + Wayland_FOUND + REQUIRED_VARS + Wayland_LIBRARIES + VERSION_VAR + Wayland_VERSION + HANDLE_COMPONENTS +) + +pkg_get_variable(Wayland_DATADIR wayland-scanner pkgdatadir) +if (CMAKE_CROSSCOMPILING AND (NOT EXISTS "${Wayland_DATADIR}/wayland.xml")) + # PKG_CONFIG_SYSROOT_DIR only applies to -I and -L flags, so pkg-config + # does not prepend CMAKE_SYSROOT when cross-compiling unless you pass + # --define-prefix explicitly. Therefore we have to manually do prepend + # it here when cross-compiling. + # See https://gitlab.kitware.com/cmake/cmake/-/issues/16647#note_844761 + set(Wayland_DATADIR ${CMAKE_SYSROOT}${Wayland_DATADIR}) +endif() +if (NOT EXISTS "${Wayland_DATADIR}/wayland.xml") + message(WARNING "Could not find wayland.xml in ${Wayland_DATADIR}") +endif() + +include(FeatureSummary) +set_package_properties(Wayland PROPERTIES + URL "https://wayland.freedesktop.org/" + DESCRIPTION "C library implementation of the Wayland protocol: a protocol for a compositor to talk to its clients" +) diff --git a/cmake/find-modules/FindWaylandProtocols.cmake b/cmake/find-modules/FindWaylandProtocols.cmake new file mode 100644 index 000000000000..a4449885d99f --- /dev/null +++ b/cmake/find-modules/FindWaylandProtocols.cmake @@ -0,0 +1,38 @@ +# SPDX-FileCopyrightText: 2019 Vlad Zahorodnii +# +# SPDX-License-Identifier: BSD-3-Clause + +#[=======================================================================[.rst: +FindWaylandProtocols +-------------------- + +Try to find wayland-protocols on a Unix system. + +This will define the following variables: + +``WaylandProtocols_FOUND`` + True if (the requested version of) wayland-protocols is available +``WaylandProtocols_VERSION`` + The version of wayland-protocols +``WaylandProtocols_DATADIR`` + The wayland protocols data directory +#]=======================================================================] + +find_package(PkgConfig QUIET) +pkg_check_modules(PKG_wayland_protocols QUIET wayland-protocols) + +set(WaylandProtocols_VERSION ${PKG_wayland_protocols_VERSION}) +pkg_get_variable(WaylandProtocols_DATADIR wayland-protocols pkgdatadir) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(WaylandProtocols + FOUND_VAR WaylandProtocols_FOUND + REQUIRED_VARS WaylandProtocols_DATADIR + VERSION_VAR WaylandProtocols_VERSION +) + +include(FeatureSummary) +set_package_properties(WaylandProtocols PROPERTIES + DESCRIPTION "Specifications of extended Wayland protocols" + URL "https://wayland.freedesktop.org/" +) diff --git a/cmake/find-modules/FindWaylandScanner.cmake b/cmake/find-modules/FindWaylandScanner.cmake new file mode 100644 index 000000000000..2fa1fd6e8bc2 --- /dev/null +++ b/cmake/find-modules/FindWaylandScanner.cmake @@ -0,0 +1,183 @@ +# SPDX-FileCopyrightText: 2012-2014 Pier Luigi Fiorini +# +# 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. + +Since 1.4.0. +#]=======================================================================] + +include(${CMAKE_CURRENT_LIST_DIR}/ECMFindModuleHelpersStub.cmake) + +ecm_find_package_version_check(WaylandScanner) + +# Find wayland-scanner +find_program(WaylandScanner_EXECUTABLE NAMES wayland-scanner) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(WaylandScanner + FOUND_VAR + WaylandScanner_FOUND + REQUIRED_VARS + WaylandScanner_EXECUTABLE +) + +mark_as_advanced(WaylandScanner_EXECUTABLE) + +if(NOT TARGET Wayland::Scanner AND WaylandScanner_FOUND) + add_executable(Wayland::Scanner IMPORTED) + set_target_properties(Wayland::Scanner PROPERTIES + IMPORTED_LOCATION "${WaylandScanner_EXECUTABLE}" + ) +endif() + +include(FeatureSummary) +set_package_properties(WaylandScanner PROPERTIES + URL "https://wayland.freedesktop.org/" + DESCRIPTION "Executable that converts XML protocol files to C code" +) + +function(ecm_add_wayland_client_protocol target_or_sources_var) + # Parse arguments + set(options PRIVATE_CODE) + set(oneValueArgs PROTOCOL BASENAME) + cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "" ${ARGN}) + + if(ARGS_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown keywords given to ecm_add_wayland_client_protocol(): \"${ARGS_UNPARSED_ARGUMENTS}\"") + endif() + + 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) + + add_custom_command(OUTPUT "${_client_header}" + COMMAND ${WaylandScanner_EXECUTABLE} client-header ${_infile} ${_client_header} + DEPENDS ${_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() +endfunction() + + +function(ecm_add_wayland_server_protocol target_or_sources_var) + # Parse arguments + set(options PRIVATE_CODE) + set(oneValueArgs PROTOCOL BASENAME) + cmake_parse_arguments(ARGS "${options}" "${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} + PROTOCOL ${ARGS_PROTOCOL} + BASENAME ${ARGS_BASENAME} + ${_private_code_option}) + + 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_source_files_properties(${_server_header} GENERATED) + + add_custom_command(OUTPUT "${_server_header}" + COMMAND ${WaylandScanner_EXECUTABLE} server-header ${_infile} ${_server_header} + DEPENDS ${_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() +endfunction() diff --git a/cmake/find-modules/FindX11_XCB.cmake b/cmake/find-modules/FindX11_XCB.cmake new file mode 100644 index 000000000000..f571bacff46e --- /dev/null +++ b/cmake/find-modules/FindX11_XCB.cmake @@ -0,0 +1,96 @@ +# SPDX-FileCopyrightText: 2014 Alex Merry +# SPDX-FileCopyrightText: 2011 Fredrik Höglund +# SPDX-FileCopyrightText: 2008 Helio Chissini de Castro +# SPDX-FileCopyrightText: 2007 Matthias Kretz +# +# SPDX-License-Identifier: BSD-3-Clause + +#[=======================================================================[.rst: +FindX11_XCB +----------- + +Try to find the X11 XCB compatibility library. + +This will define the following variables: + +``X11_XCB_FOUND`` + True if (the requested version of) libX11-xcb is available +``X11_XCB_VERSION`` + The version of libX11-xcb (this is not guaranteed to be set even when + X11_XCB_FOUND is true) +``X11_XCB_LIBRARIES`` + This can be passed to target_link_libraries() instead of the ``EGL::EGL`` + target +``X11_XCB_INCLUDE_DIR`` + This should be passed to target_include_directories() if the target is not + used for linking +``X11_XCB_DEFINITIONS`` + This should be passed to target_compile_options() if the target is not + used for linking + +If ``X11_XCB_FOUND`` is TRUE, it will also define the following imported +target: + +``X11::XCB`` + The X11 XCB compatibility 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. +#]=======================================================================] + +include(${CMAKE_CURRENT_LIST_DIR}/ECMFindModuleHelpersStub.cmake) + +ecm_find_package_version_check(X11_XCB) + +# 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_X11_XCB QUIET x11-xcb) + +set(X11_XCB_DEFINITIONS ${PKG_X11_XCB_CFLAGS_OTHER}) +set(X11_XCB_VERSION ${PKG_X11_XCB_VERSION}) + +find_path(X11_XCB_INCLUDE_DIR + NAMES X11/Xlib-xcb.h + HINTS ${PKG_X11_XCB_INCLUDE_DIRS} +) +find_library(X11_XCB_LIBRARY + NAMES X11-xcb + HINTS ${PKG_X11_XCB_LIBRARY_DIRS} +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(X11_XCB + FOUND_VAR + X11_XCB_FOUND + REQUIRED_VARS + X11_XCB_LIBRARY + X11_XCB_INCLUDE_DIR + VERSION_VAR + X11_XCB_VERSION +) + +if(X11_XCB_FOUND AND NOT TARGET X11::XCB) + add_library(X11::XCB UNKNOWN IMPORTED) + set_target_properties(X11::XCB PROPERTIES + IMPORTED_LOCATION "${X11_XCB_LIBRARY}" + INTERFACE_COMPILE_OPTIONS "${X11_XCB_DEFINITIONS}" + INTERFACE_INCLUDE_DIRECTORIES "${X11_XCB_INCLUDE_DIR}" + ) +endif() + +mark_as_advanced(X11_XCB_INCLUDE_DIR X11_XCB_LIBRARY) + +# compatibility variables +set(X11_XCB_LIBRARIES ${X11_XCB_LIBRARY}) +set(X11_XCB_INCLUDE_DIRS ${X11_XCB_INCLUDE_DIR}) +set(X11_XCB_VERSION_STRING ${X11_XCB_VERSION}) + +include(FeatureSummary) +set_package_properties(X11_XCB PROPERTIES + URL "https://xorg.freedesktop.org/" + DESCRIPTION "A compatibility library for code that translates Xlib API calls into XCB calls" +) diff --git a/cmake/find-modules/FindXCB.cmake b/cmake/find-modules/FindXCB.cmake new file mode 100644 index 000000000000..f18c2fd09595 --- /dev/null +++ b/cmake/find-modules/FindXCB.cmake @@ -0,0 +1,168 @@ +# SPDX-FileCopyrightText: 2011 Fredrik Höglund +# SPDX-FileCopyrightText: 2013 Martin Gräßlin +# SPDX-FileCopyrightText: 2014-2015 Alex Merry +# +# 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. + +Since pre-1.0.0. +#]=======================================================================] + +include(${CMAKE_CURRENT_LIST_DIR}/ECMFindModuleHelpersStub.cmake) + +ecm_find_package_version_check(XCB) + +# Note that this list needs to be ordered such that any component +# appears after its dependencies +set(XCB_known_components + XCB + RENDER + SHAPE + XFIXES + SHM + ATOM + AUX + COMPOSITE + CURSOR + DAMAGE + DPMS + DRI2 + DRI3 + EVENT + EWMH + GLX + ICCCM + IMAGE + KEYSYMS + PRESENT + RANDR + RECORD + RENDERUTIL + RES + SCREENSAVER + SYNC + UTIL + XF86DRI + XINERAMA + XINPUT + XKB + XTEST + XV + XVMC +) + +# default component info: xcb components have fairly predictable +# header files, library names and pkg-config names +foreach(_comp ${XCB_known_components}) + string(TOLOWER "${_comp}" _lc_comp) + set(XCB_${_comp}_component_deps XCB) + set(XCB_${_comp}_pkg_config "xcb-${_lc_comp}") + set(XCB_${_comp}_lib "xcb-${_lc_comp}") + set(XCB_${_comp}_header "xcb/${_lc_comp}.h") +endforeach() +# exceptions +set(XCB_XCB_component_deps) +set(XCB_COMPOSITE_component_deps XCB XFIXES) +set(XCB_DAMAGE_component_deps XCB XFIXES) +set(XCB_IMAGE_component_deps XCB SHM) +set(XCB_RENDERUTIL_component_deps XCB RENDER) +set(XCB_XFIXES_component_deps XCB RENDER SHAPE) +set(XCB_XVMC_component_deps XCB XV) +set(XCB_XV_component_deps XCB SHM) +set(XCB_XCB_pkg_config "xcb") +set(XCB_XCB_lib "xcb") +set(XCB_ATOM_header "xcb/xcb_atom.h") +set(XCB_ATOM_lib "xcb-util") +set(XCB_AUX_header "xcb/xcb_aux.h") +set(XCB_AUX_lib "xcb-util") +set(XCB_CURSOR_header "xcb/xcb_cursor.h") +set(XCB_EVENT_header "xcb/xcb_event.h") +set(XCB_EVENT_lib "xcb-util") +set(XCB_EWMH_header "xcb/xcb_ewmh.h") +set(XCB_ICCCM_header "xcb/xcb_icccm.h") +set(XCB_IMAGE_header "xcb/xcb_image.h") +set(XCB_KEYSYMS_header "xcb/xcb_keysyms.h") +set(XCB_PIXEL_header "xcb/xcb_pixel.h") +set(XCB_RENDERUTIL_header "xcb/xcb_renderutil.h") +set(XCB_RENDERUTIL_lib "xcb-render-util") +set(XCB_UTIL_header "xcb/xcb_util.h") + +ecm_find_package_parse_components(XCB + RESULT_VAR XCB_components + KNOWN_COMPONENTS ${XCB_known_components} + DEFAULT_COMPONENTS ${XCB_default_components} +) + +ecm_find_package_handle_library_components(XCB + COMPONENTS ${XCB_components} +) + +find_package_handle_standard_args(XCB + FOUND_VAR + XCB_FOUND + REQUIRED_VARS + XCB_LIBRARIES + VERSION_VAR + XCB_VERSION + HANDLE_COMPONENTS +) + +include(FeatureSummary) +set_package_properties(XCB PROPERTIES + URL "https://xcb.freedesktop.org/" + DESCRIPTION "X protocol C-language Binding" +) diff --git a/cmake/modules/ECMFindModuleHelpers.cmake b/cmake/modules/ECMFindModuleHelpers.cmake new file mode 100644 index 000000000000..92c796e6bdd5 --- /dev/null +++ b/cmake/modules/ECMFindModuleHelpers.cmake @@ -0,0 +1,277 @@ +# SPDX-FileCopyrightText: 2014 Alex Merry +# +# 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). + +Since pre-1.0.0. +#]=======================================================================] + +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") + 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") + endif() +endmacro() + +macro(ecm_find_package_parse_components module_name) + set(ecm_fppc_options SKIP_DEPENDENCY_HANDLING) + set(ecm_fppc_oneValueArgs RESULT_VAR) + set(ecm_fppc_multiValueArgs KNOWN_COMPONENTS DEFAULT_COMPONENTS) + cmake_parse_arguments(ECM_FPPC "${ecm_fppc_options}" "${ecm_fppc_oneValueArgs}" "${ecm_fppc_multiValueArgs}" ${ARGN}) + + if(ECM_FPPC_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unexpected arguments to ecm_find_package_parse_components: ${ECM_FPPC_UNPARSED_ARGUMENTS}") + endif() + if(NOT ECM_FPPC_RESULT_VAR) + message(FATAL_ERROR "Missing RESULT_VAR argument to ecm_find_package_parse_components") + endif() + if(NOT ECM_FPPC_KNOWN_COMPONENTS) + message(FATAL_ERROR "Missing KNOWN_COMPONENTS argument to ecm_find_package_parse_components") + endif() + if(NOT ECM_FPPC_DEFAULT_COMPONENTS) + set(ECM_FPPC_DEFAULT_COMPONENTS ${ECM_FPPC_KNOWN_COMPONENTS}) + endif() + + if(${module_name}_FIND_COMPONENTS) + set(ecm_fppc_requestedComps ${${module_name}_FIND_COMPONENTS}) + + if(NOT ECM_FPPC_SKIP_DEPENDENCY_HANDLING) + # Make sure deps are included + foreach(ecm_fppc_comp ${ecm_fppc_requestedComps}) + foreach(ecm_fppc_dep_comp ${${module_name}_${ecm_fppc_comp}_component_deps}) + list(FIND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}" ecm_fppc_index) + if("${ecm_fppc_index}" STREQUAL "-1") + if(NOT ${module_name}_FIND_QUIETLY) + message(STATUS "${module_name}: ${ecm_fppc_comp} requires ${${module_name}_${ecm_fppc_comp}_component_deps}") + endif() + list(APPEND ecm_fppc_requestedComps "${ecm_fppc_dep_comp}") + endif() + endforeach() + endforeach() + else() + message(STATUS "Skipping dependency handling for ${module_name}") + endif() + list(REMOVE_DUPLICATES ecm_fppc_requestedComps) + + # This makes sure components are listed in the same order as + # KNOWN_COMPONENTS (potentially important for inter-dependencies) + set(${ECM_FPPC_RESULT_VAR}) + foreach(ecm_fppc_comp ${ECM_FPPC_KNOWN_COMPONENTS}) + list(FIND ecm_fppc_requestedComps "${ecm_fppc_comp}" ecm_fppc_index) + if(NOT "${ecm_fppc_index}" STREQUAL "-1") + list(APPEND ${ECM_FPPC_RESULT_VAR} "${ecm_fppc_comp}") + list(REMOVE_AT ecm_fppc_requestedComps ${ecm_fppc_index}) + endif() + endforeach() + # if there are any left, they are unknown components + if(ecm_fppc_requestedComps) + set(ecm_fppc_msgType STATUS) + if(${module_name}_FIND_REQUIRED) + set(ecm_fppc_msgType FATAL_ERROR) + endif() + if(NOT ${module_name}_FIND_QUIETLY) + message(${ecm_fppc_msgType} "${module_name}: requested unknown components ${ecm_fppc_requestedComps}") + endif() + return() + endif() + else() + set(${ECM_FPPC_RESULT_VAR} ${ECM_FPPC_DEFAULT_COMPONENTS}) + endif() +endmacro() + +macro(ecm_find_package_handle_library_components module_name) + set(ecm_fpwc_options SKIP_PKG_CONFIG SKIP_DEPENDENCY_HANDLING) + set(ecm_fpwc_oneValueArgs) + set(ecm_fpwc_multiValueArgs COMPONENTS) + cmake_parse_arguments(ECM_FPWC "${ecm_fpwc_options}" "${ecm_fpwc_oneValueArgs}" "${ecm_fpwc_multiValueArgs}" ${ARGN}) + + if(ECM_FPWC_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unexpected arguments to ecm_find_package_handle_components: ${ECM_FPWC_UNPARSED_ARGUMENTS}") + endif() + if(NOT ECM_FPWC_COMPONENTS) + message(FATAL_ERROR "Missing COMPONENTS argument to ecm_find_package_handle_components") + endif() + + include(FindPackageHandleStandardArgs) + find_package(PkgConfig QUIET) + foreach(ecm_fpwc_comp ${ECM_FPWC_COMPONENTS}) + set(ecm_fpwc_dep_vars) + set(ecm_fpwc_dep_targets) + if(NOT SKIP_DEPENDENCY_HANDLING) + foreach(ecm_fpwc_dep ${${module_name}_${ecm_fpwc_comp}_component_deps}) + list(APPEND ecm_fpwc_dep_vars "${module_name}_${ecm_fpwc_dep}_FOUND") + list(APPEND ecm_fpwc_dep_targets "${module_name}::${ecm_fpwc_dep}") + endforeach() + endif() + + if(NOT ECM_FPWC_SKIP_PKG_CONFIG AND ${module_name}_${ecm_fpwc_comp}_pkg_config) + pkg_check_modules(PKG_${module_name}_${ecm_fpwc_comp} QUIET + ${${module_name}_${ecm_fpwc_comp}_pkg_config}) + endif() + + find_path(${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR + NAMES ${${module_name}_${ecm_fpwc_comp}_header} + HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_INCLUDE_DIRS} + PATH_SUFFIXES ${${module_name}_${ecm_fpwc_comp}_header_subdir} + ) + find_library(${module_name}_${ecm_fpwc_comp}_LIBRARY + NAMES ${${module_name}_${ecm_fpwc_comp}_lib} + HINTS ${PKG_${module_name}_${ecm_fpwc_comp}_LIBRARY_DIRS} + ) + + set(${module_name}_${ecm_fpwc_comp}_VERSION "${PKG_${module_name}_${ecm_fpwc_comp}_VERSION}") + if(NOT ${module_name}_VERSION) + set(${module_name}_VERSION ${${module_name}_${ecm_fpwc_comp}_VERSION}) + endif() + + set(FPHSA_NAME_MISMATCHED 1) + find_package_handle_standard_args(${module_name}_${ecm_fpwc_comp} + FOUND_VAR + ${module_name}_${ecm_fpwc_comp}_FOUND + REQUIRED_VARS + ${module_name}_${ecm_fpwc_comp}_LIBRARY + ${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR + ${ecm_fpwc_dep_vars} + VERSION_VAR + ${module_name}_${ecm_fpwc_comp}_VERSION + ) + unset(FPHSA_NAME_MISMATCHED) + + mark_as_advanced( + ${module_name}_${ecm_fpwc_comp}_LIBRARY + ${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR + ) + + if(${module_name}_${ecm_fpwc_comp}_FOUND) + list(APPEND ${module_name}_LIBRARIES + "${${module_name}_${ecm_fpwc_comp}_LIBRARY}") + list(APPEND ${module_name}_INCLUDE_DIRS + "${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}") + set(${module_name}_DEFINITIONS + ${${module_name}_DEFINITIONS} + ${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS}) + if(NOT TARGET ${module_name}::${ecm_fpwc_comp}) + add_library(${module_name}::${ecm_fpwc_comp} UNKNOWN IMPORTED) + set_target_properties(${module_name}::${ecm_fpwc_comp} PROPERTIES + IMPORTED_LOCATION "${${module_name}_${ecm_fpwc_comp}_LIBRARY}" + INTERFACE_COMPILE_OPTIONS "${PKG_${module_name}_${ecm_fpwc_comp}_DEFINITIONS}" + INTERFACE_INCLUDE_DIRECTORIES "${${module_name}_${ecm_fpwc_comp}_INCLUDE_DIR}" + INTERFACE_LINK_LIBRARIES "${ecm_fpwc_dep_targets}" + ) + endif() + list(APPEND ${module_name}_TARGETS + "${module_name}::${ecm_fpwc_comp}") + endif() + endforeach() + if(${module_name}_LIBRARIES) + list(REMOVE_DUPLICATES ${module_name}_LIBRARIES) + endif() + if(${module_name}_INCLUDE_DIRS) + list(REMOVE_DUPLICATES ${module_name}_INCLUDE_DIRS) + endif() + if(${module_name}_DEFINITIONS) + list(REMOVE_DUPLICATES ${module_name}_DEFINITIONS) + endif() + if(${module_name}_TARGETS) + list(REMOVE_DUPLICATES ${module_name}_TARGETS) + endif() +endmacro() diff --git a/src/AnalyzeView/CMakeLists.txt b/src/AnalyzeView/CMakeLists.txt index 4531ad9295b8..779077f2804a 100644 --- a/src/AnalyzeView/CMakeLists.txt +++ b/src/AnalyzeView/CMakeLists.txt @@ -58,10 +58,12 @@ target_link_libraries(AnalyzeView set(MINIMUM_EXIV2_VERSION 0.28.3) if(NOT QGC_BUILD_DEPENDENCIES) - find_package(Exiv2 ${MINIMUM_EXIV2_VERSION} CONFIG) - if(Exiv2_FOUND) - message(STATUS "Found Exiv2 ${Exiv2_VERSION_STRING}") - target_link_libraries(AnalyzeView PRIVATE Exiv2::exiv2lib) + find_package(Iconv) + find_package(EXPAT) + find_package(LibExiv2 ${MINIMUM_EXIV2_VERSION}) + if(LibExiv2_FOUND) + message(STATUS "Found Exiv2 ${LibExiv2_VERSION}") + target_link_libraries(AnalyzeView PRIVATE LibExiv2::LibExiv2) else() find_package(PkgConfig) if(PkgConfig_FOUND) @@ -74,7 +76,7 @@ if(NOT QGC_BUILD_DEPENDENCIES) endif() endif() -if(NOT Exiv2_FOUND) +if(NOT Exiv2_FOUND AND NOT LibExiv2_FOUND) message(STATUS "Building Exiv2") include(FetchContent) FetchContent_Declare(EXIV2 @@ -88,7 +90,7 @@ if(NOT Exiv2_FOUND) set(EXIV2_ENABLE_PNG OFF CACHE INTERNAL "" FORCE) set(EXIV2_ENABLE_NLS OFF CACHE INTERNAL "" FORCE) set(EXIV2_ENABLE_LENSDATA OFF CACHE INTERNAL "" FORCE) - set(EXIV2_ENABLE_DYNAMIC_RUNTIME ON CACHE INTERNAL "" FORCE) + set(EXIV2_ENABLE_DYNAMIC_RUNTIME OFF CACHE INTERNAL "" FORCE) set(EXIV2_ENABLE_WEBREADY OFF CACHE INTERNAL "" FORCE) set(EXIV2_ENABLE_CURL OFF CACHE INTERNAL "" FORCE) set(EXIV2_ENABLE_BMFF OFF CACHE INTERNAL "" FORCE) @@ -102,6 +104,7 @@ if(NOT Exiv2_FOUND) set(EXIV2_BUILD_FUZZ_TESTS OFF CACHE INTERNAL "" FORCE) set(EXIV2_BUILD_DOC OFF CACHE INTERNAL "" FORCE) set(BUILD_WITH_CCACHE ON CACHE INTERNAL "" FORCE) + set(Iconv_FOUND OFF) FetchContent_MakeAvailable(EXIV2) target_link_libraries(AnalyzeView PRIVATE exiv2lib) diff --git a/src/Settings/CMakeLists.txt b/src/Settings/CMakeLists.txt index 9db4d8cf6688..3a9b440c2c24 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 f8ef9fec947b..000000000000 --- 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 3b0b0df594aa..2d8931c60098 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 daed65b6a6a5..51437f6cb867 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 ee2502322ec6..b3187332eed2 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") @@ -118,7 +118,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 f1f723be9554..d77333d39936 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,8 +34,8 @@ target_sources(GStreamerReceiver if(IOS) target_sources(GStreamerReceiver PRIVATE - gst_ios_init.h gst_ios_init.m + gst_ios_init.h ) endif() @@ -47,6 +50,3 @@ target_link_libraries(GStreamerReceiver ) target_compile_definitions(QGC PUBLIC QGC_GST_STREAMING) -if(CMAKE_BUILD_TYPE STREQUAL "Release") - target_compile_definitions(GStreamerReceiver PRIVATE QGC_INSTALL_RELEASE) -endif() diff --git a/src/VideoManager/VideoReceiver/GStreamer/GLVideoItemStub.h b/src/VideoManager/VideoReceiver/GStreamer/GLVideoItemStub.h index 38d58d468817..63e454b66e70 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 0e2784c98312..e6a172a971e3 100644 --- a/src/VideoManager/VideoReceiver/GStreamer/GStreamer.cc +++ b/src/VideoManager/VideoReceiver/GStreamer/GStreamer.cc @@ -7,30 +7,56 @@ * ****************************************************************************/ - -/** - * @file - * @brief QGC Video Streaming Initialization - * @author Gus Grubba - */ - #include "GStreamer.h" #include "GstVideoReceiver.h" #include "QGCLoggingCategory.h" +#ifdef Q_OS_IOS +#include "gst_ios_init.h" +#endif -#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") -static void qt_gst_log(GstDebugCategory * category, - GstDebugLevel level, - const gchar * file, - const gchar * function, - gint line, - GObject * object, - GstDebugMessage * message, - gpointer data) +namespace GStreamer { + +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); +GST_PLUGIN_STATIC_DECLARE(va); +#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 + +void _QtGstLog(GstDebugCategory *category, + GstDebugLevel level, + const gchar *file, + const gchar *function, + gint line, + GObject *object, + GstDebugMessage *message, + gpointer data) { Q_UNUSED(data); @@ -40,7 +66,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 +92,116 @@ 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) +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())); + (void) qputenv(key.toStdString().c_str(), QByteArray(value.toStdString().c_str())); } -#endif -void -GStreamer::blacklist(VideoDecoderOptions option) +void blacklist(VideoDecoderOptions option) { - GstRegistry* registry = gst_registry_get(); + GstRegistry* const registry = gst_registry_get(); - if (registry == nullptr) { + if (!registry) { 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) { + 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); - gst_registry_add_feature(registry, feature); + (void) 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; + 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; } } -void -GStreamer::initialize(int argc, char* argv[], int debuglevel) +void initialize(int argc, char *argv[], int debuglevel) { - qRegisterMetaType("STATUS"); + (void) 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) - // 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"); + +#ifndef QGC_INSTALL_RELEASE + qunsetenv("GST_PLUGIN_SYSTEM_PATH_1_0"); + qunsetenv("GST_PLUGIN_SYSTEM_PATH"); + qunsetenv("GST_PLUGIN_PATH_1_0"); + qunsetenv("GST_PLUGIN_PATH"); + Q_UNUSED(currentDir); +#elif defined(Q_OS_MAC) + _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("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"); +#elif defined(Q_OS_WIN) + _qgcputenv("GST_REGISTRY_REUSE_PLUGIN_SCANNER", "no"); + _qgcputenv("GST_PLUGIN_SCANNER", currentDir, "/gstreamer-1.0/gst-plugin-scanner"); + _qgcputenv("GST_PTP_HELPER_1_0", currentDir, "/gstreamer-1.0/gst-ptp-helper"); + _qgcputenv("GIO_EXTRA_MODULES", currentDir, "/gio/modules"); + _qgcputenv("GST_PLUGIN_SYSTEM_PATH_1_0", currentDir); + _qgcputenv("GST_PLUGIN_SYSTEM_PATH", currentDir); + _qgcputenv("GST_PLUGIN_PATH_1_0", currentDir); + _qgcputenv("GST_PLUGIN_PATH", currentDir); #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); + gst_debug_add_log_function(_QtGstLog, nullptr, nullptr); } - // Initialize GStreamer -#if defined(Q_OS_IOS) - //-- iOS specific initialization +#ifdef Q_OS_IOS gst_ios_pre_init(); #endif - GError* error = nullptr; + GError *error = nullptr; if (!gst_init_check(&argc, &argv, &error)) { - qCCritical(GStreamerLog) << "gst_init_check() failed: " << error->message; + qCCritical(GStreamerLog) << "gst_init_check() failed:" << error->message; g_error_free(error); } - // The static plugins we use #ifdef QGC_GST_STATIC_BUILD GST_PLUGIN_STATIC_REGISTER(coreelements); GST_PLUGIN_STATIC_REGISTER(playback); @@ -240,30 +217,25 @@ 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); + GST_PLUGIN_STATIC_REGISTER(va); +#ifdef Q_OS_ANDROID GST_PLUGIN_STATIC_REGISTER(androidmedia); #elif defined(Q_OS_IOS) GST_PLUGIN_STATIC_REGISTER(applemedia); -#endif -#endif - -#if defined(Q_OS_IOS) gst_ios_post_init(); #endif - +#endif GST_PLUGIN_STATIC_REGISTER(qml6); GST_PLUGIN_STATIC_REGISTER(qgc); } -void* -GStreamer::createVideoSink(QObject* parent, QQuickItem* widget) +void *createVideoSink(QObject *parent, QQuickItem *widget) { Q_UNUSED(parent) - GstElement* sink; - - if ((sink = gst_element_factory_make("qgcvideosinkbin", nullptr)) != nullptr) { + GstElement* const sink = gst_element_factory_make("qgcvideosinkbin", nullptr); + if (sink) { g_object_set(sink, "widget", widget, NULL); } else { qCCritical(GStreamerLog) << "gst_element_factory_make('qgcvideosinkbin') failed"; @@ -272,17 +244,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); +} + } diff --git a/src/VideoManager/VideoReceiver/GStreamer/GStreamer.h b/src/VideoManager/VideoReceiver/GStreamer/GStreamer.h index 71b185b18f56..e0a9f85c616a 100644 --- a/src/VideoManager/VideoReceiver/GStreamer/GStreamer.h +++ b/src/VideoManager/VideoReceiver/GStreamer/GStreamer.h @@ -10,20 +10,26 @@ #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 blacklist(VideoDecoderOptions option); +void initialize(int argc, char *argv[], int debuglevel); +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.h b/src/VideoManager/VideoReceiver/GStreamer/gst_ios_init.h index 2f86cdb70c6c..b1379bf20c43 100644 --- a/src/VideoManager/VideoReceiver/GStreamer/gst_ios_init.h +++ b/src/VideoManager/VideoReceiver/GStreamer/gst_ios_init.h @@ -13,7 +13,9 @@ G_BEGIN_DECLS -void gst_ios_pre_init(void); -void gst_ios_post_init(void); +void gst_ios_pre_init(); + +/// Lower the ranks of filesrc and giosrc so iosavassetsrc is tried first in gst_element_make_from_uri() for file:// +void gst_ios_post_init(); G_END_DECLS diff --git a/src/VideoManager/VideoReceiver/GStreamer/gst_ios_init.m b/src/VideoManager/VideoReceiver/GStreamer/gst_ios_init.m index 6158e1bca879..76a1f2218268 100644 --- a/src/VideoManager/VideoReceiver/GStreamer/gst_ios_init.m +++ b/src/VideoManager/VideoReceiver/GStreamer/gst_ios_init.m @@ -1,8 +1,23 @@ +/**************************************************************************** + * + * (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. + * + ****************************************************************************/ + #import #import #include "gst_ios_init.h" +#define GST_IOS_GIO_MODULE_GNUTLS +#if defined(GST_IOS_GIO_MODULE_GNUTLS) + #include + GST_G_IO_MODULE_DECLARE(gnutls); +#endif + G_BEGIN_DECLS #define GST_G_IO_MODULE_DECLARE(name) \ @@ -13,68 +28,60 @@ extern void G_PASTE(g_io_module_, G_PASTE(name, _load_static)) (void) G_END_DECLS -#define GST_IOS_GIO_MODULE_GNUTLS +void gst_ios_pre_init() +{ + 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"]; -#if defined(GST_IOS_GIO_MODULE_GNUTLS) - #include - GST_G_IO_MODULE_DECLARE(gnutls); -#endif + 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]; -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); - - ca_certificates = g_build_filename (resources_dir, "ssl", "certs", "ca-certificates.crt", NULL); - g_setenv ("CA_CERTIFICATES", ca_certificates, TRUE); - g_free (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); + + 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:// */ - #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")) { + GstRegistry *const reg = gst_registry_get(); + GstPluginFeature *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); + 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 f8b8cf5d8137..000000000000 --- 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 000000000000..09dc771b85ac --- /dev/null +++ b/src/VideoManager/VideoReceiver/GStreamer/gstqgc.cc @@ -0,0 +1,39 @@ +/**************************************************************************** + * + * (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" +#include "gstqgcvideosinkbin.h" + +static gboolean +plugin_init(GstPlugin *plugin) +{ + gboolean ret = FALSE; + + ret |= GST_ELEMENT_REGISTER(qgcvideosinkbin, plugin); + + return ret; +} + +#define PACKAGE "QGC Video Receiver" +#define VERSION "current" +#define GST_LICENSE "LGPL" +#define GST_PACKAGE_NAME "GStreamer plugin for QGC's Video Receiver" +#define GST_PACKAGE_ORIGIN "https://qgroundcontrol.com/" + +GST_PLUGIN_DEFINE( + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + qgc, + "QGC Video Receiver Plugin", + plugin_init, + 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 000000000000..68154a64f986 --- /dev/null +++ b/src/VideoManager/VideoReceiver/GStreamer/gstqgcelement.cc @@ -0,0 +1,11 @@ +#include "gstqgcelements.h" +#include "gstqgcvideosinkbin.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 000000000000..82befd308cbf --- /dev/null +++ b/src/VideoManager/VideoReceiver/GStreamer/gstqgcelements.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +G_BEGIN_DECLS + +void qgc_element_init(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 59% rename from src/VideoManager/VideoReceiver/GStreamer/gstqgcvideosinkbin.c rename to src/VideoManager/VideoReceiver/GStreamer/gstqgcvideosinkbin.cc index 4e26e158f023..994d1db85395 100644 --- a/src/VideoManager/VideoReceiver/GStreamer/gstqgcvideosinkbin.c +++ b/src/VideoManager/VideoReceiver/GStreamer/gstqgcvideosinkbin.cc @@ -7,38 +7,19 @@ * ****************************************************************************/ -/** - * @file - * @brief GStreamer plugin for QGC's Video Receiver - * @author Andrew Voznyts - * @author Tomaz Canabrava - */ - -#include +#include "gstqgcvideosinkbin.h" +#include "gstqgcelements.h" + #include -#include GST_DEBUG_CATEGORY_STATIC(gst_qgc_video_sink_bin_debug); #define GST_CAT_DEFAULT gst_qgc_video_sink_bin_debug -typedef struct _GstQgcVideoSinkElement GstQgcVideoSinkElement; - -typedef struct _GstQgcVideoSinkBin { - GstBin bin; - GstElement* glupload; - GstElement* qmlglsink; -} GstQgcVideoSinkBin; - -typedef struct _GstQgcVideoSinkBinClass { - GstBinClass parent_class; -} GstQgcVideoSinkBinClass; +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); -#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)) +static gboolean gst_qgc_video_sink_bin_sink_pad_query(GstPad *pad, GstObject *parent, GstQuery *query); enum { PROP_0, @@ -63,109 +44,59 @@ enum { #define DEFAULT_PAR_D 1 #define DEFAULT_SYNC TRUE -static GstBinClass *parent_class; +#define gst_qgc_video_sink_bin_parent_class parent_class +G_DEFINE_TYPE(GstQgcVideoSinkBin, gst_qgc_video_sink_bin, GST_TYPE_BIN); -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); - -static gboolean -_vsb_sink_pad_query(GstPad* pad, GstObject* parent, GstQuery* query) -{ - GstQgcVideoSinkBin *vsb; - GstElement* element; - - vsb = GST_QGC_VIDEO_SINK_BIN(parent); - - 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 (element == NULL) { - GST_ERROR_OBJECT(vsb, "No element found"); - return FALSE; - } - - GstPad* sinkpad = gst_element_get_static_pad(element, "sink"); - - if (sinkpad == NULL) { - GST_ERROR_OBJECT(vsb, "No sink pad found"); - return FALSE; - } - - const gboolean ret = gst_pad_query(sinkpad, query); - - gst_object_unref(sinkpad); - sinkpad = NULL; - - return ret; -} +GST_ELEMENT_REGISTER_DEFINE_WITH_CODE(qgcvideosinkbin, "qgcvideosinkbin", GST_RANK_NONE, GST_TYPE_QGC_VIDEO_SINK_BIN, qgc_element_init(plugin)); 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; + (void) vsb; + gboolean initialized = FALSE; + GstElement *glcolorconvert = NULL; + GstPad *pad = NULL; do { - if ((vsb->glupload = gst_element_factory_make("glupload", NULL)) == NULL) { + if (!(vsb->glupload = gst_element_factory_make("glupload", NULL))) { GST_ERROR_OBJECT(vsb, "gst_element_factory_make('glupload') failed"); break; } - if ((vsb->qmlglsink = gst_element_factory_make("qml6glsink", NULL)) == NULL) { + if (!(vsb->qmlglsink = gst_element_factory_make("qml6glsink", NULL))) { GST_ERROR_OBJECT(vsb, "gst_element_factory_make('qml6glsink') failed"); break; } - if ((glcolorconvert = gst_element_factory_make("glcolorconvert", NULL)) == NULL) { + if (!(glcolorconvert = gst_element_factory_make("glcolorconvert", NULL))) { GST_ERROR_OBJECT(vsb, "gst_element_factory_make('glcolorconvert' failed)"); break; } - if ((pad = gst_element_get_static_pad(vsb->glupload, "sink")) == NULL) { + if (!(pad = gst_element_get_static_pad(vsb->glupload, "sink"))) { GST_ERROR_OBJECT(vsb, "gst_element_get_static_pad(glupload, 'sink') failed"); break; } - gst_object_ref(vsb->glupload); - gst_object_ref(vsb->qmlglsink); + (void) gst_object_ref(vsb->glupload); + (void) gst_object_ref(vsb->qmlglsink); gst_bin_add_many(GST_BIN(vsb), vsb->glupload, glcolorconvert, vsb->qmlglsink, NULL); - gboolean ret = gst_element_link_many(vsb->glupload, glcolorconvert, vsb->qmlglsink, NULL); - + const gboolean ret = gst_element_link_many(vsb->glupload, glcolorconvert, vsb->qmlglsink, NULL); glcolorconvert = NULL; - if (!ret) { GST_ERROR_OBJECT(vsb, "gst_element_link_many() failed"); break; } - GstPad* ghostpad; - - if ((ghostpad = gst_ghost_pad_new("sink", pad)) == NULL) { + GstPad *ghostpad = NULL; + if (!(ghostpad = gst_ghost_pad_new("sink", pad))) { GST_ERROR_OBJECT(vsb, "gst_ghost_pad_new('sink') failed"); break; } - 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"); @@ -175,23 +106,23 @@ _vsb_init(GTypeInstance *instanceData, void *vsbVoid) initialized = TRUE; } while(0); - if (pad != NULL) { + 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; } @@ -199,31 +130,95 @@ _vsb_init(GTypeInstance *instanceData, void *vsbVoid) } static void -_vsb_dispose(GObject *object) +gst_qgc_video_sink_bin_class_init(GstQgcVideoSinkBinClass * klass) { - GstQgcVideoSinkBin *vsb; + GObjectClass *const gobject_klass = reinterpret_cast(klass); + GstElementClass *const gstelement_klass = reinterpret_cast(klass); - vsb = GST_QGC_VIDEO_SINK_BIN(object); + parent_class = g_type_class_peek_parent((GstBinClass*)klass); - if (vsb->qmlglsink != NULL) { - gst_object_unref(vsb->qmlglsink); - vsb->qmlglsink = NULL; + 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; + + 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, + static_cast(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, + static_cast(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", + static_cast(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, + static_cast(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, + static_cast(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, + static_cast(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))); + + gst_element_class_set_static_metadata(gstelement_klass, + "QGC Video Sink Bin", "Sink/Video/Bin", + "Video rendering for QGC", + "Andrew Voznytsa , Tomaz Canabrava "); +} + +static gboolean +gst_qgc_video_sink_bin_sink_pad_query(GstPad *pad, GstObject *parent, GstQuery *query) +{ + GstQgcVideoSinkBin *const vsb = GST_QGC_VIDEO_SINK_BIN(parent); + GstElement *element = NULL; + + 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->glupload != NULL) { - gst_object_unref(vsb->glupload); - vsb->glupload = NULL; + if (!element) { + GST_ERROR_OBJECT(vsb, "No element found"); + return FALSE; } - G_OBJECT_CLASS(parent_class)->dispose(object); + GstPad *sinkpad = gst_element_get_static_pad(element, "sink"); + if (!sinkpad) { + GST_ERROR_OBJECT(vsb, "No sink pad found"); + return FALSE; + } + + 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: @@ -279,11 +274,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 +300,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; - - 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))); + GstQgcVideoSinkBin *const vsb = GST_QGC_VIDEO_SINK_BIN(object); - 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 000000000000..8bd3698685fb --- /dev/null +++ b/src/VideoManager/VideoReceiver/GStreamer/gstqgcvideosinkbin.h @@ -0,0 +1,31 @@ +/**************************************************************************** + * + * (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) + +struct _GstQgcVideoSinkBin { + GstBin bin; + GstElement *glupload; + GstElement *qmlglsink; +}; + +struct _GstQgcVideoSinkBinClass { + GstBinClass parent_class; +}; + +GST_ELEMENT_REGISTER_DECLARE(qgcvideosinkbin); + +G_END_DECLS diff --git a/src/VideoManager/VideoReceiver/GStreamer/gstqml6gl/CMakeLists.txt b/src/VideoManager/VideoReceiver/GStreamer/gstqml6gl/CMakeLists.txt index dcb51a009beb..8610b3e0df4e 100644 --- a/src/VideoManager/VideoReceiver/GStreamer/gstqml6gl/CMakeLists.txt +++ b/src/VideoManager/VideoReceiver/GStreamer/gstqml6gl/CMakeLists.txt @@ -1,30 +1,51 @@ -find_package(GStreamer REQUIRED COMPONENTS Allocators Audio Codecparsers Controller Mpegts Net Pbutils Riff Rtp Rtsp Sdp Tag OPTIONAL_COMPONENTS Va Photography X11 EGL Wayland) +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) + +qt_add_library(gstqml6gl STATIC) -# 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) +target_link_libraries(gstqml6gl + PRIVATE + Qt6::GuiPrivate + PUBLIC + Qt6::Core + Qt6::Gui + 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 +) -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) +# Photography not found on ubuntu 20.04? +if(GStreamer_Photography_FOUND) + target_link_libraries(gstqml6gl PUBLIC GStreamer::Photography) endif() -set(GST_PLUGINS_VERSION ${GST_VERSION_MAJOR}.${GST_VERSION_MINOR}.${GST_VERSION_PATCH}) -cmake_print_variables(GST_PLUGINS_VERSION) +if(GStreamer_Prototypes_FOUND) + target_link_libraries(gstqml6gl PUBLIC GStreamer::Prototypes) +endif() +if(GStreamer_Va_FOUND) + target_link_libraries(gstqml6gl PUBLIC GStreamer::Va) +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 - # https://gitlab.freedesktop.org/gstreamer/gstreamer/-/archive/${GST_PLUGINS_VERSION}/gstreamer-${GST_PLUGINS_VERSION}.zip?path=subprojects/gst-plugins-good/ext/qt6 + # URL https://gitlab.freedesktop.org/gstreamer/gstreamer/-/archive/${GStreamer_VERSION}/gstreamer-${GStreamer_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 ) @@ -50,77 +71,31 @@ file(GLOB gstqml6gl_SRCS ${QGC_GST_QT6_PLUGIN_PATH}/*.cc ${QGC_GST_QT6_PLUGIN_PATH}/*.h ) -qt_add_library(gstqml6gl STATIC ${gstqml6gl_SRCS}) +target_sources(gstqml6gl PRIVATE ${gstqml6gl_SRCS}) target_include_directories(gstqml6gl PUBLIC ${QGC_GST_QT6_PLUGIN_PATH}) -find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Quick) - -target_link_libraries(gstqml6gl - PRIVATE - Qt6::GuiPrivate - PUBLIC - Qt6::Core - Qt6::Gui - Qt6::Qml - Qt6::Quick - GStreamer::GStreamer - GStreamer::Allocators - GStreamer::Audio - GStreamer::Codecparsers - GStreamer::Controller - 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_Va_FOUND) - target_link_libraries(gstqml6gl PUBLIC GStreamer::Va) -endif() +################################################################################ if(GStreamer_X11_FOUND) - # X11, XCB, X11_XCB, GLX target_link_libraries(gstqml6gl PUBLIC GStreamer::X11) target_compile_definitions(gstqml6gl PRIVATE HAVE_QT_X11) endif() if(GStreamer_EGL_FOUND) - # EGL target_link_libraries(gstqml6gl PUBLIC GStreamer::EGL) target_compile_definitions(gstqml6gl PRIVATE HAVE_QT_EGLFS) endif() if(GStreamer_Wayland_FOUND) - # find_package(Wayland COMPONENTS Client Cursor Egl) - # find_package(WaylandProtocols) - # find_package(WaylandScanner) - find_package(Qt6 COMPONENTS WaylandClient) - if(Qt6WaylandClient_FOUND) - target_link_libraries(gstqml6gl PUBLIC GStreamer::Wayland Qt6::WaylandClient) - target_compile_definitions(gstqml6gl PRIVATE HAVE_QT_WAYLAND) - endif() + target_link_libraries(gstqml6gl PUBLIC GStreamer::Wayland) + target_compile_definitions(gstqml6gl PRIVATE HAVE_QT_WAYLAND) endif() if(ANDROID) - # GLESv2, egl target_compile_definitions(gstqml6gl PRIVATE HAVE_QT_ANDROID) elseif(WIN32) - # GDI32, egl, GLESv2 - find_package(OpenGL) - if(OpenGL_FOUND) - target_link_libraries(gstqml6gl PUBLIC OpenGL::GL) - target_compile_definitions(gstqml6gl PRIVATE HAVE_QT_WIN32) - endif() + target_compile_definitions(gstqml6gl PRIVATE HAVE_QT_WIN32) elseif(MACOS) # appleframeworks # Foundation, QuartzCore, CoreFoundation @@ -138,6 +113,8 @@ target_compile_definitions(gstqml6gl QT_QPA_HEADER= ) +################################################################################ + if(EXISTS "${QGC_GST_QT6_PLUGIN_PATH}/resources.qrc") find_package(Qt6 REQUIRED COMPONENTS ShaderTools) @@ -171,6 +148,8 @@ if(EXISTS "${QGC_GST_QT6_PLUGIN_PATH}/resources.qrc") ) endif() +################################################################################ + if(UNIX) target_compile_options(gstqml6gl PRIVATE @@ -180,123 +159,7 @@ if(UNIX) ) endif() -# gstva, gstasf -set(GST_TARGET_PLUGINS - gstcoreelements - gstisomp4 - gstlibav - gstmatroska - gstmpegtsdemux - gstopengl - gstplayback - gstrtp - gstrtpmanager - gstrtsp - gstsdpelem - gsttcp - gstudp - gstvideoparsersbad - gstx264 -) -if(ANDROID) - list(APPEND GST_TARGET_PLUGINS gstandroidmedia) -elseif(IOS) - list(APPEND GST_TARGET_PLUGINS gstapplemedia) -endif() - -find_package(PkgConfig QUIET) -if(PkgConfig_FOUND) - pkg_check_modules(GST_PLUGINS IMPORTED_TARGET ${GST_TARGET_PLUGINS}) - if(GST_PLUGINS_FOUND) - target_link_libraries(gstqml6gl PUBLIC PkgConfig::GST_PLUGINS) - if(WIN32) - # install(FILES ${GST_PLUGINS_LIBRARIES} DESTINATION ${CMAKE_INSTALL_BINDIR}) - elseif(LINUX) - install(FILES ${GST_PLUGINS_LIBRARIES} DESTINATION ${CMAKE_INSTALL_LIBDIR}) - endif() - endif() - - pkg_check_modules(GSTREAMER_PLUGINS_BASE IMPORTED_TARGET gstreamer-plugins-base-1.0) - if(GSTREAMER_PLUGINS_BASE_FOUND) - target_link_libraries(gstqml6gl PUBLIC PkgConfig::GSTREAMER_PLUGINS_BASE) - endif() -endif() - -if(NOT GST_PLUGINS_FOUND) - foreach(plugin IN LISTS GST_TARGET_PLUGINS) - find_library(GStreamer_${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_PREFIX}/Versions/Current/lib - ${GSTREAMER_PREFIX}/Versions/Current/lib/gstreamer-1.0 - ) - if(GStreamer_${plugin}_LIBRARY) - cmake_print_variables(GStreamer_${plugin}_LIBRARY) - target_link_libraries(gstqml6gl PUBLIC ${GStreamer_${plugin}_LIBRARY}) - if(WIN32) - # install(FILES ${GStreamer_${plugin}_LIBRARY} DESTINATION ${CMAKE_INSTALL_BINDIR}) - elseif(LINUX) - install(FILES ${GStreamer_${plugin}_LIBRARY} DESTINATION ${CMAKE_INSTALL_LIBDIR}) - endif() - endif() - endforeach() - - if(ANDROID) - target_include_directories(gstqml6gl - PUBLIC - ${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 - ) - - find_package(FFMPEG REQUIRED COMPONENTS avcodec avfilter avformat avutil swresample) - if(FFMPEG_FOUND) - target_link_libraries(gstqml6gl PUBLIC ${FFMPEG_LIBRARIES}) - target_include_directories(gstqml6gl PUBLIC ${FFMPEG_INCLUDE_DIRS}) - endif() - - find_package(BZip2) - if(BZIP2_FOUND) - target_link_libraries(gstqml6gl PUBLIC BZip2::BZip2) - endif() - - find_library(graphene_LIBRARY graphene-1.0) - if(graphene_LIBRARY) - target_link_libraries(gstqml6gl PUBLIC ${graphene_LIBRARY}) - endif() - - find_library(x264_LIBRARY x264) - if(x264_LIBRARY) - target_link_libraries(gstqml6gl PUBLIC ${x264_LIBRARY}) - endif() - - find_package(JPEG) - if(JPEG_FOUND) - target_link_libraries(gstqml6gl PUBLIC JPEG::JPEG) - endif() - - find_package(PNG) - if(PNG_FOUND) - target_link_libraries(gstqml6gl PUBLIC PNG::PNG) - endif() - - endif() -endif() - -if(ANDROID) - target_link_options(gstqml6gl PUBLIC "-Wl,-Bsymbolic") -endif() - -if(QGC_GST_STATIC_BUILD) - target_compile_definitions(gstqml6gl PUBLIC QGC_GST_STATIC_BUILD) -endif() +################################################################################ if(LINUX) install(DIRECTORY ${GSTREAMER_PREFIX}/lib/x86_64-linux-gnu/gstreamer1.0 DESTINATION ${CMAKE_INSTALL_LIBDIR}) @@ -327,3 +190,5 @@ if(WIN32) file(GLOB GST_WIN_PLUGINS ${GST_WIN_PLUGINS_PATH}) install(FILES ${GST_WIN_PLUGINS} DESTINATION ${CMAKE_INSTALL_BINDIR}) endif() + +################################################################################ diff --git a/src/VideoManager/VideoReceiver/QtMultimedia/CMakeLists.txt b/src/VideoManager/VideoReceiver/QtMultimedia/CMakeLists.txt index 76fbd8638a72..7db25fef5568 100644 --- a/src/VideoManager/VideoReceiver/QtMultimedia/CMakeLists.txt +++ b/src/VideoManager/VideoReceiver/QtMultimedia/CMakeLists.txt @@ -18,13 +18,13 @@ target_sources(QtMultimediaReceiver target_link_libraries(QtMultimediaReceiver PRIVATE + Qt6::Multimedia Qt6::MultimediaQuickPrivate Qt6::Quick QGC Utilities PUBLIC Qt6::Core - Qt6::Multimedia VideoReceiver ) diff --git a/src/VideoManager/VideoReceiver/QtMultimedia/UVCReceiver.cc b/src/VideoManager/VideoReceiver/QtMultimedia/UVCReceiver.cc index fe0167fb38ee..0c9f430dd6f5 100644 --- a/src/VideoManager/VideoReceiver/QtMultimedia/UVCReceiver.cc +++ b/src/VideoManager/VideoReceiver/QtMultimedia/UVCReceiver.cc @@ -92,5 +92,5 @@ void UVCReceiver::_checkPermission() bool UVCReceiver::enabled() { - return (QMediaDevices::videoInputs().count() > 0); + return !QMediaDevices::videoInputs().isEmpty(); } diff --git a/tools/setup/install-dependencies-debian.sh b/tools/setup/install-dependencies-debian.sh index 5213a1a04ed4..fe12e04d8edd 100755 --- a/tools/setup/install-dependencies-debian.sh +++ b/tools/setup/install-dependencies-debian.sh @@ -78,9 +78,13 @@ DEBIAN_FRONTEND=noninteractive apt -y --quiet install \ gstreamer1.0-vaapi \ gstreamer1.0-x +if apt-cache show gstreamer1.0-qt6 >/dev/null 2>&1 && apt-cache show gstreamer1.0-qt6 2>/dev/null | grep -q "^Package: gstreamer1.0-qt6"; then + DEBIAN_FRONTEND=noninteractive apt-get install -y --quiet gstreamer1.0-qt6 + # Additional DEBIAN_FRONTEND=noninteractive apt -y --quiet install \ flite1-dev \ + intel-media-va-driver \ libasound2-dev \ libass-dev \ libdrm-dev \ @@ -88,6 +92,8 @@ DEBIAN_FRONTEND=noninteractive apt -y --quiet install \ libexiv2-dev \ libgbm-dev \ libgl1-mesa-dev \ + libgl-dev \ + libglx-dev \ libgles2-mesa-dev \ libglu1-mesa-dev \ libglfw3-dev \