mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
Feature/xcb grabber (#912)
* Add Xcb grabber * update compile instruction Signed-off-by: Paulchen Panther <Paulchen-Panter@protonmail.com> * Fix problem on resolution change + Make XCB default if X11 is not avaialable * Fix decimation problem Co-authored-by: Paulchen Panther <16664240+Paulchen-Panther@users.noreply.github.com> Co-authored-by: Paulchen Panther <Paulchen-Panter@protonmail.com>
This commit is contained in:
parent
11d7614591
commit
13205a9d11
@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
### Breaking
|
||||
|
||||
### Added
|
||||
- Add XCB grabber, a faster and safer alternative for X11 grabbing (#912)
|
||||
|
||||
### Changed
|
||||
|
||||
|
@ -40,6 +40,7 @@ SET ( DEFAULT_AMLOGIC OFF )
|
||||
SET ( DEFAULT_DISPMANX OFF )
|
||||
SET ( DEFAULT_OSX OFF )
|
||||
SET ( DEFAULT_X11 OFF )
|
||||
SET ( DEFAULT_XCB OFF )
|
||||
SET ( DEFAULT_QT ON )
|
||||
SET ( DEFAULT_WS281XPWM OFF )
|
||||
SET ( DEFAULT_AVAHI ON )
|
||||
@ -122,6 +123,7 @@ elseif ( "${PLATFORM}" STREQUAL "amlogic64" )
|
||||
SET ( DEFAULT_AMLOGIC ON )
|
||||
elseif ( "${PLATFORM}" MATCHES "x11" )
|
||||
SET ( DEFAULT_X11 ON )
|
||||
SET ( DEFAULT_XCB ON )
|
||||
if ( "${PLATFORM}" STREQUAL "x11-dev" )
|
||||
SET ( DEFAULT_AMLOGIC ON)
|
||||
SET ( DEFAULT_WS281XPWM ON )
|
||||
@ -182,6 +184,9 @@ message(STATUS "ENABLE_CEC = ${ENABLE_CEC}")
|
||||
option(ENABLE_X11 "Enable the X11 grabber" ${DEFAULT_X11})
|
||||
message(STATUS "ENABLE_X11 = ${ENABLE_X11}")
|
||||
|
||||
option(ENABLE_XCB "Enable the XCB grabber" ${DEFAULT_XCB})
|
||||
message(STATUS "ENABLE_XCB = ${ENABLE_XCB}")
|
||||
|
||||
option(ENABLE_QT "Enable the qt grabber" ${DEFAULT_QT})
|
||||
message(STATUS "ENABLE_QT = ${ENABLE_QT}")
|
||||
|
||||
|
@ -40,7 +40,7 @@ wget -qN https://raw.github.com/hyperion-project/hyperion.ng/master/bin/scripts/
|
||||
|
||||
```
|
||||
sudo apt-get update
|
||||
sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev libusb-1.0-0-dev python3-dev libcec-dev libxcb-util0-dev libxcb-randr0-dev libxrandr-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev libjpeg-dev libturbojpeg0-dev libqt5sql5-sqlite libssl-dev zlib1g-dev
|
||||
sudo apt-get install git cmake build-essential qtbase5-dev libqt5serialport5-dev libqt5sql5-sqlite libqt5x11extras5-dev libusb-1.0-0-dev python3-dev libcec-dev libxcb-image0-dev libxcb-util0-dev libxcb-shm0-dev libxcb-render0-dev libxcb-randr0-dev libxrandr-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev libjpeg-dev libturbojpeg0-dev libssl-dev zlib1g-dev
|
||||
```
|
||||
|
||||
**on RPI you need the videocore IV headers**
|
||||
|
@ -11,7 +11,7 @@ Update the Ubuntu environment to the latest stage and install required additiona
|
||||
```
|
||||
sudo apt-get update
|
||||
sudo apt-get upgrade
|
||||
sudo apt-get -qq -y install git rsync cmake build-essential qtbase5-dev libqt5serialport5-dev libusb-1.0-0-dev python3-dev libcec-dev libxcb-util0-dev libxcb-randr0-dev libxrandr-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev libjpeg-dev libturbojpeg0-dev libqt5sql5-sqlite libssl-dev zlib1g-dev
|
||||
sudo apt-get -qq -y install git rsync cmake build-essential qtbase5-dev libqt5serialport5-dev libqt5sql5-sqlite libusb-1.0-0-dev python3-dev libcec-dev libxcb-image0-dev libxcb-util0-dev libxcb-shm0-dev libxcb-render0-dev libxcb-randr0-dev libxrandr-dev libxrender-dev libavahi-core-dev libavahi-compat-libdnssd-dev libjpeg-dev libturbojpeg0-dev libssl-dev zlib1g-dev
|
||||
```
|
||||
|
||||
Refine the target IP or hostname, plus userID as required and set-up cross-compilation environment:
|
||||
|
@ -18,6 +18,9 @@
|
||||
// Define to enable the x11 grabber
|
||||
#cmakedefine ENABLE_X11
|
||||
|
||||
// Define to enable the xcb grabber
|
||||
#cmakedefine ENABLE_XCB
|
||||
|
||||
// Define to enable the qt grabber
|
||||
#cmakedefine ENABLE_QT
|
||||
|
||||
|
@ -104,7 +104,7 @@ $(document).ready( function() {
|
||||
|
||||
if(grabbers.indexOf('dispmanx') > -1)
|
||||
html += 'Raspberry Pi';
|
||||
else if(grabbers.indexOf('x11') > -1)
|
||||
else if(grabbers.indexOf('x11') > -1 || grabbers.indexOf('xcb') > -1)
|
||||
html += 'X86';
|
||||
else if(grabbers.indexOf('osx') > -1)
|
||||
html += 'OSX';
|
||||
|
@ -323,7 +323,7 @@ $(document).ready( function() {
|
||||
|
||||
if (grabbers.indexOf('dispmanx') > -1)
|
||||
hideEl(["device","pixelDecimation"]);
|
||||
else if (grabbers.indexOf('x11') > -1)
|
||||
else if (grabbers.indexOf('x11') > -1 || grabbers.indexOf('xcb') > -1)
|
||||
hideEl(["device","width","height"]);
|
||||
else if (grabbers.indexOf('osx') > -1 )
|
||||
hideEl(["device","pixelDecimation"]);
|
||||
|
@ -236,6 +236,7 @@ else
|
||||
ln -fs $BINSP/hyperion-v4l2 $BINTP/hyperion-v4l2
|
||||
ln -fs $BINSP/hyperion-dispmanx $BINTP/hyperion-dispmanx 2>/dev/null
|
||||
ln -fs $BINSP/hyperion-x11 $BINTP/hyperion-x11 2>/dev/null
|
||||
ln -fs $BINSP/hyperion-xcb $BINTP/hyperion-xcb 2>/dev/null
|
||||
ln -fs $BINSP/hyperion-aml $BINTP/hyperion-aml 2>/dev/null
|
||||
fi
|
||||
|
||||
@ -260,6 +261,11 @@ elif [ $OS_OPENELEC -eq 1 ]; then
|
||||
echo '---> Adding Hyperion-x11 to OpenELEC/LibreELEC autostart.sh'
|
||||
echo "DISPLAY=:0.0 LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/storage/hyperion/bin /storage/hyperion/bin/hyperion-x11 </dev/null >/storage/logfiles/hyperion.log 2>&1 &" >> /storage/.config/autostart.sh
|
||||
fi
|
||||
# only add hyperion-xcb to startup, if not found and x32x64 detected
|
||||
if [ $CPU_X32X64 -eq 1 ] && [ `cat /storage/.config/autostart.sh 2>/dev/null | grep hyperion-xcb | wc -l` -eq 0 ]; then
|
||||
echo '---> Adding Hyperion-xcb to OpenELEC/LibreELEC autostart.sh'
|
||||
echo "DISPLAY=:0.0 LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/storage/hyperion/bin /storage/hyperion/bin/hyperion-xcb </dev/null >/storage/logfiles/hyperion.log 2>&1 &" >> /storage/.config/autostart.sh
|
||||
fi
|
||||
elif [ $USE_SYSTEMD -eq 1 ]; then
|
||||
echo '---> Installing systemd script'
|
||||
#place startup script for systemd and activate
|
||||
|
@ -79,6 +79,7 @@ elif [ $OS_OPENELEC -eq 1 ]; then
|
||||
echo "---> Remove Hyperion from OpenELEC autostart.sh"
|
||||
sed -i "/hyperiond/d" /storage/.config/autostart.sh 2>/dev/null
|
||||
sed -i "/hyperion-x11/d" /storage/.config/autostart.sh 2>/dev/null
|
||||
sed -i "/hyperion-xcb/d" /storage/.config/autostart.sh 2>/dev/null
|
||||
elif [ $USE_SYSTEMD -eq 1 ]; then
|
||||
# Delete and disable Hyperion systemd script
|
||||
echo '---> Delete and disable Hyperion systemd script'
|
||||
@ -105,6 +106,7 @@ else
|
||||
rm -v /usr/bin/hyperion-v4l2 2>/dev/null
|
||||
rm -v /usr/bin/hyperion-dispmanx 2>/dev/null
|
||||
rm -v /usr/bin/hyperion-x11 2>/dev/null
|
||||
rm -v /usr/bin/hyperion-xcb 2>/dev/null
|
||||
rm -v /usr/bin/hyperion-aml 2>/dev/null
|
||||
rm -v /etc/hyperion.config.json 2>/dev/null
|
||||
echo "---> Remove binaries"
|
||||
|
278
cmake/ECMFindModuleHelpers.cmake
Normal file
278
cmake/ECMFindModuleHelpers.cmake
Normal file
@ -0,0 +1,278 @@
|
||||
#.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(<name>)
|
||||
#
|
||||
# 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(<name>
|
||||
# RESULT_VAR <variable>
|
||||
# KNOWN_COMPONENTS <component1> [<component2> [...]]
|
||||
# [SKIP_DEPENDENCY_HANDLING])
|
||||
#
|
||||
# This macro will populate <variable> with a list of components found in
|
||||
# <name>_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 <name>_FIND_REQUIRED) and call
|
||||
# return().
|
||||
#
|
||||
# The order of components in <variable> 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
|
||||
# <name>_<component>_component_deps will be checked for dependent components.
|
||||
# If <component> is listed in <name>_FIND_COMPONENTS, then all its (transitive)
|
||||
# dependencies will also be added to <variable>.
|
||||
#
|
||||
# ::
|
||||
#
|
||||
# ecm_find_package_handle_library_components(<name>
|
||||
# COMPONENTS <component> [<component> [...]]
|
||||
# [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 <name>_<component>_lib variable should contain the name of this library,
|
||||
# and <name>_<component>_header variable should contain the name of a header
|
||||
# file associated with it (whatever relative path is normally passed to
|
||||
# '#include'). <name>_<component>_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 <name>::<component>.
|
||||
#
|
||||
# Additional variables can be used to provide additional information:
|
||||
#
|
||||
# If SKIP_PKG_CONFIG, the <name>_<component>_pkg_config variable is set, and
|
||||
# pkg-config is found, the pkg-config module given by
|
||||
# <name>_<component>_pkg_config will be searched for and used to help locate the
|
||||
# library and header file. It will also be used to set
|
||||
# <name>_<component>_VERSION.
|
||||
#
|
||||
# Note that if version information is found via pkg-config,
|
||||
# <name>_<component>_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 <component> will be set to contain the imported
|
||||
# targets for the components listed in <name>_<component>_component_deps.
|
||||
# <component>_FOUND will also be set to false if any of the compoments in
|
||||
# <name>_<component>_component_deps are not found. This requires the components
|
||||
# in <name>_<component>_component_deps to be listed before <component> in the
|
||||
# COMPONENTS argument.
|
||||
#
|
||||
# The following variables will be set:
|
||||
#
|
||||
# ``<name>_TARGETS``
|
||||
# the imported targets
|
||||
# ``<name>_LIBRARIES``
|
||||
# the found libraries
|
||||
# ``<name>_INCLUDE_DIRS``
|
||||
# the combined required include directories for the components
|
||||
# ``<name>_DEFINITIONS``
|
||||
# the "other" CFLAGS provided by pkg-config, if any
|
||||
# ``<name>_VERSION``
|
||||
# the value of ``<name>_<component>_VERSION`` for the first component that
|
||||
# has this variable set (note that components are searched for in the order
|
||||
# they are passed to the macro), although if it is already set, it will not
|
||||
# be altered
|
||||
#
|
||||
# Note that these variables are never cleared, so if
|
||||
# ecm_find_package_handle_library_components() is called multiple times with
|
||||
# different components (typically because of multiple find_package() calls) then
|
||||
# ``<name>_TARGETS``, for example, will contain all the targets found in any
|
||||
# call (although no duplicates).
|
||||
#
|
||||
# Since pre-1.0.0.
|
||||
|
||||
#=============================================================================
|
||||
# SPDX-FileCopyrightText: 2014 Alex Merry <alex.merry@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
include(CMakeParseArguments)
|
||||
|
||||
macro(ecm_find_package_version_check module_name)
|
||||
if(CMAKE_VERSION VERSION_LESS 2.8.12)
|
||||
message(FATAL_ERROR "CMake 2.8.12 is required by Find${module_name}.cmake")
|
||||
endif()
|
||||
if(CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 2.8.12)
|
||||
message(AUTHOR_WARNING "Your project should require at least CMake 2.8.12 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)
|
||||
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()
|
180
cmake/FindXCB.cmake
Normal file
180
cmake/FindXCB.cmake
Normal file
@ -0,0 +1,180 @@
|
||||
#.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
|
||||
# XEVIE XF86DRI XFIXES XINERAMA XINPUT
|
||||
# XKB XPRINT XTEST XV XVMC
|
||||
#
|
||||
# If no components are specified, this module will act as though all components
|
||||
# except XINPUT (which is considered unstable) were passed to
|
||||
# OPTIONAL_COMPONENTS.
|
||||
#
|
||||
# This module will define the following variables, independently of the
|
||||
# components searched for or found:
|
||||
#
|
||||
# ``XCB_FOUND``
|
||||
# True if (the requestion version of) xcb is available
|
||||
# ``XCB_VERSION``
|
||||
# Found xcb version
|
||||
# ``XCB_TARGETS``
|
||||
# A list of all targets imported by this module (note that there may be more
|
||||
# than the components that were requested)
|
||||
# ``XCB_LIBRARIES``
|
||||
# This can be passed to target_link_libraries() instead of the imported
|
||||
# targets
|
||||
# ``XCB_INCLUDE_DIRS``
|
||||
# This should be passed to target_include_directories() if the targets are
|
||||
# not used for linking
|
||||
# ``XCB_DEFINITIONS``
|
||||
# This should be passed to target_compile_options() if the targets are not
|
||||
# used for linking
|
||||
#
|
||||
# For each searched-for components, ``XCB_<component>_FOUND`` will be set to
|
||||
# true if the corresponding xcb library was found, and false otherwise. If
|
||||
# ``XCB_<component>_FOUND`` is true, the imported target ``XCB::<component>``
|
||||
# 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.
|
||||
|
||||
#=============================================================================
|
||||
# SPDX-FileCopyrightText: 2011 Fredrik Höglund <fredrik@kde.org>
|
||||
# SPDX-FileCopyrightText: 2013 Martin Gräßlin <mgraesslin@kde.org>
|
||||
# SPDX-FileCopyrightText: 2014-2015 Alex Merry <alex.merry@kde.org>
|
||||
#
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#=============================================================================
|
||||
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/ECMFindModuleHelpers.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
|
||||
XEVIE
|
||||
XF86DRI
|
||||
XINERAMA
|
||||
XINPUT
|
||||
XKB
|
||||
XPRINT
|
||||
XTEST
|
||||
XV
|
||||
XVMC
|
||||
)
|
||||
|
||||
# XINPUT is unstable; do not include it by default
|
||||
set(XCB_default_components ${XCB_known_components})
|
||||
list(REMOVE_ITEM XCB_default_components "XINPUT")
|
||||
|
||||
# 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}
|
||||
)
|
||||
|
||||
list(FIND XCB_components "XINPUT" _XCB_XINPUT_index)
|
||||
if (NOT _XCB_XINPUT_index EQUAL -1)
|
||||
message(AUTHOR_WARNING "XINPUT from XCB was requested: this is EXPERIMENTAL and is likely to unavailable on many systems!")
|
||||
endif()
|
||||
|
||||
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"
|
||||
)
|
@ -88,6 +88,7 @@ ln -fs $BINSP/hyperion-v4l2 $BINTP/hyperion-v4l2
|
||||
ln -fs $BINSP/hyperion-framebuffer $BINTP/hyperion-framebuffer 2>/dev/null
|
||||
ln -fs $BINSP/hyperion-dispmanx $BINTP/hyperion-dispmanx 2>/dev/null
|
||||
ln -fs $BINSP/hyperion-x11 $BINTP/hyperion-x11 2>/dev/null
|
||||
ln -fs $BINSP/hyperion-xcb $BINTP/hyperion-xcb 2>/dev/null
|
||||
ln -fs $BINSP/hyperion-aml $BINTP/hyperion-aml 2>/dev/null
|
||||
ln -fs $BINSP/hyperion-qt $BINTP/hyperion-qt 2>/dev/null
|
||||
|
||||
|
@ -135,6 +135,9 @@ endif()
|
||||
if(ENABLE_X11)
|
||||
SET ( CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} "hyperion_x11" )
|
||||
endif()
|
||||
if(ENABLE_XCB)
|
||||
SET ( CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} "hyperion_xcb" )
|
||||
endif()
|
||||
if(ENABLE_DISPMANX)
|
||||
SET ( CPACK_COMPONENTS_ALL ${CPACK_COMPONENTS_ALL} "hyperion_dispmanx" )
|
||||
endif()
|
||||
@ -211,6 +214,15 @@ if(ENABLE_X11)
|
||||
DEPENDS Hyperion
|
||||
)
|
||||
endif()
|
||||
if(ENABLE_X11)
|
||||
cpack_add_component(hyperion_xcb
|
||||
DISPLAY_NAME "XCB Standalone Screencap"
|
||||
DESCRIPTION "XCB based standalone screen capture"
|
||||
INSTALL_TYPES Full
|
||||
GROUP Screencapture
|
||||
DEPENDS Hyperion
|
||||
)
|
||||
endif()
|
||||
if(ENABLE_DISPMANX)
|
||||
cpack_add_component(hyperion_dispmanx
|
||||
DISPLAY_NAME "RPi dispmanx Standalone Screencap"
|
||||
|
@ -88,6 +88,7 @@ ln -fs $BINSP/hyperion-v4l2 $BINTP/hyperion-v4l2
|
||||
ln -fs $BINSP/hyperion-framebuffer $BINTP/hyperion-framebuffer 2>/dev/null
|
||||
ln -fs $BINSP/hyperion-dispmanx $BINTP/hyperion-dispmanx 2>/dev/null
|
||||
ln -fs $BINSP/hyperion-x11 $BINTP/hyperion-x11 2>/dev/null
|
||||
ln -fs $BINSP/hyperion-xcb $BINTP/hyperion-xcb 2>/dev/null
|
||||
ln -fs $BINSP/hyperion-aml $BINTP/hyperion-aml 2>/dev/null
|
||||
ln -fs $BINSP/hyperion-qt $BINTP/hyperion-qt 2>/dev/null
|
||||
|
||||
|
@ -142,7 +142,7 @@
|
||||
},
|
||||
|
||||
/// The configuration for the frame-grabber, contains the following items:
|
||||
/// * type : type of grabber. (auto|osx|dispmanx|amlogic|x11|framebuffer|qt) [auto]
|
||||
/// * type : type of grabber. (auto|osx|dispmanx|amlogic|x11|xcb|framebuffer|qt) [auto]
|
||||
/// * width : The width of the grabbed frames [pixels]
|
||||
/// * height : The height of the grabbed frames [pixels]
|
||||
/// * frequency_Hz : The frequency of the frame grab [Hz]
|
||||
@ -161,7 +161,7 @@
|
||||
"width" : 96,
|
||||
"height" : 96,
|
||||
|
||||
// valid for x11|qt
|
||||
// valid for x11|xcb|qt
|
||||
"pixelDecimation" : 8,
|
||||
|
||||
// valid for qt
|
||||
|
71
include/grabber/XcbGrabber.h
Normal file
71
include/grabber/XcbGrabber.h
Normal file
@ -0,0 +1,71 @@
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractNativeEventFilter>
|
||||
#include <QObject>
|
||||
|
||||
#include <utils/ColorRgb.h>
|
||||
#include <hyperion/Grabber.h>
|
||||
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
|
||||
#include <xcb/randr.h>
|
||||
#include <xcb/shm.h>
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xcb_image.h>
|
||||
|
||||
class Logger;
|
||||
|
||||
class XcbGrabber : public Grabber, public QAbstractNativeEventFilter
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
XcbGrabber(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation);
|
||||
~XcbGrabber() override;
|
||||
|
||||
bool Setup();
|
||||
int grabFrame(Image<ColorRgb> & image, bool forceUpdate = false);
|
||||
int updateScreenDimensions(bool force = false);
|
||||
void setVideoMode(VideoMode mode) override;
|
||||
bool setWidthHeight(int width, int height) override { return true; }
|
||||
void setPixelDecimation(int pixelDecimation) override;
|
||||
void setCropping(unsigned cropLeft, unsigned cropRight, unsigned cropTop, unsigned cropBottom) override;
|
||||
|
||||
private:
|
||||
bool nativeEventFilter(const QByteArray & eventType, void * message, long int * result) override;
|
||||
void freeResources();
|
||||
void setupResources();
|
||||
void setupRender();
|
||||
void setupRandr();
|
||||
void setupShm();
|
||||
xcb_screen_t * getScreen(const xcb_setup_t *setup, int screen_num) const;
|
||||
xcb_render_pictformat_t findFormatForVisual(xcb_visualid_t visual) const;
|
||||
|
||||
xcb_connection_t * _connection;
|
||||
xcb_screen_t * _screen;
|
||||
xcb_pixmap_t _pixmap;
|
||||
xcb_render_pictformat_t _srcFormat;
|
||||
xcb_render_pictformat_t _dstFormat;
|
||||
xcb_render_picture_t _srcPicture;
|
||||
xcb_render_picture_t _dstPicture;
|
||||
xcb_render_transform_t _transform;
|
||||
xcb_shm_seg_t _shminfo;
|
||||
|
||||
int _pixelDecimation;
|
||||
|
||||
unsigned _screenWidth;
|
||||
unsigned _screenHeight;
|
||||
unsigned _src_x;
|
||||
unsigned _src_y;
|
||||
|
||||
bool _XcbRenderAvailable;
|
||||
bool _XcbRandRAvailable;
|
||||
bool _XcbShmAvailable;
|
||||
bool _XcbShmPixmapAvailable;
|
||||
Logger * _logger;
|
||||
|
||||
uint8_t * _shmData;
|
||||
|
||||
int _XcbRandREventBase;
|
||||
};
|
24
include/grabber/XcbWrapper.h
Normal file
24
include/grabber/XcbWrapper.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <hyperion/GrabberWrapper.h>
|
||||
#include <grabber/XcbGrabber.h>
|
||||
|
||||
// some include of xorg defines "None" this is also used by QT and has to be undefined to avoid collisions
|
||||
#ifdef None
|
||||
#undef None
|
||||
#endif
|
||||
|
||||
class XcbWrapper: public GrabberWrapper
|
||||
{
|
||||
public:
|
||||
XcbWrapper(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation, const unsigned updateRate_Hz);
|
||||
~XcbWrapper() override;
|
||||
|
||||
public slots:
|
||||
virtual void action();
|
||||
|
||||
private:
|
||||
XcbGrabber _grabber;
|
||||
|
||||
bool _init;
|
||||
};
|
@ -54,7 +54,7 @@ public:
|
||||
virtual bool setFramerate(int fps);
|
||||
|
||||
///
|
||||
/// @brief Apply new pixelDecimation (used from x11 and qt)
|
||||
/// @brief Apply new pixelDecimation (used from x11, xcb and qt)
|
||||
///
|
||||
virtual void setPixelDecimation(int pixelDecimation) {}
|
||||
|
||||
|
@ -480,7 +480,7 @@ void JsonAPI::handleServerInfoCommand(const QJsonObject &message, const QString
|
||||
QJsonObject grabbers;
|
||||
QJsonArray availableGrabbers;
|
||||
|
||||
#if defined(ENABLE_DISPMANX) || defined(ENABLE_V4L2) || defined(ENABLE_FB) || defined(ENABLE_AMLOGIC) || defined(ENABLE_OSX) || defined(ENABLE_X11) || defined(ENABLE_QT)
|
||||
#if defined(ENABLE_DISPMANX) || defined(ENABLE_V4L2) || defined(ENABLE_FB) || defined(ENABLE_AMLOGIC) || defined(ENABLE_OSX) || defined(ENABLE_X11) || defined(ENABLE_XCB) || defined(ENABLE_QT)
|
||||
|
||||
// get available grabbers
|
||||
//grabbers["active"] = ????;
|
||||
|
@ -22,6 +22,10 @@ if (ENABLE_X11)
|
||||
add_subdirectory(x11)
|
||||
endif()
|
||||
|
||||
if (ENABLE_XCB)
|
||||
add_subdirectory(xcb)
|
||||
endif()
|
||||
|
||||
if (ENABLE_QT)
|
||||
add_subdirectory(qt)
|
||||
endif()
|
||||
|
@ -72,8 +72,13 @@ void X11Grabber::setupResources()
|
||||
_shminfo.readOnly = False;
|
||||
XShmAttach(_x11Display, &_shminfo);
|
||||
}
|
||||
|
||||
if (_XRenderAvailable)
|
||||
{
|
||||
_useImageResampler = false;
|
||||
_imageResampler.setHorizontalPixelDecimation(1);
|
||||
_imageResampler.setVerticalPixelDecimation(1);
|
||||
|
||||
if(_XShmPixmapAvailable)
|
||||
{
|
||||
_pixmap = XShmCreatePixmap(_x11Display, _window, _xImage->data, &_shminfo, _width, _height, _windowAttr.depth);
|
||||
@ -86,8 +91,16 @@ void X11Grabber::setupResources()
|
||||
_dstFormat = XRenderFindVisualFormat(_x11Display, _windowAttr.visual);
|
||||
_srcPicture = XRenderCreatePicture(_x11Display, _window, _srcFormat, CPRepeat, &_pictAttr);
|
||||
_dstPicture = XRenderCreatePicture(_x11Display, _pixmap, _dstFormat, CPRepeat, &_pictAttr);
|
||||
|
||||
XRenderSetPictureFilter(_x11Display, _srcPicture, FilterBilinear, NULL, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
_useImageResampler = true;
|
||||
_imageResampler.setHorizontalPixelDecimation(_pixelDecimation);
|
||||
_imageResampler.setVerticalPixelDecimation(_pixelDecimation);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool X11Grabber::Setup()
|
||||
@ -117,10 +130,6 @@ bool X11Grabber::Setup()
|
||||
XShmQueryVersion(_x11Display, &dummy, &dummy, &pixmaps_supported);
|
||||
_XShmPixmapAvailable = pixmaps_supported && XShmPixmapFormat(_x11Display) == ZPixmap;
|
||||
|
||||
// Image scaling is performed by XRender when available, otherwise by ImageResampler
|
||||
_imageResampler.setHorizontalPixelDecimation(_XRenderAvailable ? 1 : _pixelDecimation);
|
||||
_imageResampler.setVerticalPixelDecimation(_XRenderAvailable ? 1 : _pixelDecimation);
|
||||
|
||||
bool result = (updateScreenDimensions(true) >=0);
|
||||
ErrorIf(!result, _log, "X11 Grabber start failed");
|
||||
setEnabled(result);
|
||||
|
@ -23,13 +23,17 @@ void X11Wrapper::action()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_grabber.updateScreenDimensions() < 0 )
|
||||
{
|
||||
stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isActive())
|
||||
{
|
||||
if (_grabber.updateScreenDimensions() >= 0 )
|
||||
{
|
||||
transferFrame(_grabber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
21
libsrc/grabber/xcb/CMakeLists.txt
Normal file
21
libsrc/grabber/xcb/CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
||||
# Define the current source locations
|
||||
SET(CURRENT_HEADER_DIR ${CMAKE_SOURCE_DIR}/include/grabber)
|
||||
SET(CURRENT_SOURCE_DIR ${CMAKE_SOURCE_DIR}/libsrc/grabber/xcb)
|
||||
|
||||
find_package(XCB COMPONENTS SHM IMAGE RENDER RANDR REQUIRED)
|
||||
find_package(Qt5Widgets REQUIRED)
|
||||
find_package(Qt5X11Extras REQUIRED)
|
||||
|
||||
include_directories(${XCB_INCLUDE_DIRS})
|
||||
|
||||
FILE (GLOB XCB_SOURCES "${CURRENT_HEADER_DIR}/Xcb*.h" "${CURRENT_SOURCE_DIR}/*.h" "${CURRENT_SOURCE_DIR}/*.cpp" )
|
||||
|
||||
add_library(xcb-grabber ${XCB_SOURCES})
|
||||
|
||||
target_link_libraries(xcb-grabber
|
||||
hyperion
|
||||
Qt5::X11Extras
|
||||
Qt5::Widgets
|
||||
${XCB_LIBRARIES}
|
||||
)
|
||||
|
30
libsrc/grabber/xcb/XcbCommandExecutor.h
Normal file
30
libsrc/grabber/xcb/XcbCommandExecutor.h
Normal file
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <xcb/xcb.h>
|
||||
|
||||
template<class Request, class ...Args>
|
||||
std::unique_ptr<typename Request::ResponseType, decltype(&free)>
|
||||
query(xcb_connection_t * connection, Args&& ...args)
|
||||
{
|
||||
auto cookie = Request::RequestFunction(connection,args...);
|
||||
|
||||
xcb_generic_error_t * error = nullptr;
|
||||
std::unique_ptr<typename Request::ResponseType, decltype(&free)> xcbResponse(
|
||||
Request::ReplyFunction(connection, cookie, &error), free);
|
||||
|
||||
if (error) {
|
||||
Logger * LOGGER = Logger::getInstance("XCB");
|
||||
Error(LOGGER,
|
||||
"Cannot get the image data event_error: response_type:%u error_code:%u "
|
||||
"sequence:%u resource_id:%u minor_code:%u major_code:%u.\n",
|
||||
error->response_type, error->error_code, error->sequence,
|
||||
error->resource_id, error->minor_code, error->major_code);
|
||||
|
||||
free(error);
|
||||
return {nullptr, nullptr};
|
||||
}
|
||||
|
||||
return xcbResponse;
|
||||
}
|
55
libsrc/grabber/xcb/XcbCommands.h
Normal file
55
libsrc/grabber/xcb/XcbCommands.h
Normal file
@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include <xcb/randr.h>
|
||||
#include <xcb/shm.h>
|
||||
#include <xcb/xcb.h>
|
||||
#include <xcb/xcb_image.h>
|
||||
|
||||
struct GetImage
|
||||
{
|
||||
typedef xcb_get_image_reply_t ResponseType;
|
||||
|
||||
static constexpr auto RequestFunction = xcb_get_image;
|
||||
static constexpr auto ReplyFunction = xcb_get_image_reply;
|
||||
};
|
||||
|
||||
struct GetGeometry
|
||||
{
|
||||
typedef xcb_get_geometry_reply_t ResponseType;
|
||||
|
||||
static constexpr auto RequestFunction = xcb_get_geometry;
|
||||
static constexpr auto ReplyFunction = xcb_get_geometry_reply;
|
||||
};
|
||||
|
||||
struct ShmQueryVersion
|
||||
{
|
||||
typedef xcb_shm_query_version_reply_t ResponseType;
|
||||
|
||||
static constexpr auto RequestFunction = xcb_shm_query_version;
|
||||
static constexpr auto ReplyFunction = xcb_shm_query_version_reply;
|
||||
};
|
||||
|
||||
struct RenderQueryVersion
|
||||
{
|
||||
typedef xcb_render_query_version_reply_t ResponseType;
|
||||
|
||||
static constexpr auto RequestFunction = xcb_render_query_version;
|
||||
static constexpr auto ReplyFunction = xcb_render_query_version_reply;
|
||||
};
|
||||
|
||||
struct ShmGetImage
|
||||
{
|
||||
typedef xcb_shm_get_image_reply_t ResponseType;
|
||||
|
||||
static constexpr auto RequestFunction = xcb_shm_get_image;
|
||||
static constexpr auto ReplyFunction = xcb_shm_get_image_reply;
|
||||
};
|
||||
|
||||
struct RenderQueryPictFormats
|
||||
{
|
||||
typedef xcb_render_query_pict_formats_reply_t ResponseType;
|
||||
|
||||
static constexpr auto RequestFunction = xcb_render_query_pict_formats;
|
||||
static constexpr auto ReplyFunction = xcb_render_query_pict_formats_reply;
|
||||
};
|
||||
|
452
libsrc/grabber/xcb/XcbGrabber.cpp
Normal file
452
libsrc/grabber/xcb/XcbGrabber.cpp
Normal file
@ -0,0 +1,452 @@
|
||||
#include <utils/Logger.h>
|
||||
#include <grabber/XcbGrabber.h>
|
||||
|
||||
#include "XcbCommands.h"
|
||||
#include "XcbCommandExecutor.h"
|
||||
|
||||
#include <xcb/xcb_event.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QX11Info>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#define DOUBLE_TO_FIXED(d) ((xcb_render_fixed_t) ((d) * 65536))
|
||||
|
||||
XcbGrabber::XcbGrabber(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation)
|
||||
: Grabber("XCBGRABBER", 0, 0, cropLeft, cropRight, cropTop, cropBottom)
|
||||
, _connection{}
|
||||
, _screen{}
|
||||
, _pixmap{}
|
||||
, _srcFormat{}
|
||||
, _dstFormat{}
|
||||
, _srcPicture{}
|
||||
, _dstPicture{}
|
||||
, _transform{}
|
||||
, _shminfo{}
|
||||
, _pixelDecimation(pixelDecimation)
|
||||
, _screenWidth{}
|
||||
, _screenHeight{}
|
||||
, _src_x(cropLeft)
|
||||
, _src_y(cropTop)
|
||||
, _XcbRenderAvailable{}
|
||||
, _XcbRandRAvailable{}
|
||||
, _XcbShmAvailable{}
|
||||
, _XcbShmPixmapAvailable{}
|
||||
, _logger{}
|
||||
, _shmData{}
|
||||
, _XcbRandREventBase{-1}
|
||||
{
|
||||
_logger = Logger::getInstance("XCB");
|
||||
|
||||
// cropping is performed by XcbRender, XcbShmGetImage or XcbGetImage
|
||||
_useImageResampler = false;
|
||||
_imageResampler.setCropping(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
XcbGrabber::~XcbGrabber()
|
||||
{
|
||||
if (_connection != nullptr)
|
||||
{
|
||||
freeResources();
|
||||
xcb_disconnect(_connection);
|
||||
}
|
||||
}
|
||||
|
||||
void XcbGrabber::freeResources()
|
||||
{
|
||||
if (_XcbRandRAvailable)
|
||||
{
|
||||
qApp->removeNativeEventFilter(this);
|
||||
}
|
||||
|
||||
if(_XcbShmAvailable)
|
||||
{
|
||||
xcb_shm_detach(_connection, _shminfo);
|
||||
shmdt(_shmData);
|
||||
shmctl(_shminfo, IPC_RMID, 0);
|
||||
|
||||
}
|
||||
|
||||
if (_XcbRenderAvailable)
|
||||
{
|
||||
xcb_free_pixmap(_connection, _pixmap);
|
||||
xcb_render_free_picture(_connection, _srcPicture);
|
||||
xcb_render_free_picture(_connection, _dstPicture);
|
||||
}
|
||||
}
|
||||
|
||||
void XcbGrabber::setupResources()
|
||||
{
|
||||
if (_XcbRandRAvailable)
|
||||
{
|
||||
qApp->installNativeEventFilter(this);
|
||||
}
|
||||
|
||||
if(_XcbShmAvailable)
|
||||
{
|
||||
_shminfo = xcb_generate_id(_connection);
|
||||
int id = shmget(IPC_PRIVATE, _width * _height * 4, IPC_CREAT | 0777);
|
||||
_shmData = static_cast<uint8_t*>(shmat(id, nullptr, 0));
|
||||
xcb_shm_attach(_connection, _shminfo, id, 0);
|
||||
}
|
||||
|
||||
if (_XcbRenderAvailable)
|
||||
{
|
||||
_useImageResampler = false;
|
||||
_imageResampler.setHorizontalPixelDecimation(1);
|
||||
_imageResampler.setVerticalPixelDecimation(1);
|
||||
|
||||
if(_XcbShmPixmapAvailable)
|
||||
{
|
||||
_pixmap = xcb_generate_id(_connection);
|
||||
xcb_shm_create_pixmap(
|
||||
_connection, _pixmap, _screen->root, _width,
|
||||
_height, _screen->root_depth, _shminfo, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
_pixmap = xcb_generate_id(_connection);
|
||||
xcb_create_pixmap(_connection, _screen->root_depth, _pixmap, _screen->root, _width, _height);
|
||||
}
|
||||
|
||||
_srcFormat = findFormatForVisual(_screen->root_visual);
|
||||
_dstFormat = findFormatForVisual(_screen->root_visual);
|
||||
|
||||
_srcPicture = xcb_generate_id(_connection);
|
||||
_dstPicture = xcb_generate_id(_connection);
|
||||
|
||||
const uint32_t value_mask = XCB_RENDER_CP_REPEAT;
|
||||
const uint32_t values[] = { XCB_RENDER_REPEAT_NONE };
|
||||
|
||||
xcb_render_create_picture(_connection, _srcPicture, _screen->root, _srcFormat, value_mask, values);
|
||||
xcb_render_create_picture(_connection, _dstPicture, _pixmap, _dstFormat, value_mask, values);
|
||||
|
||||
const std::string filter = "fast";
|
||||
xcb_render_set_picture_filter(_connection, _srcPicture, filter.size(), filter.c_str(), 0, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
_useImageResampler = true;
|
||||
_imageResampler.setHorizontalPixelDecimation(_pixelDecimation);
|
||||
_imageResampler.setVerticalPixelDecimation(_pixelDecimation);
|
||||
}
|
||||
}
|
||||
|
||||
xcb_screen_t * XcbGrabber::getScreen(const xcb_setup_t *setup, int screen_num) const
|
||||
{
|
||||
xcb_screen_iterator_t it = xcb_setup_roots_iterator(setup);
|
||||
xcb_screen_t * screen = nullptr;
|
||||
|
||||
for (; it.rem > 0; xcb_screen_next(&it))
|
||||
{
|
||||
if (!screen_num)
|
||||
{
|
||||
screen = it.data;
|
||||
break;
|
||||
}
|
||||
screen_num--;
|
||||
}
|
||||
return screen;
|
||||
}
|
||||
|
||||
void XcbGrabber::setupRandr()
|
||||
{
|
||||
auto randrQueryExtensionReply = xcb_get_extension_data(_connection, &xcb_randr_id);
|
||||
_XcbRandRAvailable = randrQueryExtensionReply != nullptr;
|
||||
_XcbRandREventBase = randrQueryExtensionReply ? randrQueryExtensionReply->first_event : -1;
|
||||
}
|
||||
|
||||
void XcbGrabber::setupRender()
|
||||
{
|
||||
auto renderQueryVersionReply = query<RenderQueryVersion>(_connection, 0, 0);
|
||||
|
||||
_XcbRenderAvailable = renderQueryVersionReply != nullptr;
|
||||
}
|
||||
|
||||
void XcbGrabber::setupShm()
|
||||
{
|
||||
auto shmQueryExtensionReply = xcb_get_extension_data(_connection, &xcb_render_id);
|
||||
_XcbShmAvailable = shmQueryExtensionReply != nullptr;
|
||||
|
||||
_XcbShmPixmapAvailable = false;
|
||||
if (_XcbShmAvailable)
|
||||
{
|
||||
auto shmQueryVersionReply = query<ShmQueryVersion>(_connection);
|
||||
|
||||
_XcbShmPixmapAvailable = shmQueryVersionReply ? shmQueryVersionReply->shared_pixmaps : false;
|
||||
}
|
||||
}
|
||||
|
||||
bool XcbGrabber::Setup()
|
||||
{
|
||||
int screen_num;
|
||||
_connection = xcb_connect(nullptr, &screen_num);
|
||||
|
||||
int ret = xcb_connection_has_error(_connection);
|
||||
if (ret != 0)
|
||||
{
|
||||
Error(_logger, "Cannot open display, error %d", ret);
|
||||
return false;
|
||||
}
|
||||
|
||||
const xcb_setup_t * setup = xcb_get_setup(_connection);
|
||||
_screen = getScreen(setup, screen_num);
|
||||
|
||||
if (!_screen)
|
||||
{
|
||||
Error(_log, "Unable to open display, screen %d does not exist", screen_num);
|
||||
|
||||
if (getenv("DISPLAY"))
|
||||
Error(_log, "%s", getenv("DISPLAY"));
|
||||
else
|
||||
Error(_log, "DISPLAY environment variable not set");
|
||||
|
||||
freeResources();
|
||||
return false;
|
||||
}
|
||||
|
||||
setupRandr();
|
||||
setupRender();
|
||||
setupShm();
|
||||
|
||||
Info(_log, "XcbRandR : %s", _XcbRandRAvailable ? "available" : "unavailable");
|
||||
Info(_log, "XcbRender : %s", _XcbRenderAvailable ? "available" : "unavailable");
|
||||
Info(_log, "XcbShm : %s", _XcbShmAvailable ? "available" : "unavailable");
|
||||
Info(_log, "XcbPixmap : %s", _XcbShmPixmapAvailable ? "available" : "unavailable");
|
||||
|
||||
bool result = (updateScreenDimensions(true) >=0);
|
||||
ErrorIf(!result, _log, "XCB Grabber start failed");
|
||||
setEnabled(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
int XcbGrabber::grabFrame(Image<ColorRgb> & image, bool forceUpdate)
|
||||
{
|
||||
if (!_enabled)
|
||||
return 0;
|
||||
|
||||
if (forceUpdate)
|
||||
updateScreenDimensions(forceUpdate);
|
||||
|
||||
if (_XcbRenderAvailable)
|
||||
{
|
||||
double scale_x = static_cast<double>(_screenWidth / _pixelDecimation) / static_cast<double>(_screenWidth);
|
||||
double scale_y = static_cast<double>(_screenHeight / _pixelDecimation) / static_cast<double>(_screenHeight);
|
||||
double scale = qMin(scale_y, scale_x);
|
||||
|
||||
_transform = {
|
||||
DOUBLE_TO_FIXED(1), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0),
|
||||
DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(1), DOUBLE_TO_FIXED(0),
|
||||
DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(0), DOUBLE_TO_FIXED(scale)
|
||||
};
|
||||
|
||||
xcb_render_set_picture_transform(_connection, _srcPicture, _transform);
|
||||
xcb_render_composite(_connection,
|
||||
XCB_RENDER_PICT_OP_SRC, _srcPicture,
|
||||
XCB_RENDER_PICTURE_NONE, _dstPicture,
|
||||
(_src_x/_pixelDecimation),
|
||||
(_src_y/_pixelDecimation),
|
||||
0, 0, 0, 0, _width, _height);
|
||||
|
||||
xcb_flush(_connection);
|
||||
|
||||
if (_XcbShmAvailable)
|
||||
{
|
||||
query<ShmGetImage>(_connection,
|
||||
_pixmap, 0, 0, _width, _height,
|
||||
~0, XCB_IMAGE_FORMAT_Z_PIXMAP, _shminfo, 0);
|
||||
|
||||
_imageResampler.processImage(
|
||||
reinterpret_cast<const uint8_t *>(_shmData),
|
||||
_width, _height, _width * 4, PixelFormat::BGR32, image);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto result = query<GetImage>(_connection,
|
||||
XCB_IMAGE_FORMAT_Z_PIXMAP, _pixmap,
|
||||
0, 0, _width, _height, ~0);
|
||||
|
||||
auto buffer = xcb_get_image_data(result.get());
|
||||
|
||||
_imageResampler.processImage(
|
||||
reinterpret_cast<const uint8_t *>(buffer),
|
||||
_width, _height, _width * 4, PixelFormat::BGR32, image);
|
||||
}
|
||||
|
||||
}
|
||||
else if (_XcbShmAvailable)
|
||||
{
|
||||
query<ShmGetImage>(_connection,
|
||||
_screen->root, _src_x, _src_y, _width, _height,
|
||||
~0, XCB_IMAGE_FORMAT_Z_PIXMAP, _shminfo, 0);
|
||||
|
||||
_imageResampler.processImage(
|
||||
reinterpret_cast<const uint8_t *>(_shmData),
|
||||
_width, _height, _width * 4, PixelFormat::BGR32, image);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto result = query<GetImage>(_connection,
|
||||
XCB_IMAGE_FORMAT_Z_PIXMAP, _screen->root,
|
||||
_src_x, _src_y, _width, _height, ~0);
|
||||
|
||||
auto buffer = xcb_get_image_data(result.get());
|
||||
|
||||
_imageResampler.processImage(
|
||||
reinterpret_cast<const uint8_t *>(buffer),
|
||||
_width, _height, _width * 4, PixelFormat::BGR32, image);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int XcbGrabber::updateScreenDimensions(bool force)
|
||||
{
|
||||
auto geometry = query<GetGeometry>(_connection, _screen->root);
|
||||
if (geometry == nullptr)
|
||||
{
|
||||
setEnabled(false);
|
||||
Error(_log, "Failed to obtain screen geometry");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!_enabled)
|
||||
setEnabled(true);
|
||||
|
||||
if (!force && _screenWidth == unsigned(geometry->width) &&
|
||||
_screenHeight == unsigned(geometry->height))
|
||||
return 0;
|
||||
|
||||
if (_screenWidth || _screenHeight)
|
||||
freeResources();
|
||||
|
||||
Info(_log, "Update of screen resolution: [%dx%d] to [%dx%d]", _screenWidth, _screenHeight, geometry->width, geometry->height);
|
||||
|
||||
_screenWidth = geometry->width;
|
||||
_screenHeight = geometry->height;
|
||||
|
||||
int width = 0, height = 0;
|
||||
|
||||
// Image scaling is performed by XRender when available, otherwise by ImageResampler
|
||||
if (_XcbRenderAvailable)
|
||||
{
|
||||
width = (_screenWidth > unsigned(_cropLeft + _cropRight))
|
||||
? ((_screenWidth - _cropLeft - _cropRight) / _pixelDecimation)
|
||||
: _screenWidth / _pixelDecimation;
|
||||
|
||||
height = (_screenHeight > unsigned(_cropTop + _cropBottom))
|
||||
? ((_screenHeight - _cropTop - _cropBottom) / _pixelDecimation)
|
||||
: _screenHeight / _pixelDecimation;
|
||||
|
||||
Info(_log, "Using XcbRender for grabbing [%dx%d]", width, height);
|
||||
}
|
||||
else
|
||||
{
|
||||
width = (_screenWidth > unsigned(_cropLeft + _cropRight))
|
||||
? (_screenWidth - _cropLeft - _cropRight)
|
||||
: _screenWidth;
|
||||
|
||||
height = (_screenHeight > unsigned(_cropTop + _cropBottom))
|
||||
? (_screenHeight - _cropTop - _cropBottom)
|
||||
: _screenHeight;
|
||||
|
||||
Info(_log, "Using XcbGetImage for grabbing [%dx%d]", width, height);
|
||||
}
|
||||
|
||||
// Calculate final image dimensions and adjust top/left cropping in 3D modes
|
||||
switch (_videoMode)
|
||||
{
|
||||
case VideoMode::VIDEO_3DSBS:
|
||||
_width = width /2;
|
||||
_height = height;
|
||||
_src_x = _cropLeft / 2;
|
||||
_src_y = _cropTop;
|
||||
break;
|
||||
case VideoMode::VIDEO_3DTAB:
|
||||
_width = width;
|
||||
_height = height / 2;
|
||||
_src_x = _cropLeft;
|
||||
_src_y = _cropTop / 2;
|
||||
break;
|
||||
case VideoMode::VIDEO_2D:
|
||||
default:
|
||||
_width = width;
|
||||
_height = height;
|
||||
_src_x = _cropLeft;
|
||||
_src_y = _cropTop;
|
||||
break;
|
||||
}
|
||||
|
||||
setupResources();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void XcbGrabber::setVideoMode(VideoMode mode)
|
||||
{
|
||||
Grabber::setVideoMode(mode);
|
||||
updateScreenDimensions(true);
|
||||
}
|
||||
|
||||
void XcbGrabber::setPixelDecimation(int pixelDecimation)
|
||||
{
|
||||
if(_pixelDecimation != pixelDecimation)
|
||||
{
|
||||
_pixelDecimation = pixelDecimation;
|
||||
updateScreenDimensions(true);
|
||||
}
|
||||
}
|
||||
|
||||
void XcbGrabber::setCropping(unsigned cropLeft, unsigned cropRight, unsigned cropTop, unsigned cropBottom)
|
||||
{
|
||||
Grabber::setCropping(cropLeft, cropRight, cropTop, cropBottom);
|
||||
if(_connection != nullptr)
|
||||
updateScreenDimensions(true);
|
||||
}
|
||||
|
||||
bool XcbGrabber::nativeEventFilter(const QByteArray & eventType, void * message, long int * /*result*/)
|
||||
{
|
||||
if (!_XcbRandRAvailable || eventType != "xcb_generic_event_t" || _XcbRandREventBase == -1)
|
||||
return false;
|
||||
|
||||
xcb_generic_event_t *e = static_cast<xcb_generic_event_t*>(message);
|
||||
const uint8_t xEventType = XCB_EVENT_RESPONSE_TYPE(e);
|
||||
|
||||
if (xEventType == _XcbRandREventBase + XCB_RANDR_SCREEN_CHANGE_NOTIFY)
|
||||
updateScreenDimensions(true);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
xcb_render_pictformat_t XcbGrabber::findFormatForVisual(xcb_visualid_t visual) const
|
||||
{
|
||||
auto formats = query<RenderQueryPictFormats>(_connection);
|
||||
if (formats == nullptr)
|
||||
return {};
|
||||
|
||||
int screen = QX11Info::appScreen();
|
||||
xcb_render_pictscreen_iterator_t sit =
|
||||
xcb_render_query_pict_formats_screens_iterator(formats.get());
|
||||
|
||||
for (; sit.rem; --screen, xcb_render_pictscreen_next(&sit)) {
|
||||
if (screen != 0)
|
||||
continue;
|
||||
|
||||
xcb_render_pictdepth_iterator_t dit =
|
||||
xcb_render_pictscreen_depths_iterator(sit.data);
|
||||
for (; dit.rem; xcb_render_pictdepth_next(&dit))
|
||||
{
|
||||
xcb_render_pictvisual_iterator_t vit
|
||||
= xcb_render_pictdepth_visuals_iterator(dit.data);
|
||||
for (; vit.rem; xcb_render_pictvisual_next(&vit))
|
||||
{
|
||||
if (vit.data->visual == visual)
|
||||
{
|
||||
return vit.data->format;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
39
libsrc/grabber/xcb/XcbWrapper.cpp
Normal file
39
libsrc/grabber/xcb/XcbWrapper.cpp
Normal file
@ -0,0 +1,39 @@
|
||||
#include <grabber/XcbWrapper.h>
|
||||
|
||||
XcbWrapper::XcbWrapper(int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation, const unsigned updateRate_Hz)
|
||||
: GrabberWrapper("Xcb", &_grabber, 0, 0, updateRate_Hz)
|
||||
, _grabber(cropLeft, cropRight, cropTop, cropBottom, pixelDecimation)
|
||||
, _init(false)
|
||||
{}
|
||||
|
||||
XcbWrapper::~XcbWrapper()
|
||||
{
|
||||
if ( _init )
|
||||
{
|
||||
stop();
|
||||
}
|
||||
}
|
||||
|
||||
void XcbWrapper::action()
|
||||
{
|
||||
if (! _init )
|
||||
{
|
||||
_init = true;
|
||||
if ( ! _grabber.Setup() )
|
||||
{
|
||||
stop();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_grabber.updateScreenDimensions() < 0 )
|
||||
{
|
||||
stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isActive())
|
||||
{
|
||||
transferFrame(_grabber);
|
||||
}
|
||||
}
|
@ -94,6 +94,10 @@ QStringList GrabberWrapper::availableGrabbers()
|
||||
grabbers << "x11";
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_XCB
|
||||
grabbers << "xcb";
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_QT
|
||||
grabbers << "qt";
|
||||
#endif
|
||||
|
@ -7,10 +7,10 @@
|
||||
{
|
||||
"type" : "string",
|
||||
"title" : "edt_conf_fg_type_title",
|
||||
"enum" : ["auto","dispmanx","amlogic","x11","framebuffer","qt"],
|
||||
"enum" : ["auto","dispmanx","amlogic","x11", "xcb", "framebuffer","qt"],
|
||||
"options":
|
||||
{
|
||||
"enum_titles": ["edt_conf_enum_automatic","DispmanX","AMLogic","X11","Framebuffer","QT"]
|
||||
"enum_titles": ["edt_conf_enum_automatic","DispmanX","AMLogic","X11", "XCB", "Framebuffer","QT"]
|
||||
},
|
||||
"default" : "auto",
|
||||
"propertyOrder" : 2
|
||||
|
@ -14,6 +14,10 @@ if(ENABLE_X11)
|
||||
add_subdirectory(hyperion-x11)
|
||||
endif()
|
||||
|
||||
if(ENABLE_XCB)
|
||||
add_subdirectory(hyperion-xcb)
|
||||
endif()
|
||||
|
||||
if(ENABLE_DISPMANX)
|
||||
add_subdirectory(hyperion-dispmanx)
|
||||
endif()
|
||||
|
38
src/hyperion-xcb/CMakeLists.txt
Normal file
38
src/hyperion-xcb/CMakeLists.txt
Normal file
@ -0,0 +1,38 @@
|
||||
cmake_minimum_required(VERSION 3.0.0)
|
||||
project(hyperion-xcb)
|
||||
|
||||
include_directories(
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../../libsrc/flatbufserver
|
||||
${FLATBUFFERS_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
set(Hyperion_XCB_HEADERS
|
||||
XcbWrapper.h
|
||||
)
|
||||
|
||||
set(Hyperion_XCB_SOURCES
|
||||
hyperion-xcb.cpp
|
||||
XcbWrapper.cpp
|
||||
)
|
||||
|
||||
add_executable(${PROJECT_NAME}
|
||||
${Hyperion_XCB_HEADERS}
|
||||
${Hyperion_XCB_SOURCES}
|
||||
)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
commandline
|
||||
hyperion-utils
|
||||
flatbufserver
|
||||
flatbuffers
|
||||
xcb-grabber
|
||||
ssdp
|
||||
)
|
||||
|
||||
install (TARGETS ${PROJECT_NAME} DESTINATION "share/hyperion/bin" COMPONENT "hyperion_xcb")
|
||||
|
||||
if(CMAKE_HOST_UNIX)
|
||||
install(CODE "EXECUTE_PROCESS(COMMAND ln -sf \"../share/hyperion/bin/${PROJECT_NAME}\" \"${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}\" )" COMPONENT "hyperion_xcb" )
|
||||
install(FILES "${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME}" DESTINATION "bin" RENAME "${PROJECT_NAME}" COMPONENT "hyperion_xcb" )
|
||||
install(CODE "FILE (REMOVE ${CMAKE_BINARY_DIR}/symlink_${PROJECT_NAME} )" COMPONENT "hyperion_xcb" )
|
||||
endif(CMAKE_HOST_UNIX)
|
47
src/hyperion-xcb/XcbWrapper.cpp
Normal file
47
src/hyperion-xcb/XcbWrapper.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
|
||||
// Hyperion-Xcb includes
|
||||
#include "XcbWrapper.h"
|
||||
|
||||
XcbWrapper::XcbWrapper(int grabInterval, int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation) :
|
||||
_timer(this),
|
||||
_grabber(cropLeft, cropRight, cropTop, cropBottom, pixelDecimation)
|
||||
{
|
||||
_timer.setSingleShot(false);
|
||||
_timer.setInterval(grabInterval);
|
||||
|
||||
// Connect capturing to the timeout signal of the timer
|
||||
connect(&_timer, SIGNAL(timeout()), this, SLOT(capture()));
|
||||
}
|
||||
|
||||
const Image<ColorRgb> & XcbWrapper::getScreenshot()
|
||||
{
|
||||
_grabber.grabFrame(_screenshot, true);
|
||||
return _screenshot;
|
||||
}
|
||||
|
||||
void XcbWrapper::start()
|
||||
{
|
||||
_timer.start();
|
||||
}
|
||||
|
||||
void XcbWrapper::stop()
|
||||
{
|
||||
_timer.stop();
|
||||
}
|
||||
|
||||
bool XcbWrapper::displayInit()
|
||||
{
|
||||
return _grabber.Setup();
|
||||
}
|
||||
|
||||
void XcbWrapper::capture()
|
||||
{
|
||||
_grabber.grabFrame(_screenshot, !_inited);
|
||||
emit sig_screenshot(_screenshot);
|
||||
_inited = true;
|
||||
}
|
||||
|
||||
void XcbWrapper::setVideoMode(const VideoMode mode)
|
||||
{
|
||||
_grabber.setVideoMode(mode);
|
||||
}
|
57
src/hyperion-xcb/XcbWrapper.h
Normal file
57
src/hyperion-xcb/XcbWrapper.h
Normal file
@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
// QT includes
|
||||
#include <QTimer>
|
||||
|
||||
// Hyperion-Xcb includes
|
||||
#include <grabber/XcbGrabber.h>
|
||||
|
||||
//Utils includes
|
||||
#include <utils/VideoMode.h>
|
||||
|
||||
class XcbWrapper : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
XcbWrapper(int grabInterval, int cropLeft, int cropRight, int cropTop, int cropBottom, int pixelDecimation);
|
||||
|
||||
const Image<ColorRgb> & getScreenshot();
|
||||
|
||||
///
|
||||
/// Starts the timed capturing of screenshots
|
||||
///
|
||||
void start();
|
||||
|
||||
void stop();
|
||||
|
||||
bool displayInit();
|
||||
|
||||
signals:
|
||||
void sig_screenshot(const Image<ColorRgb> & screenshot);
|
||||
|
||||
public slots:
|
||||
///
|
||||
/// Set the video mode (2D/3D)
|
||||
/// @param[in] mode The new video mode
|
||||
///
|
||||
void setVideoMode(const VideoMode videoMode);
|
||||
|
||||
private slots:
|
||||
///
|
||||
/// Performs a single screenshot capture and publishes the capture screenshot on the screenshot
|
||||
/// signal.
|
||||
///
|
||||
void capture();
|
||||
|
||||
private:
|
||||
/// The QT timer to generate capture-publish events
|
||||
QTimer _timer;
|
||||
|
||||
/// The grabber for creating screenshots
|
||||
XcbGrabber _grabber;
|
||||
|
||||
Image<ColorRgb> _screenshot;
|
||||
|
||||
// prevent cont dimension updates
|
||||
bool _inited = false;
|
||||
};
|
115
src/hyperion-xcb/hyperion-xcb.cpp
Normal file
115
src/hyperion-xcb/hyperion-xcb.cpp
Normal file
@ -0,0 +1,115 @@
|
||||
|
||||
// QT includes
|
||||
#include <QApplication>
|
||||
#include <QImage>
|
||||
|
||||
#include <commandline/Parser.h>
|
||||
#include <flatbufserver/FlatBufferConnection.h>
|
||||
#include <utils/DefaultSignalHandler.h>
|
||||
#include "XcbWrapper.h"
|
||||
#include "HyperionConfig.h"
|
||||
|
||||
// ssdp discover
|
||||
#include <ssdp/SSDPDiscover.h>
|
||||
|
||||
using namespace commandline;
|
||||
|
||||
// save the image as screenshot
|
||||
void saveScreenshot(QString filename, const Image<ColorRgb> & image)
|
||||
{
|
||||
// store as PNG
|
||||
QImage pngImage((const uint8_t *) image.memptr(), image.width(), image.height(), 3*image.width(), QImage::Format_RGB888);
|
||||
pngImage.save(filename);
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
std::cout
|
||||
<< "hyperion-xcb:" << std::endl
|
||||
<< "\tVersion : " << HYPERION_VERSION << " (" << HYPERION_BUILD_ID << ")" << std::endl
|
||||
<< "\tbuild time: " << __DATE__ << " " << __TIME__ << std::endl;
|
||||
|
||||
DefaultSignalHandler::install();
|
||||
|
||||
QApplication app(argc, argv);
|
||||
try
|
||||
{
|
||||
// create the option parser and initialize all parameters
|
||||
Parser parser("XCB capture application for Hyperion. Will automatically search a Hyperion server if -a option isn't used. Please note that if you have more than one server running it's more or less random which one will be used.");
|
||||
|
||||
IntOption & argFps = parser.add<IntOption> ('f', "framerate", "Capture frame rate [default: %1]", "10");
|
||||
IntOption & argCropWidth = parser.add<IntOption> (0x0, "crop-width", "Number of pixels to crop from the left and right sides of the picture before decimation [default: %1]", "0");
|
||||
IntOption & argCropHeight = parser.add<IntOption> (0x0, "crop-height", "Number of pixels to crop from the top and the bottom of the picture before decimation [default: %1]", "0");
|
||||
IntOption & argCropLeft = parser.add<IntOption> (0x0, "crop-left", "Number of pixels to crop from the left of the picture before decimation (overrides --crop-width)");
|
||||
IntOption & argCropRight = parser.add<IntOption> (0x0, "crop-right", "Number of pixels to crop from the right of the picture before decimation (overrides --crop-width)");
|
||||
IntOption & argCropTop = parser.add<IntOption> (0x0, "crop-top", "Number of pixels to crop from the top of the picture before decimation (overrides --crop-height)");
|
||||
IntOption & argCropBottom = parser.add<IntOption> (0x0, "crop-bottom", "Number of pixels to crop from the bottom of the picture before decimation (overrides --crop-height)");
|
||||
IntOption & argSizeDecimation = parser.add<IntOption> ('s', "size-decimator", "Decimation factor for the output size [default=%1]", "8", 1);
|
||||
BooleanOption & argScreenshot = parser.add<BooleanOption>(0x0, "screenshot", "Take a single screenshot, save it to file and quit");
|
||||
Option & argAddress = parser.add<Option> ('a', "address", "Set the address of the hyperion server [default: %1]", "127.0.0.1:19400");
|
||||
IntOption & argPriority = parser.add<IntOption> ('p', "priority", "Use the provided priority channel (suggested 100-199) [default: %1]", "150");
|
||||
BooleanOption & argSkipReply = parser.add<BooleanOption>(0x0, "skip-reply", "Do not receive and check reply messages from Hyperion");
|
||||
BooleanOption & argHelp = parser.add<BooleanOption>('h', "help", "Show this help message and exit");
|
||||
|
||||
// parse all options
|
||||
parser.process(app);
|
||||
|
||||
// check if we need to display the usage. exit if we do.
|
||||
if (parser.isSet(argHelp))
|
||||
{
|
||||
parser.showHelp(0);
|
||||
}
|
||||
|
||||
// Create the XCB grabbing stuff
|
||||
XcbWrapper xcbWrapper(
|
||||
1000 / argFps.getInt(parser),
|
||||
parser.isSet(argCropLeft) ? argCropLeft.getInt(parser) : argCropWidth.getInt(parser),
|
||||
parser.isSet(argCropRight) ? argCropRight.getInt(parser) : argCropWidth.getInt(parser),
|
||||
parser.isSet(argCropTop) ? argCropTop.getInt(parser) : argCropHeight.getInt(parser),
|
||||
parser.isSet(argCropBottom) ? argCropBottom.getInt(parser) : argCropHeight.getInt(parser),
|
||||
argSizeDecimation.getInt(parser)); // decimation
|
||||
|
||||
if (!xcbWrapper.displayInit())
|
||||
return -1;
|
||||
|
||||
if (parser.isSet(argScreenshot))
|
||||
{
|
||||
// Capture a single screenshot and finish
|
||||
const Image<ColorRgb> & screenshot = xcbWrapper.getScreenshot();
|
||||
saveScreenshot("screenshot.png", screenshot);
|
||||
}
|
||||
else
|
||||
{
|
||||
// server searching by ssdp
|
||||
QString address = argAddress.value(parser);
|
||||
if(argAddress.value(parser) == "127.0.0.1:19400")
|
||||
{
|
||||
SSDPDiscover discover;
|
||||
address = discover.getFirstService(searchType::STY_FLATBUFSERVER);
|
||||
if(address.isEmpty())
|
||||
{
|
||||
address = argAddress.value(parser);
|
||||
}
|
||||
}
|
||||
// Create the Flatbuf-connection
|
||||
FlatBufferConnection flatbuf("XCB Standalone", address, argPriority.getInt(parser), parser.isSet(argSkipReply));
|
||||
|
||||
// Connect the screen capturing to flatbuf connection processing
|
||||
QObject::connect(&xcbWrapper, SIGNAL(sig_screenshot(const Image<ColorRgb> &)), &flatbuf, SLOT(setImage(Image<ColorRgb>)));
|
||||
|
||||
// Start the capturing
|
||||
xcbWrapper.start();
|
||||
|
||||
// Start the application
|
||||
app.exec();
|
||||
}
|
||||
}
|
||||
catch (const std::runtime_error & e)
|
||||
{
|
||||
// An error occured. Display error and quit
|
||||
Error(Logger::getInstance("XCBGRABBER"), "%s", e.what());
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -95,6 +95,10 @@ if (ENABLE_X11)
|
||||
target_link_libraries(hyperiond x11-grabber)
|
||||
endif ()
|
||||
|
||||
if (ENABLE_XCB)
|
||||
target_link_libraries(hyperiond xcb-grabber)
|
||||
endif ()
|
||||
|
||||
if (ENABLE_QT)
|
||||
target_link_libraries(hyperiond qt-grabber)
|
||||
endif ()
|
||||
|
@ -75,6 +75,7 @@ HyperionDaemon::HyperionDaemon(const QString rootPath, QObject *parent, const bo
|
||||
, _v4l2Grabber(nullptr)
|
||||
, _dispmanx(nullptr)
|
||||
, _x11Grabber(nullptr)
|
||||
, _xcbGrabber(nullptr)
|
||||
, _amlGrabber(nullptr)
|
||||
, _fbGrabber(nullptr)
|
||||
, _osxGrabber(nullptr)
|
||||
@ -133,7 +134,7 @@ HyperionDaemon::HyperionDaemon(const QString rootPath, QObject *parent, const bo
|
||||
connect(this, &HyperionDaemon::videoMode, _instanceManager, &HyperionIManager::newVideoMode);
|
||||
|
||||
// ---- grabber -----
|
||||
#if !defined(ENABLE_DISPMANX) && !defined(ENABLE_OSX) && !defined(ENABLE_FB) && !defined(ENABLE_X11) && !defined(ENABLE_AMLOGIC) && !defined(ENABLE_QT)
|
||||
#if !defined(ENABLE_DISPMANX) && !defined(ENABLE_OSX) && !defined(ENABLE_FB) && !defined(ENABLE_X11) && !defined(ENABLE_XCB) && !defined(ENABLE_AMLOGIC) && !defined(ENABLE_QT)
|
||||
Warning(_log, "No platform capture can be instantiated, because all grabbers have been left out from the build");
|
||||
#endif
|
||||
|
||||
@ -374,7 +375,13 @@ void HyperionDaemon::handleSettingsUpdate(const settings::type &settingsType, co
|
||||
QByteArray envDisplay = qgetenv("DISPLAY");
|
||||
if ( !envDisplay.isEmpty() )
|
||||
{
|
||||
#if defined(ENABLE_X11)
|
||||
type = "x11";
|
||||
#elif defined(ENABLE_XCB)
|
||||
type = "xcb";
|
||||
#else
|
||||
type = "qt";
|
||||
#endif
|
||||
}
|
||||
// qt -> if nothing other applies
|
||||
else
|
||||
@ -429,6 +436,14 @@ void HyperionDaemon::handleSettingsUpdate(const settings::type &settingsType, co
|
||||
_x11Grabber = nullptr;
|
||||
}
|
||||
#endif
|
||||
#ifdef ENABLE_XCB
|
||||
if(_xcbGrabber != nullptr)
|
||||
{
|
||||
_xcbGrabber->stop();
|
||||
delete _xcbGrabber;
|
||||
_xcbGrabber = nullptr;
|
||||
}
|
||||
#endif
|
||||
#ifdef ENABLE_QT
|
||||
if(_qtGrabber != nullptr)
|
||||
{
|
||||
@ -479,6 +494,14 @@ void HyperionDaemon::handleSettingsUpdate(const settings::type &settingsType, co
|
||||
_x11Grabber->tryStart();
|
||||
#endif
|
||||
}
|
||||
else if (type == "xcb")
|
||||
{
|
||||
if (_xcbGrabber == nullptr)
|
||||
createGrabberXcb(grabberConfig);
|
||||
#ifdef ENABLE_XCB
|
||||
_xcbGrabber->tryStart();
|
||||
#endif
|
||||
}
|
||||
else if (type == "qt")
|
||||
{
|
||||
if (_qtGrabber == nullptr)
|
||||
@ -603,6 +626,25 @@ void HyperionDaemon::createGrabberX11(const QJsonObject &grabberConfig)
|
||||
#endif
|
||||
}
|
||||
|
||||
void HyperionDaemon::createGrabberXcb(const QJsonObject &grabberConfig)
|
||||
{
|
||||
#ifdef ENABLE_XCB
|
||||
_xcbGrabber = new XcbWrapper(
|
||||
_grabber_cropLeft, _grabber_cropRight, _grabber_cropTop, _grabber_cropBottom,
|
||||
grabberConfig["pixelDecimation"].toInt(8),
|
||||
_grabber_frequency);
|
||||
_xcbGrabber->setCropping(_grabber_cropLeft, _grabber_cropRight, _grabber_cropTop, _grabber_cropBottom);
|
||||
|
||||
// connect to HyperionDaemon signal
|
||||
connect(this, &HyperionDaemon::videoMode, _xcbGrabber, &XcbWrapper::setVideoMode);
|
||||
connect(this, &HyperionDaemon::settingsChanged, _xcbGrabber, &XcbWrapper::handleSettingsUpdate);
|
||||
|
||||
Info(_log, "XCB grabber created");
|
||||
#else
|
||||
Error(_log, "The XCB grabber can not be instantiated, because it has been left out from the build");
|
||||
#endif
|
||||
}
|
||||
|
||||
void HyperionDaemon::createGrabberQt(const QJsonObject &grabberConfig)
|
||||
{
|
||||
#ifdef ENABLE_QT
|
||||
|
@ -40,6 +40,12 @@
|
||||
typedef QObject X11Wrapper;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_XCB
|
||||
#include <grabber/XcbWrapper.h>
|
||||
#else
|
||||
typedef QObject XcbWrapper;
|
||||
#endif
|
||||
|
||||
#ifdef ENABLE_QT
|
||||
#include <grabber/QtWrapper.h>
|
||||
#else
|
||||
@ -144,6 +150,7 @@ private:
|
||||
void createGrabberFramebuffer(const QJsonObject & grabberConfig);
|
||||
void createGrabberOsx(const QJsonObject & grabberConfig);
|
||||
void createGrabberX11(const QJsonObject & grabberConfig);
|
||||
void createGrabberXcb(const QJsonObject & grabberConfig);
|
||||
void createGrabberQt(const QJsonObject & grabberConfig);
|
||||
void createCecHandler();
|
||||
|
||||
@ -159,6 +166,7 @@ private:
|
||||
V4L2Wrapper* _v4l2Grabber;
|
||||
DispmanxWrapper* _dispmanx;
|
||||
X11Wrapper* _x11Grabber;
|
||||
XcbWrapper* _xcbGrabber;
|
||||
AmlogicWrapper* _amlGrabber;
|
||||
FramebufferWrapper* _fbGrabber;
|
||||
OsxWrapper* _osxGrabber;
|
||||
|
@ -105,13 +105,21 @@ QCoreApplication* createApplication(int &argc, char *argv[])
|
||||
if (!forceNoGui)
|
||||
{
|
||||
// if x11, then test if xserver is available
|
||||
#ifdef ENABLE_X11
|
||||
#if defined(ENABLE_X11)
|
||||
Display* dpy = XOpenDisplay(NULL);
|
||||
if (dpy != NULL)
|
||||
{
|
||||
XCloseDisplay(dpy);
|
||||
isGuiApp = true;
|
||||
}
|
||||
#elif defined(ENABLE_XCB)
|
||||
int screen_num;
|
||||
xcb_connection_t * connection = xcb_connect(nullptr, &screen_num);
|
||||
if (!xcb_connection_has_error(connection))
|
||||
{
|
||||
isGuiApp = true;
|
||||
}
|
||||
xcb_disconnect(connection);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user