mirror of
https://github.com/moonlight-stream/moonlight-embedded.git
synced 2025-07-03 16:25:31 +00:00
Compare commits
No commits in common. "master" and "v2.4.10" have entirely different histories.
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
@ -1,7 +1,7 @@
|
|||||||
# Contribution Guide
|
# Contribution Guide
|
||||||
|
|
||||||
## Got a Question or Problem?
|
## Got a Question or Problem?
|
||||||
Please take a look at the [wiki](https://github.com/moonlight-stream/moonlight-embedded/wiki) for answers to your questions about using Moonlight Embedded.
|
Please take a look at the [wiki](https://github.com/irtimmer/moonlight-embedded/wiki) for answers to your questions about using Moonlight Embedded.
|
||||||
|
|
||||||
If you still have questions about Moonlight Embedded, please use one of the different forums discussing Moonlight Embedded.
|
If you still have questions about Moonlight Embedded, please use one of the different forums discussing Moonlight Embedded.
|
||||||
|
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
build/
|
|
5
.gitmodules
vendored
5
.gitmodules
vendored
@ -1,6 +1,3 @@
|
|||||||
[submodule "common"]
|
[submodule "common"]
|
||||||
path = third_party/moonlight-common-c
|
path = third_party/moonlight-common-c
|
||||||
url = https://github.com/moonlight-stream/moonlight-common-c.git
|
url = https://github.com/irtimmer/moonlight-common-c.git
|
||||||
[submodule "third_party/SDL_GameControllerDB"]
|
|
||||||
path = third_party/SDL_GameControllerDB
|
|
||||||
url = https://github.com/gabomdq/SDL_GameControllerDB.git
|
|
||||||
|
102
CMakeLists.txt
102
CMakeLists.txt
@ -1,13 +1,8 @@
|
|||||||
cmake_minimum_required(VERSION 3.6)
|
cmake_minimum_required(VERSION 3.1)
|
||||||
project(moonlight-embedded VERSION 2.7.0 LANGUAGES C)
|
project(moonlight-embedded VERSION 2.4.10 LANGUAGES C)
|
||||||
SET(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
|
SET(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
|
||||||
SET(CMAKE_C_STANDARD 99)
|
SET(CMAKE_C_STANDARD 99)
|
||||||
include(${CMAKE_ROOT}/Modules/GNUInstallDirs.cmake)
|
include(${CMAKE_ROOT}/Modules/GNUInstallDirs.cmake)
|
||||||
include(${CMAKE_SOURCE_DIR}/cmake/generate_version_header.cmake)
|
|
||||||
|
|
||||||
include(CheckCSourceCompiles)
|
|
||||||
|
|
||||||
add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-pointer-sign -Wno-sign-compare -Wno-switch)
|
|
||||||
|
|
||||||
aux_source_directory(./src SRC_LIST)
|
aux_source_directory(./src SRC_LIST)
|
||||||
list(APPEND SRC_LIST ./src/input/evdev.c ./src/input/mapping.c ./src/input/udev.c)
|
list(APPEND SRC_LIST ./src/input/evdev.c ./src/input/mapping.c ./src/input/udev.c)
|
||||||
@ -16,48 +11,25 @@ set(MOONLIGHT_DEFINITIONS)
|
|||||||
|
|
||||||
find_package(ALSA)
|
find_package(ALSA)
|
||||||
find_package(Opus REQUIRED)
|
find_package(Opus REQUIRED)
|
||||||
find_package(Broadcom-OMX)
|
find_package(Broadcom)
|
||||||
find_package(Freescale)
|
find_package(Freescale)
|
||||||
find_package(Amlogic)
|
find_package(Amlogic)
|
||||||
find_package(Rockchip)
|
find_package(Rockchip)
|
||||||
|
|
||||||
find_package(PkgConfig REQUIRED)
|
find_package(PkgConfig REQUIRED)
|
||||||
|
|
||||||
option(ENABLE_SDL "Compile SDL support" ON)
|
|
||||||
option(ENABLE_FFMPEG "Compile FFMPEG support" ON)
|
|
||||||
option(ENABLE_X11 "Compile X11 support (requires ENABLE_FFMPEG)" ON)
|
|
||||||
option(ENABLE_CEC "Compile CEC support" ON)
|
|
||||||
option(ENABLE_PULSE "Compile PulseAudio support" ON)
|
|
||||||
|
|
||||||
pkg_check_modules(EVDEV REQUIRED libevdev)
|
pkg_check_modules(EVDEV REQUIRED libevdev)
|
||||||
pkg_check_modules(UDEV REQUIRED libudev)
|
pkg_check_modules(UDEV REQUIRED libudev)
|
||||||
if (ENABLE_SDL)
|
pkg_check_modules(SDL sdl2>=2.0.4)
|
||||||
pkg_check_modules(SDL sdl2>=2.0.4)
|
pkg_check_modules(AVCODEC libavcodec)
|
||||||
endif()
|
pkg_check_modules(AVUTIL libavutil)
|
||||||
if (ENABLE_FFMPEG)
|
pkg_check_modules(XLIB x11)
|
||||||
pkg_check_modules(AVCODEC libavcodec)
|
pkg_check_modules(VDPAU vdpau)
|
||||||
pkg_check_modules(AVUTIL libavutil)
|
pkg_check_modules(LIBVA libva)
|
||||||
pkg_check_modules(VDPAU vdpau)
|
pkg_check_modules(LIBVA_X11 libva-x11)
|
||||||
pkg_check_modules(LIBVA libva)
|
pkg_check_modules(PULSE libpulse-simple)
|
||||||
pkg_check_modules(EGL egl)
|
pkg_check_modules(CEC libcec>=4)
|
||||||
pkg_check_modules(GLES glesv2)
|
pkg_check_modules(EGL egl)
|
||||||
|
pkg_check_modules(GLES glesv2)
|
||||||
if (ENABLE_X11)
|
|
||||||
pkg_check_modules(XLIB x11)
|
|
||||||
pkg_check_modules(LIBVA_X11 libva-x11)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
if (ENABLE_PULSE)
|
|
||||||
pkg_check_modules(PULSE libpulse-simple)
|
|
||||||
endif()
|
|
||||||
if (ENABLE_CEC)
|
|
||||||
pkg_check_modules(CEC libcec>=4)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
pkg_check_modules(MMAL mmal)
|
|
||||||
if (NOT MMAL_FOUND)
|
|
||||||
find_package(MMAL)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(VDPAU_ACCEL_FOUND FALSE)
|
set(VDPAU_ACCEL_FOUND FALSE)
|
||||||
set(VA_ACCEL_FOUND FALSE)
|
set(VA_ACCEL_FOUND FALSE)
|
||||||
@ -86,25 +58,8 @@ include_directories("${PROJECT_BINARY_DIR}")
|
|||||||
add_subdirectory(libgamestream)
|
add_subdirectory(libgamestream)
|
||||||
|
|
||||||
add_executable(moonlight ${SRC_LIST})
|
add_executable(moonlight ${SRC_LIST})
|
||||||
target_link_libraries(moonlight m)
|
|
||||||
target_link_libraries(moonlight gamestream)
|
target_link_libraries(moonlight gamestream)
|
||||||
|
|
||||||
if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
|
|
||||||
set(ALSA_FOUND FALSE)
|
|
||||||
target_sources(moonlight PRIVATE ./src/audio/oss.c)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
check_c_source_compiles("#include <sys/auxv.h>
|
|
||||||
int main(void) { return getauxval(AT_HWCAP); }" HAVE_GETAUXVAL)
|
|
||||||
if (HAVE_GETAUXVAL)
|
|
||||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_GETAUXVAL)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
check_c_source_compiles("int main(void) { return __builtin_cpu_supports(\"aes\"); }" HAVE_BICS_AES)
|
|
||||||
if (HAVE_BICS_AES)
|
|
||||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_BICS_AES)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (CEC_FOUND)
|
if (CEC_FOUND)
|
||||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_LIBCEC)
|
list(APPEND MOONLIGHT_DEFINITIONS HAVE_LIBCEC)
|
||||||
list(APPEND MOONLIGHT_OPTIONS CEC)
|
list(APPEND MOONLIGHT_OPTIONS CEC)
|
||||||
@ -116,37 +71,28 @@ endif()
|
|||||||
if(AMLOGIC_FOUND)
|
if(AMLOGIC_FOUND)
|
||||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_AML)
|
list(APPEND MOONLIGHT_DEFINITIONS HAVE_AML)
|
||||||
list(APPEND MOONLIGHT_OPTIONS AML)
|
list(APPEND MOONLIGHT_OPTIONS AML)
|
||||||
add_library(moonlight-aml SHARED ./src/video/aml.c ./src/util.c ${ILCLIENT_SRC_LIST})
|
add_library(moonlight-aml SHARED ./src/video/aml.c ${ILCLIENT_SRC_LIST})
|
||||||
target_include_directories(moonlight-aml PRIVATE ${AMLOGIC_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR})
|
target_include_directories(moonlight-aml PRIVATE ${AMLOGIC_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR})
|
||||||
target_link_libraries(moonlight-aml gamestream ${AMLOGIC_LIBRARIES})
|
target_link_libraries(moonlight-aml gamestream ${AMLOGIC_LIBRARIES})
|
||||||
set_property(TARGET moonlight-aml PROPERTY COMPILE_DEFINITIONS ${AMLOGIC_DEFINITIONS})
|
set_property(TARGET moonlight-aml PROPERTY COMPILE_DEFINITIONS ${AMLOGIC_DEFINITIONS})
|
||||||
install(TARGETS moonlight-aml DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
install(TARGETS moonlight-aml DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(BROADCOM-OMX_FOUND)
|
if(BROADCOM_FOUND)
|
||||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_PI)
|
list(APPEND MOONLIGHT_DEFINITIONS HAVE_PI)
|
||||||
list(APPEND MOONLIGHT_OPTIONS PI)
|
list(APPEND MOONLIGHT_OPTIONS PI)
|
||||||
aux_source_directory(./third_party/ilclient ILCLIENT_SRC_LIST)
|
aux_source_directory(./third_party/ilclient ILCLIENT_SRC_LIST)
|
||||||
add_library(moonlight-pi SHARED ./src/video/pi.c ./src/audio/omx.c ./src/util.c ${ILCLIENT_SRC_LIST})
|
add_library(moonlight-pi SHARED ./src/video/pi.c ./src/audio/omx.c ${ILCLIENT_SRC_LIST})
|
||||||
target_include_directories(moonlight-pi PRIVATE ./third_party/ilclient ${BROADCOM_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR} ${OPUS_INCLUDE_DIRS})
|
target_include_directories(moonlight-pi PRIVATE ./third_party/ilclient ${BROADCOM_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR} ${OPUS_INCLUDE_DIRS})
|
||||||
target_link_libraries(moonlight-pi gamestream ${BROADCOM_OMX_LIBRARIES} ${OPUS_LIBRARY})
|
target_link_libraries(moonlight-pi gamestream ${BROADCOM_LIBRARIES} ${OPUS_LIBRARY})
|
||||||
set_property(TARGET moonlight-pi PROPERTY COMPILE_DEFINITIONS ${BROADCOM_OMX_DEFINITIONS})
|
set_property(TARGET moonlight-pi PROPERTY COMPILE_DEFINITIONS ${BROADCOM_DEFINITIONS})
|
||||||
install(TARGETS moonlight-pi DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
install(TARGETS moonlight-pi DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(MMAL_FOUND)
|
|
||||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_MMAL)
|
|
||||||
list(APPEND MOONLIGHT_OPTIONS MMAL)
|
|
||||||
add_library(moonlight-mmal SHARED ./src/video/mmal.c ./src/util.c)
|
|
||||||
target_include_directories(moonlight-mmal PRIVATE ${MMAL_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR})
|
|
||||||
target_link_libraries(moonlight-mmal gamestream ${MMAL_LINK_LIBRARIES})
|
|
||||||
install(TARGETS moonlight-mmal DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(FREESCALE_FOUND)
|
if(FREESCALE_FOUND)
|
||||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_IMX)
|
list(APPEND MOONLIGHT_DEFINITIONS HAVE_IMX)
|
||||||
list(APPEND MOONLIGHT_OPTIONS IMX)
|
list(APPEND MOONLIGHT_OPTIONS IMX)
|
||||||
add_library(moonlight-imx SHARED ./src/video/imx.c ./src/video/imx_vpu.c ./src/util.c)
|
add_library(moonlight-imx SHARED ./src/video/imx.c ./src/video/imx_vpu.c)
|
||||||
target_include_directories(moonlight-imx PRIVATE ${FREESCALE_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR})
|
target_include_directories(moonlight-imx PRIVATE ${FREESCALE_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR})
|
||||||
target_link_libraries(moonlight-imx gamestream ${FREESCALE_LIBRARIES})
|
target_link_libraries(moonlight-imx gamestream ${FREESCALE_LIBRARIES})
|
||||||
install(TARGETS moonlight-imx DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
install(TARGETS moonlight-imx DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||||
@ -155,7 +101,7 @@ endif()
|
|||||||
if(ROCKCHIP_FOUND)
|
if(ROCKCHIP_FOUND)
|
||||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_ROCKCHIP)
|
list(APPEND MOONLIGHT_DEFINITIONS HAVE_ROCKCHIP)
|
||||||
list(APPEND MOONLIGHT_OPTIONS ROCKCHIP)
|
list(APPEND MOONLIGHT_OPTIONS ROCKCHIP)
|
||||||
add_library(moonlight-rk SHARED ./src/video/rk.c ./src/util.c)
|
add_library(moonlight-rk SHARED ./src/video/rk.c)
|
||||||
target_include_directories(moonlight-rk PRIVATE ${ROCKCHIP_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR})
|
target_include_directories(moonlight-rk PRIVATE ${ROCKCHIP_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR})
|
||||||
target_link_libraries(moonlight-rk gamestream ${ROCKCHIP_LIBRARIES})
|
target_link_libraries(moonlight-rk gamestream ${ROCKCHIP_LIBRARIES})
|
||||||
set_property(TARGET moonlight-rk PROPERTY COMPILE_DEFINITIONS ${ROCKCHIP_DEFINITIONS})
|
set_property(TARGET moonlight-rk PROPERTY COMPILE_DEFINITIONS ${ROCKCHIP_DEFINITIONS})
|
||||||
@ -211,12 +157,12 @@ if (PULSE_FOUND)
|
|||||||
target_link_libraries(moonlight ${PULSE_LIBRARIES})
|
target_link_libraries(moonlight ${PULSE_LIBRARIES})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if (AMLOGIC_FOUND OR BROADCOM-OMX_FOUND OR MMAL_FOUND OR FREESCALE_FOUND OR ROCKCHIP_FOUND OR X11_FOUND)
|
if (AMLOGIC_FOUND OR BROADCOM_FOUND OR FREESCALE_FOUND OR ROCKCHIP_FOUND OR X11_FOUND)
|
||||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_EMBEDDED)
|
list(APPEND MOONLIGHT_DEFINITIONS HAVE_EMBEDDED)
|
||||||
list(APPEND MOONLIGHT_OPTIONS EMBEDDED)
|
list(APPEND MOONLIGHT_OPTIONS EMBEDDED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(NOT AMLOGIC_FOUND AND NOT BROADCOM-OMX_FOUND AND NOT MMAL_FOUND AND NOT FREESCALE_FOUND AND NOT ROCKCHIP_FOUND AND NOT SOFTWARE_FOUND)
|
if(NOT AMLOGIC_FOUND AND NOT BROADCOM_FOUND AND NOT FREESCALE_FOUND AND NOT ROCKCHIP_FOUND AND NOT SOFTWARE_FOUND)
|
||||||
message(FATAL_ERROR "No video output available")
|
message(FATAL_ERROR "No video output available")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -229,5 +175,5 @@ target_link_libraries(moonlight ${EVDEV_LIBRARIES} ${OPUS_LIBRARY} ${UDEV_LIBRAR
|
|||||||
add_subdirectory(docs)
|
add_subdirectory(docs)
|
||||||
|
|
||||||
install(TARGETS moonlight DESTINATION ${CMAKE_INSTALL_BINDIR})
|
install(TARGETS moonlight DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
install(FILES ./third_party/SDL_GameControllerDB/gamecontrollerdb.txt DESTINATION ${CMAKE_INSTALL_DATADIR}/moonlight)
|
install(FILES gamecontrollerdb.txt DESTINATION ${CMAKE_INSTALL_DATADIR}/moonlight)
|
||||||
install(FILES moonlight.conf DESTINATION ${CMAKE_INSTALL_SYSCONFDIR})
|
install(FILES moonlight.conf DESTINATION ${CMAKE_INSTALL_SYSCONFDIR})
|
||||||
|
41
README.md
41
README.md
@ -1,24 +1,49 @@
|
|||||||
# Moonlight Embedded
|
# Moonlight Embedded
|
||||||
|
|
||||||
[](https://ci.appveyor.com/project/cgutman/moonlight-embedded/branch/master)
|
Moonlight Embedded is an open source implementation of NVIDIA's GameStream, as used by the NVIDIA Shield, but built for Linux.
|
||||||
|
|
||||||
Moonlight Embedded is an open source client for [Sunshine](https://github.com/LizardByte/Sunshine) and NVIDIA GameStream for embedded Linux systems, like Raspberry Pi, CuBox-i and ODROID. Moonlight allows you to stream your full collection of games and applications from your PC to other devices to play them remotely.
|
Moonlight Embedded allows you to stream your full collection of games from
|
||||||
|
your powerful Windows desktop to your (embedded) Linux system, like Raspberry Pi, CuBox-i and ODROID.
|
||||||
Moonlight also has [PC](https://github.com/moonlight-stream/moonlight-qt), [Android](https://github.com/moonlight-stream/moonlight-android), and [iOS](https://github.com/moonlight-stream/moonlight-ios) clients.
|
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
More information about installing and runnning Moonlight Embedded is available on the [wiki](https://github.com/moonlight-stream/moonlight-embedded/wiki).
|
More information about installing and runnning Moonlight Embedded is available on the [wiki](https://github.com/irtimmer/moonlight-embedded/wiki).
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
* [GFE compatible](http://shield.nvidia.com/play-pc-games/) computer with GTX 600/700/900/1000 series GPU (for the PC you're streaming from)
|
||||||
|
* High-end wireless router (802.11n dual-band recommended) or wired network
|
||||||
|
* Geforce Experience 2.1.1 or higher
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
* Ensure your GFE server and client are on the same network
|
||||||
|
* Turn on Shield Streaming in the GFE settings
|
||||||
|
* Pair Moonlight Embedded with the GFE server
|
||||||
|
* Accept the pairing confirmation on your PC
|
||||||
|
* Connect to the GFE Server with Moonlight Embedded
|
||||||
|
* Play games!
|
||||||
|
|
||||||
## Bugs
|
## Bugs
|
||||||
|
|
||||||
Please check the wiki and old bug reports before submitting a new bug report.
|
Please check the fora, wiki and old bug reports before submitting a new bug report.
|
||||||
|
|
||||||
Bugs can be reported to the [issue tracker](https://github.com/moonlight-stream/moonlight-embedded/issues).
|
Bugs can be reported to the [issue tracker](https://github.com/irtimmer/moonlight-embedded/issues).
|
||||||
|
|
||||||
## See also
|
## See also
|
||||||
|
|
||||||
[Moonlight-common-c](https://github.com/moonlight-stream/moonlight-common-c) is the shared codebase between different Moonlight implementations
|
[Moonlight-common-c](https://github.com/moonlight-stream/moonlight-common-c) is the shared codebase between
|
||||||
|
different C implementations of Moonlight
|
||||||
|
|
||||||
|
[Moonlight-common-c](https://github.com/irtimmer/moonlight-common-c) is the fork used by Moonlight Embedded
|
||||||
|
|
||||||
|
## Discussion
|
||||||
|
|
||||||
|
[Discord](https://discord.gg/6ERtzFY) Moonlight in General with Moonlight Embedded channel
|
||||||
|
[XDA](http://forum.xda-developers.com/showthread.php?t=2505510) Moonlight in General
|
||||||
|
[Raspberry Pi Forum](http://www.raspberrypi.org/forums/viewtopic.php?f=78&t=65878) Moonlight Embedded for Raspberry Pi
|
||||||
|
[SolidRun Community](http://www.solid-run.com/community/viewtopic.php?f=13&t=1489&p=11173) Moonlight Embedded for Cubox-i and Hummingboard
|
||||||
|
[ODROID Forum](http://forum.odroid.com/viewtopic.php?f=91&t=15456) Moonlight Embedded on ODROID
|
||||||
|
|
||||||
## Contribute
|
## Contribute
|
||||||
|
|
||||||
|
30
appveyor.yml
30
appveyor.yml
@ -1,30 +0,0 @@
|
|||||||
version: 0.0.0.{build}
|
|
||||||
|
|
||||||
clone_depth: 1
|
|
||||||
|
|
||||||
environment:
|
|
||||||
matrix:
|
|
||||||
- APPVEYOR_BUILD_WORKER_IMAGE: Ubuntu2004
|
|
||||||
PACKAGES: libssl-dev libopus-dev libasound2-dev libudev-dev libavahi-client-dev libcurl4-openssl-dev libevdev-dev libexpat1-dev libpulse-dev uuid-dev cmake gcc g++ libavcodec-dev libavutil-dev libsdl2-dev libva-dev libvdpau-dev libcec-dev libp8-platform-dev
|
|
||||||
BUILD_TARGET: ubuntu
|
|
||||||
- APPVEYOR_BUILD_WORKER_IMAGE: Ubuntu2004
|
|
||||||
PACKAGES: qemu binfmt-support qemu-user-static
|
|
||||||
BUILD_TARGET: raspbian
|
|
||||||
|
|
||||||
install:
|
|
||||||
- 'sudo apt update || true'
|
|
||||||
- 'sudo apt install -y $PACKAGES'
|
|
||||||
- '[ "$BUILD_TARGET" != raspbian ] || docker run --rm --privileged multiarch/qemu-user-static --reset -p yes'
|
|
||||||
|
|
||||||
before_build:
|
|
||||||
- 'git submodule update --init --recursive'
|
|
||||||
|
|
||||||
build_script:
|
|
||||||
- 'if [[ "$BUILD_TARGET" = ubuntu ]]; then mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX=/tmp .. && make -j$(nproc) && make install; fi'
|
|
||||||
- 'if [[ "$BUILD_TARGET" = raspbian ]]; then git clone --recursive https://github.com/cgutman/moonlight-embedded-packaging.git && cd moonlight-embedded-packaging && sh -c "./build-rpi-buster.sh $APPVEYOR_REPO_COMMIT"; fi'
|
|
||||||
|
|
||||||
after_build:
|
|
||||||
- sh: '[ "$BUILD_TARGET" != raspbian ] || appveyor PushArtifact out_*/moonlight-embedded_*.deb'
|
|
||||||
- sh: '[ "$BUILD_TARGET" != raspbian ] || appveyor PushArtifact out_*/moonlight-embedded-dbgsym_*.deb'
|
|
||||||
|
|
||||||
deploy: off
|
|
@ -1,17 +1,29 @@
|
|||||||
find_path(AMLOGIC_INCLUDE_DIR
|
find_path(AMLOGIC_INCLUDE_DIR
|
||||||
NAMES codec.h
|
NAMES codec.h
|
||||||
DOC "Amlogic include directory"
|
DOC "Amlogic include directory"
|
||||||
PATHS /usr/local/include/amcodec /usr/osmc/include/amcodec /usr/include/amcodec /usr/include/)
|
PATHS /usr/local/include/amcodec /usr/include/amcodec /usr/include/)
|
||||||
mark_as_advanced(AMLOGIC_INCLUDE_DIR)
|
mark_as_advanced(AMLOGIC_INCLUDE_DIR)
|
||||||
|
|
||||||
|
find_library(AMAVUTILS_LIBRARY
|
||||||
|
NAMES libamavutils.so
|
||||||
|
DOC "Path to Amlogic Audio Video Utils Library"
|
||||||
|
PATHS /usr/lib/aml_libs /usr/local/lib /usr/lib)
|
||||||
|
mark_as_advanced(AMAVUTILS_LIBRARY)
|
||||||
|
|
||||||
|
find_library(AMADEC_LIBRARY
|
||||||
|
NAMES libamadec.so
|
||||||
|
DOC "Path to Amlogic Audio Decoder Library"
|
||||||
|
PATHS /usr/lib/aml_libs /usr/local/lib /usr/lib)
|
||||||
|
mark_as_advanced(AMADEC_LIBRARY)
|
||||||
|
|
||||||
find_library(AMCODEC_LIBRARY
|
find_library(AMCODEC_LIBRARY
|
||||||
NAMES libamcodec.so
|
NAMES libamcodec.so
|
||||||
DOC "Path to Amlogic Video Codec Library"
|
DOC "Path to Amlogic Video Codec Library"
|
||||||
PATHS /usr/lib/aml_libs /usr/osmc/lib /usr/local/lib /usr/lib)
|
PATHS /usr/lib/aml_libs /usr/local/lib /usr/lib)
|
||||||
mark_as_advanced(AMCODEC_LIBRARY)
|
mark_as_advanced(AMCODEC_LIBRARY)
|
||||||
|
|
||||||
include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake)
|
include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake)
|
||||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Amlogic DEFAULT_MSG AMLOGIC_INCLUDE_DIR AMCODEC_LIBRARY)
|
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Amlogic DEFAULT_MSG AMLOGIC_INCLUDE_DIR AMCODEC_LIBRARY AMADEC_LIBRARY AMAVUTILS_LIBRARY)
|
||||||
|
|
||||||
set(AMLOGIC_LIBRARIES ${AMCODEC_LIBRARY})
|
set(AMLOGIC_LIBRARIES ${AMCODEC_LIBRARY} ${AMADEC_LIBRARY} ${AMAVUTILS_LIBRARY})
|
||||||
set(AMLOGIC_INCLUDE_DIRS ${AMLOGIC_INCLUDE_DIR})
|
set(AMLOGIC_INCLUDE_DIRS ${AMLOGIC_INCLUDE_DIR})
|
||||||
|
@ -29,8 +29,8 @@ find_library(BCM_HOST_LIBRARY
|
|||||||
mark_as_advanced(BCM_HOST_LIBRARY)
|
mark_as_advanced(BCM_HOST_LIBRARY)
|
||||||
|
|
||||||
include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake)
|
include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake)
|
||||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Broadcom-OMX DEFAULT_MSG BROADCOM_INCLUDE_DIR VCOS_LIBRARY VCHIQ_LIBRARY OPENMAXIL_LIBRARY BCM_HOST_LIBRARY)
|
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Broadcom DEFAULT_MSG BROADCOM_INCLUDE_DIR VCOS_LIBRARY VCHIQ_LIBRARY OPENMAXIL_LIBRARY BCM_HOST_LIBRARY)
|
||||||
|
|
||||||
set(BROADCOM_OMX_LIBRARIES ${BCM_HOST_LIBRARY} ${OPENMAXIL_LIBRARY} ${VCHIQ_LIBRARY} ${VCOS_LIBRARY})
|
set(BROADCOM_LIBRARIES ${BCM_HOST_LIBRARY} ${OPENMAXIL_LIBRARY} ${VCHIQ_LIBRARY} ${VCOS_LIBRARY})
|
||||||
set(BROADCOM_INCLUDE_DIRS ${BROADCOM_INCLUDE_DIR} ${BROADCOM_INCLUDE_DIR}/interface/vmcs_host/linux ${BROADCOM_INCLUDE_DIR}/interface/vcos/pthreads)
|
set(BROADCOM_INCLUDE_DIRS ${BROADCOM_INCLUDE_DIR} ${BROADCOM_INCLUDE_DIR}/interface/vmcs_host/linux ${BROADCOM_INCLUDE_DIR}/interface/vcos/pthreads)
|
||||||
set(BROADCOM_OMX_DEFINITIONS USE_VCHIQ_ARM HAVE_LIBOPENMAX=2 OMX OMX_SKIP64BIT USE_EXTERNAL_OMX HAVE_LIBBCM_HOST USE_EXTERNAL_LIBBCM_HOST)
|
set(BROADCOM_DEFINITIONS USE_VCHIQ_ARM HAVE_LIBOPENMAX=2 OMX OMX_SKIP64BIT USE_EXTERNAL_OMX HAVE_LIBBCM_HOST USE_EXTERNAL_LIBBCM_HOST)
|
@ -1,77 +1,51 @@
|
|||||||
# CMake - Cross Platform Makefile Generator
|
# - Try to find LIBUUID
|
||||||
# Copyright 2000-2024 Kitware, Inc. and Contributors
|
# Find LIBUUID headers, libraries and the answer to all questions.
|
||||||
# All rights reserved.
|
|
||||||
#
|
#
|
||||||
# Distributed under the OSI-approved BSD 3-Clause License. See
|
# LIBUUID_FOUND True if libuuid got found
|
||||||
# https://cmake.org/licensing for details.
|
# LIBUUID_INCLUDE_DIRS Location of libuuid headers
|
||||||
|
# LIBUUID_LIBRARIES List of libraries to use libuuid
|
||||||
|
#
|
||||||
|
# Copyright (c) 2008 Bjoern Ricks <bjoern.ricks@googlemail.com>
|
||||||
|
#
|
||||||
|
# Redistribution and use is allowed according to the terms of the New
|
||||||
|
# BSD license.
|
||||||
|
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||||
#
|
#
|
||||||
#[=======================================================================[.rst:
|
|
||||||
FindLibUUID
|
|
||||||
------------
|
|
||||||
|
|
||||||
Find LibUUID include directory and library.
|
INCLUDE( FindPkgConfig )
|
||||||
|
|
||||||
Imported Targets
|
IF ( LibUuid_FIND_REQUIRED )
|
||||||
^^^^^^^^^^^^^^^^
|
SET( _pkgconfig_REQUIRED "REQUIRED" )
|
||||||
|
ELSE( LibUuid_FIND_REQUIRED )
|
||||||
|
SET( _pkgconfig_REQUIRED "" )
|
||||||
|
ENDIF ( LibUuid_FIND_REQUIRED )
|
||||||
|
|
||||||
An :ref:`imported target <Imported targets>` named
|
IF ( LIBUUID_MIN_VERSION )
|
||||||
``LibUUID::LibUUID`` is provided if LibUUID has been found.
|
PKG_SEARCH_MODULE( LIBUUID ${_pkgconfig_REQUIRED} uuid>=${LIBUUID_MIN_VERSION} )
|
||||||
|
ELSE ( LIBUUID_MIN_VERSION )
|
||||||
|
PKG_SEARCH_MODULE( LIBUUID ${_pkgconfig_REQUIRED} uuid )
|
||||||
|
ENDIF ( LIBUUID_MIN_VERSION )
|
||||||
|
|
||||||
Result Variables
|
|
||||||
^^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
This module defines the following variables:
|
IF( NOT LIBUUID_FOUND AND NOT PKG_CONFIG_FOUND )
|
||||||
|
FIND_PATH( LIBUUID_INCLUDE_DIRS uuid/uuid.h )
|
||||||
|
FIND_LIBRARY( LIBUUID_LIBRARIES uuid)
|
||||||
|
|
||||||
``LibUUID_FOUND``
|
# Report results
|
||||||
True if LibUUID was found, false otherwise.
|
IF ( LIBUUID_LIBRARIES AND LIBUUID_INCLUDE_DIRS )
|
||||||
``LibUUID_INCLUDE_DIRS``
|
SET( LIBUUID_FOUND 1 )
|
||||||
Include directories needed to include LibUUID headers.
|
IF ( NOT LIBUUID_FIND_QUIETLY )
|
||||||
``LibUUID_LIBRARIES``
|
MESSAGE( STATUS "Found libuuid: ${LIBUUID_LIBRARIES}" )
|
||||||
Libraries needed to link to LibUUID.
|
ENDIF ( NOT LIBUUID_FIND_QUIETLY )
|
||||||
|
ELSE ( LIBUUID_LIBRARIES AND LIBUUID_INCLUDE_DIRS )
|
||||||
|
IF ( LIBUUID_FIND_REQUIRED )
|
||||||
|
MESSAGE( SEND_ERROR "Could NOT find libuuid" )
|
||||||
|
ELSE ( LIBUUID_FIND_REQUIRED )
|
||||||
|
IF ( NOT LIBUUID_FIND_QUIETLY )
|
||||||
|
MESSAGE( STATUS "Could NOT find libuuid" )
|
||||||
|
ENDIF ( NOT LIBUUID_FIND_QUIETLY )
|
||||||
|
ENDIF ( LIBUUID_FIND_REQUIRED )
|
||||||
|
ENDIF ( LIBUUID_LIBRARIES AND LIBUUID_INCLUDE_DIRS )
|
||||||
|
ENDIF( NOT LIBUUID_FOUND AND NOT PKG_CONFIG_FOUND )
|
||||||
|
|
||||||
Cache Variables
|
MARK_AS_ADVANCED( LIBUUID_LIBRARIES LIBUUID_INCLUDE_DIRS )
|
||||||
^^^^^^^^^^^^^^^
|
|
||||||
|
|
||||||
This module uses the following cache variables:
|
|
||||||
|
|
||||||
``LibUUID_LIBRARY``
|
|
||||||
The location of the LibUUID library file.
|
|
||||||
``LibUUID_INCLUDE_DIR``
|
|
||||||
The location of the LibUUID include directory containing ``uuid/uuid.h``.
|
|
||||||
|
|
||||||
The cache variables should not be used by project code.
|
|
||||||
They may be set by end users to point at LibUUID components.
|
|
||||||
#]=======================================================================]
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
find_library(LibUUID_LIBRARY
|
|
||||||
NAMES uuid
|
|
||||||
)
|
|
||||||
mark_as_advanced(LibUUID_LIBRARY)
|
|
||||||
|
|
||||||
find_path(LibUUID_INCLUDE_DIR
|
|
||||||
NAMES uuid/uuid.h
|
|
||||||
)
|
|
||||||
mark_as_advanced(LibUUID_INCLUDE_DIR)
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
include(FindPackageHandleStandardArgs)
|
|
||||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibUUID
|
|
||||||
FOUND_VAR LibUUID_FOUND
|
|
||||||
REQUIRED_VARS LibUUID_LIBRARY LibUUID_INCLUDE_DIR
|
|
||||||
)
|
|
||||||
set(LIBUUID_FOUND ${LibUUID_FOUND})
|
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
|
||||||
# Provide documented result variables and targets.
|
|
||||||
if(LibUUID_FOUND)
|
|
||||||
set(LibUUID_INCLUDE_DIRS ${LibUUID_INCLUDE_DIR})
|
|
||||||
set(LibUUID_LIBRARIES ${LibUUID_LIBRARY})
|
|
||||||
if(NOT TARGET LibUUID::LibUUID)
|
|
||||||
add_library(LibUUID::LibUUID UNKNOWN IMPORTED)
|
|
||||||
set_target_properties(LibUUID::LibUUID PROPERTIES
|
|
||||||
IMPORTED_LOCATION "${LibUUID_LIBRARY}"
|
|
||||||
INTERFACE_INCLUDE_DIRECTORIES "${LibUUID_INCLUDE_DIRS}"
|
|
||||||
)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
@ -1,41 +0,0 @@
|
|||||||
find_path(BROADCOM_INCLUDE_DIR
|
|
||||||
NAMES bcm_host.h
|
|
||||||
DOC "Broadcom include directory"
|
|
||||||
PATHS /opt/vc/include)
|
|
||||||
mark_as_advanced(BROADCOM_INCLUDE_DIR)
|
|
||||||
|
|
||||||
find_library(VCOS_LIBRARY
|
|
||||||
NAMES libvcos.so
|
|
||||||
DOC "Path to VCOS Library"
|
|
||||||
PATHS /opt/vc/lib)
|
|
||||||
mark_as_advanced(VCOS_LIBRARY)
|
|
||||||
|
|
||||||
find_library(BCM_HOST_LIBRARY
|
|
||||||
NAMES libbcm_host.so
|
|
||||||
DOC "Path to Broadcom Host Library"
|
|
||||||
PATHS /opt/vc/lib)
|
|
||||||
mark_as_advanced(BCM_HOST_LIBRARY)
|
|
||||||
|
|
||||||
find_library(MMAL_CORE_LIBRARY
|
|
||||||
NAMES libmmal_core.so
|
|
||||||
DOC "Path to MMAL Core Library"
|
|
||||||
PATHS /opt/vc/lib)
|
|
||||||
mark_as_advanced(MMAL_CORE_LIBRARY)
|
|
||||||
|
|
||||||
find_library(MMAL_UTIL_LIBRARY
|
|
||||||
NAMES libmmal_util.so
|
|
||||||
DOC "Path to MMAL Util Library"
|
|
||||||
PATHS /opt/vc/lib)
|
|
||||||
mark_as_advanced(MMAL_UTIL_LIBRARY)
|
|
||||||
|
|
||||||
find_library(MMAL_VC_CLIENT_LIBRARY
|
|
||||||
NAMES libmmal_vc_client.so
|
|
||||||
DOC "Path to MMAL Client Library"
|
|
||||||
PATHS /opt/vc/lib)
|
|
||||||
mark_as_advanced(MMAL_VC_CLIENT_LIBRARY)
|
|
||||||
|
|
||||||
include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake)
|
|
||||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(MMAL DEFAULT_MSG BROADCOM_INCLUDE_DIR VCOS_LIBRARY MMAL_CORE_LIBRARY MMAL_UTIL_LIBRARY MMAL_VC_CLIENT_LIBRARY BCM_HOST_LIBRARY)
|
|
||||||
|
|
||||||
set(MMAL_LINK_LIBRARIES ${BCM_HOST_LIBRARY} ${VCOS_LIBRARY} ${MMAL_CORE_LIBRARY} ${MMAL_UTIL_LIBRARY} ${MMAL_VC_CLIENT_LIBRARY})
|
|
||||||
set(MMAL_INCLUDE_DIRS ${BROADCOM_INCLUDE_DIR})
|
|
@ -1,29 +0,0 @@
|
|||||||
include(FindGit)
|
|
||||||
|
|
||||||
if(GIT_FOUND AND IS_DIRECTORY "${PROJECT_SOURCE_DIR}/.git")
|
|
||||||
set(GIT_BRANCH "")
|
|
||||||
set(GIT_COMMIT_HASH "")
|
|
||||||
|
|
||||||
# Get the current working branch
|
|
||||||
execute_process(
|
|
||||||
COMMAND ${GIT_EXECUTABLE} name-rev --name-only HEAD
|
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
|
||||||
OUTPUT_VARIABLE GIT_BRANCH0
|
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
||||||
)
|
|
||||||
|
|
||||||
# Clean Branch name to have only the branch
|
|
||||||
STRING(REGEX REPLACE "((origin))+" "" GIT_BRANCH1 ${GIT_BRANCH0})
|
|
||||||
STRING(REGEX REPLACE "((remotes))+" "" GIT_BRANCH2 ${GIT_BRANCH1})
|
|
||||||
STRING(REGEX REPLACE "\\/+" "" GIT_BRANCH3 ${GIT_BRANCH2})
|
|
||||||
STRING(REGEX REPLACE "~[0-9]*" "" GIT_BRANCH4 ${GIT_BRANCH3})
|
|
||||||
STRING(REGEX REPLACE "\\^[0-9]*" "" GIT_BRANCH ${GIT_BRANCH4})
|
|
||||||
|
|
||||||
# Get the latest abbreviated commit hash of the working branch
|
|
||||||
execute_process(
|
|
||||||
COMMAND git log -1 --format=%h
|
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
|
||||||
OUTPUT_VARIABLE GIT_COMMIT_HASH
|
|
||||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
||||||
)
|
|
||||||
endif()
|
|
@ -1,4 +1,4 @@
|
|||||||
add_custom_command(OUTPUT moonlight.1 COMMAND pod2man --section=1 --center="Moonlight Embedded Manual" --name="MOONLIGHT" --release="moonlight 2.5.0" ${CMAKE_CURRENT_SOURCE_DIR}/README.pod > moonlight.1)
|
add_custom_command(OUTPUT moonlight.1 COMMAND pod2man --section=1 --center="Moonlight Embedded Manual" --name="MOONLIGHT" --release="moonlight 2.1.0" ${CMAKE_CURRENT_SOURCE_DIR}/README.pod > moonlight.1)
|
||||||
add_custom_target(docs ALL DEPENDS moonlight.1)
|
add_custom_target(docs ALL DEPENDS moonlight.1)
|
||||||
|
|
||||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/moonlight.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
|
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/moonlight.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
|
||||||
|
@ -79,33 +79,34 @@ Change the vertical resolution to I<HEIGHT>
|
|||||||
=item B<-fps> [I<FPS>]
|
=item B<-fps> [I<FPS>]
|
||||||
|
|
||||||
Change the number of frame per second to I<FPS>.
|
Change the number of frame per second to I<FPS>.
|
||||||
Defaults to 60 FPS.
|
Defaults to 60fps for 720p and 30fps for 1080p and higher.
|
||||||
|
Only 30 and 60 fps are currently supported by Gamestream.
|
||||||
|
|
||||||
=item B<-bitrate> [I<BITRATE>]
|
=item B<-bitrate> [I<BITRATE>]
|
||||||
|
|
||||||
Change bitrate to I<BITRATE> Kbps.
|
Change bitrate to I<BITRATE> Kbps.
|
||||||
By default the bitrate depends on the selected resolution and FPS.
|
By default the bitrate depends on the selected resolution and fps.
|
||||||
For resolution 1080p and 60 FPS and higher, 20 Mbps is used.
|
For resolution 1080p and 60 fps and higher 20 Mbps is used.
|
||||||
For resolution 1080p or 60 FPS and higher, 10 Mbps is used
|
For resolution 1080p or 60 fps and higher 10 Mbps is used
|
||||||
For other configurations, 5 Mbps is used by default.
|
For other configurations 5 Mbps is used by default.
|
||||||
|
|
||||||
=item B<-packetsize> [I<PACKETSIZE>]
|
=item B<-packetsize> [I<PACKETSIZE>]
|
||||||
|
|
||||||
Change the network packetsize to I<PACKETSIZE> bytes.
|
Change the network packetsize to I<PACKETSIZE> bytes.
|
||||||
The packetsize should the smaller than the MTU of the network.
|
The packetsize should the smaller than the MTU of the network.
|
||||||
This value must be a multiple of 16.
|
This value must be a multiply of 16.
|
||||||
By default, 1392 is used on LAN and 1024 on WAN.
|
By default a safe value of 1024 is used.
|
||||||
|
|
||||||
=item B<-codec> [I<CODEC>]
|
=item B<-codec> [I<CODEC>]
|
||||||
|
|
||||||
Select codec to use.
|
Select codec to use.
|
||||||
Can be 'auto', 'h264', 'h265', 'hevc', or 'av1'.
|
Can be 'auto', 'h264', 'h265' or 'hevc'.
|
||||||
Not all video decoders support H.265/HEVC or AV1.
|
Not all video decoders do support H.265/HEVC.
|
||||||
Will still use H.264 if server doesn't support HEVC or AV1.
|
Will still use H.264 if server doesn't support HEVC.
|
||||||
|
|
||||||
=item B<-remote> [I<yes/no/auto>]
|
=item B<-remote>
|
||||||
|
|
||||||
Enable optimizations for LAN or WAN streaming.
|
Enable the optimizations for remote connections in GFE.
|
||||||
|
|
||||||
=item B<-app> [I<APP>]
|
=item B<-app> [I<APP>]
|
||||||
|
|
||||||
@ -120,9 +121,9 @@ Stop GFE from changing the graphical settings of the requested game or applicati
|
|||||||
|
|
||||||
Play the audio on the host computer instead of this device.
|
Play the audio on the host computer instead of this device.
|
||||||
|
|
||||||
=item B<-surround> [I<5.1/7.1>]
|
=item B<-surround>
|
||||||
|
|
||||||
Enable surround sound instead of stereo.
|
Enable 5.1 surround sound instead of stereo.
|
||||||
|
|
||||||
=item B<-keydir> [I<DIRECTORY>]
|
=item B<-keydir> [I<DIRECTORY>]
|
||||||
|
|
||||||
@ -140,22 +141,14 @@ By default the gamecontrollerdb.txt provided by Moonlight Embedded is used.
|
|||||||
Select platform for audio and video output and input.
|
Select platform for audio and video output and input.
|
||||||
<PLATFORM> can be pi, imx, aml, x11, x11_vdpau, sdl or fake.
|
<PLATFORM> can be pi, imx, aml, x11, x11_vdpau, sdl or fake.
|
||||||
|
|
||||||
=item B<-nounsupported>
|
=item B<-unsupported>
|
||||||
|
|
||||||
Don't stream if resolution is not officially supported by the server
|
Try streaming if GFE version or options are unsupported
|
||||||
|
|
||||||
=item B<-quitappafter>
|
=item B<-quitappafter>
|
||||||
|
|
||||||
Send quit app request to remote after quitting session
|
Send quit app request to remote after quitting session
|
||||||
|
|
||||||
=item B<-viewonly>
|
|
||||||
|
|
||||||
Disable all input processing (view-only mode)
|
|
||||||
|
|
||||||
=item B<-nomouseemulation>
|
|
||||||
|
|
||||||
Disable gamepad mouse emulation (activated by long pressing Start button)
|
|
||||||
|
|
||||||
=item B<-verbose>
|
=item B<-verbose>
|
||||||
|
|
||||||
Enable verbose output
|
Enable verbose output
|
||||||
|
226
gamecontrollerdb.txt
Normal file
226
gamecontrollerdb.txt
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
default,Default Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
xwc,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
0000000058626f782033363020576900,Xbox 360 Wireless Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,
|
||||||
|
0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,
|
||||||
|
0300000000f000000300000000010000,RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,
|
||||||
|
0300000000f00000f100000000010000,Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,
|
||||||
|
03000000022000000090000011010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,
|
||||||
|
030000000d0500000308000010010000,Nostromo n45 Dual Analog Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,
|
||||||
|
030000000d0f00000900000010010000,Natec Genesis P44,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||||
|
030000000d0f00000d00000000010000,hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftx:b4,lefty:b5,rightshoulder:b7,start:b9,x:b1,y:b2,
|
||||||
|
030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,
|
||||||
|
030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,
|
||||||
|
030000000d0f00005e00000011010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||||
|
030000000d0f00005f00000011010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||||
|
030000000d0f00006600000011010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||||
|
030000000d0f00006700000001010000,HORIPAD ONE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000000d0f00006a00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||||
|
030000000d0f00006b00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||||
|
030000000d0f00006e00000011010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||||
|
030000000d0f00009200000011010000,Hori Pokken Tournament DX Pro Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,
|
||||||
|
030000000d0f0000ee00000011010000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||||
|
03000000100000008200000011010000,Akishop Customs PS360+ v1.66,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,
|
||||||
|
03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,
|
||||||
|
03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,
|
||||||
|
03000000120c00000500000000010000,Manta Dualshock 2,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,
|
||||||
|
03000000120c00000500000010010000,AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,
|
||||||
|
03000000120c0000100e000011010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||||
|
03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,
|
||||||
|
03000000250900006688000000010000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,
|
||||||
|
0300000025090000e803000001010000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:a5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,
|
||||||
|
03000000260900008888000000010000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,
|
||||||
|
03000000280400000140000000010000,Gravis GamePad Pro USB ,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,
|
||||||
|
03000000300f00000b01000010010000,Jess Tech GGE909 PC Recoil Pad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,
|
||||||
|
03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,
|
||||||
|
03000000300f00001211000011010000,QanBa Arcade JoyStick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,
|
||||||
|
03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,
|
||||||
|
03000000321500000010000011010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||||
|
03000000321500000104000011010000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||||
|
03000000321500000204000011010000,Razer Panthera (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||||
|
0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,
|
||||||
|
03000000341a00000908000010010000,SL-6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,
|
||||||
|
03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||||
|
03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
03000000380700001888000010010000,MadCatz PC USB Wired Stick 8818,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||||
|
03000000380700003847000090040000,Mad Catz Wired Xbox 360 Controller (SFIV),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,
|
||||||
|
03000000380700003888000010010000,MadCatz PC USB Wired Stick 8838,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||||
|
03000000380700005032000011010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||||
|
03000000380700005082000011010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||||
|
03000000380700008034000011010000,Mad Catz fightstick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||||
|
03000000380700008084000011010000,Mad Catz fightstick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||||
|
03000000380700008433000011010000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||||
|
03000000380700008483000011010000,Mad Catz FightStick TE S+ (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||||
|
03000000450c00002043000010010000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,
|
||||||
|
03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||||
|
030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,
|
||||||
|
030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
|
||||||
|
030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,
|
||||||
|
030000004c0500006802000011810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
|
||||||
|
030000004c050000a00b000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||||
|
030000004c050000a00b000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
|
||||||
|
030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||||
|
030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
|
||||||
|
030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||||
|
030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||||
|
030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
|
||||||
|
030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,
|
||||||
|
030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||||
|
030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||||
|
030000004f04000012b3000010010000,Thrustmaster vibrating gamepad,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,
|
||||||
|
030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,
|
||||||
|
030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,
|
||||||
|
030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||||
|
030000004f04000026b3000002040000,Thrustmaster Gamepad GP XID,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,
|
||||||
|
03000000550900001472000011010000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,
|
||||||
|
030000005e0400000202000000010000,Old Xbox pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,
|
||||||
|
030000005e0400000e00000000010000,Microsoft SideWinder,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,
|
||||||
|
030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000005e0400008502000000010000,Microsoft X-Box pad (Japan),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,
|
||||||
|
030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,
|
||||||
|
030000005e0400008e02000004010000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000005e0400008e02000062230000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000005e040000a102000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000005e040000a102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000005e040000a102000014010000,Xbox 360 Wireless Receiver (XBOX),a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000005e040000d102000001010000,Microsoft X-Box One pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000005e040000d102000003020000,Microsoft X-Box One pad v2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000005e040000dd02000003020000,Microsoft X-Box One pad (Firmware 2015),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000005e040000e302000003020000,Microsoft X-Box One Elite pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000005e040000ea02000001030000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
03000000632500002305000010010000,ShanWan USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,
|
||||||
|
03000000632500007505000010010000,SHANWAN PS3/PC Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,
|
||||||
|
03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,
|
||||||
|
03000000666600006706000000010000,boom PSX to PC Converter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,
|
||||||
|
030000006b140000010d000011010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||||
|
030000006d0400000ac2000010010000,Logitech Inc. WingMan RumblePad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b2,rightx:a3,righty:a4,x:b3,y:b4,
|
||||||
|
030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b10,rightx:a3,righty:a4,start:b8,x:b3,y:b4,
|
||||||
|
030000006d04000015c2000010010000,Logitech Logitech Extreme 3D,a:b0,b:b4,back:b6,guide:b8,leftshoulder:b9,leftstick:h0.8,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:h0.2,start:b7,x:b2,y:b5,
|
||||||
|
030000006d04000016c2000010010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||||
|
030000006d04000016c2000011010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||||
|
030000006d04000018c2000010010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||||
|
030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||||
|
030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||||
|
030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,
|
||||||
|
030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000006f0e00001302000000010000,Afterglow,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000006f0e00001304000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:a0,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:a3,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000006f0e00001402000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||||
|
030000006f0e00001e01000011010000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||||
|
030000006f0e00001f01000000010000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000006f0e00003001000001010000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||||
|
030000006f0e00003901000000430000,Afterglow Prismatic Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000006f0e00003901000020060000,Afterglow Controller for Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000006f0e00004601000001010000,Rock Candy Xbox One Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000006f0e00006401000001010000,PDP Battlefield One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
03000000780000000600000010010000,Microntek USB Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,
|
||||||
|
03000000790000000600000007010000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,
|
||||||
|
03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,
|
||||||
|
03000000790000001100000000010000,USB Gamepad1,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,
|
||||||
|
03000000790000004318000010010000,Mayflash GameCube Controller Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,
|
||||||
|
03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,
|
||||||
|
0300000079000000d218000011010000,MAGIC-NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||||
|
0300000079000000d418000000010000,GPD Win 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,
|
||||||
|
030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,
|
||||||
|
030000008916000000fd000024010000,Razer Onza Tournament Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000008916000000fe000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
030000008f0e00000300000010010000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,
|
||||||
|
030000008f0e00000610000000010000,GreenAsia Electronics 4Axes 12Keys GamePad ,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,
|
||||||
|
030000008f0e00000800000010010000,Gasia Co. Ltd PS(R) Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,
|
||||||
|
030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,
|
||||||
|
030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,
|
||||||
|
03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,
|
||||||
|
03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,
|
||||||
|
03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b12,x:b0,y:b3,
|
||||||
|
03000000a30600000cff000010010000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,
|
||||||
|
03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,
|
||||||
|
03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,
|
||||||
|
03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,
|
||||||
|
03000000ad1b000038f0000090040000,Street Fighter IV FightStick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
03000000b40400000a01000000010000,CYPRESS USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,
|
||||||
|
03000000b50700001503000010010000,impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,
|
||||||
|
03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,
|
||||||
|
03000000bc2000000055000010010000,ShanWan PS3/PC Wired GamePad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,
|
||||||
|
03000000bc2000000055000011010000,GameSir G3w,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,
|
||||||
|
03000000c01600008704000011010000,Serial/Keyboard/Mouse/Joystick,a:b12,b:b10,back:b4,dpdown:b2,dpleft:b3,dpright:b1,dpup:b0,leftshoulder:b9,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b8,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b5,x:b13,y:b11,
|
||||||
|
03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,
|
||||||
|
03000000c62400000053000000010000,PowerA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
03000000c6240000025b000002020000,Thrustmaster GPX Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
03000000c6240000045d000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
03000000c62400003a54000001010000,PowerA 1428124-01,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
03000000c82d00000190000011010000,8Bitdo NES30 Pro 8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,
|
||||||
|
03000000c9110000f055000011010000,HJC Game GAMEPAD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,
|
||||||
|
03000000d62000006dca000011010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||||
|
03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,
|
||||||
|
03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,a:b1,b:b2,back:b8,guide:b9,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,
|
||||||
|
03000000de2800000112000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,
|
||||||
|
03000000de2800000211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,
|
||||||
|
03000000de2800004211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,
|
||||||
|
03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,
|
||||||
|
03000000f025000021c1000010010000,ShanWan Gioteck PS3 Wired Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,
|
||||||
|
03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||||
|
03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),a:b3,b:b4,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b7,x:b0,y:b1,
|
||||||
|
03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,
|
||||||
|
03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,
|
||||||
|
05000000010000000100000003000000,Nintendo Wiimote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,
|
||||||
|
05000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,
|
||||||
|
05000000050b00000045000040000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,
|
||||||
|
05000000102800000900000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,
|
||||||
|
05000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,
|
||||||
|
05000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,
|
||||||
|
050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,
|
||||||
|
05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,
|
||||||
|
05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,
|
||||||
|
05000000380700006652000025010000,Mad Catz C.T.R.L.R ,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||||
|
0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,
|
||||||
|
0500000047532067616d657061640000,GS gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,
|
||||||
|
0500000049190000020400001b010000,Ipega PG-9069 - Bluetooth Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b161,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,
|
||||||
|
05000000491900000204000021000000,Amazon Fire Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,
|
||||||
|
050000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:a12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:a13,rightx:a2,righty:a3,start:b3,x:b15,y:b12,
|
||||||
|
050000004c0500006802000000800000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
|
||||||
|
050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
|
||||||
|
050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||||
|
050000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
|
||||||
|
050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
|
||||||
|
050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
|
||||||
|
050000004c050000cc09000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
|
||||||
|
05000000504c415953544154494f4e00,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,
|
||||||
|
05000000550900001472000001000000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,
|
||||||
|
050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
||||||
|
050000005e040000fd02000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,
|
||||||
|
050000006964726f69643a636f6e0000,idroid:con,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
|
||||||
|
050000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,
|
||||||
|
050000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,
|
||||||
|
050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,
|
||||||
|
050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,
|
||||||
|
05000000a00500003232000001000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,
|
||||||
|
05000000ac0500003232000001000000,VR-BOX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,
|
||||||
|
05000000c82d00000061000000010000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,
|
||||||
|
05000000c82d00001038000000010000,8Bitdo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,
|
||||||
|
05000000c82d00002038000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,
|
||||||
|
05000000c82d00003028000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,
|
||||||
|
05000000d62000007162000001000000,Moga Pro 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,
|
||||||
|
05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,
|
||||||
|
05000000d6200000e589000001000000,Moga 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,
|
||||||
|
05000000de2800000212000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,
|
||||||
|
05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,
|
||||||
|
05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,
|
||||||
|
060000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,
|
||||||
|
06000000adde0000efbe000002010000,Hidromancer Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
|
@ -7,11 +7,11 @@ find_package(OpenSSL 1.0.2 REQUIRED)
|
|||||||
find_package(EXPAT REQUIRED)
|
find_package(EXPAT REQUIRED)
|
||||||
|
|
||||||
pkg_check_modules(AVAHI REQUIRED avahi-client)
|
pkg_check_modules(AVAHI REQUIRED avahi-client)
|
||||||
|
pkg_check_modules(ENET REQUIRED libenet)
|
||||||
|
|
||||||
aux_source_directory(./ GAMESTREAM_SRC_LIST)
|
aux_source_directory(./ GAMESTREAM_SRC_LIST)
|
||||||
aux_source_directory(../third_party/h264bitstream GAMESTREAM_SRC_LIST)
|
aux_source_directory(../third_party/h264bitstream GAMESTREAM_SRC_LIST)
|
||||||
|
|
||||||
aux_source_directory(../third_party/moonlight-common-c/enet MOONLIGHT_COMMON_SRC_LIST)
|
|
||||||
aux_source_directory(../third_party/moonlight-common-c/src MOONLIGHT_COMMON_SRC_LIST)
|
aux_source_directory(../third_party/moonlight-common-c/src MOONLIGHT_COMMON_SRC_LIST)
|
||||||
aux_source_directory(../third_party/moonlight-common-c/reedsolomon MOONLIGHT_COMMON_SRC_LIST)
|
aux_source_directory(../third_party/moonlight-common-c/reedsolomon MOONLIGHT_COMMON_SRC_LIST)
|
||||||
|
|
||||||
@ -23,9 +23,10 @@ target_link_libraries(gamestream moonlight-common)
|
|||||||
set_target_properties(gamestream PROPERTIES SOVERSION ${SO_VERSION} VERSION ${PROJECT_VERSION})
|
set_target_properties(gamestream PROPERTIES SOVERSION ${SO_VERSION} VERSION ${PROJECT_VERSION})
|
||||||
set_target_properties(moonlight-common PROPERTIES SOVERSION ${SO_VERSION} VERSION ${PROJECT_VERSION})
|
set_target_properties(moonlight-common PROPERTIES SOVERSION ${SO_VERSION} VERSION ${PROJECT_VERSION})
|
||||||
|
|
||||||
target_include_directories(gamestream PRIVATE ../third_party/moonlight-common-c/src ../third_party/h264bitstream ${AVAHI_INCLUDE_DIRS} ${LibUUID_INCLUDE_DIRS})
|
target_include_directories(gamestream PRIVATE ../third_party/moonlight-common-c/src ../third_party/h264bitstream ${AVAHI_INCLUDE_DIRS} ${LIBUUID_INCLUDE_DIRS})
|
||||||
target_include_directories(moonlight-common PRIVATE ../third_party/moonlight-common-c/reedsolomon ../third_party/moonlight-common-c/enet/include)
|
target_include_directories(moonlight-common PRIVATE ../third_party/moonlight-common-c/reedsolomon ${ENET_INCLUDE_DIRS})
|
||||||
target_link_libraries(gamestream ${CURL_LIBRARIES} ${OPENSSL_LIBRARIES} ${EXPAT_LIBRARIES} ${AVAHI_LIBRARIES} ${LibUUID_LIBRARIES})
|
target_link_libraries(gamestream ${CURL_LIBRARIES} ${OPENSSL_LIBRARIES} ${EXPAT_LIBRARIES} ${AVAHI_LIBRARIES} ${LIBUUID_LIBRARIES})
|
||||||
|
target_link_libraries(moonlight-common ${ENET_LIBRARIES})
|
||||||
|
|
||||||
target_link_libraries(gamestream ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS})
|
target_link_libraries(gamestream ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS})
|
||||||
|
|
||||||
|
@ -30,7 +30,6 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <uuid/uuid.h>
|
#include <uuid/uuid.h>
|
||||||
#include <openssl/sha.h>
|
#include <openssl/sha.h>
|
||||||
#include <openssl/aes.h>
|
#include <openssl/aes.h>
|
||||||
@ -46,18 +45,19 @@
|
|||||||
#define UNIQUEID_BYTES 8
|
#define UNIQUEID_BYTES 8
|
||||||
#define UNIQUEID_CHARS (UNIQUEID_BYTES*2)
|
#define UNIQUEID_CHARS (UNIQUEID_BYTES*2)
|
||||||
|
|
||||||
|
#define CHANNEL_COUNT_STEREO 2
|
||||||
|
#define CHANNEL_COUNT_51_SURROUND 6
|
||||||
|
|
||||||
|
#define CHANNEL_MASK_STEREO 0x3
|
||||||
|
#define CHANNEL_MASK_51_SURROUND 0xFC
|
||||||
|
|
||||||
static char unique_id[UNIQUEID_CHARS+1];
|
static char unique_id[UNIQUEID_CHARS+1];
|
||||||
static X509 *cert;
|
static X509 *cert;
|
||||||
static char cert_hex[8192];
|
static char cert_hex[4096];
|
||||||
static EVP_PKEY *privateKey;
|
static EVP_PKEY *privateKey;
|
||||||
|
|
||||||
const char* gs_error;
|
const char* gs_error;
|
||||||
|
|
||||||
#define LEN_AS_HEX_STR(x) ((x) * 2 + 1)
|
|
||||||
#define SIZEOF_AS_HEX_STR(x) LEN_AS_HEX_STR(sizeof(x))
|
|
||||||
|
|
||||||
#define UUID_STRLEN 37
|
|
||||||
|
|
||||||
static int mkdirtree(const char* directory) {
|
static int mkdirtree(const char* directory) {
|
||||||
char buffer[PATH_MAX];
|
char buffer[PATH_MAX];
|
||||||
char* p = buffer;
|
char* p = buffer;
|
||||||
@ -92,16 +92,19 @@ static int load_unique_id(const char* keyDirectory) {
|
|||||||
snprintf(uniqueFilePath, PATH_MAX, "%s/%s", keyDirectory, UNIQUE_FILE_NAME);
|
snprintf(uniqueFilePath, PATH_MAX, "%s/%s", keyDirectory, UNIQUE_FILE_NAME);
|
||||||
|
|
||||||
FILE *fd = fopen(uniqueFilePath, "r");
|
FILE *fd = fopen(uniqueFilePath, "r");
|
||||||
if (fd == NULL || fread(unique_id, UNIQUEID_CHARS, 1, fd) != UNIQUEID_CHARS) {
|
if (fd == NULL) {
|
||||||
snprintf(unique_id,UNIQUEID_CHARS+1,"0123456789ABCDEF");
|
unsigned char unique_data[UNIQUEID_BYTES];
|
||||||
|
RAND_bytes(unique_data, UNIQUEID_BYTES);
|
||||||
if (fd)
|
for (int i = 0; i < UNIQUEID_BYTES; i++) {
|
||||||
fclose(fd);
|
sprintf(unique_id + (i * 2), "%02x", unique_data[i]);
|
||||||
|
}
|
||||||
fd = fopen(uniqueFilePath, "w");
|
fd = fopen(uniqueFilePath, "w");
|
||||||
if (fd == NULL)
|
if (fd == NULL)
|
||||||
return GS_FAILED;
|
return GS_FAILED;
|
||||||
|
|
||||||
fwrite(unique_id, UNIQUEID_CHARS, 1, fd);
|
fwrite(unique_id, UNIQUEID_CHARS, 1, fd);
|
||||||
|
} else {
|
||||||
|
fread(unique_id, UNIQUEID_CHARS, 1, fd);
|
||||||
}
|
}
|
||||||
fclose(fd);
|
fclose(fd);
|
||||||
unique_id[UNIQUEID_CHARS] = 0;
|
unique_id[UNIQUEID_CHARS] = 0;
|
||||||
@ -164,129 +167,109 @@ static int load_cert(const char* keyDirectory) {
|
|||||||
return GS_OK;
|
return GS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int load_serverinfo(PSERVER_DATA server, bool https) {
|
|
||||||
uuid_t uuid;
|
|
||||||
char uuid_str[UUID_STRLEN];
|
|
||||||
char url[4096];
|
|
||||||
int ret = GS_INVALID;
|
|
||||||
char *pairedText = NULL;
|
|
||||||
char *currentGameText = NULL;
|
|
||||||
char *stateText = NULL;
|
|
||||||
char *serverCodecModeSupportText = NULL;
|
|
||||||
char *httpsPortText = NULL;
|
|
||||||
|
|
||||||
uuid_generate_random(uuid);
|
|
||||||
uuid_unparse(uuid, uuid_str);
|
|
||||||
|
|
||||||
snprintf(url, sizeof(url), "%s://%s:%d/serverinfo?uniqueid=%s&uuid=%s",
|
|
||||||
https ? "https" : "http", server->serverInfo.address, https ? server->httpsPort : server->httpPort, unique_id, uuid_str);
|
|
||||||
|
|
||||||
PHTTP_DATA data = http_create_data();
|
|
||||||
if (data == NULL) {
|
|
||||||
ret = GS_OUT_OF_MEMORY;
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
if (http_request(url, data) != GS_OK) {
|
|
||||||
ret = GS_IO_ERROR;
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xml_status(data->memory, data->size) == GS_ERROR) {
|
|
||||||
ret = GS_ERROR;
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xml_search(data->memory, data->size, "currentgame", ¤tGameText) != GS_OK) {
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (xml_search(data->memory, data->size, "PairStatus", &pairedText) != GS_OK)
|
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
if (xml_search(data->memory, data->size, "appversion", (char**) &server->serverInfo.serverInfoAppVersion) != GS_OK)
|
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
if (xml_search(data->memory, data->size, "state", &stateText) != GS_OK)
|
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
if (xml_search(data->memory, data->size, "ServerCodecModeSupport", &serverCodecModeSupportText) != GS_OK)
|
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
if (xml_search(data->memory, data->size, "gputype", &server->gpuType) != GS_OK)
|
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
if (xml_search(data->memory, data->size, "GsVersion", &server->gsVersion) != GS_OK)
|
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
if (xml_search(data->memory, data->size, "GfeVersion", (char**) &server->serverInfo.serverInfoGfeVersion) != GS_OK)
|
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
if (xml_search(data->memory, data->size, "HttpsPort", &httpsPortText) != GS_OK)
|
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
if (xml_modelist(data->memory, data->size, &server->modes) != GS_OK)
|
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
// These fields are present on all version of GFE that this client supports
|
|
||||||
if (!strlen(currentGameText) || !strlen(pairedText) || !strlen(server->serverInfo.serverInfoAppVersion) || !strlen(stateText))
|
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
server->paired = pairedText != NULL && strcmp(pairedText, "1") == 0;
|
|
||||||
server->currentGame = currentGameText == NULL ? 0 : atoi(currentGameText);
|
|
||||||
server->serverInfo.serverCodecModeSupport = serverCodecModeSupportText == NULL ? SCM_H264 : atoi(serverCodecModeSupportText);
|
|
||||||
server->serverMajorVersion = atoi(server->serverInfo.serverInfoAppVersion);
|
|
||||||
server->isNvidiaSoftware = strstr(stateText, "MJOLNIR") != NULL;
|
|
||||||
|
|
||||||
server->httpsPort = atoi(httpsPortText);
|
|
||||||
if (!server->httpsPort)
|
|
||||||
server->httpsPort = 47984;
|
|
||||||
|
|
||||||
if (strstr(stateText, "_SERVER_BUSY") == NULL) {
|
|
||||||
// After GFE 2.8, current game remains set even after streaming
|
|
||||||
// has ended. We emulate the old behavior by forcing it to zero
|
|
||||||
// if streaming is not active.
|
|
||||||
server->currentGame = 0;
|
|
||||||
}
|
|
||||||
ret = GS_OK;
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
if (data != NULL)
|
|
||||||
http_free_data(data);
|
|
||||||
|
|
||||||
if (pairedText != NULL)
|
|
||||||
free(pairedText);
|
|
||||||
|
|
||||||
if (currentGameText != NULL)
|
|
||||||
free(currentGameText);
|
|
||||||
|
|
||||||
if (serverCodecModeSupportText != NULL)
|
|
||||||
free(serverCodecModeSupportText);
|
|
||||||
|
|
||||||
if (httpsPortText != NULL)
|
|
||||||
free(httpsPortText);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int load_server_status(PSERVER_DATA server) {
|
static int load_server_status(PSERVER_DATA server) {
|
||||||
|
|
||||||
|
uuid_t uuid;
|
||||||
|
char uuid_str[37];
|
||||||
|
|
||||||
int ret;
|
int ret;
|
||||||
|
char url[4096];
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/* Fetch the HTTPS port if we don't have one yet */
|
i = 0;
|
||||||
if (!server->httpsPort) {
|
do {
|
||||||
ret = load_serverinfo(server, false);
|
char *pairedText = NULL;
|
||||||
if (ret != GS_OK)
|
char *currentGameText = NULL;
|
||||||
return ret;
|
char *stateText = NULL;
|
||||||
}
|
char *serverCodecModeSupportText = NULL;
|
||||||
|
|
||||||
// Modern GFE versions don't allow serverinfo to be fetched over HTTPS if the client
|
ret = GS_INVALID;
|
||||||
// is not already paired. Since we can't pair without knowing the server version, we
|
|
||||||
// make another request over HTTP if the HTTPS request fails. We can't just use HTTP
|
uuid_generate_random(uuid);
|
||||||
// for everything because it doesn't accurately tell us if we're paired.
|
uuid_unparse(uuid, uuid_str);
|
||||||
ret = GS_INVALID;
|
|
||||||
for (i = 0; i < 2 && ret != GS_OK; i++) {
|
// Modern GFE versions don't allow serverinfo to be fetched over HTTPS if the client
|
||||||
ret = load_serverinfo(server, i == 0);
|
// is not already paired. Since we can't pair without knowing the server version, we
|
||||||
}
|
// make another request over HTTP if the HTTPS request fails. We can't just use HTTP
|
||||||
|
// for everything because it doesn't accurately tell us if we're paired.
|
||||||
|
snprintf(url, sizeof(url), "%s://%s:%d/serverinfo?uniqueid=%s&uuid=%s",
|
||||||
|
i == 0 ? "https" : "http", server->serverInfo.address, i == 0 ? 47984 : 47989, unique_id, uuid_str);
|
||||||
|
|
||||||
|
PHTTP_DATA data = http_create_data();
|
||||||
|
if (data == NULL) {
|
||||||
|
ret = GS_OUT_OF_MEMORY;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
if (http_request(url, data) != GS_OK) {
|
||||||
|
ret = GS_IO_ERROR;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xml_status(data->memory, data->size) == GS_ERROR) {
|
||||||
|
ret = GS_ERROR;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xml_search(data->memory, data->size, "currentgame", ¤tGameText) != GS_OK) {
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xml_search(data->memory, data->size, "PairStatus", &pairedText) != GS_OK)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (xml_search(data->memory, data->size, "appversion", (char**) &server->serverInfo.serverInfoAppVersion) != GS_OK)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (xml_search(data->memory, data->size, "state", &stateText) != GS_OK)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (xml_search(data->memory, data->size, "ServerCodecModeSupport", &serverCodecModeSupportText) != GS_OK)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (xml_search(data->memory, data->size, "gputype", &server->gpuType) != GS_OK)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (xml_search(data->memory, data->size, "GsVersion", &server->gsVersion) != GS_OK)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (xml_search(data->memory, data->size, "GfeVersion", (char**) &server->serverInfo.serverInfoGfeVersion) != GS_OK)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (xml_modelist(data->memory, data->size, &server->modes) != GS_OK)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
// These fields are present on all version of GFE that this client supports
|
||||||
|
if (!strlen(currentGameText) || !strlen(pairedText) || !strlen(server->serverInfo.serverInfoAppVersion) || !strlen(stateText))
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
server->paired = pairedText != NULL && strcmp(pairedText, "1") == 0;
|
||||||
|
server->currentGame = currentGameText == NULL ? 0 : atoi(currentGameText);
|
||||||
|
server->supports4K = serverCodecModeSupportText != NULL;
|
||||||
|
server->serverMajorVersion = atoi(server->serverInfo.serverInfoAppVersion);
|
||||||
|
|
||||||
|
if (strstr(stateText, "_SERVER_BUSY") == NULL) {
|
||||||
|
// After GFE 2.8, current game remains set even after streaming
|
||||||
|
// has ended. We emulate the old behavior by forcing it to zero
|
||||||
|
// if streaming is not active.
|
||||||
|
server->currentGame = 0;
|
||||||
|
}
|
||||||
|
ret = GS_OK;
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if (data != NULL)
|
||||||
|
http_free_data(data);
|
||||||
|
|
||||||
|
if (pairedText != NULL)
|
||||||
|
free(pairedText);
|
||||||
|
|
||||||
|
if (currentGameText != NULL)
|
||||||
|
free(currentGameText);
|
||||||
|
|
||||||
|
if (serverCodecModeSupportText != NULL)
|
||||||
|
free(serverCodecModeSupportText);
|
||||||
|
|
||||||
|
i++;
|
||||||
|
} while (ret != GS_OK && i < 2);
|
||||||
|
|
||||||
if (ret == GS_OK && !server->unsupported) {
|
if (ret == GS_OK && !server->unsupported) {
|
||||||
if (server->serverMajorVersion > MAX_SUPPORTED_GFE_VERSION) {
|
if (server->serverMajorVersion > MAX_SUPPORTED_GFE_VERSION) {
|
||||||
@ -308,12 +291,6 @@ static void bytes_to_hex(unsigned char *in, char *out, size_t len) {
|
|||||||
out[len * 2] = 0;
|
out[len * 2] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void hex_to_bytes(const char *in, unsigned char* out, size_t len) {
|
|
||||||
for (int count = 0; count < len; count += 2) {
|
|
||||||
sscanf(&in[count], "%2hhx", &out[count / 2]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sign_it(const char *msg, size_t mlen, unsigned char **sig, size_t *slen, EVP_PKEY *pkey) {
|
static int sign_it(const char *msg, size_t mlen, unsigned char **sig, size_t *slen, EVP_PKEY *pkey) {
|
||||||
int result = GS_FAILED;
|
int result = GS_FAILED;
|
||||||
|
|
||||||
@ -324,7 +301,15 @@ static int sign_it(const char *msg, size_t mlen, unsigned char **sig, size_t *sl
|
|||||||
if (ctx == NULL)
|
if (ctx == NULL)
|
||||||
return GS_FAILED;
|
return GS_FAILED;
|
||||||
|
|
||||||
int rc = EVP_DigestSignInit(ctx, NULL, EVP_sha256(), NULL, pkey);
|
const EVP_MD *md = EVP_get_digestbyname("SHA256");
|
||||||
|
if (md == NULL)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
int rc = EVP_DigestInit_ex(ctx, md, NULL);
|
||||||
|
if (rc != 1)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
rc = EVP_DigestSignInit(ctx, NULL, md, NULL, pkey);
|
||||||
if (rc != 1)
|
if (rc != 1)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
@ -360,63 +345,39 @@ static bool verifySignature(const char *data, int dataLength, char *signature, i
|
|||||||
BIO* bio = BIO_new(BIO_s_mem());
|
BIO* bio = BIO_new(BIO_s_mem());
|
||||||
BIO_puts(bio, cert);
|
BIO_puts(bio, cert);
|
||||||
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
||||||
|
|
||||||
BIO_free(bio);
|
BIO_free(bio);
|
||||||
|
|
||||||
if (!x509) {
|
if (!x509) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
EVP_PKEY* pubKey = X509_get_pubkey(x509);
|
EVP_PKEY* pubKey = X509_get_pubkey(x509);
|
||||||
EVP_MD_CTX *mdctx = NULL;
|
EVP_MD_CTX *mdctx = NULL;
|
||||||
mdctx = EVP_MD_CTX_create();
|
mdctx = EVP_MD_CTX_create();
|
||||||
EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, pubKey);
|
EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, pubKey);
|
||||||
EVP_DigestVerifyUpdate(mdctx, data, dataLength);
|
EVP_DigestVerifyUpdate(mdctx, data, dataLength);
|
||||||
int result = EVP_DigestVerifyFinal(mdctx, signature, signatureLength);
|
int result = EVP_DigestVerifyFinal(mdctx, signature, signatureLength);
|
||||||
|
|
||||||
X509_free(x509);
|
X509_free(x509);
|
||||||
EVP_PKEY_free(pubKey);
|
EVP_PKEY_free(pubKey);
|
||||||
EVP_MD_CTX_destroy(mdctx);
|
EVP_MD_CTX_destroy(mdctx);
|
||||||
|
|
||||||
return result > 0;
|
return result > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void encrypt(const unsigned char *plaintext, int plaintextLen, const unsigned char *key, unsigned char *ciphertext) {
|
|
||||||
EVP_CIPHER_CTX* cipher = EVP_CIPHER_CTX_new();
|
|
||||||
|
|
||||||
EVP_EncryptInit(cipher, EVP_aes_128_ecb(), key, NULL);
|
|
||||||
EVP_CIPHER_CTX_set_padding(cipher, 0);
|
|
||||||
|
|
||||||
int ciphertextLen = 0;
|
|
||||||
EVP_EncryptUpdate(cipher, ciphertext, &ciphertextLen, plaintext, plaintextLen);
|
|
||||||
|
|
||||||
EVP_CIPHER_CTX_free(cipher);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void decrypt(const unsigned char *ciphertext, int ciphertextLen, const unsigned char *key, unsigned char *plaintext) {
|
|
||||||
EVP_CIPHER_CTX* cipher = EVP_CIPHER_CTX_new();
|
|
||||||
|
|
||||||
EVP_DecryptInit(cipher, EVP_aes_128_ecb(), key, NULL);
|
|
||||||
EVP_CIPHER_CTX_set_padding(cipher, 0);
|
|
||||||
|
|
||||||
int plaintextLen = 0;
|
|
||||||
EVP_DecryptUpdate(cipher, plaintext, &plaintextLen, ciphertext, ciphertextLen);
|
|
||||||
|
|
||||||
EVP_CIPHER_CTX_free(cipher);
|
|
||||||
}
|
|
||||||
|
|
||||||
int gs_unpair(PSERVER_DATA server) {
|
int gs_unpair(PSERVER_DATA server) {
|
||||||
int ret = GS_OK;
|
int ret = GS_OK;
|
||||||
char url[4096];
|
char url[4096];
|
||||||
uuid_t uuid;
|
uuid_t uuid;
|
||||||
char uuid_str[UUID_STRLEN];
|
char uuid_str[37];
|
||||||
PHTTP_DATA data = http_create_data();
|
PHTTP_DATA data = http_create_data();
|
||||||
if (data == NULL)
|
if (data == NULL)
|
||||||
return GS_OUT_OF_MEMORY;
|
return GS_OUT_OF_MEMORY;
|
||||||
|
|
||||||
uuid_generate_random(uuid);
|
uuid_generate_random(uuid);
|
||||||
uuid_unparse(uuid, uuid_str);
|
uuid_unparse(uuid, uuid_str);
|
||||||
snprintf(url, sizeof(url), "http://%s:%u/unpair?uniqueid=%s&uuid=%s", server->serverInfo.address, server->httpPort, unique_id, uuid_str);
|
snprintf(url, sizeof(url), "http://%s:47989/unpair?uniqueid=%s&uuid=%s", server->serverInfo.address, unique_id, uuid_str);
|
||||||
ret = http_request(url, data);
|
ret = http_request(url, data);
|
||||||
|
|
||||||
http_free_data(data);
|
http_free_data(data);
|
||||||
@ -426,32 +387,29 @@ int gs_unpair(PSERVER_DATA server) {
|
|||||||
int gs_pair(PSERVER_DATA server, char* pin) {
|
int gs_pair(PSERVER_DATA server, char* pin) {
|
||||||
int ret = GS_OK;
|
int ret = GS_OK;
|
||||||
char* result = NULL;
|
char* result = NULL;
|
||||||
size_t url_max_len = 16384;
|
char url[4096];
|
||||||
char* url = malloc(url_max_len);
|
|
||||||
uuid_t uuid;
|
uuid_t uuid;
|
||||||
char uuid_str[UUID_STRLEN];
|
char uuid_str[37];
|
||||||
char* plaincert = NULL;
|
|
||||||
char* challenge_response = NULL;
|
|
||||||
char* pairing_secret = NULL;
|
|
||||||
char* client_pairing_secret = NULL;
|
|
||||||
char* client_pairing_secret_hex = NULL;
|
|
||||||
PHTTP_DATA data = NULL;
|
|
||||||
|
|
||||||
if (server->paired) {
|
if (server->paired) {
|
||||||
gs_error = "Already paired";
|
gs_error = "Already paired";
|
||||||
ret = GS_WRONG_STATE;
|
return GS_WRONG_STATE;
|
||||||
goto cleanup;
|
}
|
||||||
|
|
||||||
|
if (server->currentGame != 0) {
|
||||||
|
gs_error = "The computer is currently in a game. You must close the game before pairing";
|
||||||
|
return GS_WRONG_STATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char salt_data[16];
|
unsigned char salt_data[16];
|
||||||
char salt_hex[SIZEOF_AS_HEX_STR(salt_data)];
|
char salt_hex[33];
|
||||||
RAND_bytes(salt_data, sizeof(salt_data));
|
RAND_bytes(salt_data, 16);
|
||||||
bytes_to_hex(salt_data, salt_hex, sizeof(salt_data));
|
bytes_to_hex(salt_data, salt_hex, 16);
|
||||||
|
|
||||||
uuid_generate_random(uuid);
|
uuid_generate_random(uuid);
|
||||||
uuid_unparse(uuid, uuid_str);
|
uuid_unparse(uuid, uuid_str);
|
||||||
snprintf(url, url_max_len, "http://%s:%u/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&phrase=getservercert&salt=%s&clientcert=%s", server->serverInfo.address, server->httpPort, unique_id, uuid_str, salt_hex, cert_hex);
|
snprintf(url, sizeof(url), "http://%s:47989/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&phrase=getservercert&salt=%s&clientcert=%s", server->serverInfo.address, unique_id, uuid_str, salt_hex, cert_hex);
|
||||||
data = http_create_data();
|
PHTTP_DATA data = http_create_data();
|
||||||
if (data == NULL)
|
if (data == NULL)
|
||||||
return GS_OUT_OF_MEMORY;
|
return GS_OUT_OF_MEMORY;
|
||||||
else if ((ret = http_request(url, data)) != GS_OK)
|
else if ((ret = http_request(url, data)) != GS_OK)
|
||||||
@ -473,33 +431,43 @@ int gs_pair(PSERVER_DATA server, char* pin) {
|
|||||||
if ((ret = xml_search(data->memory, data->size, "plaincert", &result)) != GS_OK)
|
if ((ret = xml_search(data->memory, data->size, "plaincert", &result)) != GS_OK)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
|
if (strlen(result)/2 > 8191) {
|
||||||
|
gs_error = "Server certificate too big";
|
||||||
|
ret = GS_FAILED;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
size_t plaincertlen = strlen(result)/2;
|
char plaincert[8192];
|
||||||
plaincert = malloc(plaincertlen + 1);
|
for (int count = 0; count < strlen(result); count += 2) {
|
||||||
hex_to_bytes(result, plaincert, plaincertlen*2);
|
sscanf(&result[count], "%2hhx", &plaincert[count / 2]);
|
||||||
plaincert[plaincertlen] = 0;
|
}
|
||||||
|
plaincert[strlen(result)/2] = '\0';
|
||||||
|
|
||||||
unsigned char salt_pin[sizeof(salt_data) + 4];
|
unsigned char salt_pin[20];
|
||||||
unsigned char aes_key[32]; // Must fit SHA256
|
unsigned char aes_key_hash[32];
|
||||||
memcpy(salt_pin, salt_data, sizeof(salt_data));
|
AES_KEY enc_key, dec_key;
|
||||||
memcpy(salt_pin+sizeof(salt_data), pin, 4);
|
memcpy(salt_pin, salt_data, 16);
|
||||||
|
memcpy(salt_pin+16, pin, 4);
|
||||||
|
|
||||||
int hash_length = server->serverMajorVersion >= 7 ? 32 : 20;
|
int hash_length = server->serverMajorVersion >= 7 ? 32 : 20;
|
||||||
if (server->serverMajorVersion >= 7)
|
if (server->serverMajorVersion >= 7)
|
||||||
SHA256(salt_pin, sizeof(salt_pin), aes_key);
|
SHA256(salt_pin, 20, aes_key_hash);
|
||||||
else
|
else
|
||||||
SHA1(salt_pin, sizeof(salt_pin), aes_key);
|
SHA1(salt_pin, 20, aes_key_hash);
|
||||||
|
|
||||||
|
AES_set_encrypt_key((unsigned char *)aes_key_hash, 128, &enc_key);
|
||||||
|
AES_set_decrypt_key((unsigned char *)aes_key_hash, 128, &dec_key);
|
||||||
|
|
||||||
unsigned char challenge_data[16];
|
unsigned char challenge_data[16];
|
||||||
unsigned char challenge_enc[sizeof(challenge_data)];
|
unsigned char challenge_enc[16];
|
||||||
char challenge_hex[SIZEOF_AS_HEX_STR(challenge_enc)];
|
char challenge_hex[33];
|
||||||
RAND_bytes(challenge_data, sizeof(challenge_data));
|
RAND_bytes(challenge_data, 16);
|
||||||
encrypt(challenge_data, sizeof(challenge_data), aes_key, challenge_enc);
|
AES_encrypt(challenge_data, challenge_enc, &enc_key);
|
||||||
bytes_to_hex(challenge_enc, challenge_hex, sizeof(challenge_enc));
|
bytes_to_hex(challenge_enc, challenge_hex, 16);
|
||||||
|
|
||||||
uuid_generate_random(uuid);
|
uuid_generate_random(uuid);
|
||||||
uuid_unparse(uuid, uuid_str);
|
uuid_unparse(uuid, uuid_str);
|
||||||
snprintf(url, url_max_len, "http://%s:%u/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&clientchallenge=%s", server->serverInfo.address, server->httpPort, unique_id, uuid_str, challenge_hex);
|
snprintf(url, sizeof(url), "http://%s:47989/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&clientchallenge=%s", server->serverInfo.address, unique_id, uuid_str, challenge_hex);
|
||||||
if ((ret = http_request(url, data)) != GS_OK)
|
if ((ret = http_request(url, data)) != GS_OK)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
@ -523,42 +491,42 @@ int gs_pair(PSERVER_DATA server, char* pin) {
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
char challenge_response_data_enc[64];
|
char challenge_response_data_enc[48];
|
||||||
char challenge_response_data[sizeof(challenge_response_data_enc)];
|
char challenge_response_data[48];
|
||||||
|
for (int count = 0; count < strlen(result); count += 2) {
|
||||||
if (strlen(result) / 2 > sizeof(challenge_response_data_enc)) {
|
sscanf(&result[count], "%2hhx", &challenge_response_data_enc[count / 2]);
|
||||||
gs_error = "Server challenge response too big";
|
|
||||||
ret = GS_FAILED;
|
|
||||||
goto cleanup;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hex_to_bytes(result, challenge_response_data_enc, strlen(result));
|
for (int i = 0; i < 48; i += 16) {
|
||||||
decrypt(challenge_response_data_enc, sizeof(challenge_response_data_enc), aes_key, challenge_response_data);
|
AES_decrypt(&challenge_response_data_enc[i], &challenge_response_data[i], &dec_key);
|
||||||
|
}
|
||||||
|
|
||||||
char client_secret_data[16];
|
char client_secret_data[16];
|
||||||
RAND_bytes(client_secret_data, sizeof(client_secret_data));
|
RAND_bytes(client_secret_data, 16);
|
||||||
|
|
||||||
const ASN1_BIT_STRING *asnSignature;
|
const ASN1_BIT_STRING *asnSignature;
|
||||||
X509_get0_signature(&asnSignature, NULL, cert);
|
X509_get0_signature(&asnSignature, NULL, cert);
|
||||||
|
|
||||||
challenge_response = malloc(16 + asnSignature->length + sizeof(client_secret_data));
|
char challenge_response[16 + 256 + 16];
|
||||||
char challenge_response_hash[32];
|
char challenge_response_hash[32];
|
||||||
char challenge_response_hash_enc[sizeof(challenge_response_hash)];
|
char challenge_response_hash_enc[32];
|
||||||
char challenge_response_hex[SIZEOF_AS_HEX_STR(challenge_response_hash_enc)];
|
char challenge_response_hex[65];
|
||||||
memcpy(challenge_response, challenge_response_data + hash_length, 16);
|
memcpy(challenge_response, challenge_response_data + hash_length, 16);
|
||||||
memcpy(challenge_response + 16, asnSignature->data, asnSignature->length);
|
memcpy(challenge_response + 16, asnSignature->data, 256);
|
||||||
memcpy(challenge_response + 16 + asnSignature->length, client_secret_data, sizeof(client_secret_data));
|
memcpy(challenge_response + 16 + 256, client_secret_data, 16);
|
||||||
if (server->serverMajorVersion >= 7)
|
if (server->serverMajorVersion >= 7)
|
||||||
SHA256(challenge_response, 16 + asnSignature->length + sizeof(client_secret_data), challenge_response_hash);
|
SHA256(challenge_response, 16 + 256 + 16, challenge_response_hash);
|
||||||
else
|
else
|
||||||
SHA1(challenge_response, 16 + asnSignature->length + sizeof(client_secret_data), challenge_response_hash);
|
SHA1(challenge_response, 16 + 256 + 16, challenge_response_hash);
|
||||||
|
|
||||||
encrypt(challenge_response_hash, sizeof(challenge_response_hash), aes_key, challenge_response_hash_enc);
|
for (int i = 0; i < 32; i += 16) {
|
||||||
bytes_to_hex(challenge_response_hash_enc, challenge_response_hex, sizeof(challenge_response_hash_enc));
|
AES_encrypt(&challenge_response_hash[i], &challenge_response_hash_enc[i], &enc_key);
|
||||||
|
}
|
||||||
|
bytes_to_hex(challenge_response_hash_enc, challenge_response_hex, 32);
|
||||||
|
|
||||||
uuid_generate_random(uuid);
|
uuid_generate_random(uuid);
|
||||||
uuid_unparse(uuid, uuid_str);
|
uuid_unparse(uuid, uuid_str);
|
||||||
snprintf(url, url_max_len, "http://%s:%u/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&serverchallengeresp=%s", server->serverInfo.address, server->httpPort, unique_id, uuid_str, challenge_response_hex);
|
snprintf(url, sizeof(url), "http://%s:47989/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&serverchallengeresp=%s", server->serverInfo.address, unique_id, uuid_str, challenge_response_hex);
|
||||||
if ((ret = http_request(url, data)) != GS_OK)
|
if ((ret = http_request(url, data)) != GS_OK)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
@ -582,15 +550,12 @@ int gs_pair(PSERVER_DATA server, char* pin) {
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t pairing_secret_len = strlen(result) / 2;
|
char pairing_secret[16 + 256];
|
||||||
if (pairing_secret_len <= 16) {
|
for (int count = 0; count < strlen(result); count += 2) {
|
||||||
ret = GS_INVALID;
|
sscanf(&result[count], "%2hhx", &pairing_secret[count / 2]);
|
||||||
goto cleanup;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pairing_secret = malloc(pairing_secret_len);
|
if (!verifySignature(pairing_secret, 16, pairing_secret+16, 256, plaincert)) {
|
||||||
hex_to_bytes(result, pairing_secret, pairing_secret_len*2);
|
|
||||||
if (!verifySignature(pairing_secret, 16, pairing_secret+16, pairing_secret_len-16, plaincert)) {
|
|
||||||
gs_error = "MITM attack detected";
|
gs_error = "MITM attack detected";
|
||||||
ret = GS_FAILED;
|
ret = GS_FAILED;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
@ -598,21 +563,21 @@ int gs_pair(PSERVER_DATA server, char* pin) {
|
|||||||
|
|
||||||
unsigned char *signature = NULL;
|
unsigned char *signature = NULL;
|
||||||
size_t s_len;
|
size_t s_len;
|
||||||
if (sign_it(client_secret_data, sizeof(client_secret_data), &signature, &s_len, privateKey) != GS_OK) {
|
if (sign_it(client_secret_data, 16, &signature, &s_len, privateKey) != GS_OK) {
|
||||||
gs_error = "Failed to sign data";
|
gs_error = "Failed to sign data";
|
||||||
ret = GS_FAILED;
|
ret = GS_FAILED;
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
client_pairing_secret = malloc(sizeof(client_secret_data) + s_len);
|
char client_pairing_secret[16 + 256];
|
||||||
client_pairing_secret_hex = malloc(LEN_AS_HEX_STR(sizeof(client_secret_data) + s_len));
|
char client_pairing_secret_hex[(16 + 256) * 2 + 1];
|
||||||
memcpy(client_pairing_secret, client_secret_data, sizeof(client_secret_data));
|
memcpy(client_pairing_secret, client_secret_data, 16);
|
||||||
memcpy(client_pairing_secret + sizeof(client_secret_data), signature, s_len);
|
memcpy(client_pairing_secret + 16, signature, 256);
|
||||||
bytes_to_hex(client_pairing_secret, client_pairing_secret_hex, sizeof(client_secret_data) + s_len);
|
bytes_to_hex(client_pairing_secret, client_pairing_secret_hex, 16 + 256);
|
||||||
|
|
||||||
uuid_generate_random(uuid);
|
uuid_generate_random(uuid);
|
||||||
uuid_unparse(uuid, uuid_str);
|
uuid_unparse(uuid, uuid_str);
|
||||||
snprintf(url, url_max_len, "http://%s:%u/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&clientpairingsecret=%s", server->serverInfo.address, server->httpPort, unique_id, uuid_str, client_pairing_secret_hex);
|
snprintf(url, sizeof(url), "http://%s:47989/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&clientpairingsecret=%s", server->serverInfo.address, unique_id, uuid_str, client_pairing_secret_hex);
|
||||||
if ((ret = http_request(url, data)) != GS_OK)
|
if ((ret = http_request(url, data)) != GS_OK)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
@ -631,7 +596,7 @@ int gs_pair(PSERVER_DATA server, char* pin) {
|
|||||||
|
|
||||||
uuid_generate_random(uuid);
|
uuid_generate_random(uuid);
|
||||||
uuid_unparse(uuid, uuid_str);
|
uuid_unparse(uuid, uuid_str);
|
||||||
snprintf(url, url_max_len, "https://%s:%u/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&phrase=pairchallenge", server->serverInfo.address, server->httpsPort, unique_id, uuid_str);
|
snprintf(url, sizeof(url), "https://%s:47984/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&phrase=pairchallenge", server->serverInfo.address, unique_id, uuid_str);
|
||||||
if ((ret = http_request(url, data)) != GS_OK)
|
if ((ret = http_request(url, data)) != GS_OK)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
@ -653,24 +618,12 @@ int gs_pair(PSERVER_DATA server, char* pin) {
|
|||||||
cleanup:
|
cleanup:
|
||||||
if (ret != GS_OK)
|
if (ret != GS_OK)
|
||||||
gs_unpair(server);
|
gs_unpair(server);
|
||||||
|
|
||||||
free(url);
|
if (result != NULL)
|
||||||
free(plaincert);
|
free(result);
|
||||||
free(challenge_response);
|
|
||||||
free(pairing_secret);
|
|
||||||
free(client_pairing_secret);
|
|
||||||
free(client_pairing_secret_hex);
|
|
||||||
free(result);
|
|
||||||
|
|
||||||
http_free_data(data);
|
http_free_data(data);
|
||||||
|
|
||||||
// If we failed when attempting to pair with a game running, that's likely the issue.
|
|
||||||
// Sunshine supports pairing with an active session, but GFE does not.
|
|
||||||
if (ret != GS_OK && server->currentGame != 0) {
|
|
||||||
gs_error = "The computer is currently in a game. You must close the game before pairing.";
|
|
||||||
ret = GS_WRONG_STATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -678,14 +631,14 @@ int gs_applist(PSERVER_DATA server, PAPP_LIST *list) {
|
|||||||
int ret = GS_OK;
|
int ret = GS_OK;
|
||||||
char url[4096];
|
char url[4096];
|
||||||
uuid_t uuid;
|
uuid_t uuid;
|
||||||
char uuid_str[UUID_STRLEN];
|
char uuid_str[37];
|
||||||
PHTTP_DATA data = http_create_data();
|
PHTTP_DATA data = http_create_data();
|
||||||
if (data == NULL)
|
if (data == NULL)
|
||||||
return GS_OUT_OF_MEMORY;
|
return GS_OUT_OF_MEMORY;
|
||||||
|
|
||||||
uuid_generate_random(uuid);
|
uuid_generate_random(uuid);
|
||||||
uuid_unparse(uuid, uuid_str);
|
uuid_unparse(uuid, uuid_str);
|
||||||
snprintf(url, sizeof(url), "https://%s:%u/applist?uniqueid=%s&uuid=%s", server->serverInfo.address, server->httpsPort, unique_id, uuid_str);
|
snprintf(url, sizeof(url), "https://%s:47984/applist?uniqueid=%s&uuid=%s", server->serverInfo.address, unique_id, uuid_str);
|
||||||
if (http_request(url, data) != GS_OK)
|
if (http_request(url, data) != GS_OK)
|
||||||
ret = GS_IO_ERROR;
|
ret = GS_IO_ERROR;
|
||||||
else if (xml_status(data->memory, data->size) == GS_ERROR)
|
else if (xml_status(data->memory, data->size) == GS_ERROR)
|
||||||
@ -701,15 +654,13 @@ int gs_start_app(PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, b
|
|||||||
int ret = GS_OK;
|
int ret = GS_OK;
|
||||||
uuid_t uuid;
|
uuid_t uuid;
|
||||||
char* result = NULL;
|
char* result = NULL;
|
||||||
char uuid_str[UUID_STRLEN];
|
char uuid_str[37];
|
||||||
|
|
||||||
PDISPLAY_MODE mode = server->modes;
|
PDISPLAY_MODE mode = server->modes;
|
||||||
bool correct_mode = false;
|
bool correct_mode = false;
|
||||||
while (mode != NULL) {
|
while (mode != NULL) {
|
||||||
if (mode->width == config->width && mode->height == config->height) {
|
if (mode->width == config->width && mode->height == config->height && mode->refresh == config->fps)
|
||||||
if (mode->refresh == config->fps)
|
correct_mode = true;
|
||||||
correct_mode = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
mode = mode->next;
|
mode = mode->next;
|
||||||
}
|
}
|
||||||
@ -717,34 +668,31 @@ int gs_start_app(PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, b
|
|||||||
if (!correct_mode && !server->unsupported)
|
if (!correct_mode && !server->unsupported)
|
||||||
return GS_NOT_SUPPORTED_MODE;
|
return GS_NOT_SUPPORTED_MODE;
|
||||||
|
|
||||||
RAND_bytes(config->remoteInputAesKey, sizeof(config->remoteInputAesKey));
|
if (config->height >= 2160 && !server->supports4K)
|
||||||
memset(config->remoteInputAesIv, 0, sizeof(config->remoteInputAesIv));
|
return GS_NOT_SUPPORTED_4K;
|
||||||
|
|
||||||
|
RAND_bytes(config->remoteInputAesKey, 16);
|
||||||
|
memset(config->remoteInputAesIv, 0, 16);
|
||||||
|
|
||||||
|
srand(time(NULL));
|
||||||
char url[4096];
|
char url[4096];
|
||||||
u_int32_t rikeyid = 0;
|
u_int32_t rikeyid = 0;
|
||||||
RAND_bytes(config->remoteInputAesIv, sizeof(rikeyid));
|
char rikey_hex[33];
|
||||||
memcpy(&rikeyid, config->remoteInputAesIv, sizeof(rikeyid));
|
bytes_to_hex(config->remoteInputAesKey, rikey_hex, 16);
|
||||||
rikeyid = htonl(rikeyid);
|
|
||||||
char rikey_hex[SIZEOF_AS_HEX_STR(config->remoteInputAesKey)];
|
|
||||||
bytes_to_hex(config->remoteInputAesKey, rikey_hex, sizeof(config->remoteInputAesKey));
|
|
||||||
|
|
||||||
PHTTP_DATA data = http_create_data();
|
PHTTP_DATA data = http_create_data();
|
||||||
if (data == NULL)
|
if (data == NULL)
|
||||||
return GS_OUT_OF_MEMORY;
|
return GS_OUT_OF_MEMORY;
|
||||||
|
|
||||||
// Using an FPS value over 60 causes SOPS to default to 720p60,
|
|
||||||
// so force it to 0 to ensure the correct resolution is set. We
|
|
||||||
// used to use 60 here but that locked the frame rate to 60 FPS
|
|
||||||
// on GFE 3.20.3.
|
|
||||||
int fps = (server->isNvidiaSoftware && config->fps > 60) ? 0 : config->fps;
|
|
||||||
|
|
||||||
uuid_generate_random(uuid);
|
uuid_generate_random(uuid);
|
||||||
uuid_unparse(uuid, uuid_str);
|
uuid_unparse(uuid, uuid_str);
|
||||||
int surround_info = SURROUNDAUDIOINFO_FROM_AUDIO_CONFIGURATION(config->audioConfiguration);
|
if (server->currentGame == 0) {
|
||||||
snprintf(url, sizeof(url), "https://%s:%u/%s?uniqueid=%s&uuid=%s&appid=%d&mode=%dx%dx%d&additionalStates=1&sops=%d&rikey=%s&rikeyid=%d&localAudioPlayMode=%d&surroundAudioInfo=%d&remoteControllersBitmap=%d&gcmap=%d%s%s",
|
int channelCounnt = config->audioConfiguration == AUDIO_CONFIGURATION_STEREO ? CHANNEL_COUNT_STEREO : CHANNEL_COUNT_51_SURROUND;
|
||||||
server->serverInfo.address, server->httpsPort, server->currentGame ? "resume" : "launch", unique_id, uuid_str, appId, config->width, config->height, fps, sops, rikey_hex, rikeyid, localaudio, surround_info, gamepad_mask, gamepad_mask,
|
int mask = config->audioConfiguration == AUDIO_CONFIGURATION_STEREO ? CHANNEL_MASK_STEREO : CHANNEL_MASK_51_SURROUND;
|
||||||
(config->supportedVideoFormats & VIDEO_FORMAT_MASK_10BIT) ? "&hdrMode=1&clientHdrCapVersion=0&clientHdrCapSupportedFlagsInUint32=0&clientHdrCapMetaDataId=NV_STATIC_METADATA_TYPE_1&clientHdrCapDisplayData=0x0x0x0x0x0x0x0x0x0x0" : "",
|
snprintf(url, sizeof(url), "https://%s:47984/launch?uniqueid=%s&uuid=%s&appid=%d&mode=%dx%dx%d&additionalStates=1&sops=%d&rikey=%s&rikeyid=%d&localAudioPlayMode=%d&surroundAudioInfo=%d&remoteControllersBitmap=%d&gcmap=%d", server->serverInfo.address, unique_id, uuid_str, appId, config->width, config->height, config->fps, sops, rikey_hex, rikeyid, localaudio, (mask << 16) + channelCounnt, gamepad_mask, gamepad_mask);
|
||||||
LiGetLaunchUrlQueryParameters());
|
} else
|
||||||
|
snprintf(url, sizeof(url), "https://%s:47984/resume?uniqueid=%s&uuid=%s&rikey=%s&rikeyid=%d", server->serverInfo.address, unique_id, uuid_str, rikey_hex, rikeyid);
|
||||||
|
|
||||||
if ((ret = http_request(url, data)) == GS_OK)
|
if ((ret = http_request(url, data)) == GS_OK)
|
||||||
server->currentGame = appId;
|
server->currentGame = appId;
|
||||||
else
|
else
|
||||||
@ -752,8 +700,7 @@ int gs_start_app(PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, b
|
|||||||
|
|
||||||
if ((ret = xml_status(data->memory, data->size) != GS_OK))
|
if ((ret = xml_status(data->memory, data->size) != GS_OK))
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
else if ((ret = xml_search(data->memory, data->size, "gamesession", &result)) != GS_OK &&
|
else if ((ret = xml_search(data->memory, data->size, "gamesession", &result)) != GS_OK)
|
||||||
(ret = xml_search(data->memory, data->size, "resume", &result)) != GS_OK)
|
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
if (!strcmp(result, "0")) {
|
if (!strcmp(result, "0")) {
|
||||||
@ -761,14 +708,6 @@ int gs_start_app(PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, b
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
free(result);
|
|
||||||
result = NULL;
|
|
||||||
|
|
||||||
if (xml_search(data->memory, data->size, "sessionUrl0", &result) == GS_OK) {
|
|
||||||
server->serverInfo.rtspSessionUrl = result;
|
|
||||||
result = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
if (result != NULL)
|
if (result != NULL)
|
||||||
free(result);
|
free(result);
|
||||||
@ -781,7 +720,7 @@ int gs_quit_app(PSERVER_DATA server) {
|
|||||||
int ret = GS_OK;
|
int ret = GS_OK;
|
||||||
char url[4096];
|
char url[4096];
|
||||||
uuid_t uuid;
|
uuid_t uuid;
|
||||||
char uuid_str[UUID_STRLEN];
|
char uuid_str[37];
|
||||||
char* result = NULL;
|
char* result = NULL;
|
||||||
PHTTP_DATA data = http_create_data();
|
PHTTP_DATA data = http_create_data();
|
||||||
if (data == NULL)
|
if (data == NULL)
|
||||||
@ -789,7 +728,7 @@ int gs_quit_app(PSERVER_DATA server) {
|
|||||||
|
|
||||||
uuid_generate_random(uuid);
|
uuid_generate_random(uuid);
|
||||||
uuid_unparse(uuid, uuid_str);
|
uuid_unparse(uuid, uuid_str);
|
||||||
snprintf(url, sizeof(url), "https://%s:%u/cancel?uniqueid=%s&uuid=%s", server->serverInfo.address, server->httpsPort, unique_id, uuid_str);
|
snprintf(url, sizeof(url), "https://%s:47984/cancel?uniqueid=%s&uuid=%s", server->serverInfo.address, unique_id, uuid_str);
|
||||||
if ((ret = http_request(url, data)) != GS_OK)
|
if ((ret = http_request(url, data)) != GS_OK)
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
@ -811,7 +750,7 @@ int gs_quit_app(PSERVER_DATA server) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int gs_init(PSERVER_DATA server, char *address, unsigned short httpPort, const char *keyDirectory, int log_level, bool unsupported) {
|
int gs_init(PSERVER_DATA server, char *address, const char *keyDirectory, int log_level, bool unsupported) {
|
||||||
mkdirtree(keyDirectory);
|
mkdirtree(keyDirectory);
|
||||||
if (load_unique_id(keyDirectory) != GS_OK)
|
if (load_unique_id(keyDirectory) != GS_OK)
|
||||||
return GS_FAILED;
|
return GS_FAILED;
|
||||||
@ -824,7 +763,5 @@ int gs_init(PSERVER_DATA server, char *address, unsigned short httpPort, const c
|
|||||||
LiInitializeServerInformation(&server->serverInfo);
|
LiInitializeServerInformation(&server->serverInfo);
|
||||||
server->serverInfo.address = address;
|
server->serverInfo.address = address;
|
||||||
server->unsupported = unsupported;
|
server->unsupported = unsupported;
|
||||||
server->httpPort = httpPort ? httpPort : 47989;
|
|
||||||
server->httpsPort = 0; /* Populated by load_server_status() */
|
|
||||||
return load_server_status(server);
|
return load_server_status(server);
|
||||||
}
|
}
|
||||||
|
@ -29,20 +29,19 @@
|
|||||||
#define MAX_SUPPORTED_GFE_VERSION 7
|
#define MAX_SUPPORTED_GFE_VERSION 7
|
||||||
|
|
||||||
typedef struct _SERVER_DATA {
|
typedef struct _SERVER_DATA {
|
||||||
|
const char* address;
|
||||||
char* gpuType;
|
char* gpuType;
|
||||||
bool paired;
|
bool paired;
|
||||||
|
bool supports4K;
|
||||||
bool unsupported;
|
bool unsupported;
|
||||||
bool isNvidiaSoftware;
|
|
||||||
int currentGame;
|
int currentGame;
|
||||||
int serverMajorVersion;
|
int serverMajorVersion;
|
||||||
char* gsVersion;
|
char* gsVersion;
|
||||||
PDISPLAY_MODE modes;
|
PDISPLAY_MODE modes;
|
||||||
SERVER_INFORMATION serverInfo;
|
SERVER_INFORMATION serverInfo;
|
||||||
unsigned short httpPort;
|
|
||||||
unsigned short httpsPort;
|
|
||||||
} SERVER_DATA, *PSERVER_DATA;
|
} SERVER_DATA, *PSERVER_DATA;
|
||||||
|
|
||||||
int gs_init(PSERVER_DATA server, char* address, unsigned short httpPort, const char *keyDirectory, int logLevel, bool unsupported);
|
int gs_init(PSERVER_DATA server, char* address, const char *keyDirectory, int logLevel, bool unsupported);
|
||||||
int gs_start_app(PSERVER_DATA server, PSTREAM_CONFIGURATION config, int appId, bool sops, bool localaudio, int gamepad_mask);
|
int gs_start_app(PSERVER_DATA server, PSTREAM_CONFIGURATION config, int appId, bool sops, bool localaudio, int gamepad_mask);
|
||||||
int gs_applist(PSERVER_DATA server, PAPP_LIST *app_list);
|
int gs_applist(PSERVER_DATA server, PAPP_LIST *app_list);
|
||||||
int gs_unpair(PSERVER_DATA server);
|
int gs_unpair(PSERVER_DATA server);
|
||||||
|
@ -33,11 +33,6 @@
|
|||||||
|
|
||||||
static AvahiSimplePoll *simple_poll = NULL;
|
static AvahiSimplePoll *simple_poll = NULL;
|
||||||
|
|
||||||
struct cb_ctx {
|
|
||||||
char* address;
|
|
||||||
unsigned short* port;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {
|
static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {
|
||||||
if (state == AVAHI_CLIENT_FAILURE) {
|
if (state == AVAHI_CLIENT_FAILURE) {
|
||||||
gs_error = "Server connection failure";
|
gs_error = "Server connection failure";
|
||||||
@ -48,14 +43,12 @@ static void client_callback(AvahiClient *c, AvahiClientState state, void *userda
|
|||||||
static void resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void *userdata) {
|
static void resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void *userdata) {
|
||||||
if (event == AVAHI_RESOLVER_FOUND) {
|
if (event == AVAHI_RESOLVER_FOUND) {
|
||||||
if (userdata != NULL) {
|
if (userdata != NULL) {
|
||||||
struct cb_ctx* ctx = userdata;
|
avahi_address_snprint(userdata, AVAHI_ADDRESS_STR_MAX, address);
|
||||||
avahi_address_snprint(ctx->address, AVAHI_ADDRESS_STR_MAX, address);
|
|
||||||
*ctx->port = port;
|
|
||||||
avahi_simple_poll_quit(simple_poll);
|
avahi_simple_poll_quit(simple_poll);
|
||||||
} else {
|
} else {
|
||||||
char strAddress[AVAHI_ADDRESS_STR_MAX];
|
char strAddress[AVAHI_ADDRESS_STR_MAX];
|
||||||
avahi_address_snprint(strAddress, sizeof(strAddress), address);
|
avahi_address_snprint(strAddress, sizeof(strAddress), address);
|
||||||
printf(" %s (%s:%u)\n", host_name, strAddress, port);
|
printf(" %s (%s)\n", host_name, strAddress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,7 +73,7 @@ static void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, Avah
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void gs_discover_server(char* dest, unsigned short* port) {
|
void gs_discover_server(char* dest) {
|
||||||
AvahiClient *client = NULL;
|
AvahiClient *client = NULL;
|
||||||
AvahiServiceBrowser *sb = NULL;
|
AvahiServiceBrowser *sb = NULL;
|
||||||
|
|
||||||
@ -96,10 +89,7 @@ void gs_discover_server(char* dest, unsigned short* port) {
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct cb_ctx ctx;
|
if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET, "_nvstream._tcp", NULL, 0, browse_callback, dest))) {
|
||||||
ctx.address = dest;
|
|
||||||
ctx.port = port;
|
|
||||||
if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET, "_nvstream._tcp", NULL, 0, browse_callback, &ctx))) {
|
|
||||||
gs_error = "Failed to create service browser";
|
gs_error = "Failed to create service browser";
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
@ -21,4 +21,4 @@
|
|||||||
|
|
||||||
#define MAX_ADDRESS_SIZE 40
|
#define MAX_ADDRESS_SIZE 40
|
||||||
|
|
||||||
void gs_discover_server(char* dest, unsigned short* port);
|
void gs_discover_server(char* dest);
|
||||||
|
@ -29,6 +29,5 @@
|
|||||||
#define GS_UNSUPPORTED_VERSION -7
|
#define GS_UNSUPPORTED_VERSION -7
|
||||||
#define GS_NOT_SUPPORTED_MODE -8
|
#define GS_NOT_SUPPORTED_MODE -8
|
||||||
#define GS_ERROR -9
|
#define GS_ERROR -9
|
||||||
#define GS_NOT_SUPPORTED_SOPS_RESOLUTION -10
|
|
||||||
|
|
||||||
extern const char* gs_error;
|
extern const char* gs_error;
|
||||||
|
@ -26,21 +26,24 @@
|
|||||||
|
|
||||||
static CURL *curl;
|
static CURL *curl;
|
||||||
|
|
||||||
|
static const char *pCertFile = "./client.pem";
|
||||||
|
static const char *pKeyFile = "./key.pem";
|
||||||
|
|
||||||
static bool debug;
|
static bool debug;
|
||||||
|
|
||||||
static size_t _write_curl(void *contents, size_t size, size_t nmemb, void *userp)
|
static size_t _write_curl(void *contents, size_t size, size_t nmemb, void *userp)
|
||||||
{
|
{
|
||||||
size_t realsize = size * nmemb;
|
size_t realsize = size * nmemb;
|
||||||
PHTTP_DATA mem = (PHTTP_DATA)userp;
|
PHTTP_DATA mem = (PHTTP_DATA)userp;
|
||||||
|
|
||||||
mem->memory = realloc(mem->memory, mem->size + realsize + 1);
|
mem->memory = realloc(mem->memory, mem->size + realsize + 1);
|
||||||
if(mem->memory == NULL)
|
if(mem->memory == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
memcpy(&(mem->memory[mem->size]), contents, realsize);
|
memcpy(&(mem->memory[mem->size]), contents, realsize);
|
||||||
mem->size += realsize;
|
mem->size += realsize;
|
||||||
mem->memory[mem->size] = 0;
|
mem->memory[mem->size] = 0;
|
||||||
|
|
||||||
return realsize;
|
return realsize;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,10 +54,10 @@ int http_init(const char* keyDirectory, int logLevel) {
|
|||||||
return GS_FAILED;
|
return GS_FAILED;
|
||||||
|
|
||||||
char certificateFilePath[4096];
|
char certificateFilePath[4096];
|
||||||
snprintf(certificateFilePath, sizeof(certificateFilePath), "%s/%s", keyDirectory, CERTIFICATE_FILE_NAME);
|
sprintf(certificateFilePath, "%s/%s", keyDirectory, CERTIFICATE_FILE_NAME);
|
||||||
|
|
||||||
char keyFilePath[4096];
|
char keyFilePath[4096];
|
||||||
snprintf(keyFilePath, sizeof(keyFilePath), "%s/%s", keyDirectory, KEY_FILE_NAME);
|
sprintf(&keyFilePath[0], "%s/%s", keyDirectory, KEY_FILE_NAME);
|
||||||
|
|
||||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
|
||||||
curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1L);
|
curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1L);
|
||||||
@ -73,9 +76,6 @@ int http_init(const char* keyDirectory, int logLevel) {
|
|||||||
int http_request(char* url, PHTTP_DATA data) {
|
int http_request(char* url, PHTTP_DATA data) {
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, data);
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, data);
|
||||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||||
#ifdef __FreeBSD__
|
|
||||||
curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (debug)
|
if (debug)
|
||||||
printf("Request %s\n", url);
|
printf("Request %s\n", url);
|
||||||
@ -89,7 +89,7 @@ int http_request(char* url, PHTTP_DATA data) {
|
|||||||
data->size = 0;
|
data->size = 0;
|
||||||
}
|
}
|
||||||
CURLcode res = curl_easy_perform(curl);
|
CURLcode res = curl_easy_perform(curl);
|
||||||
|
|
||||||
if(res != CURLE_OK) {
|
if(res != CURLE_OK) {
|
||||||
gs_error = curl_easy_strerror(res);
|
gs_error = curl_easy_strerror(res);
|
||||||
return GS_FAILED;
|
return GS_FAILED;
|
||||||
@ -99,7 +99,7 @@ int http_request(char* url, PHTTP_DATA data) {
|
|||||||
|
|
||||||
if (debug)
|
if (debug)
|
||||||
printf("Response:\n%s\n\n", data->memory);
|
printf("Response:\n%s\n\n", data->memory);
|
||||||
|
|
||||||
return GS_OK;
|
return GS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,18 +32,20 @@ static const int SERIAL = 0;
|
|||||||
static const int NUM_YEARS = 10;
|
static const int NUM_YEARS = 10;
|
||||||
|
|
||||||
int mkcert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int years);
|
int mkcert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int years);
|
||||||
|
int add_ext(X509 *cert, int nid, char *value);
|
||||||
|
|
||||||
CERT_KEY_PAIR mkcert_generate() {
|
CERT_KEY_PAIR mkcert_generate() {
|
||||||
BIO *bio_err;
|
BIO *bio_err;
|
||||||
X509 *x509 = NULL;
|
X509 *x509 = NULL;
|
||||||
EVP_PKEY *pkey = NULL;
|
EVP_PKEY *pkey = NULL;
|
||||||
PKCS12 *p12 = NULL;
|
PKCS12 *p12 = NULL;
|
||||||
|
|
||||||
|
CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
|
||||||
bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
|
bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
|
||||||
|
|
||||||
OpenSSL_add_all_algorithms();
|
OpenSSL_add_all_algorithms();
|
||||||
ERR_load_crypto_strings();
|
ERR_load_crypto_strings();
|
||||||
|
|
||||||
mkcert(&x509, &pkey, NUM_BITS, SERIAL, NUM_YEARS);
|
mkcert(&x509, &pkey, NUM_BITS, SERIAL, NUM_YEARS);
|
||||||
|
|
||||||
p12 = PKCS12_create("limelight", "GameStream", pkey, x509, NULL, 0, 0, 0, 0, 0);
|
p12 = PKCS12_create("limelight", "GameStream", pkey, x509, NULL, 0, 0, 0, 0, 0);
|
||||||
@ -52,9 +54,9 @@ CERT_KEY_PAIR mkcert_generate() {
|
|||||||
ENGINE_cleanup();
|
ENGINE_cleanup();
|
||||||
#endif
|
#endif
|
||||||
CRYPTO_cleanup_all_ex_data();
|
CRYPTO_cleanup_all_ex_data();
|
||||||
|
|
||||||
BIO_free(bio_err);
|
BIO_free(bio_err);
|
||||||
|
|
||||||
return (CERT_KEY_PAIR) {x509, pkey, p12};
|
return (CERT_KEY_PAIR) {x509, pkey, p12};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,62 +70,117 @@ void mkcert_save(const char* certFile, const char* p12File, const char* keyPairF
|
|||||||
FILE* certFilePtr = fopen(certFile, "w");
|
FILE* certFilePtr = fopen(certFile, "w");
|
||||||
FILE* keyPairFilePtr = fopen(keyPairFile, "w");
|
FILE* keyPairFilePtr = fopen(keyPairFile, "w");
|
||||||
FILE* p12FilePtr = fopen(p12File, "wb");
|
FILE* p12FilePtr = fopen(p12File, "wb");
|
||||||
|
|
||||||
//TODO: error check
|
//TODO: error check
|
||||||
PEM_write_PrivateKey(keyPairFilePtr, certKeyPair.pkey, NULL, NULL, 0, NULL, NULL);
|
PEM_write_PrivateKey(keyPairFilePtr, certKeyPair.pkey, NULL, NULL, 0, NULL, NULL);
|
||||||
PEM_write_X509(certFilePtr, certKeyPair.x509);
|
PEM_write_X509(certFilePtr, certKeyPair.x509);
|
||||||
i2d_PKCS12_fp(p12FilePtr, certKeyPair.p12);
|
i2d_PKCS12_fp(p12FilePtr, certKeyPair.p12);
|
||||||
|
|
||||||
fclose(p12FilePtr);
|
fclose(p12FilePtr);
|
||||||
fclose(certFilePtr);
|
fclose(certFilePtr);
|
||||||
fclose(keyPairFilePtr);
|
fclose(keyPairFilePtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
int mkcert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int years) {
|
int mkcert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int years) {
|
||||||
EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
|
X509 *x;
|
||||||
EVP_PKEY_keygen_init(ctx);
|
EVP_PKEY *pk;
|
||||||
EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits);
|
RSA *rsa;
|
||||||
|
X509_NAME *name = NULL;
|
||||||
|
|
||||||
|
if (*pkeyp == NULL) {
|
||||||
|
if ((pk=EVP_PKEY_new()) == NULL) {
|
||||||
|
abort();
|
||||||
|
return(0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pk = *pkeyp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*x509p == NULL) {
|
||||||
|
if ((x = X509_new()) == NULL) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
x = *x509p;
|
||||||
|
}
|
||||||
|
|
||||||
// pk must be initialized on input
|
if ((rsa = RSA_new()) == NULL)
|
||||||
EVP_PKEY *pk = NULL;;
|
goto err;
|
||||||
EVP_PKEY_keygen(ctx, &pk);
|
|
||||||
|
BIGNUM* bne = BN_new();
|
||||||
EVP_PKEY_CTX_free(ctx);
|
if (bne == NULL) {
|
||||||
|
abort();
|
||||||
X509* cert = X509_new();
|
|
||||||
X509_set_version(cert, 2);
|
|
||||||
ASN1_INTEGER_set(X509_get_serialNumber(cert), serial);
|
|
||||||
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
||||||
X509_gmtime_adj(X509_get_notBefore(cert), 0);
|
|
||||||
X509_gmtime_adj(X509_get_notAfter(cert), 60 * 60 * 24 * 365 * years);
|
|
||||||
#else
|
|
||||||
ASN1_TIME* before = ASN1_STRING_dup(X509_get0_notBefore(cert));
|
|
||||||
ASN1_TIME* after = ASN1_STRING_dup(X509_get0_notAfter(cert));
|
|
||||||
|
|
||||||
X509_gmtime_adj(before, 0);
|
|
||||||
X509_gmtime_adj(after, 60 * 60 * 24 * 365 * years);
|
|
||||||
|
|
||||||
X509_set1_notBefore(cert, before);
|
|
||||||
X509_set1_notAfter(cert, after);
|
|
||||||
|
|
||||||
ASN1_STRING_free(before);
|
|
||||||
ASN1_STRING_free(after);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
X509_set_pubkey(cert, pk);
|
|
||||||
|
|
||||||
X509_NAME* name = X509_get_subject_name(cert);
|
|
||||||
X509_NAME_add_entry_by_txt(name,"CN", MBSTRING_ASC, (unsigned char*)"NVIDIA GameStream Client", -1, -1, 0);
|
|
||||||
X509_set_issuer_name(cert, name);
|
|
||||||
|
|
||||||
if (!X509_sign(cert, pk, EVP_sha256())) {
|
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
*x509p = cert;
|
BN_set_word(bne, RSA_F4);
|
||||||
*pkeyp = pk;
|
if (RSA_generate_key_ex(rsa, bits, bne, NULL) == 0) {
|
||||||
|
abort();
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!EVP_PKEY_assign_RSA(pk, rsa)) {
|
||||||
|
abort();
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
X509_set_version(x, 2);
|
||||||
|
ASN1_INTEGER_set(X509_get_serialNumber(x), serial);
|
||||||
|
X509_gmtime_adj(X509_get_notBefore(x), 0);
|
||||||
|
X509_gmtime_adj(X509_get_notAfter(x), (long)60*60*24*365*years);
|
||||||
|
X509_set_pubkey(x, pk);
|
||||||
|
|
||||||
|
name = X509_get_subject_name(x);
|
||||||
|
|
||||||
|
/* This function creates and adds the entry, working out the
|
||||||
|
* correct string type and performing checks on its length.
|
||||||
|
*/
|
||||||
|
X509_NAME_add_entry_by_txt(name,"CN", MBSTRING_ASC, (unsigned char*)"NVIDIA GameStream Client", -1, -1, 0);
|
||||||
|
|
||||||
|
/* Its self signed so set the issuer name to be the same as the
|
||||||
|
* subject.
|
||||||
|
*/
|
||||||
|
X509_set_issuer_name(x, name);
|
||||||
|
|
||||||
|
/* Add various extensions: standard extensions */
|
||||||
|
add_ext(x, NID_key_usage, "critical,digitalSignature,keyEncipherment");
|
||||||
|
|
||||||
|
add_ext(x, NID_subject_key_identifier, "hash");
|
||||||
|
|
||||||
|
if (!X509_sign(x, pk, EVP_sha1())) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
*x509p = x;
|
||||||
|
*pkeyp = pk;
|
||||||
|
|
||||||
return(1);
|
return(1);
|
||||||
err:
|
err:
|
||||||
return(0);
|
return(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Add extension using V3 code: we can set the config file as NULL
|
||||||
|
* because we wont reference any other sections.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int add_ext(X509 *cert, int nid, char *value)
|
||||||
|
{
|
||||||
|
X509_EXTENSION *ex;
|
||||||
|
X509V3_CTX ctx;
|
||||||
|
/* This sets the 'context' of the extensions. */
|
||||||
|
/* No configuration database */
|
||||||
|
X509V3_set_ctx_nodb(&ctx);
|
||||||
|
/* Issuer and subject certs: both the target since it is self signed,
|
||||||
|
* no request and no CRL
|
||||||
|
*/
|
||||||
|
X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0);
|
||||||
|
ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, value);
|
||||||
|
if (!ex) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
X509_add_ext(cert, ex, -1);
|
||||||
|
X509_EXTENSION_free(ex);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -31,9 +31,9 @@ void gs_sps_init(int width, int height) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void gs_sps_fix(PLENTRY sps, int flags, uint8_t* out_buf, uint32_t* out_offset) {
|
void gs_sps_fix(PLENTRY sps, int flags, uint8_t* out_buf, uint32_t* out_offset) {
|
||||||
int start_len = sps->data[2] == 0x01 ? 3 : 4;
|
const char naluHeader[] = {0x00, 0x00, 0x00, 0x01};
|
||||||
|
|
||||||
read_nal_unit(h264_stream, sps->data+start_len, sps->length-start_len);
|
read_nal_unit(h264_stream, sps->data+4, sps->length-4);
|
||||||
|
|
||||||
// Some decoders rely on H264 level to decide how many buffers are needed
|
// Some decoders rely on H264 level to decide how many buffers are needed
|
||||||
// Since we only need one frame buffered, we'll set level as low as we can
|
// Since we only need one frame buffered, we'll set level as low as we can
|
||||||
@ -48,16 +48,14 @@ void gs_sps_fix(PLENTRY sps, int flags, uint8_t* out_buf, uint32_t* out_offset)
|
|||||||
|
|
||||||
// GFE 2.5.11 changed the SPS to add additional extensions
|
// GFE 2.5.11 changed the SPS to add additional extensions
|
||||||
// Some devices don't like these so we remove them here.
|
// Some devices don't like these so we remove them here.
|
||||||
if (flags & GS_SPS_REMOVE_VST_FIXUP)
|
h264_stream->sps->vui.video_signal_type_present_flag = 0;
|
||||||
h264_stream->sps->vui.video_signal_type_present_flag = 0;
|
h264_stream->sps->vui.chroma_loc_info_present_flag = 0;
|
||||||
if (flags & GS_SPS_REMOVE_CLI_FIXUP)
|
|
||||||
h264_stream->sps->vui.chroma_loc_info_present_flag = 0;
|
|
||||||
|
|
||||||
if ((flags & GS_SPS_BITSTREAM_FIXUP) == GS_SPS_BITSTREAM_FIXUP) {
|
if ((flags & GS_SPS_BITSTREAM_FIXUP) == GS_SPS_BITSTREAM_FIXUP) {
|
||||||
// The SPS that comes in the current H264 bytestream doesn't set the bitstream_restriction_flag
|
// The SPS that comes in the current H264 bytestream doesn't set the bitstream_restriction_flag
|
||||||
// or the max_dec_frame_buffering which increases decoding latency on some devices.
|
// or the max_dec_frame_buffering which increases decoding latency on some devices
|
||||||
// log2_max_mv_length_horizontal and log2_max_mv_length_vertical are set to more
|
// log2_max_mv_length_horizontal and log2_max_mv_length_vertical are set to more
|
||||||
// conservative values by GFE 2.5.11. We'll let those values stand.
|
// conservite values by GFE 25.11. We'll let those values stand.
|
||||||
if (!h264_stream->sps->vui.bitstream_restriction_flag) {
|
if (!h264_stream->sps->vui.bitstream_restriction_flag) {
|
||||||
h264_stream->sps->vui.bitstream_restriction_flag = 1;
|
h264_stream->sps->vui.bitstream_restriction_flag = 1;
|
||||||
h264_stream->sps->vui.motion_vectors_over_pic_boundaries_flag = 1;
|
h264_stream->sps->vui.motion_vectors_over_pic_boundaries_flag = 1;
|
||||||
@ -74,10 +72,11 @@ void gs_sps_fix(PLENTRY sps, int flags, uint8_t* out_buf, uint32_t* out_offset)
|
|||||||
// than what GFE sends in 2.5.11, but it doesn't seem to cause picture problems.
|
// than what GFE sends in 2.5.11, but it doesn't seem to cause picture problems.
|
||||||
h264_stream->sps->vui.max_bytes_per_pic_denom = 2;
|
h264_stream->sps->vui.max_bytes_per_pic_denom = 2;
|
||||||
h264_stream->sps->vui.max_bits_per_mb_denom = 1;
|
h264_stream->sps->vui.max_bits_per_mb_denom = 1;
|
||||||
}
|
} else // Devices that didn't/couldn't get bitstream restrictions before GFE 2.5.11 will continue to not receive them now
|
||||||
|
h264_stream->sps->vui.bitstream_restriction_flag = 0;
|
||||||
|
|
||||||
memcpy(out_buf+*out_offset, sps->data, start_len);
|
memcpy(out_buf+*out_offset, naluHeader, 4);
|
||||||
*out_offset += start_len;
|
*out_offset += 4;
|
||||||
|
|
||||||
*out_offset += write_nal_unit(h264_stream, out_buf+*out_offset, 128);
|
*out_offset += write_nal_unit(h264_stream, out_buf+*out_offset, 128);
|
||||||
}
|
}
|
||||||
|
@ -19,9 +19,7 @@
|
|||||||
|
|
||||||
#include <Limelight.h>
|
#include <Limelight.h>
|
||||||
|
|
||||||
#define GS_SPS_BITSTREAM_FIXUP 0x01
|
#define GS_SPS_BITSTREAM_FIXUP 0x01
|
||||||
#define GS_SPS_REMOVE_VST_FIXUP 0x02
|
|
||||||
#define GS_SPS_REMOVE_CLI_FIXUP 0x04
|
|
||||||
|
|
||||||
void gs_sps_init(int width, int height);
|
void gs_sps_init(int width, int height);
|
||||||
void gs_sps_fix(PLENTRY sps, int flags, uint8_t* out_buf, uint32_t* out_offset);
|
void gs_sps_fix(PLENTRY sps, int flags, uint8_t* out_buf, uint32_t* out_offset);
|
||||||
|
@ -25,6 +25,8 @@
|
|||||||
|
|
||||||
#define STATUS_OK 200
|
#define STATUS_OK 200
|
||||||
|
|
||||||
|
static XML_Parser parser;
|
||||||
|
|
||||||
struct xml_query {
|
struct xml_query {
|
||||||
char *memory;
|
char *memory;
|
||||||
size_t size;
|
size_t size;
|
||||||
@ -130,7 +132,7 @@ static void XMLCALL _xml_write_data(void *userData, const XML_Char *s, int len)
|
|||||||
search->memory = realloc(search->memory, search->size + len + 1);
|
search->memory = realloc(search->memory, search->size + len + 1);
|
||||||
if(search->memory == NULL)
|
if(search->memory == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
memcpy(&(search->memory[search->size]), s, len);
|
memcpy(&(search->memory[search->size]), s, len);
|
||||||
search->size += len;
|
search->size += len;
|
||||||
search->memory[search->size] = 0;
|
search->memory[search->size] = 0;
|
||||||
@ -160,7 +162,7 @@ int xml_search(char* data, size_t len, char* node, char** result) {
|
|||||||
|
|
||||||
XML_ParserFree(parser);
|
XML_ParserFree(parser);
|
||||||
*result = search.memory;
|
*result = search.memory;
|
||||||
|
|
||||||
return GS_OK;
|
return GS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,10 +7,6 @@
|
|||||||
#height = 720
|
#height = 720
|
||||||
#fps = 60
|
#fps = 60
|
||||||
|
|
||||||
## Output rotation (independent of xrandr or framebuffer settings!)
|
|
||||||
## Allowed values: 0, 90, 180, 270
|
|
||||||
#rotate = 0
|
|
||||||
|
|
||||||
## Bitrate depends by default on resolution and fps
|
## Bitrate depends by default on resolution and fps
|
||||||
## Set to -1 to enable default
|
## Set to -1 to enable default
|
||||||
## 20Mbps (20000) for 1080p (60 fps)
|
## 20Mbps (20000) for 1080p (60 fps)
|
||||||
@ -19,8 +15,7 @@
|
|||||||
#bitrate = -1
|
#bitrate = -1
|
||||||
|
|
||||||
## Size of network packets should be lower than MTU
|
## Size of network packets should be lower than MTU
|
||||||
## If streaming with WAN optimizations, this will be capped at 1024.
|
#packetsize = 1024
|
||||||
#packetsize = 1392
|
|
||||||
|
|
||||||
## Select video codec (auto/h264/h265)
|
## Select video codec (auto/h264/h265)
|
||||||
#codec = auto
|
#codec = auto
|
||||||
@ -48,9 +43,6 @@
|
|||||||
## Send quit app request to remote after quitting session
|
## Send quit app request to remote after quitting session
|
||||||
#quitappafter = false
|
#quitappafter = false
|
||||||
|
|
||||||
## Disable all input processing (view-only mode)
|
|
||||||
#viewonly = false
|
|
||||||
|
|
||||||
## Select audio device to play sound on
|
## Select audio device to play sound on
|
||||||
#audio = sysdefault
|
#audio = sysdefault
|
||||||
|
|
||||||
@ -69,14 +61,11 @@
|
|||||||
## By default keys are stored in $XDG_CACHE_DIR/moonlight or ~/.cache/moonlight
|
## By default keys are stored in $XDG_CACHE_DIR/moonlight or ~/.cache/moonlight
|
||||||
#keydir = /dir/to/keys
|
#keydir = /dir/to/keys
|
||||||
|
|
||||||
## Enable QOS settings to optimize for internet or local network
|
## Enable QOS settings to optimize for internet instead of local network
|
||||||
## yes - optimize for WAN streaming
|
#remote = false
|
||||||
## no - optimize for LAN streaming
|
|
||||||
## auto (default) - decide automatically based on target IP address
|
|
||||||
#remote = auto
|
|
||||||
|
|
||||||
## Enable 5.1/7.1 surround sound
|
## Enable 5.1 surround sound
|
||||||
#surround = 5.1
|
#surround = false
|
||||||
|
|
||||||
## Load additional configuration files
|
## Load additional configuration files
|
||||||
#config = /path/to/config
|
#config = /path/to/config
|
||||||
|
@ -20,8 +20,6 @@
|
|||||||
#include "audio.h"
|
#include "audio.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <opus_multistream.h>
|
#include <opus_multistream.h>
|
||||||
#include <alsa/asoundlib.h>
|
#include <alsa/asoundlib.h>
|
||||||
|
|
||||||
@ -29,36 +27,31 @@
|
|||||||
|
|
||||||
static snd_pcm_t *handle;
|
static snd_pcm_t *handle;
|
||||||
static OpusMSDecoder* decoder;
|
static OpusMSDecoder* decoder;
|
||||||
static short* pcmBuffer;
|
static short pcmBuffer[FRAME_SIZE * MAX_CHANNEL_COUNT];
|
||||||
static int samplesPerFrame;
|
|
||||||
|
|
||||||
static int alsa_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int arFlags) {
|
static int alsa_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int arFlags) {
|
||||||
int rc;
|
int rc;
|
||||||
unsigned char alsaMapping[AUDIO_CONFIGURATION_MAX_CHANNEL_COUNT];
|
unsigned char alsaMapping[MAX_CHANNEL_COUNT];
|
||||||
|
|
||||||
/* The supplied mapping array has order: FL-FR-C-LFE-RL-RR-SL-SR
|
/* The supplied mapping array has order: FL-FR-C-LFE-RL-RR
|
||||||
* ALSA expects the order: FL-FR-RL-RR-C-LFE-SL-SR
|
* ALSA expects the order: FL-FR-RL-RR-C-LFE
|
||||||
* We need copy the mapping locally and swap the channels around.
|
* We need copy the mapping locally and swap the channels around.
|
||||||
*/
|
*/
|
||||||
memcpy(alsaMapping, opusConfig->mapping, sizeof(alsaMapping));
|
alsaMapping[0] = opusConfig->mapping[0];
|
||||||
if (opusConfig->channelCount >= 6) {
|
alsaMapping[1] = opusConfig->mapping[1];
|
||||||
|
if (opusConfig->channelCount == 6) {
|
||||||
alsaMapping[2] = opusConfig->mapping[4];
|
alsaMapping[2] = opusConfig->mapping[4];
|
||||||
alsaMapping[3] = opusConfig->mapping[5];
|
alsaMapping[3] = opusConfig->mapping[5];
|
||||||
alsaMapping[4] = opusConfig->mapping[2];
|
alsaMapping[4] = opusConfig->mapping[2];
|
||||||
alsaMapping[5] = opusConfig->mapping[3];
|
alsaMapping[5] = opusConfig->mapping[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
samplesPerFrame = opusConfig->samplesPerFrame;
|
|
||||||
pcmBuffer = malloc(sizeof(short) * opusConfig->channelCount * samplesPerFrame);
|
|
||||||
if (pcmBuffer == NULL)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
decoder = opus_multistream_decoder_create(opusConfig->sampleRate, opusConfig->channelCount, opusConfig->streams, opusConfig->coupledStreams, alsaMapping, &rc);
|
decoder = opus_multistream_decoder_create(opusConfig->sampleRate, opusConfig->channelCount, opusConfig->streams, opusConfig->coupledStreams, alsaMapping, &rc);
|
||||||
|
|
||||||
snd_pcm_hw_params_t *hw_params;
|
snd_pcm_hw_params_t *hw_params;
|
||||||
snd_pcm_sw_params_t *sw_params;
|
snd_pcm_sw_params_t *sw_params;
|
||||||
snd_pcm_uframes_t period_size = (opusConfig->sampleRate * 20) / 1000; // 20 ms period
|
snd_pcm_uframes_t period_size = FRAME_SIZE * FRAME_BUFFER;
|
||||||
snd_pcm_uframes_t buffer_size = 3 * period_size; // 60 ms buffer
|
snd_pcm_uframes_t buffer_size = 2 * period_size;
|
||||||
unsigned int sampleRate = opusConfig->sampleRate;
|
unsigned int sampleRate = opusConfig->sampleRate;
|
||||||
|
|
||||||
char* audio_device = (char*) context;
|
char* audio_device = (char*) context;
|
||||||
@ -84,7 +77,7 @@ static int alsa_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGUR
|
|||||||
CHECK_RETURN(snd_pcm_sw_params_malloc(&sw_params));
|
CHECK_RETURN(snd_pcm_sw_params_malloc(&sw_params));
|
||||||
CHECK_RETURN(snd_pcm_sw_params_current(handle, sw_params));
|
CHECK_RETURN(snd_pcm_sw_params_current(handle, sw_params));
|
||||||
CHECK_RETURN(snd_pcm_sw_params_set_avail_min(handle, sw_params, period_size));
|
CHECK_RETURN(snd_pcm_sw_params_set_avail_min(handle, sw_params, period_size));
|
||||||
CHECK_RETURN(snd_pcm_sw_params_set_start_threshold(handle, sw_params, period_size));
|
CHECK_RETURN(snd_pcm_sw_params_set_start_threshold(handle, sw_params, 1));
|
||||||
CHECK_RETURN(snd_pcm_sw_params(handle, sw_params));
|
CHECK_RETURN(snd_pcm_sw_params(handle, sw_params));
|
||||||
snd_pcm_sw_params_free(sw_params);
|
snd_pcm_sw_params_free(sw_params);
|
||||||
|
|
||||||
@ -94,38 +87,27 @@ static int alsa_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGUR
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void alsa_renderer_cleanup() {
|
static void alsa_renderer_cleanup() {
|
||||||
if (decoder != NULL) {
|
if (decoder != NULL)
|
||||||
opus_multistream_decoder_destroy(decoder);
|
opus_multistream_decoder_destroy(decoder);
|
||||||
decoder = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handle != NULL) {
|
if (handle != NULL) {
|
||||||
snd_pcm_drain(handle);
|
snd_pcm_drain(handle);
|
||||||
snd_pcm_close(handle);
|
snd_pcm_close(handle);
|
||||||
handle = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pcmBuffer != NULL) {
|
|
||||||
free(pcmBuffer);
|
|
||||||
pcmBuffer = NULL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void alsa_renderer_decode_and_play_sample(char* data, int length) {
|
static void alsa_renderer_decode_and_play_sample(char* data, int length) {
|
||||||
int decodeLen = opus_multistream_decode(decoder, data, length, pcmBuffer, samplesPerFrame, 0);
|
int decodeLen = opus_multistream_decode(decoder, data, length, pcmBuffer, FRAME_SIZE, 0);
|
||||||
if (decodeLen > 0) {
|
if (decodeLen > 0) {
|
||||||
int rc = snd_pcm_writei(handle, pcmBuffer, decodeLen);
|
int rc = snd_pcm_writei(handle, pcmBuffer, decodeLen);
|
||||||
if (rc < 0) {
|
if (rc == -EPIPE)
|
||||||
rc = snd_pcm_recover(handle, rc, 0);
|
snd_pcm_recover(handle, rc, 1);
|
||||||
if (rc == 0)
|
|
||||||
rc = snd_pcm_writei(handle, pcmBuffer, decodeLen);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rc<0)
|
if (rc<0)
|
||||||
printf("Alsa error from writei: %d\n", rc);
|
printf("Alsa error from writei: %d\n", rc);
|
||||||
else if (decodeLen != rc)
|
else if (decodeLen != rc)
|
||||||
printf("Alsa shortm write, write %d frames\n", rc);
|
printf("Alsa shortm write, write %d frames\n", rc);
|
||||||
} else if (decodeLen < 0) {
|
} else {
|
||||||
printf("Opus error from decode: %d\n", decodeLen);
|
printf("Opus error from decode: %d\n", decodeLen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,5 +116,5 @@ AUDIO_RENDERER_CALLBACKS audio_callbacks_alsa = {
|
|||||||
.init = alsa_renderer_init,
|
.init = alsa_renderer_init,
|
||||||
.cleanup = alsa_renderer_cleanup,
|
.cleanup = alsa_renderer_cleanup,
|
||||||
.decodeAndPlaySample = alsa_renderer_decode_and_play_sample,
|
.decodeAndPlaySample = alsa_renderer_decode_and_play_sample,
|
||||||
.capabilities = CAPABILITY_DIRECT_SUBMIT | CAPABILITY_SUPPORTS_ARBITRARY_AUDIO_DURATION,
|
.capabilities = CAPABILITY_DIRECT_SUBMIT,
|
||||||
};
|
};
|
||||||
|
@ -21,6 +21,10 @@
|
|||||||
|
|
||||||
#include <Limelight.h>
|
#include <Limelight.h>
|
||||||
|
|
||||||
|
#define MAX_CHANNEL_COUNT 6
|
||||||
|
#define FRAME_SIZE 240
|
||||||
|
#define FRAME_BUFFER 12
|
||||||
|
|
||||||
#ifdef HAVE_ALSA
|
#ifdef HAVE_ALSA
|
||||||
extern AUDIO_RENDERER_CALLBACKS audio_callbacks_alsa;
|
extern AUDIO_RENDERER_CALLBACKS audio_callbacks_alsa;
|
||||||
#endif
|
#endif
|
||||||
@ -31,6 +35,3 @@ extern AUDIO_RENDERER_CALLBACKS audio_callbacks_sdl;
|
|||||||
extern AUDIO_RENDERER_CALLBACKS audio_callbacks_pulse;
|
extern AUDIO_RENDERER_CALLBACKS audio_callbacks_pulse;
|
||||||
bool audio_pulse_init(char* audio_device);
|
bool audio_pulse_init(char* audio_device);
|
||||||
#endif
|
#endif
|
||||||
#ifdef __FreeBSD__
|
|
||||||
extern AUDIO_RENDERER_CALLBACKS audio_callbacks_oss;
|
|
||||||
#endif
|
|
||||||
|
@ -29,24 +29,18 @@ static OpusMSDecoder* decoder;
|
|||||||
ILCLIENT_T* handle;
|
ILCLIENT_T* handle;
|
||||||
COMPONENT_T* component;
|
COMPONENT_T* component;
|
||||||
static OMX_BUFFERHEADERTYPE *buf;
|
static OMX_BUFFERHEADERTYPE *buf;
|
||||||
static short* pcmBuffer;
|
static short pcmBuffer[FRAME_SIZE * MAX_CHANNEL_COUNT];
|
||||||
static int channelCount;
|
static int channelCount;
|
||||||
static int samplesPerFrame;
|
|
||||||
|
|
||||||
static int omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int arFlags) {
|
static int omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int arFlags) {
|
||||||
int rc;
|
int rc, error;
|
||||||
OMX_ERRORTYPE err;
|
OMX_ERRORTYPE err;
|
||||||
unsigned char omxMapping[AUDIO_CONFIGURATION_MAX_CHANNEL_COUNT];
|
unsigned char omxMapping[MAX_CHANNEL_COUNT];
|
||||||
char* componentName = "audio_render";
|
char* componentName = "audio_render";
|
||||||
|
|
||||||
channelCount = opusConfig->channelCount;
|
channelCount = opusConfig->channelCount;
|
||||||
samplesPerFrame = opusConfig->samplesPerFrame;
|
/* The supplied mapping array has order: FL-FR-C-LFE-RL-RR
|
||||||
pcmBuffer = malloc(sizeof(short) * channelCount * samplesPerFrame);
|
* OMX expects the order: FL-FR-LFE-C-RL-RR
|
||||||
if (pcmBuffer == NULL)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* The supplied mapping array has order: FL-FR-C-LFE-RL-RR-SL-SR
|
|
||||||
* OMX expects the order: FL-FR-LFE-C-RL-RR-SL-SR
|
|
||||||
* We need copy the mapping locally and swap the channels around.
|
* We need copy the mapping locally and swap the channels around.
|
||||||
*/
|
*/
|
||||||
memcpy(omxMapping, opusConfig->mapping, sizeof(omxMapping));
|
memcpy(omxMapping, opusConfig->mapping, sizeof(omxMapping));
|
||||||
@ -161,10 +155,8 @@ static int omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURA
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void omx_renderer_cleanup() {
|
static void omx_renderer_cleanup() {
|
||||||
if (decoder != NULL) {
|
if (decoder != NULL)
|
||||||
opus_multistream_decoder_destroy(decoder);
|
opus_multistream_decoder_destroy(decoder);
|
||||||
decoder = NULL;
|
|
||||||
}
|
|
||||||
if (handle != NULL) {
|
if (handle != NULL) {
|
||||||
if((buf = ilclient_get_input_buffer(component, 100, 1)) == NULL){
|
if((buf = ilclient_get_input_buffer(component, 100, 1)) == NULL){
|
||||||
fprintf(stderr, "Can't get audio buffer\n");
|
fprintf(stderr, "Can't get audio buffer\n");
|
||||||
@ -182,28 +174,23 @@ static void omx_renderer_cleanup() {
|
|||||||
ilclient_disable_port_buffers(component, 100, NULL, NULL, NULL);
|
ilclient_disable_port_buffers(component, 100, NULL, NULL, NULL);
|
||||||
ilclient_change_component_state(component, OMX_StateIdle);
|
ilclient_change_component_state(component, OMX_StateIdle);
|
||||||
ilclient_change_component_state(component, OMX_StateLoaded);
|
ilclient_change_component_state(component, OMX_StateLoaded);
|
||||||
handle = NULL;
|
|
||||||
}
|
|
||||||
if (pcmBuffer != NULL) {
|
|
||||||
free(pcmBuffer);
|
|
||||||
pcmBuffer = NULL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void omx_renderer_decode_and_play_sample(char* data, int length) {
|
static void omx_renderer_decode_and_play_sample(char* data, int length) {
|
||||||
int decodeLen = opus_multistream_decode(decoder, data, length, pcmBuffer, samplesPerFrame, 0);
|
int decodeLen = opus_multistream_decode(decoder, data, length, pcmBuffer, FRAME_SIZE, 0);
|
||||||
if (decodeLen > 0) {
|
if (decodeLen > 0) {
|
||||||
buf = ilclient_get_input_buffer(component, 100, 1);
|
buf = ilclient_get_input_buffer(component, 100, 1);
|
||||||
buf->nOffset = 0;
|
buf->nOffset = 0;
|
||||||
buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN;
|
buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN;
|
||||||
int bufLength = decodeLen * sizeof(short) * channelCount;
|
int bufLength = decodeLen * sizeof(short) * channelCount;
|
||||||
memcpy(buf->pBuffer, pcmBuffer, bufLength);
|
memcpy(buf->pBuffer, pcmBuffer, bufLength);
|
||||||
buf->nFilledLen = bufLength;
|
buf->nFilledLen = bufLength;
|
||||||
int r = OMX_EmptyThisBuffer(ilclient_get_handle(component), buf);
|
int r = OMX_EmptyThisBuffer(ilclient_get_handle(component), buf);
|
||||||
if (r != OMX_ErrorNone) {
|
if (r != OMX_ErrorNone) {
|
||||||
fprintf(stderr, "Empty buffer error\n");
|
fprintf(stderr, "Empty buffer error\n");
|
||||||
}
|
}
|
||||||
} else if (decodeLen < 0) {
|
} else {
|
||||||
printf("Opus error from decode: %d\n", decodeLen);
|
printf("Opus error from decode: %d\n", decodeLen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -212,5 +199,5 @@ AUDIO_RENDERER_CALLBACKS audio_callbacks_omx = {
|
|||||||
.init = omx_renderer_init,
|
.init = omx_renderer_init,
|
||||||
.cleanup = omx_renderer_cleanup,
|
.cleanup = omx_renderer_cleanup,
|
||||||
.decodeAndPlaySample = omx_renderer_decode_and_play_sample,
|
.decodeAndPlaySample = omx_renderer_decode_and_play_sample,
|
||||||
.capabilities = CAPABILITY_DIRECT_SUBMIT | CAPABILITY_SUPPORTS_ARBITRARY_AUDIO_DURATION,
|
.capabilities = CAPABILITY_DIRECT_SUBMIT,
|
||||||
};
|
};
|
||||||
|
105
src/audio/oss.c
105
src/audio/oss.c
@ -1,105 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Moonlight Embedded.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2015-2017 Iwan Timmer
|
|
||||||
*
|
|
||||||
* Moonlight is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Moonlight is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifdef __FreeBSD__
|
|
||||||
#include <sys/soundcard.h>
|
|
||||||
#include <sys/ioctl.h>
|
|
||||||
#include "audio.h"
|
|
||||||
|
|
||||||
#include <opus_multistream.h>
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
static OpusMSDecoder* decoder;
|
|
||||||
static short* pcmBuffer;
|
|
||||||
static int samplesPerFrame;
|
|
||||||
static int channelCount;
|
|
||||||
static int fd = -1;
|
|
||||||
|
|
||||||
static int oss_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int arFlags) {
|
|
||||||
int rc;
|
|
||||||
decoder = opus_multistream_decoder_create(opusConfig->sampleRate, opusConfig->channelCount, opusConfig->streams, opusConfig->coupledStreams, opusConfig->mapping, &rc);
|
|
||||||
|
|
||||||
channelCount = opusConfig->channelCount;
|
|
||||||
samplesPerFrame = opusConfig->samplesPerFrame;
|
|
||||||
pcmBuffer = malloc(sizeof(short) * channelCount * samplesPerFrame);
|
|
||||||
if (pcmBuffer == NULL)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
const char* oss_name = "/dev/dsp";
|
|
||||||
fd = open(oss_name, O_WRONLY);
|
|
||||||
if (fd == -1) {
|
|
||||||
printf("Open audio device /dev/dsp failed! error %d\n", errno);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
// buffer size for fragment ,selector 12 is 4096;11 is 2048;10 is 1024; 13is 8192
|
|
||||||
int frag = 12;
|
|
||||||
if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frag) == -1)
|
|
||||||
printf("Set fragment for /dev/dsp failed.");
|
|
||||||
|
|
||||||
int format = AFMT_S16_LE;
|
|
||||||
int channels = opusConfig->channelCount;
|
|
||||||
int rate = opusConfig->sampleRate;
|
|
||||||
if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) == -1)
|
|
||||||
printf("Set format for /dev/dsp failed.");
|
|
||||||
if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1)
|
|
||||||
printf("Set channels for /dev/dsp failed.");
|
|
||||||
if (ioctl(fd, SNDCTL_DSP_SPEED, &rate) == -1)
|
|
||||||
printf("Set sample rate for /dev/dsp failed.");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void oss_renderer_cleanup() {
|
|
||||||
if (decoder != NULL) {
|
|
||||||
opus_multistream_decoder_destroy(decoder);
|
|
||||||
decoder = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pcmBuffer != NULL) {
|
|
||||||
free(pcmBuffer);
|
|
||||||
pcmBuffer = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fd != -1) {
|
|
||||||
close(fd);
|
|
||||||
fd = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void oss_renderer_decode_and_play_sample(char* data, int length) {
|
|
||||||
int decodeLen = opus_multistream_decode(decoder, data, length, pcmBuffer, samplesPerFrame, 0);
|
|
||||||
if (decodeLen > 0) {
|
|
||||||
write(fd, pcmBuffer, decodeLen * channelCount * sizeof(short));
|
|
||||||
} else if (decodeLen < 0) {
|
|
||||||
printf("Opus error from decode: %d\n", decodeLen);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AUDIO_RENDERER_CALLBACKS audio_callbacks_oss = {
|
|
||||||
.init = oss_renderer_init,
|
|
||||||
.cleanup = oss_renderer_cleanup,
|
|
||||||
.decodeAndPlaySample = oss_renderer_decode_and_play_sample,
|
|
||||||
.capabilities = CAPABILITY_DIRECT_SUBMIT | CAPABILITY_SUPPORTS_ARBITRARY_AUDIO_DURATION,
|
|
||||||
};
|
|
||||||
#endif
|
|
@ -21,7 +21,6 @@
|
|||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <opus_multistream.h>
|
#include <opus_multistream.h>
|
||||||
#include <pulse/simple.h>
|
#include <pulse/simple.h>
|
||||||
@ -29,19 +28,18 @@
|
|||||||
|
|
||||||
static OpusMSDecoder* decoder;
|
static OpusMSDecoder* decoder;
|
||||||
static pa_simple *dev = NULL;
|
static pa_simple *dev = NULL;
|
||||||
static short* pcmBuffer;
|
static short pcmBuffer[FRAME_SIZE * MAX_CHANNEL_COUNT];
|
||||||
static int samplesPerFrame;
|
|
||||||
static int channelCount;
|
static int channelCount;
|
||||||
|
|
||||||
bool audio_pulse_init(char* audio_device) {
|
bool audio_pulse_init(char* audio_device) {
|
||||||
pa_sample_spec spec = {
|
pa_sample_spec spec = {
|
||||||
.format = PA_SAMPLE_S16LE,
|
.format = PA_SAMPLE_S16LE,
|
||||||
.rate = 48000,
|
.rate = 44000,
|
||||||
.channels = 2
|
.channels = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
int error;
|
int error;
|
||||||
dev = pa_simple_new(NULL, "Moonlight Embedded", PA_STREAM_PLAYBACK, audio_device, "Streaming", &spec, NULL, NULL, &error);
|
dev = pa_simple_new(audio_device, "Moonlight Embedded", PA_STREAM_PLAYBACK, NULL, "Streaming", &spec, NULL, NULL, &error);
|
||||||
|
|
||||||
if (dev)
|
if (dev)
|
||||||
pa_simple_free(dev);
|
pa_simple_free(dev);
|
||||||
@ -51,20 +49,17 @@ bool audio_pulse_init(char* audio_device) {
|
|||||||
|
|
||||||
static int pulse_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int arFlags) {
|
static int pulse_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int arFlags) {
|
||||||
int rc, error;
|
int rc, error;
|
||||||
unsigned char alsaMapping[AUDIO_CONFIGURATION_MAX_CHANNEL_COUNT];
|
unsigned char alsaMapping[MAX_CHANNEL_COUNT];
|
||||||
|
|
||||||
channelCount = opusConfig->channelCount;
|
channelCount = opusConfig->channelCount;
|
||||||
samplesPerFrame = opusConfig->samplesPerFrame;
|
|
||||||
pcmBuffer = malloc(sizeof(short) * channelCount * samplesPerFrame);
|
|
||||||
if (pcmBuffer == NULL)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
/* The supplied mapping array has order: FL-FR-C-LFE-RL-RR-SL-SR
|
/* The supplied mapping array has order: FL-FR-C-LFE-RL-RR
|
||||||
* ALSA expects the order: FL-FR-RL-RR-C-LFE-SL-SR
|
* ALSA expects the order: FL-FR-RL-RR-C-LFE
|
||||||
* We need copy the mapping locally and swap the channels around.
|
* We need copy the mapping locally and swap the channels around.
|
||||||
*/
|
*/
|
||||||
memcpy(alsaMapping, opusConfig->mapping, sizeof(alsaMapping));
|
alsaMapping[0] = opusConfig->mapping[0];
|
||||||
if (opusConfig->channelCount >= 6) {
|
alsaMapping[1] = opusConfig->mapping[1];
|
||||||
|
if (opusConfig->channelCount == 6) {
|
||||||
alsaMapping[2] = opusConfig->mapping[4];
|
alsaMapping[2] = opusConfig->mapping[4];
|
||||||
alsaMapping[3] = opusConfig->mapping[5];
|
alsaMapping[3] = opusConfig->mapping[5];
|
||||||
alsaMapping[4] = opusConfig->mapping[2];
|
alsaMapping[4] = opusConfig->mapping[2];
|
||||||
@ -79,11 +74,8 @@ static int pulse_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGU
|
|||||||
.channels = opusConfig->channelCount
|
.channels = opusConfig->channelCount
|
||||||
};
|
};
|
||||||
|
|
||||||
pa_channel_map map;
|
|
||||||
pa_channel_map_init_auto(&map, opusConfig->channelCount, PA_CHANNEL_MAP_ALSA);
|
|
||||||
|
|
||||||
char* audio_device = (char*) context;
|
char* audio_device = (char*) context;
|
||||||
dev = pa_simple_new(NULL, "Moonlight Embedded", PA_STREAM_PLAYBACK, audio_device, "Streaming", &spec, &map, NULL, &error);
|
dev = pa_simple_new(audio_device, "Moonlight Embedded", PA_STREAM_PLAYBACK, NULL, "Streaming", &spec, NULL, NULL, &error);
|
||||||
|
|
||||||
if (!dev) {
|
if (!dev) {
|
||||||
printf("Pulseaudio error: %s\n", pa_strerror(error));
|
printf("Pulseaudio error: %s\n", pa_strerror(error));
|
||||||
@ -94,36 +86,25 @@ static int pulse_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGU
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void pulse_renderer_decode_and_play_sample(char* data, int length) {
|
static void pulse_renderer_decode_and_play_sample(char* data, int length) {
|
||||||
int decodeLen = opus_multistream_decode(decoder, data, length, pcmBuffer, samplesPerFrame, 0);
|
int decodeLen = opus_multistream_decode(decoder, data, length, pcmBuffer, FRAME_SIZE, 0);
|
||||||
if (decodeLen > 0) {
|
if (decodeLen > 0) {
|
||||||
int error;
|
int error;
|
||||||
int rc = pa_simple_write(dev, pcmBuffer, decodeLen * sizeof(short) * channelCount, &error);
|
int rc = pa_simple_write(dev, pcmBuffer, decodeLen * sizeof(short) * channelCount, &error);
|
||||||
|
|
||||||
if (rc<0)
|
if (rc<0)
|
||||||
printf("Pulseaudio error: %s\n", pa_strerror(error));
|
printf("Pulseaudio error: %s\n", pa_strerror(error));
|
||||||
} else if (decodeLen < 0) {
|
} else {
|
||||||
printf("Opus error from decode: %d\n", decodeLen);
|
printf("Opus error from decode: %d\n", decodeLen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pulse_renderer_cleanup() {
|
static void pulse_renderer_cleanup() {
|
||||||
if (decoder != NULL) {
|
pa_simple_free(dev);
|
||||||
opus_multistream_decoder_destroy(decoder);
|
|
||||||
decoder = NULL;
|
|
||||||
}
|
|
||||||
if (dev != NULL) {
|
|
||||||
pa_simple_free(dev);
|
|
||||||
dev = NULL;
|
|
||||||
}
|
|
||||||
if (pcmBuffer != NULL) {
|
|
||||||
free(pcmBuffer);
|
|
||||||
pcmBuffer = NULL;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AUDIO_RENDERER_CALLBACKS audio_callbacks_pulse = {
|
AUDIO_RENDERER_CALLBACKS audio_callbacks_pulse = {
|
||||||
.init = pulse_renderer_init,
|
.init = pulse_renderer_init,
|
||||||
.cleanup = pulse_renderer_cleanup,
|
.cleanup = pulse_renderer_cleanup,
|
||||||
.decodeAndPlaySample = pulse_renderer_decode_and_play_sample,
|
.decodeAndPlaySample = pulse_renderer_decode_and_play_sample,
|
||||||
.capabilities = CAPABILITY_DIRECT_SUBMIT | CAPABILITY_SUPPORTS_ARBITRARY_AUDIO_DURATION,
|
.capabilities = CAPABILITY_DIRECT_SUBMIT,
|
||||||
};
|
};
|
||||||
|
@ -26,8 +26,7 @@
|
|||||||
#include <opus_multistream.h>
|
#include <opus_multistream.h>
|
||||||
|
|
||||||
static OpusMSDecoder* decoder;
|
static OpusMSDecoder* decoder;
|
||||||
static short* pcmBuffer;
|
static short pcmBuffer[FRAME_SIZE * MAX_CHANNEL_COUNT];
|
||||||
static int samplesPerFrame;
|
|
||||||
static SDL_AudioDeviceID dev;
|
static SDL_AudioDeviceID dev;
|
||||||
static int channelCount;
|
static int channelCount;
|
||||||
|
|
||||||
@ -36,10 +35,6 @@ static int sdl_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURA
|
|||||||
decoder = opus_multistream_decoder_create(opusConfig->sampleRate, opusConfig->channelCount, opusConfig->streams, opusConfig->coupledStreams, opusConfig->mapping, &rc);
|
decoder = opus_multistream_decoder_create(opusConfig->sampleRate, opusConfig->channelCount, opusConfig->streams, opusConfig->coupledStreams, opusConfig->mapping, &rc);
|
||||||
|
|
||||||
channelCount = opusConfig->channelCount;
|
channelCount = opusConfig->channelCount;
|
||||||
samplesPerFrame = opusConfig->samplesPerFrame;
|
|
||||||
pcmBuffer = malloc(sizeof(short) * channelCount * samplesPerFrame);
|
|
||||||
if (pcmBuffer == NULL)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
SDL_InitSubSystem(SDL_INIT_AUDIO);
|
SDL_InitSubSystem(SDL_INIT_AUDIO);
|
||||||
|
|
||||||
@ -50,11 +45,13 @@ static int sdl_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURA
|
|||||||
want.channels = opusConfig->channelCount;
|
want.channels = opusConfig->channelCount;
|
||||||
want.samples = 4096;
|
want.samples = 4096;
|
||||||
|
|
||||||
dev = SDL_OpenAudioDevice(NULL, 0, &want, &have, 0);
|
dev = SDL_OpenAudioDevice(NULL, 0, &want, &have, SDL_AUDIO_ALLOW_FORMAT_CHANGE);
|
||||||
if (dev == 0) {
|
if (dev == 0) {
|
||||||
printf("Failed to open audio: %s\n", SDL_GetError());
|
printf("Failed to open audio: %s\n", SDL_GetError());
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
|
if (have.format != want.format) // we let this one thing change.
|
||||||
|
printf("We didn't get requested audio format.\n");
|
||||||
SDL_PauseAudioDevice(dev, 0); // start audio playing.
|
SDL_PauseAudioDevice(dev, 0); // start audio playing.
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,27 +59,17 @@ static int sdl_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURA
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void sdl_renderer_cleanup() {
|
static void sdl_renderer_cleanup() {
|
||||||
if (decoder != NULL) {
|
if (decoder != NULL)
|
||||||
opus_multistream_decoder_destroy(decoder);
|
opus_multistream_decoder_destroy(decoder);
|
||||||
decoder = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pcmBuffer != NULL) {
|
SDL_CloseAudioDevice(dev);
|
||||||
free(pcmBuffer);
|
|
||||||
pcmBuffer = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dev != 0) {
|
|
||||||
SDL_CloseAudioDevice(dev);
|
|
||||||
dev = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sdl_renderer_decode_and_play_sample(char* data, int length) {
|
static void sdl_renderer_decode_and_play_sample(char* data, int length) {
|
||||||
int decodeLen = opus_multistream_decode(decoder, data, length, pcmBuffer, samplesPerFrame, 0);
|
int decodeLen = opus_multistream_decode(decoder, data, length, pcmBuffer, FRAME_SIZE, 0);
|
||||||
if (decodeLen > 0) {
|
if (decodeLen > 0) {
|
||||||
SDL_QueueAudio(dev, pcmBuffer, decodeLen * channelCount * sizeof(short));
|
SDL_QueueAudio(dev, pcmBuffer, decodeLen * channelCount * sizeof(short));
|
||||||
} else if (decodeLen < 0) {
|
} else {
|
||||||
printf("Opus error from decode: %d\n", decodeLen);
|
printf("Opus error from decode: %d\n", decodeLen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,5 +78,5 @@ AUDIO_RENDERER_CALLBACKS audio_callbacks_sdl = {
|
|||||||
.init = sdl_renderer_init,
|
.init = sdl_renderer_init,
|
||||||
.cleanup = sdl_renderer_cleanup,
|
.cleanup = sdl_renderer_cleanup,
|
||||||
.decodeAndPlaySample = sdl_renderer_decode_and_play_sample,
|
.decodeAndPlaySample = sdl_renderer_decode_and_play_sample,
|
||||||
.capabilities = CAPABILITY_DIRECT_SUBMIT | CAPABILITY_SUPPORTS_ARBITRARY_AUDIO_DURATION,
|
.capabilities = CAPABILITY_DIRECT_SUBMIT,
|
||||||
};
|
};
|
||||||
|
133
src/config.c
133
src/config.c
@ -17,10 +17,7 @@
|
|||||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "platform.h"
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include "util.h"
|
|
||||||
#include "cpu.h"
|
|
||||||
|
|
||||||
#include "input/evdev.h"
|
#include "input/evdev.h"
|
||||||
#include "audio/audio.h"
|
#include "audio/audio.h"
|
||||||
@ -40,7 +37,7 @@
|
|||||||
|
|
||||||
#define write_config_string(fd, key, value) fprintf(fd, "%s = %s\n", key, value)
|
#define write_config_string(fd, key, value) fprintf(fd, "%s = %s\n", key, value)
|
||||||
#define write_config_int(fd, key, value) fprintf(fd, "%s = %d\n", key, value)
|
#define write_config_int(fd, key, value) fprintf(fd, "%s = %d\n", key, value)
|
||||||
#define write_config_bool(fd, key, value) fprintf(fd, "%s = %s\n", key, value ? "true":"false")
|
#define write_config_bool(fd, key, value) fprintf(fd, "%s = %s\n", key, value?"true":"false");
|
||||||
|
|
||||||
bool inputAdded = false;
|
bool inputAdded = false;
|
||||||
|
|
||||||
@ -59,24 +56,18 @@ static struct option long_options[] = {
|
|||||||
{"audio", required_argument, NULL, 'm'},
|
{"audio", required_argument, NULL, 'm'},
|
||||||
{"localaudio", no_argument, NULL, 'n'},
|
{"localaudio", no_argument, NULL, 'n'},
|
||||||
{"config", required_argument, NULL, 'o'},
|
{"config", required_argument, NULL, 'o'},
|
||||||
{"platform", required_argument, NULL, 'p'},
|
{"platform", required_argument, 0, 'p'},
|
||||||
{"save", required_argument, NULL, 'q'},
|
{"save", required_argument, NULL, 'q'},
|
||||||
{"keydir", required_argument, NULL, 'r'},
|
{"keydir", required_argument, NULL, 'r'},
|
||||||
{"remote", required_argument, NULL, 's'},
|
{"remote", no_argument, NULL, 's'},
|
||||||
{"windowed", no_argument, NULL, 't'},
|
{"windowed", no_argument, NULL, 't'},
|
||||||
{"surround", required_argument, NULL, 'u'},
|
{"surround", no_argument, NULL, 'u'},
|
||||||
{"fps", required_argument, NULL, 'v'},
|
{"fps", required_argument, NULL, 'v'},
|
||||||
{"codec", required_argument, NULL, 'x'},
|
{"codec", required_argument, NULL, 'x'},
|
||||||
{"nounsupported", no_argument, NULL, 'y'},
|
{"unsupported", no_argument, NULL, 'y'},
|
||||||
{"quitappafter", no_argument, NULL, '1'},
|
{"quitappafter", no_argument, NULL, '1'},
|
||||||
{"viewonly", no_argument, NULL, '2'},
|
|
||||||
{"rotate", required_argument, NULL, '3'},
|
|
||||||
{"verbose", no_argument, NULL, 'z'},
|
{"verbose", no_argument, NULL, 'z'},
|
||||||
{"debug", no_argument, NULL, 'Z'},
|
{"debug", no_argument, NULL, 'Z'},
|
||||||
{"nomouseemulation", no_argument, NULL, '4'},
|
|
||||||
{"pin", required_argument, NULL, '5'},
|
|
||||||
{"port", required_argument, NULL, '6'},
|
|
||||||
{"hdr", no_argument, NULL, '7'},
|
|
||||||
{0, 0, 0, 0},
|
{0, 0, 0, 0},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -111,7 +102,7 @@ char* get_path(char* name, char* extra_data_dirs) {
|
|||||||
char* end;
|
char* end;
|
||||||
do {
|
do {
|
||||||
end = strstr(data_dir, ":");
|
end = strstr(data_dir, ":");
|
||||||
int length = end != NULL ? end - data_dir:strlen(data_dir);
|
int length = end != NULL?end - data_dir:strlen(data_dir);
|
||||||
memcpy(path, data_dir, length);
|
memcpy(path, data_dir, length);
|
||||||
if (path[0] == '/')
|
if (path[0] == '/')
|
||||||
sprintf(path+length, MOONLIGHT_PATH "/%s", name);
|
sprintf(path+length, MOONLIGHT_PATH "/%s", name);
|
||||||
@ -200,22 +191,13 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) {
|
|||||||
strcpy(config->key_dir, value);
|
strcpy(config->key_dir, value);
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
if (strcasecmp(value, "auto") == 0)
|
config->stream.streamingRemotely = 1;
|
||||||
config->stream.streamingRemotely = STREAM_CFG_AUTO;
|
|
||||||
else if (strcasecmp(value, "true") == 0 || strcasecmp(value, "yes") == 0)
|
|
||||||
config->stream.streamingRemotely = STREAM_CFG_REMOTE;
|
|
||||||
else if (strcasecmp(value, "false") == 0 || strcasecmp(value, "no") == 0)
|
|
||||||
config->stream.streamingRemotely = STREAM_CFG_LOCAL;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 't':
|
case 't':
|
||||||
config->fullscreen = false;
|
config->fullscreen = false;
|
||||||
break;
|
break;
|
||||||
case 'u':
|
case 'u':
|
||||||
if (strcasecmp(value, "5.1") == 0)
|
config->stream.audioConfiguration = AUDIO_CONFIGURATION_51_SURROUND;
|
||||||
config->stream.audioConfiguration = AUDIO_CONFIGURATION_51_SURROUND;
|
|
||||||
else if (strcasecmp(value, "7.1") == 0)
|
|
||||||
config->stream.audioConfiguration = AUDIO_CONFIGURATION_71_SURROUND;
|
|
||||||
break;
|
break;
|
||||||
case 'v':
|
case 'v':
|
||||||
config->stream.fps = atoi(value);
|
config->stream.fps = atoi(value);
|
||||||
@ -225,41 +207,21 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) {
|
|||||||
config->codec = CODEC_UNSPECIFIED;
|
config->codec = CODEC_UNSPECIFIED;
|
||||||
else if (strcasecmp(value, "h264") == 0)
|
else if (strcasecmp(value, "h264") == 0)
|
||||||
config->codec = CODEC_H264;
|
config->codec = CODEC_H264;
|
||||||
else if (strcasecmp(value, "h265") == 0 || strcasecmp(value, "hevc") == 0)
|
if (strcasecmp(value, "h265") == 0 || strcasecmp(value, "hevc") == 0)
|
||||||
config->codec = CODEC_HEVC;
|
config->codec = CODEC_HEVC;
|
||||||
else if (strcasecmp(value, "av1") == 0)
|
|
||||||
config->codec = CODEC_AV1;
|
|
||||||
break;
|
break;
|
||||||
case 'y':
|
case 'y':
|
||||||
config->unsupported = false;
|
config->unsupported = true;
|
||||||
break;
|
break;
|
||||||
case '1':
|
case '1':
|
||||||
config->quitappafter = true;
|
config->quitappafter = true;
|
||||||
break;
|
break;
|
||||||
case '2':
|
|
||||||
config->viewonly = true;
|
|
||||||
break;
|
|
||||||
case '3':
|
|
||||||
config->rotate = atoi(value);
|
|
||||||
break;
|
|
||||||
case 'z':
|
case 'z':
|
||||||
config->debug_level = 1;
|
config->debug_level = 1;
|
||||||
break;
|
break;
|
||||||
case 'Z':
|
case 'Z':
|
||||||
config->debug_level = 2;
|
config->debug_level = 2;
|
||||||
break;
|
break;
|
||||||
case '4':
|
|
||||||
config->mouse_emulation = false;
|
|
||||||
break;
|
|
||||||
case '5':
|
|
||||||
config->pin = atoi(value);
|
|
||||||
break;
|
|
||||||
case '6':
|
|
||||||
config->port = atoi(value);
|
|
||||||
break;
|
|
||||||
case '7':
|
|
||||||
config->hdr = true;
|
|
||||||
break;
|
|
||||||
case 1:
|
case 1:
|
||||||
if (config->action == NULL)
|
if (config->action == NULL)
|
||||||
config->action = value;
|
config->action = value;
|
||||||
@ -289,6 +251,10 @@ bool config_file_parse(char* filename, PCONFIGURATION config) {
|
|||||||
config->address = value;
|
config->address = value;
|
||||||
} else if (strcmp(key, "sops") == 0) {
|
} else if (strcmp(key, "sops") == 0) {
|
||||||
config->sops = strcmp("true", value) == 0;
|
config->sops = strcmp("true", value) == 0;
|
||||||
|
} else if (strcmp(key, "localaudio") == 0) {
|
||||||
|
config->localaudio = strcmp("true", value) == 0;
|
||||||
|
} else if (strcmp(key, "quitappafter") == 0) {
|
||||||
|
config->quitappafter = strcmp("true", value) == 0;
|
||||||
} else {
|
} else {
|
||||||
for (int i=0;long_options[i].name != NULL;i++) {
|
for (int i=0;long_options[i].name != NULL;i++) {
|
||||||
if (strcmp(long_options[i].name, key) == 0) {
|
if (strcmp(long_options[i].name, key) == 0) {
|
||||||
@ -327,10 +293,6 @@ void config_save(char* filename, PCONFIGURATION config) {
|
|||||||
write_config_bool(fd, "localaudio", config->localaudio);
|
write_config_bool(fd, "localaudio", config->localaudio);
|
||||||
if (config->quitappafter)
|
if (config->quitappafter)
|
||||||
write_config_bool(fd, "quitappafter", config->quitappafter);
|
write_config_bool(fd, "quitappafter", config->quitappafter);
|
||||||
if (config->viewonly)
|
|
||||||
write_config_bool(fd, "viewonly", config->viewonly);
|
|
||||||
if (config->rotate != 0)
|
|
||||||
write_config_int(fd, "rotate", config->rotate);
|
|
||||||
|
|
||||||
if (strcmp(config->app, "Steam") != 0)
|
if (strcmp(config->app, "Steam") != 0)
|
||||||
write_config_string(fd, "app", config->app);
|
write_config_string(fd, "app", config->app);
|
||||||
@ -343,25 +305,12 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) {
|
|||||||
|
|
||||||
config->stream.width = 1280;
|
config->stream.width = 1280;
|
||||||
config->stream.height = 720;
|
config->stream.height = 720;
|
||||||
config->stream.fps = 60;
|
config->stream.fps = -1;
|
||||||
config->stream.bitrate = -1;
|
config->stream.bitrate = -1;
|
||||||
config->stream.packetSize = 1392;
|
config->stream.packetSize = 1024;
|
||||||
config->stream.streamingRemotely = STREAM_CFG_AUTO;
|
config->stream.streamingRemotely = 0;
|
||||||
config->stream.audioConfiguration = AUDIO_CONFIGURATION_STEREO;
|
config->stream.audioConfiguration = AUDIO_CONFIGURATION_STEREO;
|
||||||
config->stream.supportedVideoFormats = SCM_H264;
|
config->stream.supportsHevc = false;
|
||||||
|
|
||||||
// Opt in for video encryption if the CPU has good AES performance
|
|
||||||
if (has_fast_aes()) {
|
|
||||||
config->stream.encryptionFlags = ENCFLG_ALL;
|
|
||||||
}
|
|
||||||
else if (has_slow_aes()) {
|
|
||||||
// For extremely slow CPUs, opt out of audio encryption
|
|
||||||
config->stream.encryptionFlags = ENCFLG_NONE;
|
|
||||||
printf("Disabling encryption on low performance CPU\n");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
config->stream.encryptionFlags = ENCFLG_AUDIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
config->debug_level = 0;
|
config->debug_level = 0;
|
||||||
config->platform = "auto";
|
config->platform = "auto";
|
||||||
@ -373,15 +322,9 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) {
|
|||||||
config->sops = true;
|
config->sops = true;
|
||||||
config->localaudio = false;
|
config->localaudio = false;
|
||||||
config->fullscreen = true;
|
config->fullscreen = true;
|
||||||
config->unsupported = true;
|
config->unsupported = false;
|
||||||
config->quitappafter = false;
|
config->quitappafter = false;
|
||||||
config->viewonly = false;
|
|
||||||
config->mouse_emulation = true;
|
|
||||||
config->rotate = 0;
|
|
||||||
config->codec = CODEC_UNSPECIFIED;
|
config->codec = CODEC_UNSPECIFIED;
|
||||||
config->hdr = false;
|
|
||||||
config->pin = 0;
|
|
||||||
config->port = 47989;
|
|
||||||
|
|
||||||
config->inputsCount = 0;
|
config->inputsCount = 0;
|
||||||
config->mapping = get_path("gamecontrollerdb.txt", getenv("XDG_DATA_DIRS"));
|
config->mapping = get_path("gamecontrollerdb.txt", getenv("XDG_DATA_DIRS"));
|
||||||
@ -390,7 +333,7 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) {
|
|||||||
char* config_file = get_path("moonlight.conf", "/etc");
|
char* config_file = get_path("moonlight.conf", "/etc");
|
||||||
if (config_file)
|
if (config_file)
|
||||||
config_file_parse(config_file, config);
|
config_file_parse(config_file, config);
|
||||||
|
|
||||||
if (argc == 2 && access(argv[1], F_OK) == 0) {
|
if (argc == 2 && access(argv[1], F_OK) == 0) {
|
||||||
config->action = "stream";
|
config->action = "stream";
|
||||||
if (!config_file_parse(argv[1], config))
|
if (!config_file_parse(argv[1], config))
|
||||||
@ -399,7 +342,7 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) {
|
|||||||
} else {
|
} else {
|
||||||
int option_index = 0;
|
int option_index = 0;
|
||||||
int c;
|
int c;
|
||||||
while ((c = getopt_long_only(argc, argv, "-abc:d:efg:h:i:j:k:lm:no:p:q:r:s:tu:v:w:xy45:6:7", long_options, &option_index)) != -1) {
|
while ((c = getopt_long_only(argc, argv, "-abc:d:efg:h:i:j:k:lm:no:p:q:r:stuv:w:xy", long_options, &option_index)) != -1) {
|
||||||
parse_argument(c, optarg, config);
|
parse_argument(c, optarg, config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -411,32 +354,22 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) {
|
|||||||
struct passwd *pw = getpwuid(getuid());
|
struct passwd *pw = getpwuid(getuid());
|
||||||
const char *dir;
|
const char *dir;
|
||||||
if ((dir = getenv("XDG_CACHE_DIR")) != NULL)
|
if ((dir = getenv("XDG_CACHE_DIR")) != NULL)
|
||||||
snprintf(config->key_dir, sizeof(config->key_dir), "%s" MOONLIGHT_PATH, dir);
|
sprintf(config->key_dir, "%s" MOONLIGHT_PATH, dir);
|
||||||
else if ((dir = getenv("HOME")) != NULL)
|
else if ((dir = getenv("HOME")) != NULL)
|
||||||
snprintf(config->key_dir, sizeof(config->key_dir), "%s" DEFAULT_CACHE_DIR MOONLIGHT_PATH, dir);
|
sprintf(config->key_dir, "%s" DEFAULT_CACHE_DIR MOONLIGHT_PATH, dir);
|
||||||
else
|
else
|
||||||
snprintf(config->key_dir, sizeof(config->key_dir), "%s" DEFAULT_CACHE_DIR MOONLIGHT_PATH, pw->pw_dir);
|
sprintf(config->key_dir, "%s" DEFAULT_CACHE_DIR MOONLIGHT_PATH, pw->pw_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config->stream.fps == -1)
|
||||||
|
config->stream.fps = config->stream.height >= 1080 ? 30 : 60;
|
||||||
|
|
||||||
if (config->stream.bitrate == -1) {
|
if (config->stream.bitrate == -1) {
|
||||||
// This table prefers 16:10 resolutions because they are
|
if (config->stream.height >= 1080 && config->stream.fps >= 60)
|
||||||
// only slightly more pixels than the 16:9 equivalents, so
|
config->stream.bitrate = 20000;
|
||||||
// we don't want to bump those 16:10 resolutions up to the
|
else if (config->stream.height >= 1080 || config->stream.fps >= 60)
|
||||||
// next 16:9 slot.
|
config->stream.bitrate = 10000;
|
||||||
|
else
|
||||||
if (config->stream.width * config->stream.height <= 640 * 360) {
|
config->stream.bitrate = 5000;
|
||||||
config->stream.bitrate = (int)(1000 * (config->stream.fps / 30.0));
|
|
||||||
} else if (config->stream.width * config->stream.height <= 854 * 480) {
|
|
||||||
config->stream.bitrate = (int)(1500 * (config->stream.fps / 30.0));
|
|
||||||
} else if (config->stream.width * config->stream.height <= 1366 * 768) {
|
|
||||||
// This covers 1280x720 and 1280x800 too
|
|
||||||
config->stream.bitrate = (int)(5000 * (config->stream.fps / 30.0));
|
|
||||||
} else if (config->stream.width * config->stream.height <= 1920 * 1200) {
|
|
||||||
config->stream.bitrate = (int)(10000 * (config->stream.fps / 30.0));
|
|
||||||
} else if (config->stream.width * config->stream.height <= 2560 * 1600) {
|
|
||||||
config->stream.bitrate = (int)(20000 * (config->stream.fps / 30.0));
|
|
||||||
} else /* if (config->stream.width * config->stream.height <= 3840 * 2160) */ {
|
|
||||||
config->stream.bitrate = (int)(40000 * (config->stream.fps / 30.0));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
10
src/config.h
10
src/config.h
@ -23,6 +23,8 @@
|
|||||||
|
|
||||||
#define MAX_INPUTS 6
|
#define MAX_INPUTS 6
|
||||||
|
|
||||||
|
enum codecs { CODEC_UNSPECIFIED, CODEC_H264, CODEC_HEVC };
|
||||||
|
|
||||||
typedef struct _CONFIGURATION {
|
typedef struct _CONFIGURATION {
|
||||||
STREAM_CONFIGURATION stream;
|
STREAM_CONFIGURATION stream;
|
||||||
int debug_level;
|
int debug_level;
|
||||||
@ -37,20 +39,14 @@ typedef struct _CONFIGURATION {
|
|||||||
bool sops;
|
bool sops;
|
||||||
bool localaudio;
|
bool localaudio;
|
||||||
bool fullscreen;
|
bool fullscreen;
|
||||||
int rotate;
|
|
||||||
bool unsupported;
|
bool unsupported;
|
||||||
bool quitappafter;
|
bool quitappafter;
|
||||||
bool viewonly;
|
|
||||||
bool mouse_emulation;
|
|
||||||
char* inputs[MAX_INPUTS];
|
char* inputs[MAX_INPUTS];
|
||||||
int inputsCount;
|
int inputsCount;
|
||||||
enum codecs codec;
|
enum codecs codec;
|
||||||
bool hdr;
|
|
||||||
int pin;
|
|
||||||
unsigned short port;
|
|
||||||
} CONFIGURATION, *PCONFIGURATION;
|
} CONFIGURATION, *PCONFIGURATION;
|
||||||
|
|
||||||
extern bool inputAdded;
|
bool inputAdded;
|
||||||
|
|
||||||
bool config_file_parse(char* filename, PCONFIGURATION config);
|
bool config_file_parse(char* filename, PCONFIGURATION config);
|
||||||
void config_parse(int argc, char* argv[], PCONFIGURATION config);
|
void config_parse(int argc, char* argv[], PCONFIGURATION config);
|
||||||
|
@ -20,7 +20,5 @@
|
|||||||
#define VERSION_MAJOR @PROJECT_VERSION_MAJOR@
|
#define VERSION_MAJOR @PROJECT_VERSION_MAJOR@
|
||||||
#define VERSION_MINOR @PROJECT_VERSION_MINOR@
|
#define VERSION_MINOR @PROJECT_VERSION_MINOR@
|
||||||
#define VERSION_PATCH @PROJECT_VERSION_PATCH@
|
#define VERSION_PATCH @PROJECT_VERSION_PATCH@
|
||||||
#cmakedefine GIT_BRANCH "@GIT_BRANCH@"
|
|
||||||
#cmakedefine GIT_COMMIT_HASH "@GIT_COMMIT_HASH@"
|
|
||||||
|
|
||||||
#define COMPILE_OPTIONS "@MOONLIGHT_OPTIONS@"
|
#define COMPILE_OPTIONS "@MOONLIGHT_OPTIONS@"
|
||||||
|
@ -23,45 +23,11 @@
|
|||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
|
||||||
#ifdef HAVE_SDL
|
|
||||||
#include <SDL.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
pthread_t main_thread_id = 0;
|
pthread_t main_thread_id = 0;
|
||||||
bool connection_debug;
|
bool connection_debug;
|
||||||
ConnListenerRumble rumble_handler = NULL;
|
ConnListenerRumble rumble_handler = NULL;
|
||||||
ConnListenerRumbleTriggers rumble_triggers_handler = NULL;
|
|
||||||
ConnListenerSetMotionEventState set_motion_event_state_handler = NULL;
|
|
||||||
ConnListenerSetControllerLED set_controller_led_handler = NULL;
|
|
||||||
|
|
||||||
static void connection_terminated(int errorCode) {
|
|
||||||
switch (errorCode) {
|
|
||||||
case ML_ERROR_GRACEFUL_TERMINATION:
|
|
||||||
printf("Connection has been terminated gracefully.\n");
|
|
||||||
break;
|
|
||||||
case ML_ERROR_NO_VIDEO_TRAFFIC:
|
|
||||||
printf("No video received from host. Check the host PC's firewall and port forwarding rules.\n");
|
|
||||||
break;
|
|
||||||
case ML_ERROR_NO_VIDEO_FRAME:
|
|
||||||
printf("Your network connection isn't performing well. Reduce your video bitrate setting or try a faster connection.\n");
|
|
||||||
break;
|
|
||||||
case ML_ERROR_UNEXPECTED_EARLY_TERMINATION:
|
|
||||||
printf("The connection was unexpectedly terminated by the host due to a video capture error. Make sure no DRM-protected content is playing on the host.\n");
|
|
||||||
break;
|
|
||||||
case ML_ERROR_PROTECTED_CONTENT:
|
|
||||||
printf("The connection was terminated by the host due to DRM-protected content. Close any DRM-protected content on the host and try again.\n");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
printf("Connection terminated with error: %d\n", errorCode);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef HAVE_SDL
|
|
||||||
SDL_Event event;
|
|
||||||
event.type = SDL_QUIT;
|
|
||||||
SDL_PushEvent(&event);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
static void connection_terminated() {
|
||||||
if (main_thread_id != 0)
|
if (main_thread_id != 0)
|
||||||
pthread_kill(main_thread_id, SIGTERM);
|
pthread_kill(main_thread_id, SIGTERM);
|
||||||
}
|
}
|
||||||
@ -78,21 +44,6 @@ static void rumble(unsigned short controllerNumber, unsigned short lowFreqMotor,
|
|||||||
rumble_handler(controllerNumber, lowFreqMotor, highFreqMotor);
|
rumble_handler(controllerNumber, lowFreqMotor, highFreqMotor);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rumble_triggers(unsigned short controllerNumber, unsigned short leftTrigger, unsigned short rightTrigger) {
|
|
||||||
if (rumble_handler)
|
|
||||||
rumble_triggers_handler(controllerNumber, leftTrigger, rightTrigger);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void set_motion_event_state(unsigned short controllerNumber, unsigned char motionType, unsigned short reportRateHz) {
|
|
||||||
if (set_motion_event_state_handler)
|
|
||||||
set_motion_event_state_handler(controllerNumber, motionType, reportRateHz);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void set_controller_led(unsigned short controllerNumber, unsigned char r, unsigned char g, unsigned char b) {
|
|
||||||
if (set_controller_led_handler)
|
|
||||||
set_controller_led_handler(controllerNumber, r, g, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void connection_status_update(int status) {
|
static void connection_status_update(int status) {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case CONN_STATUS_OKAY:
|
case CONN_STATUS_OKAY:
|
||||||
@ -112,9 +63,5 @@ CONNECTION_LISTENER_CALLBACKS connection_callbacks = {
|
|||||||
.connectionTerminated = connection_terminated,
|
.connectionTerminated = connection_terminated,
|
||||||
.logMessage = connection_log_message,
|
.logMessage = connection_log_message,
|
||||||
.rumble = rumble,
|
.rumble = rumble,
|
||||||
.connectionStatusUpdate = connection_status_update,
|
.connectionStatusUpdate = connection_status_update
|
||||||
.setHdrMode = NULL,
|
|
||||||
.rumbleTriggers = rumble_triggers,
|
|
||||||
.setMotionEventState = set_motion_event_state,
|
|
||||||
.setControllerLED = set_controller_led,
|
|
||||||
};
|
};
|
||||||
|
@ -26,6 +26,3 @@ extern CONNECTION_LISTENER_CALLBACKS connection_callbacks;
|
|||||||
extern pthread_t main_thread_id;
|
extern pthread_t main_thread_id;
|
||||||
extern bool connection_debug;
|
extern bool connection_debug;
|
||||||
extern ConnListenerRumble rumble_handler;
|
extern ConnListenerRumble rumble_handler;
|
||||||
extern ConnListenerRumbleTriggers rumble_triggers_handler;
|
|
||||||
extern ConnListenerSetMotionEventState set_motion_event_state_handler;
|
|
||||||
extern ConnListenerSetControllerLED set_controller_led_handler;
|
|
||||||
|
121
src/cpu.c
121
src/cpu.c
@ -1,121 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Moonlight Embedded.
|
|
||||||
*
|
|
||||||
* Moonlight is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Moonlight is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "cpu.h"
|
|
||||||
#include "util.h"
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#ifdef HAVE_GETAUXVAL
|
|
||||||
#include <sys/auxv.h>
|
|
||||||
|
|
||||||
#ifndef HWCAP2_AES
|
|
||||||
#define HWCAP2_AES (1 << 0)
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__linux__) && defined(__riscv)
|
|
||||||
#if __has_include(<sys/hwprobe.h>)
|
|
||||||
#include <sys/hwprobe.h>
|
|
||||||
#else
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#if __has_include(<asm/hwprobe.h>)
|
|
||||||
#include <asm/hwprobe.h>
|
|
||||||
#include <sys/syscall.h>
|
|
||||||
#else
|
|
||||||
#define __NR_riscv_hwprobe 258
|
|
||||||
struct riscv_hwprobe {
|
|
||||||
int64_t key;
|
|
||||||
uint64_t value;
|
|
||||||
};
|
|
||||||
#define RISCV_HWPROBE_KEY_IMA_EXT_0 4
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// RISC-V Scalar AES [E]ncryption and [D]ecryption
|
|
||||||
#ifndef RISCV_HWPROBE_EXT_ZKND
|
|
||||||
#define RISCV_HWPROBE_EXT_ZKND (1 << 11)
|
|
||||||
#define RISCV_HWPROBE_EXT_ZKNE (1 << 12)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// RISC-V Vector AES
|
|
||||||
#ifndef RISCV_HWPROBE_EXT_ZVKNED
|
|
||||||
#define RISCV_HWPROBE_EXT_ZVKNED (1 << 21)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int __riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
|
|
||||||
size_t cpu_count, unsigned long *cpus,
|
|
||||||
unsigned int flags)
|
|
||||||
{
|
|
||||||
return syscall(__NR_riscv_hwprobe, pairs, pair_count, cpu_count, cpus, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool has_fast_aes() {
|
|
||||||
#if defined(HAVE_GETAUXVAL) && (defined(__arm__) || defined(__aarch64__))
|
|
||||||
#if defined(__arm__) && defined(HWCAP2_AES)
|
|
||||||
return !!(getauxval(AT_HWCAP2) & HWCAP2_AES);
|
|
||||||
#elif defined(__aarch64__)
|
|
||||||
return !!(getauxval(AT_HWCAP) & HWCAP_AES);
|
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
#elif defined(HAVE_BICS_AES)
|
|
||||||
return __builtin_cpu_supports("aes");
|
|
||||||
#elif defined(__BUILTIN_CPU_SUPPORTS__) && defined(__powerpc__)
|
|
||||||
return __builtin_cpu_supports("vcrypto");
|
|
||||||
#elif defined(__linux__) && defined(__riscv)
|
|
||||||
struct riscv_hwprobe pairs[1] = {
|
|
||||||
{ RISCV_HWPROBE_KEY_IMA_EXT_0, 0 },
|
|
||||||
};
|
|
||||||
|
|
||||||
// If this syscall is not implemented, we'll get -ENOSYS
|
|
||||||
// and the value field will remain zero.
|
|
||||||
__riscv_hwprobe(pairs, sizeof(pairs) / sizeof(struct riscv_hwprobe), 0, NULL, 0);
|
|
||||||
|
|
||||||
return (pairs[0].value & (RISCV_HWPROBE_EXT_ZKNE | RISCV_HWPROBE_EXT_ZKND)) ==
|
|
||||||
(RISCV_HWPROBE_EXT_ZKNE | RISCV_HWPROBE_EXT_ZKND) ||
|
|
||||||
(pairs[0].value & RISCV_HWPROBE_EXT_ZVKNED);
|
|
||||||
#elif __SIZEOF_SIZE_T__ == 4
|
|
||||||
#warning Unknown 32-bit platform. Assuming AES is slow on this CPU.
|
|
||||||
return false;
|
|
||||||
#else
|
|
||||||
#warning Unknown 64-bit platform. Assuming AES is fast on this CPU.
|
|
||||||
return true;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
bool has_slow_aes() {
|
|
||||||
#ifdef __arm__
|
|
||||||
char cpuinfo[4096] = {};
|
|
||||||
if (read_file("/proc/cpuinfo", cpuinfo, sizeof(cpuinfo) - 1) > 0) {
|
|
||||||
// If this is a ARMv6 CPU (like the Pi 1), we'll assume it's not
|
|
||||||
// powerful enough to handle audio encryption. The Pi 1 could
|
|
||||||
// barely handle Opus decoding alone.
|
|
||||||
if (strstr(cpuinfo, "ARMv6")) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
21
src/cpu.h
21
src/cpu.h
@ -1,21 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Moonlight Embedded.
|
|
||||||
*
|
|
||||||
* Moonlight is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* Moonlight is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
bool has_fast_aes(void);
|
|
||||||
bool has_slow_aes(void);
|
|
@ -64,7 +64,7 @@ static void on_cec_keypress(void* userdata, const cec_keypress* key) {
|
|||||||
value = 0;
|
value = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value != 0) {
|
if (value != 0) {
|
||||||
short code = 0x80 << 8 | value;
|
short code = 0x80 << 8 | value;
|
||||||
LiSendKeyboardEvent(code, (key->duration > 0)?KEY_ACTION_UP:KEY_ACTION_DOWN, 0);
|
LiSendKeyboardEvent(code, (key->duration > 0)?KEY_ACTION_UP:KEY_ACTION_DOWN, 0);
|
||||||
@ -80,25 +80,25 @@ void cec_init() {
|
|||||||
snprintf(g_config.strDeviceName, sizeof(g_config.strDeviceName), "Moonlight");
|
snprintf(g_config.strDeviceName, sizeof(g_config.strDeviceName), "Moonlight");
|
||||||
g_config.callbacks = &g_callbacks;
|
g_config.callbacks = &g_callbacks;
|
||||||
g_config.deviceTypes.types[0] = CEC_DEVICE_TYPE_PLAYBACK_DEVICE;
|
g_config.deviceTypes.types[0] = CEC_DEVICE_TYPE_PLAYBACK_DEVICE;
|
||||||
|
|
||||||
if (libcecc_initialise(&g_config, &g_iface, NULL) != 1) {
|
if (libcecc_initialise(&g_config, &g_iface, NULL) != 1) {
|
||||||
fprintf(stderr, "Failed to initialize libcec interface\n");
|
fprintf(stderr, "Failed to initialize libcec interface\n");
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_iface.init_video_standalone(g_iface.connection);
|
g_iface.init_video_standalone(g_iface.connection);
|
||||||
|
|
||||||
cec_adapter devices[10];
|
cec_adapter devices[10];
|
||||||
int8_t iDevicesFound = g_iface.find_adapters(g_iface.connection, devices, sizeof(devices) / sizeof(devices[0]), NULL);
|
int8_t iDevicesFound = g_iface.find_adapters(g_iface.connection, devices, sizeof(devices) / sizeof(devices), NULL);
|
||||||
|
|
||||||
if (iDevicesFound <= 0) {
|
if (iDevicesFound <= 0) {
|
||||||
fprintf(stderr, "No CEC devices found\n");
|
fprintf(stderr, "No CEC devices found\n");
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
libcecc_destroy(&g_iface);
|
libcecc_destroy(&g_iface);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
strcpy(g_strPort, devices[0].comm);
|
strcpy(g_strPort, devices[0].comm);
|
||||||
if (!g_iface.open(g_iface.connection, g_strPort, 5000)) {
|
if (!g_iface.open(g_iface.connection, g_strPort, 5000)) {
|
||||||
fprintf(stderr, "Unable to open the device on port %s\n", g_strPort);
|
fprintf(stderr, "Unable to open the device on port %s\n", g_strPort);
|
||||||
@ -106,6 +106,6 @@ void cec_init() {
|
|||||||
libcecc_destroy(&g_iface);
|
libcecc_destroy(&g_iface);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_iface.set_active_source(g_iface.connection, g_config.deviceTypes.types[0]);
|
g_iface.set_active_source(g_iface.connection, g_config.deviceTypes.types[0]);
|
||||||
}
|
}
|
||||||
|
@ -38,12 +38,7 @@
|
|||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#ifdef __linux__
|
|
||||||
#include <endian.h>
|
#include <endian.h>
|
||||||
#else
|
|
||||||
#include <sys/endian.h>
|
|
||||||
#endif
|
|
||||||
#include <math.h>
|
|
||||||
|
|
||||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||||
#define int16_to_le(val) val
|
#define int16_to_le(val) val
|
||||||
@ -61,35 +56,21 @@ struct input_abs_parms {
|
|||||||
struct input_device {
|
struct input_device {
|
||||||
struct libevdev *dev;
|
struct libevdev *dev;
|
||||||
bool is_keyboard;
|
bool is_keyboard;
|
||||||
bool is_mouse;
|
|
||||||
bool is_touchscreen;
|
|
||||||
int rotate;
|
|
||||||
struct mapping* map;
|
struct mapping* map;
|
||||||
int key_map[KEY_CNT];
|
int key_map[KEY_MAX];
|
||||||
int abs_map[ABS_CNT];
|
int abs_map[ABS_MAX];
|
||||||
int hats_state[3][2];
|
int hats_state[3][2];
|
||||||
int fd;
|
int fd;
|
||||||
char modifiers;
|
char modifiers;
|
||||||
#ifdef __linux__
|
__s32 mouseDeltaX, mouseDeltaY, mouseScroll;
|
||||||
__s32 mouseDeltaX, mouseDeltaY, mouseVScroll, mouseHScroll;
|
|
||||||
__s32 touchDownX, touchDownY, touchX, touchY;
|
|
||||||
#else
|
|
||||||
int32_t mouseDeltaX, mouseDeltaY, mouseVScroll, mouseHScroll;
|
|
||||||
int32_t touchDownX, touchDownY, touchX, touchY;
|
|
||||||
#endif
|
|
||||||
struct timeval touchDownTime;
|
|
||||||
struct timeval btnDownTime;
|
|
||||||
short controllerId;
|
short controllerId;
|
||||||
int haptic_effect_id;
|
int haptic_effect_id;
|
||||||
int buttonFlags;
|
int buttonFlags;
|
||||||
unsigned char leftTrigger, rightTrigger;
|
char leftTrigger, rightTrigger;
|
||||||
short leftStickX, leftStickY;
|
short leftStickX, leftStickY;
|
||||||
short rightStickX, rightStickY;
|
short rightStickX, rightStickY;
|
||||||
bool gamepadModified;
|
bool gamepadModified;
|
||||||
bool mouseEmulation;
|
|
||||||
pthread_t meThread;
|
|
||||||
struct input_abs_parms xParms, yParms, rxParms, ryParms, zParms, rzParms;
|
struct input_abs_parms xParms, yParms, rxParms, ryParms, zParms, rzParms;
|
||||||
struct input_abs_parms leftParms, rightParms, upParms, downParms;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define HAT_UP 1
|
#define HAT_UP 1
|
||||||
@ -100,23 +81,6 @@ static const int hat_constants[3][3] = {{HAT_UP | HAT_LEFT, HAT_UP, HAT_UP | HAT
|
|||||||
|
|
||||||
#define set_hat(flags, flag, hat, hat_flag) flags = (hat & hat_flag) == hat_flag ? flags | flag : flags & ~flag
|
#define set_hat(flags, flag, hat, hat_flag) flags = (hat & hat_flag) == hat_flag ? flags | flag : flags & ~flag
|
||||||
|
|
||||||
#define TOUCH_UP -1
|
|
||||||
#define TOUCH_CLICK_RADIUS 10
|
|
||||||
#define TOUCH_CLICK_DELAY 100000 // microseconds
|
|
||||||
#define TOUCH_RCLICK_TIME 750 // milliseconds
|
|
||||||
|
|
||||||
// How long the Start button must be pressed to toggle mouse emulation
|
|
||||||
#define MOUSE_EMULATION_LONG_PRESS_TIME 750
|
|
||||||
// How long between polling the gamepad to send virtual mouse input
|
|
||||||
#define MOUSE_EMULATION_POLLING_INTERVAL 50000
|
|
||||||
// Determines how fast the mouse will move each interval
|
|
||||||
#define MOUSE_EMULATION_MOTION_MULTIPLIER 3
|
|
||||||
// Determines the maximum motion amount before allowing movement
|
|
||||||
#define MOUSE_EMULATION_DEADZONE 2
|
|
||||||
|
|
||||||
// Limited by number of bits in activeGamepadMask
|
|
||||||
#define MAX_GAMEPADS 16
|
|
||||||
|
|
||||||
static struct input_device* devices = NULL;
|
static struct input_device* devices = NULL;
|
||||||
static int numDevices = 0;
|
static int numDevices = 0;
|
||||||
static int assignedControllerIds = 0;
|
static int assignedControllerIds = 0;
|
||||||
@ -128,9 +92,6 @@ static short* currentAbs;
|
|||||||
static bool* currentReverse;
|
static bool* currentReverse;
|
||||||
|
|
||||||
static bool grabbingDevices;
|
static bool grabbingDevices;
|
||||||
static bool mouseEmulationEnabled;
|
|
||||||
|
|
||||||
static bool waitingToExitOnModifiersUp = false;
|
|
||||||
|
|
||||||
int evdev_gamepads = 0;
|
int evdev_gamepads = 0;
|
||||||
|
|
||||||
@ -168,23 +129,13 @@ static bool evdev_init_parms(struct input_device *dev, struct input_abs_parms *p
|
|||||||
static void evdev_remove(int devindex) {
|
static void evdev_remove(int devindex) {
|
||||||
numDevices--;
|
numDevices--;
|
||||||
|
|
||||||
printf("Input device removed: %s (player %d)\n", libevdev_get_name(devices[devindex].dev), devices[devindex].controllerId + 1);
|
if (devices[devindex].controllerId >= 0)
|
||||||
|
|
||||||
if (devices[devindex].controllerId >= 0) {
|
|
||||||
assignedControllerIds &= ~(1 << devices[devindex].controllerId);
|
assignedControllerIds &= ~(1 << devices[devindex].controllerId);
|
||||||
LiSendMultiControllerEvent(devices[devindex].controllerId, assignedControllerIds, 0, 0, 0, 0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
if (devices[devindex].mouseEmulation) {
|
|
||||||
devices[devindex].mouseEmulation = false;
|
|
||||||
pthread_join(devices[devindex].meThread, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
libevdev_free(devices[devindex].dev);
|
|
||||||
loop_remove_fd(devices[devindex].fd);
|
|
||||||
close(devices[devindex].fd);
|
|
||||||
|
|
||||||
if (devindex != numDevices && numDevices > 0)
|
if (devindex != numDevices && numDevices > 0)
|
||||||
memcpy(&devices[devindex], &devices[numDevices], sizeof(struct input_device));
|
memcpy(&devices[devindex], &devices[numDevices], sizeof(struct input_device));
|
||||||
|
|
||||||
|
fprintf(stderr, "Removed input device\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static short evdev_convert_value(struct input_event *ev, struct input_device *dev, struct input_abs_parms *parms, bool reverse) {
|
static short evdev_convert_value(struct input_event *ev, struct input_device *dev, struct input_abs_parms *parms, bool reverse) {
|
||||||
@ -205,121 +156,18 @@ static short evdev_convert_value(struct input_event *ev, struct input_device *de
|
|||||||
return (long long)(ev->value - (ev->value>parms->avg?parms->flat*2:0) - parms->min) * (SHRT_MAX-SHRT_MIN) / (parms->max-parms->min-parms->flat*2) + SHRT_MIN;
|
return (long long)(ev->value - (ev->value>parms->avg?parms->flat*2:0) - parms->min) * (SHRT_MAX-SHRT_MIN) / (parms->max-parms->min-parms->flat*2) + SHRT_MIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned char evdev_convert_value_byte(struct input_event *ev, struct input_device *dev, struct input_abs_parms *parms, char halfaxis) {
|
static char evdev_convert_value_byte(struct input_event *ev, struct input_device *dev, struct input_abs_parms *parms) {
|
||||||
if (parms->max == 0 && parms->min == 0) {
|
if (parms->max == 0 && parms->min == 0) {
|
||||||
fprintf(stderr, "Axis not found: %d\n", ev->code);
|
fprintf(stderr, "Axis not found: %d\n", ev->code);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (halfaxis == 0) {
|
if (abs(ev->value-parms->min)<parms->flat)
|
||||||
if (abs(ev->value-parms->min)<parms->flat)
|
return 0;
|
||||||
return 0;
|
else if (ev->value>parms->max)
|
||||||
else if (ev->value>parms->max)
|
return UCHAR_MAX;
|
||||||
return UCHAR_MAX;
|
else
|
||||||
else
|
return (ev->value - parms->flat - parms->min) * UCHAR_MAX / (parms->diff - parms->flat);
|
||||||
return (ev->value - parms->flat - parms->min) * UCHAR_MAX / (parms->diff - parms->flat);
|
|
||||||
} else {
|
|
||||||
short val = evdev_convert_value(ev, dev, parms, false);
|
|
||||||
if (halfaxis == '-' && val < 0)
|
|
||||||
return -(int)val * UCHAR_MAX / (SHRT_MAX-SHRT_MIN);
|
|
||||||
else if (halfaxis == '+' && val > 0)
|
|
||||||
return (int)val * UCHAR_MAX / (SHRT_MAX-SHRT_MIN);
|
|
||||||
else
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void *HandleMouseEmulation(void* param)
|
|
||||||
{
|
|
||||||
struct input_device* dev = (struct input_device*) param;
|
|
||||||
|
|
||||||
while (dev->mouseEmulation) {
|
|
||||||
usleep(MOUSE_EMULATION_POLLING_INTERVAL);
|
|
||||||
|
|
||||||
short rawX;
|
|
||||||
short rawY;
|
|
||||||
|
|
||||||
// Determine which analog stick is currently receiving the strongest input
|
|
||||||
if ((uint32_t)abs(dev->leftStickX) + abs(dev->leftStickY) > (uint32_t)abs(dev->rightStickX) + abs(dev->rightStickY)) {
|
|
||||||
rawX = dev->leftStickX;
|
|
||||||
rawY = dev->leftStickY;
|
|
||||||
} else {
|
|
||||||
rawX = dev->rightStickX;
|
|
||||||
rawY = dev->rightStickY;
|
|
||||||
}
|
|
||||||
|
|
||||||
float deltaX;
|
|
||||||
float deltaY;
|
|
||||||
|
|
||||||
// Produce a base vector for mouse movement with increased speed as we deviate further from center
|
|
||||||
deltaX = pow((float)rawX / 32767.0f * MOUSE_EMULATION_MOTION_MULTIPLIER, 3);
|
|
||||||
deltaY = pow((float)rawY / 32767.0f * MOUSE_EMULATION_MOTION_MULTIPLIER, 3);
|
|
||||||
|
|
||||||
// Enforce deadzones
|
|
||||||
deltaX = fabs(deltaX) > MOUSE_EMULATION_DEADZONE ? deltaX - MOUSE_EMULATION_DEADZONE : 0;
|
|
||||||
deltaY = fabs(deltaY) > MOUSE_EMULATION_DEADZONE ? deltaY - MOUSE_EMULATION_DEADZONE : 0;
|
|
||||||
|
|
||||||
if (deltaX != 0 || deltaY != 0)
|
|
||||||
LiSendMouseMoveEvent(deltaX, -deltaY);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define SET_BTN_FLAG(x, y) supportedButtonFlags |= (x >= 0) ? y : 0
|
|
||||||
|
|
||||||
static void send_controller_arrival(struct input_device *dev) {
|
|
||||||
unsigned char type = LI_CTYPE_UNKNOWN;
|
|
||||||
unsigned int supportedButtonFlags = 0;
|
|
||||||
unsigned short capabilities = 0;
|
|
||||||
|
|
||||||
switch (libevdev_get_id_vendor(dev->dev)) {
|
|
||||||
case 0x045e: // Microsoft
|
|
||||||
type = LI_CTYPE_XBOX;
|
|
||||||
break;
|
|
||||||
case 0x054c: // Sony
|
|
||||||
type = LI_CTYPE_PS;
|
|
||||||
break;
|
|
||||||
case 0x057e: // Nintendo
|
|
||||||
type = LI_CTYPE_NINTENDO;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* name = libevdev_get_name(dev->dev);
|
|
||||||
if (name && type == LI_CTYPE_UNKNOWN) {
|
|
||||||
|
|
||||||
// Try to guess based on the name
|
|
||||||
if (strstr(name, "Xbox") || strstr(name, "X-Box") || strstr(name, "XBox") || strstr(name, "XBOX")) {
|
|
||||||
type = LI_CTYPE_XBOX;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SET_BTN_FLAG(dev->map->btn_a, A_FLAG);
|
|
||||||
SET_BTN_FLAG(dev->map->btn_b, B_FLAG);
|
|
||||||
SET_BTN_FLAG(dev->map->btn_x, X_FLAG);
|
|
||||||
SET_BTN_FLAG(dev->map->btn_y, Y_FLAG);
|
|
||||||
SET_BTN_FLAG(dev->map->btn_back, BACK_FLAG);
|
|
||||||
SET_BTN_FLAG(dev->map->btn_start, PLAY_FLAG);
|
|
||||||
SET_BTN_FLAG(dev->map->btn_guide, SPECIAL_FLAG);
|
|
||||||
SET_BTN_FLAG(dev->map->btn_leftstick, LS_CLK_FLAG);
|
|
||||||
SET_BTN_FLAG(dev->map->btn_rightstick, RS_CLK_FLAG);
|
|
||||||
SET_BTN_FLAG(dev->map->btn_leftshoulder, LB_FLAG);
|
|
||||||
SET_BTN_FLAG(dev->map->btn_rightshoulder, RB_FLAG);
|
|
||||||
SET_BTN_FLAG(dev->map->btn_misc1, MISC_FLAG);
|
|
||||||
SET_BTN_FLAG(dev->map->btn_paddle1, PADDLE1_FLAG);
|
|
||||||
SET_BTN_FLAG(dev->map->btn_paddle2, PADDLE2_FLAG);
|
|
||||||
SET_BTN_FLAG(dev->map->btn_paddle3, PADDLE3_FLAG);
|
|
||||||
SET_BTN_FLAG(dev->map->btn_paddle4, PADDLE4_FLAG);
|
|
||||||
SET_BTN_FLAG(dev->map->btn_touchpad, TOUCHPAD_FLAG);
|
|
||||||
|
|
||||||
if (dev->map->abs_lefttrigger >= 0 && dev->map->abs_righttrigger >= 0)
|
|
||||||
capabilities |= LI_CCAP_ANALOG_TRIGGERS;
|
|
||||||
|
|
||||||
// TODO: Probe for this properly
|
|
||||||
capabilities |= LI_CCAP_RUMBLE;
|
|
||||||
|
|
||||||
LiSendControllerArrivalEvent(dev->controllerId, assignedControllerIds, type,
|
|
||||||
supportedButtonFlags, capabilities);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool evdev_handle_event(struct input_event *ev, struct input_device *dev) {
|
static bool evdev_handle_event(struct input_event *ev, struct input_device *dev) {
|
||||||
@ -328,57 +176,32 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev)
|
|||||||
switch (ev->type) {
|
switch (ev->type) {
|
||||||
case EV_SYN:
|
case EV_SYN:
|
||||||
if (dev->mouseDeltaX != 0 || dev->mouseDeltaY != 0) {
|
if (dev->mouseDeltaX != 0 || dev->mouseDeltaY != 0) {
|
||||||
switch (dev->rotate) {
|
LiSendMouseMoveEvent(dev->mouseDeltaX, dev->mouseDeltaY);
|
||||||
case 90:
|
|
||||||
LiSendMouseMoveEvent(dev->mouseDeltaY, -dev->mouseDeltaX);
|
|
||||||
break;
|
|
||||||
case 180:
|
|
||||||
LiSendMouseMoveEvent(-dev->mouseDeltaX, -dev->mouseDeltaY);
|
|
||||||
break;
|
|
||||||
case 270:
|
|
||||||
LiSendMouseMoveEvent(-dev->mouseDeltaY, dev->mouseDeltaX);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
LiSendMouseMoveEvent(dev->mouseDeltaX, dev->mouseDeltaY);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
dev->mouseDeltaX = 0;
|
dev->mouseDeltaX = 0;
|
||||||
dev->mouseDeltaY = 0;
|
dev->mouseDeltaY = 0;
|
||||||
}
|
}
|
||||||
if (dev->mouseVScroll != 0) {
|
if (dev->mouseScroll != 0) {
|
||||||
LiSendScrollEvent(dev->mouseVScroll);
|
LiSendScrollEvent(dev->mouseScroll);
|
||||||
dev->mouseVScroll = 0;
|
dev->mouseScroll = 0;
|
||||||
}
|
|
||||||
if (dev->mouseHScroll != 0) {
|
|
||||||
LiSendHScrollEvent(dev->mouseHScroll);
|
|
||||||
dev->mouseHScroll = 0;
|
|
||||||
}
|
}
|
||||||
if (dev->gamepadModified) {
|
if (dev->gamepadModified) {
|
||||||
if (dev->controllerId < 0) {
|
if (dev->controllerId < 0) {
|
||||||
for (int i = 0; i < MAX_GAMEPADS; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
if ((assignedControllerIds & (1 << i)) == 0) {
|
if ((assignedControllerIds & (1 << i)) == 0) {
|
||||||
assignedControllerIds |= (1 << i);
|
assignedControllerIds |= (1 << i);
|
||||||
dev->controllerId = i;
|
dev->controllerId = i;
|
||||||
printf("Assigned %s as player %d\n", libevdev_get_name(dev->dev), i+1);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//Use id 0 when too many gamepads are connected
|
//Use id 0 when too many gamepads are connected
|
||||||
if (dev->controllerId < 0)
|
if (dev->controllerId < 0)
|
||||||
dev->controllerId = 0;
|
dev->controllerId = 0;
|
||||||
|
|
||||||
// Send controller arrival event to the host
|
|
||||||
send_controller_arrival(dev);
|
|
||||||
}
|
}
|
||||||
// Send event only if mouse emulation is disabled.
|
LiSendMultiControllerEvent(dev->controllerId, assignedControllerIds, dev->buttonFlags, dev->leftTrigger, dev->rightTrigger, dev->leftStickX, dev->leftStickY, dev->rightStickX, dev->rightStickY);
|
||||||
if (dev->mouseEmulation == false)
|
|
||||||
LiSendMultiControllerEvent(dev->controllerId, assignedControllerIds, dev->buttonFlags, dev->leftTrigger, dev->rightTrigger, dev->leftStickX, dev->leftStickY, dev->rightStickX, dev->rightStickY);
|
|
||||||
dev->gamepadModified = false;
|
dev->gamepadModified = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case EV_KEY:
|
case EV_KEY:
|
||||||
if (ev->code > KEY_MAX)
|
|
||||||
return true;
|
|
||||||
if (ev->code < sizeof(keyCodes)/sizeof(keyCodes[0])) {
|
if (ev->code < sizeof(keyCodes)/sizeof(keyCodes[0])) {
|
||||||
char modifier = 0;
|
char modifier = 0;
|
||||||
switch (ev->code) {
|
switch (ev->code) {
|
||||||
@ -394,10 +217,6 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev)
|
|||||||
case KEY_RIGHTCTRL:
|
case KEY_RIGHTCTRL:
|
||||||
modifier = MODIFIER_CTRL;
|
modifier = MODIFIER_CTRL;
|
||||||
break;
|
break;
|
||||||
case KEY_LEFTMETA:
|
|
||||||
case KEY_RIGHTMETA:
|
|
||||||
modifier = MODIFIER_META;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if (modifier != 0) {
|
if (modifier != 0) {
|
||||||
if (ev->value)
|
if (ev->value)
|
||||||
@ -406,20 +225,18 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev)
|
|||||||
dev->modifiers &= ~modifier;
|
dev->modifiers &= ~modifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
// After the quit key combo is pressed, quit once all keys are raised
|
// Quit the stream if all the required quit keys are down
|
||||||
if ((dev->modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS &&
|
if ((dev->modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS &&
|
||||||
ev->code == QUIT_KEY && ev->value != 0) {
|
ev->code == QUIT_KEY && ev->value != 0) {
|
||||||
waitingToExitOnModifiersUp = true;
|
|
||||||
return true;
|
|
||||||
} else if (waitingToExitOnModifiersUp && dev->modifiers == 0)
|
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
short code = 0x80 << 8 | keyCodes[ev->code];
|
short code = 0x80 << 8 | keyCodes[ev->code];
|
||||||
LiSendKeyboardEvent(code, ev->value?KEY_ACTION_DOWN:KEY_ACTION_UP, dev->modifiers);
|
LiSendKeyboardEvent(code, ev->value?KEY_ACTION_DOWN:KEY_ACTION_UP, dev->modifiers);
|
||||||
} else {
|
} else {
|
||||||
int mouseCode = 0;
|
int mouseCode = 0;
|
||||||
int gamepadCode = 0;
|
short gamepadCode = 0;
|
||||||
int index = dev->key_map[ev->code];
|
int index = ev->code > BTN_MISC && ev->code < (BTN_MISC + KEY_MAX) ? dev->key_map[ev->code - BTN_MISC] : -1;
|
||||||
|
|
||||||
switch (ev->code) {
|
switch (ev->code) {
|
||||||
case BTN_LEFT:
|
case BTN_LEFT:
|
||||||
@ -437,27 +254,6 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev)
|
|||||||
case BTN_EXTRA:
|
case BTN_EXTRA:
|
||||||
mouseCode = BUTTON_X2;
|
mouseCode = BUTTON_X2;
|
||||||
break;
|
break;
|
||||||
case BTN_TOUCH:
|
|
||||||
if (ev->value == 1) {
|
|
||||||
dev->touchDownTime = ev->time;
|
|
||||||
} else {
|
|
||||||
if (dev->touchDownX != TOUCH_UP && dev->touchDownY != TOUCH_UP) {
|
|
||||||
int deltaX = dev->touchX - dev->touchDownX;
|
|
||||||
int deltaY = dev->touchY - dev->touchDownY;
|
|
||||||
if (deltaX * deltaX + deltaY * deltaY < TOUCH_CLICK_RADIUS * TOUCH_CLICK_RADIUS) {
|
|
||||||
struct timeval elapsedTime;
|
|
||||||
timersub(&ev->time, &dev->touchDownTime, &elapsedTime);
|
|
||||||
int holdTimeMs = elapsedTime.tv_sec * 1000 + elapsedTime.tv_usec / 1000;
|
|
||||||
int button = holdTimeMs >= TOUCH_RCLICK_TIME ? BUTTON_RIGHT : BUTTON_LEFT;
|
|
||||||
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, button);
|
|
||||||
usleep(TOUCH_CLICK_DELAY);
|
|
||||||
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, button);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
dev->touchDownX = TOUCH_UP;
|
|
||||||
dev->touchDownY = TOUCH_UP;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
gamepadModified = true;
|
gamepadModified = true;
|
||||||
if (dev->map == NULL)
|
if (dev->map == NULL)
|
||||||
@ -492,68 +288,16 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev)
|
|||||||
gamepadCode = BACK_FLAG;
|
gamepadCode = BACK_FLAG;
|
||||||
else if (index == dev->map->btn_guide)
|
else if (index == dev->map->btn_guide)
|
||||||
gamepadCode = SPECIAL_FLAG;
|
gamepadCode = SPECIAL_FLAG;
|
||||||
else if (index == dev->map->btn_misc1)
|
|
||||||
gamepadCode = MISC_FLAG;
|
|
||||||
else if (index == dev->map->btn_paddle1)
|
|
||||||
gamepadCode = PADDLE1_FLAG;
|
|
||||||
else if (index == dev->map->btn_paddle2)
|
|
||||||
gamepadCode = PADDLE2_FLAG;
|
|
||||||
else if (index == dev->map->btn_paddle3)
|
|
||||||
gamepadCode = PADDLE3_FLAG;
|
|
||||||
else if (index == dev->map->btn_paddle4)
|
|
||||||
gamepadCode = PADDLE4_FLAG;
|
|
||||||
else if (index == dev->map->btn_touchpad)
|
|
||||||
gamepadCode = TOUCHPAD_FLAG;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mouseCode != 0) {
|
if (mouseCode != 0) {
|
||||||
LiSendMouseButtonEvent(ev->value?BUTTON_ACTION_PRESS:BUTTON_ACTION_RELEASE, mouseCode);
|
LiSendMouseButtonEvent(ev->value?BUTTON_ACTION_PRESS:BUTTON_ACTION_RELEASE, mouseCode);
|
||||||
gamepadModified = false;
|
gamepadModified = false;
|
||||||
} else if (gamepadCode != 0) {
|
} else if (gamepadCode != 0) {
|
||||||
if (ev->value) {
|
if (ev->value)
|
||||||
dev->buttonFlags |= gamepadCode;
|
dev->buttonFlags |= gamepadCode;
|
||||||
dev->btnDownTime = ev->time;
|
else
|
||||||
} else
|
|
||||||
dev->buttonFlags &= ~gamepadCode;
|
dev->buttonFlags &= ~gamepadCode;
|
||||||
|
|
||||||
if (mouseEmulationEnabled && gamepadCode == PLAY_FLAG && ev->value == 0) {
|
|
||||||
struct timeval elapsedTime;
|
|
||||||
timersub(&ev->time, &dev->btnDownTime, &elapsedTime);
|
|
||||||
int holdTimeMs = elapsedTime.tv_sec * 1000 + elapsedTime.tv_usec / 1000;
|
|
||||||
if (holdTimeMs >= MOUSE_EMULATION_LONG_PRESS_TIME) {
|
|
||||||
if (dev->mouseEmulation) {
|
|
||||||
dev->mouseEmulation = false;
|
|
||||||
pthread_join(dev->meThread, NULL);
|
|
||||||
dev->meThread = 0;
|
|
||||||
printf("Mouse emulation disabled for controller %d.\n", dev->controllerId);
|
|
||||||
} else {
|
|
||||||
dev->mouseEmulation = true;
|
|
||||||
pthread_create(&dev->meThread, NULL, HandleMouseEmulation, dev);
|
|
||||||
printf("Mouse emulation enabled for controller %d.\n", dev->controllerId);
|
|
||||||
}
|
|
||||||
// clear gamepad state.
|
|
||||||
LiSendMultiControllerEvent(dev->controllerId, assignedControllerIds, 0, 0, 0, 0, 0, 0, 0);
|
|
||||||
}
|
|
||||||
} else if (dev->mouseEmulation) {
|
|
||||||
char action = ev->value ? BUTTON_ACTION_PRESS : BUTTON_ACTION_RELEASE;
|
|
||||||
switch (gamepadCode) {
|
|
||||||
case A_FLAG:
|
|
||||||
LiSendMouseButtonEvent(action, BUTTON_LEFT);
|
|
||||||
break;
|
|
||||||
case B_FLAG:
|
|
||||||
LiSendMouseButtonEvent(action, BUTTON_RIGHT);
|
|
||||||
break;
|
|
||||||
case X_FLAG:
|
|
||||||
LiSendMouseButtonEvent(action, BUTTON_MIDDLE);
|
|
||||||
break;
|
|
||||||
case LB_FLAG:
|
|
||||||
LiSendMouseButtonEvent(action, BUTTON_X1);
|
|
||||||
break;
|
|
||||||
case RB_FLAG:
|
|
||||||
LiSendMouseButtonEvent(action, BUTTON_X2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (dev->map != NULL && index == dev->map->btn_lefttrigger)
|
} else if (dev->map != NULL && index == dev->map->btn_lefttrigger)
|
||||||
dev->leftTrigger = ev->value ? UCHAR_MAX : 0;
|
dev->leftTrigger = ev->value ? UCHAR_MAX : 0;
|
||||||
else if (dev->map != NULL && index == dev->map->btn_righttrigger)
|
else if (dev->map != NULL && index == dev->map->btn_righttrigger)
|
||||||
@ -574,41 +318,12 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev)
|
|||||||
case REL_Y:
|
case REL_Y:
|
||||||
dev->mouseDeltaY = ev->value;
|
dev->mouseDeltaY = ev->value;
|
||||||
break;
|
break;
|
||||||
case REL_HWHEEL:
|
|
||||||
dev->mouseHScroll = ev->value;
|
|
||||||
break;
|
|
||||||
case REL_WHEEL:
|
case REL_WHEEL:
|
||||||
dev->mouseVScroll = ev->value;
|
dev->mouseScroll = ev->value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case EV_ABS:
|
case EV_ABS:
|
||||||
if (ev->code > ABS_MAX)
|
|
||||||
return true;
|
|
||||||
if (dev->is_touchscreen) {
|
|
||||||
switch (ev->code) {
|
|
||||||
case ABS_X:
|
|
||||||
if (dev->touchDownX == TOUCH_UP) {
|
|
||||||
dev->touchDownX = ev->value;
|
|
||||||
dev->touchX = ev->value;
|
|
||||||
} else {
|
|
||||||
dev->mouseDeltaX += (ev->value - dev->touchX);
|
|
||||||
dev->touchX = ev->value;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ABS_Y:
|
|
||||||
if (dev->touchDownY == TOUCH_UP) {
|
|
||||||
dev->touchDownY = ev->value;
|
|
||||||
dev->touchY = ev->value;
|
|
||||||
} else {
|
|
||||||
dev->mouseDeltaY += (ev->value - dev->touchY);
|
|
||||||
dev->touchY = ev->value;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dev->map == NULL)
|
if (dev->map == NULL)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -646,57 +361,17 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev)
|
|||||||
dev->rightStickX = evdev_convert_value(ev, dev, &dev->rxParms, dev->map->reverse_rightx);
|
dev->rightStickX = evdev_convert_value(ev, dev, &dev->rxParms, dev->map->reverse_rightx);
|
||||||
else if (index == dev->map->abs_righty)
|
else if (index == dev->map->abs_righty)
|
||||||
dev->rightStickY = evdev_convert_value(ev, dev, &dev->ryParms, !dev->map->reverse_righty);
|
dev->rightStickY = evdev_convert_value(ev, dev, &dev->ryParms, !dev->map->reverse_righty);
|
||||||
|
else if (index == dev->map->abs_lefttrigger)
|
||||||
|
dev->leftTrigger = evdev_convert_value_byte(ev, dev, &dev->zParms);
|
||||||
|
else if (index == dev->map->abs_righttrigger)
|
||||||
|
dev->rightTrigger = evdev_convert_value_byte(ev, dev, &dev->rzParms);
|
||||||
else
|
else
|
||||||
gamepadModified = false;
|
gamepadModified = false;
|
||||||
|
|
||||||
if (index == dev->map->abs_lefttrigger) {
|
|
||||||
dev->leftTrigger = evdev_convert_value_byte(ev, dev, &dev->zParms, dev->map->halfaxis_lefttrigger);
|
|
||||||
gamepadModified = true;
|
|
||||||
}
|
|
||||||
if (index == dev->map->abs_righttrigger) {
|
|
||||||
dev->rightTrigger = evdev_convert_value_byte(ev, dev, &dev->rzParms, dev->map->halfaxis_righttrigger);
|
|
||||||
gamepadModified = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index == dev->map->abs_dpright) {
|
|
||||||
if (evdev_convert_value_byte(ev, dev, &dev->rightParms, dev->map->halfaxis_dpright) > 127)
|
|
||||||
dev->buttonFlags |= RIGHT_FLAG;
|
|
||||||
else
|
|
||||||
dev->buttonFlags &= ~RIGHT_FLAG;
|
|
||||||
|
|
||||||
gamepadModified = true;
|
|
||||||
}
|
|
||||||
if (index == dev->map->abs_dpleft) {
|
|
||||||
if (evdev_convert_value_byte(ev, dev, &dev->leftParms, dev->map->halfaxis_dpleft) > 127)
|
|
||||||
dev->buttonFlags |= LEFT_FLAG;
|
|
||||||
else
|
|
||||||
dev->buttonFlags &= ~LEFT_FLAG;
|
|
||||||
|
|
||||||
gamepadModified = true;
|
|
||||||
}
|
|
||||||
if (index == dev->map->abs_dpup) {
|
|
||||||
if (evdev_convert_value_byte(ev, dev, &dev->upParms, dev->map->halfaxis_dpup) > 127)
|
|
||||||
dev->buttonFlags |= UP_FLAG;
|
|
||||||
else
|
|
||||||
dev->buttonFlags &= ~UP_FLAG;
|
|
||||||
|
|
||||||
gamepadModified = true;
|
|
||||||
}
|
|
||||||
if (index == dev->map->abs_dpdown) {
|
|
||||||
if (evdev_convert_value_byte(ev, dev, &dev->downParms, dev->map->halfaxis_dpdown) > 127)
|
|
||||||
dev->buttonFlags |= DOWN_FLAG;
|
|
||||||
else
|
|
||||||
dev->buttonFlags &= ~DOWN_FLAG;
|
|
||||||
|
|
||||||
gamepadModified = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gamepadModified && (dev->buttonFlags & QUIT_BUTTONS) == QUIT_BUTTONS) {
|
if (gamepadModified && (dev->buttonFlags & QUIT_BUTTONS) == QUIT_BUTTONS)
|
||||||
LiSendMultiControllerEvent(dev->controllerId, assignedControllerIds, 0, 0, 0, 0, 0, 0, 0);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
dev->gamepadModified |= gamepadModified;
|
dev->gamepadModified |= gamepadModified;
|
||||||
return true;
|
return true;
|
||||||
@ -706,7 +381,7 @@ static bool evdev_handle_mapping_event(struct input_event *ev, struct input_devi
|
|||||||
int index, hat_index;
|
int index, hat_index;
|
||||||
switch (ev->type) {
|
switch (ev->type) {
|
||||||
case EV_KEY:
|
case EV_KEY:
|
||||||
index = dev->key_map[ev->code];
|
index = ev->code > BTN_MISC && ev->code < (BTN_MISC + KEY_MAX) ? dev->key_map[ev->code - BTN_MISC] : -1;
|
||||||
if (currentKey != NULL) {
|
if (currentKey != NULL) {
|
||||||
if (ev->value)
|
if (ev->value)
|
||||||
*currentKey = index;
|
*currentKey = index;
|
||||||
@ -775,7 +450,7 @@ static int evdev_handle(int fd) {
|
|||||||
return LOOP_OK;
|
return LOOP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void evdev_create(const char* device, struct mapping* mappings, bool verbose, int rotate) {
|
void evdev_create(const char* device, struct mapping* mappings, bool verbose) {
|
||||||
int fd = open(device, O_RDWR|O_NONBLOCK);
|
int fd = open(device, O_RDWR|O_NONBLOCK);
|
||||||
if (fd <= 0) {
|
if (fd <= 0) {
|
||||||
fprintf(stderr, "Failed to open device %s\n", device);
|
fprintf(stderr, "Failed to open device %s\n", device);
|
||||||
@ -824,55 +499,15 @@ void evdev_create(const char* device, struct mapping* mappings, bool verbose, in
|
|||||||
|
|
||||||
bool is_keyboard = libevdev_has_event_code(evdev, EV_KEY, KEY_Q);
|
bool is_keyboard = libevdev_has_event_code(evdev, EV_KEY, KEY_Q);
|
||||||
bool is_mouse = libevdev_has_event_type(evdev, EV_REL) || libevdev_has_event_code(evdev, EV_KEY, BTN_LEFT);
|
bool is_mouse = libevdev_has_event_type(evdev, EV_REL) || libevdev_has_event_code(evdev, EV_KEY, BTN_LEFT);
|
||||||
bool is_touchscreen = libevdev_has_event_code(evdev, EV_KEY, BTN_TOUCH);
|
|
||||||
|
|
||||||
// This classification logic comes from SDL
|
if (mappings == NULL && !(is_keyboard || is_mouse)) {
|
||||||
bool is_accelerometer =
|
fprintf(stderr, "No mapping available for %s (%s) on %s\n", name, str_guid, device);
|
||||||
((libevdev_has_event_code(evdev, EV_ABS, ABS_X) &&
|
mappings = default_mapping;
|
||||||
libevdev_has_event_code(evdev, EV_ABS, ABS_Y) &&
|
|
||||||
libevdev_has_event_code(evdev, EV_ABS, ABS_Z)) ||
|
|
||||||
(libevdev_has_event_code(evdev, EV_ABS, ABS_RX) &&
|
|
||||||
libevdev_has_event_code(evdev, EV_ABS, ABS_RY) &&
|
|
||||||
libevdev_has_event_code(evdev, EV_ABS, ABS_RZ))) &&
|
|
||||||
!libevdev_has_event_type(evdev, EV_KEY);
|
|
||||||
bool is_gamepad =
|
|
||||||
((libevdev_has_event_code(evdev, EV_ABS, ABS_X) &&
|
|
||||||
libevdev_has_event_code(evdev, EV_ABS, ABS_Y)) ||
|
|
||||||
(libevdev_has_event_code(evdev, EV_ABS, ABS_HAT0X) &&
|
|
||||||
libevdev_has_event_code(evdev, EV_ABS, ABS_HAT0Y))) &&
|
|
||||||
(libevdev_has_event_code(evdev, EV_KEY, BTN_TRIGGER) ||
|
|
||||||
libevdev_has_event_code(evdev, EV_KEY, BTN_A) ||
|
|
||||||
libevdev_has_event_code(evdev, EV_KEY, BTN_1) ||
|
|
||||||
libevdev_has_event_code(evdev, EV_ABS, ABS_RX) ||
|
|
||||||
libevdev_has_event_code(evdev, EV_ABS, ABS_RY) ||
|
|
||||||
libevdev_has_event_code(evdev, EV_ABS, ABS_RZ) ||
|
|
||||||
libevdev_has_event_code(evdev, EV_ABS, ABS_THROTTLE) ||
|
|
||||||
libevdev_has_event_code(evdev, EV_ABS, ABS_RUDDER) ||
|
|
||||||
libevdev_has_event_code(evdev, EV_ABS, ABS_WHEEL) ||
|
|
||||||
libevdev_has_event_code(evdev, EV_ABS, ABS_GAS) ||
|
|
||||||
libevdev_has_event_code(evdev, EV_ABS, ABS_BRAKE));
|
|
||||||
|
|
||||||
if (is_accelerometer) {
|
|
||||||
if (verbose)
|
|
||||||
printf("Ignoring accelerometer: %s\n", name);
|
|
||||||
libevdev_free(evdev);
|
|
||||||
close(fd);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_gamepad) {
|
if (!is_keyboard && !is_mouse)
|
||||||
evdev_gamepads++;
|
evdev_gamepads++;
|
||||||
|
|
||||||
if (mappings == NULL) {
|
|
||||||
fprintf(stderr, "No mapping available for %s (%s) on %s\n", name, str_guid, device);
|
|
||||||
mappings = default_mapping;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (verbose)
|
|
||||||
printf("Not mapping %s as a gamepad\n", name);
|
|
||||||
mappings = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dev = numDevices;
|
int dev = numDevices;
|
||||||
numDevices++;
|
numDevices++;
|
||||||
|
|
||||||
@ -891,25 +526,18 @@ void evdev_create(const char* device, struct mapping* mappings, bool verbose, in
|
|||||||
devices[dev].fd = fd;
|
devices[dev].fd = fd;
|
||||||
devices[dev].dev = evdev;
|
devices[dev].dev = evdev;
|
||||||
devices[dev].map = mappings;
|
devices[dev].map = mappings;
|
||||||
/* Set unused evdev indices to -2 to avoid aliasing with the default -1 in our mappings */
|
|
||||||
memset(&devices[dev].key_map, -2, sizeof(devices[dev].key_map));
|
memset(&devices[dev].key_map, -2, sizeof(devices[dev].key_map));
|
||||||
memset(&devices[dev].abs_map, -2, sizeof(devices[dev].abs_map));
|
memset(&devices[dev].abs_map, -2, sizeof(devices[dev].abs_map));
|
||||||
devices[dev].is_keyboard = is_keyboard;
|
devices[dev].is_keyboard = is_keyboard;
|
||||||
devices[dev].is_mouse = is_mouse;
|
|
||||||
devices[dev].is_touchscreen = is_touchscreen;
|
|
||||||
devices[dev].rotate = rotate;
|
|
||||||
devices[dev].touchDownX = TOUCH_UP;
|
|
||||||
devices[dev].touchDownY = TOUCH_UP;
|
|
||||||
|
|
||||||
int nbuttons = 0;
|
int nbuttons = 0;
|
||||||
/* Count joystick buttons first like SDL does */
|
|
||||||
for (int i = BTN_JOYSTICK; i < KEY_MAX; ++i) {
|
for (int i = BTN_JOYSTICK; i < KEY_MAX; ++i) {
|
||||||
if (libevdev_has_event_code(devices[dev].dev, EV_KEY, i))
|
if (libevdev_has_event_code(devices[dev].dev, EV_KEY, i))
|
||||||
devices[dev].key_map[i] = nbuttons++;
|
devices[dev].key_map[i - BTN_MISC] = nbuttons++;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < BTN_JOYSTICK; ++i) {
|
for (int i = BTN_MISC; i < BTN_JOYSTICK; ++i) {
|
||||||
if (libevdev_has_event_code(devices[dev].dev, EV_KEY, i))
|
if (libevdev_has_event_code(devices[dev].dev, EV_KEY, i))
|
||||||
devices[dev].key_map[i] = nbuttons++;
|
devices[dev].key_map[i - BTN_MISC] = nbuttons++;
|
||||||
}
|
}
|
||||||
|
|
||||||
int naxes = 0;
|
int naxes = 0;
|
||||||
@ -931,15 +559,11 @@ void evdev_create(const char* device, struct mapping* mappings, bool verbose, in
|
|||||||
valid &= evdev_init_parms(&devices[dev], &(devices[dev].rxParms), devices[dev].map->abs_rightx);
|
valid &= evdev_init_parms(&devices[dev], &(devices[dev].rxParms), devices[dev].map->abs_rightx);
|
||||||
valid &= evdev_init_parms(&devices[dev], &(devices[dev].ryParms), devices[dev].map->abs_righty);
|
valid &= evdev_init_parms(&devices[dev], &(devices[dev].ryParms), devices[dev].map->abs_righty);
|
||||||
valid &= evdev_init_parms(&devices[dev], &(devices[dev].rzParms), devices[dev].map->abs_righttrigger);
|
valid &= evdev_init_parms(&devices[dev], &(devices[dev].rzParms), devices[dev].map->abs_righttrigger);
|
||||||
valid &= evdev_init_parms(&devices[dev], &(devices[dev].leftParms), devices[dev].map->abs_dpleft);
|
|
||||||
valid &= evdev_init_parms(&devices[dev], &(devices[dev].rightParms), devices[dev].map->abs_dpright);
|
|
||||||
valid &= evdev_init_parms(&devices[dev], &(devices[dev].upParms), devices[dev].map->abs_dpup);
|
|
||||||
valid &= evdev_init_parms(&devices[dev], &(devices[dev].downParms), devices[dev].map->abs_dpdown);
|
|
||||||
if (!valid)
|
if (!valid)
|
||||||
fprintf(stderr, "Mapping for %s (%s) on %s is incorrect\n", name, str_guid, device);
|
fprintf(stderr, "Mapping for %s (%s) on %s is incorrect\n", name, str_guid, device);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (grabbingDevices && (is_keyboard || is_mouse || is_touchscreen)) {
|
if (grabbingDevices && is_keyboard) {
|
||||||
if (ioctl(fd, EVIOCGRAB, 1) < 0) {
|
if (ioctl(fd, EVIOCGRAB, 1) < 0) {
|
||||||
fprintf(stderr, "EVIOCGRAB failed with error %d\n", errno);
|
fprintf(stderr, "EVIOCGRAB failed with error %d\n", errno);
|
||||||
}
|
}
|
||||||
@ -1019,10 +643,10 @@ void evdev_map(char* device) {
|
|||||||
char* buf = str_guid;
|
char* buf = str_guid;
|
||||||
for (int i = 0; i < 16; i++)
|
for (int i = 0; i < 16; i++)
|
||||||
buf += sprintf(buf, "%02x", ((unsigned char*) guid)[i]);
|
buf += sprintf(buf, "%02x", ((unsigned char*) guid)[i]);
|
||||||
|
|
||||||
struct mapping map = {0};
|
struct mapping map;
|
||||||
strncpy(map.name, name, sizeof(map.name) - 1);
|
strncpy(map.name, libevdev_get_name(evdev), sizeof(map.name));
|
||||||
strncpy(map.guid, str_guid, sizeof(map.guid) - 1);
|
strncpy(map.guid, str_guid, sizeof(map.guid));
|
||||||
|
|
||||||
libevdev_free(evdev);
|
libevdev_free(evdev);
|
||||||
close(fd);
|
close(fd);
|
||||||
@ -1066,7 +690,7 @@ void evdev_start() {
|
|||||||
// we're ready to take input events. Ctrl+C works up until
|
// we're ready to take input events. Ctrl+C works up until
|
||||||
// this point.
|
// this point.
|
||||||
for (int i = 0; i < numDevices; i++) {
|
for (int i = 0; i < numDevices; i++) {
|
||||||
if ((devices[i].is_keyboard || devices[i].is_mouse || devices[i].is_touchscreen) && ioctl(devices[i].fd, EVIOCGRAB, 1) < 0) {
|
if (devices[i].is_keyboard && ioctl(devices[i].fd, EVIOCGRAB, 1) < 0) {
|
||||||
fprintf(stderr, "EVIOCGRAB failed with error %d\n", errno);
|
fprintf(stderr, "EVIOCGRAB failed with error %d\n", errno);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1081,9 +705,8 @@ void evdev_stop() {
|
|||||||
evdev_drain();
|
evdev_drain();
|
||||||
}
|
}
|
||||||
|
|
||||||
void evdev_init(bool mouse_emulation_enabled) {
|
void evdev_init() {
|
||||||
handler = evdev_handle_event;
|
handler = evdev_handle_event;
|
||||||
mouseEmulationEnabled = mouse_emulation_enabled;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct input_device* evdev_get_input_device(unsigned short controller_id) {
|
static struct input_device* evdev_get_input_device(unsigned short controller_id) {
|
||||||
|
@ -21,10 +21,10 @@
|
|||||||
|
|
||||||
extern int evdev_gamepads;
|
extern int evdev_gamepads;
|
||||||
|
|
||||||
void evdev_create(const char* device, struct mapping* mappings, bool verbose, int rotate);
|
void evdev_create(const char* device, struct mapping* mappings, bool verbose);
|
||||||
void evdev_loop();
|
void evdev_loop();
|
||||||
|
|
||||||
void evdev_init(bool mouse_emulation_enabled);
|
void evdev_init();
|
||||||
void evdev_start();
|
void evdev_start();
|
||||||
void evdev_stop();
|
void evdev_stop();
|
||||||
void evdev_map(char* device);
|
void evdev_map(char* device);
|
||||||
|
@ -47,7 +47,7 @@ static const short keyCodes[] = {
|
|||||||
0xDB, //VK_BRACELEFT
|
0xDB, //VK_BRACELEFT
|
||||||
0xDD, //VK_BRACERIGHT
|
0xDD, //VK_BRACERIGHT
|
||||||
0x0D, //VK_ENTER
|
0x0D, //VK_ENTER
|
||||||
0xA2, //VK_CONTROL Left control
|
0x11, //VK_CONTROL Left control
|
||||||
0x41, //VK_A
|
0x41, //VK_A
|
||||||
0x53, //VK_S
|
0x53, //VK_S
|
||||||
0x44, //VK_D
|
0x44, //VK_D
|
||||||
@ -60,7 +60,7 @@ static const short keyCodes[] = {
|
|||||||
0xBA, //VK_SEMICOLON
|
0xBA, //VK_SEMICOLON
|
||||||
0xDE, //VK_APOSTROPHE
|
0xDE, //VK_APOSTROPHE
|
||||||
0xC0, //VK_GRAVE
|
0xC0, //VK_GRAVE
|
||||||
0xA0, //VK_SHIFT Left shift
|
0x10, //VK_SHIFT Left shift
|
||||||
0xDC, //VK_BACK_SLASH
|
0xDC, //VK_BACK_SLASH
|
||||||
0x5A, //VK_Z
|
0x5A, //VK_Z
|
||||||
0x58, //VK_X
|
0x58, //VK_X
|
||||||
@ -72,9 +72,9 @@ static const short keyCodes[] = {
|
|||||||
0xBC, //VK_COMMA
|
0xBC, //VK_COMMA
|
||||||
0xBE, //VK_DOT
|
0xBE, //VK_DOT
|
||||||
0xBF, //VK_SLASH
|
0xBF, //VK_SLASH
|
||||||
0xA1, //VK_SHIFT Right shift
|
0x10, //VK_SHIFT Right shift
|
||||||
0x6A, //VK_KPASTERISK
|
0x6A, //VK_KPASTERISK
|
||||||
0xA4, //VK_ALT Left alt
|
0x12, //VK_ALT Left alt
|
||||||
0x20, //VK_SPACE
|
0x20, //VK_SPACE
|
||||||
0x14, //VK_CAPS_LOCK
|
0x14, //VK_CAPS_LOCK
|
||||||
0x70, //VK_F1
|
0x70, //VK_F1
|
||||||
@ -115,10 +115,10 @@ static const short keyCodes[] = {
|
|||||||
0, //VK_MUHENKAN
|
0, //VK_MUHENKAN
|
||||||
0, //VK_KPJPCOMMA
|
0, //VK_KPJPCOMMA
|
||||||
0x0D, //VK_KPENTER
|
0x0D, //VK_KPENTER
|
||||||
0xA3, //VK_CONTROL Right ctrl
|
0x11, //VK_CONTROL Right ctrl
|
||||||
0x6F, //VK_KPSLASH
|
0x6F, //VK_KPSLASH
|
||||||
0x2C, //VK_SYSRQ
|
0x2C, //VK_SYSRQ
|
||||||
0xA5, //VK_ALT Right alt
|
0x12, //VK_ALT Right alt
|
||||||
0, //KeyEvent.VK_LINEFEED
|
0, //KeyEvent.VK_LINEFEED
|
||||||
0x24, //VK_HOME
|
0x24, //VK_HOME
|
||||||
0x26, //VK_UP
|
0x26, //VK_UP
|
||||||
@ -128,7 +128,7 @@ static const short keyCodes[] = {
|
|||||||
0x23, //VK_END
|
0x23, //VK_END
|
||||||
0x28, //VK_DOWN
|
0x28, //VK_DOWN
|
||||||
0x22, //VK_PAGE_DOWN
|
0x22, //VK_PAGE_DOWN
|
||||||
0x2D, //VK_INSERT
|
0x9B, //VK_INSERT
|
||||||
0x2E, //VK_DELETE
|
0x2E, //VK_DELETE
|
||||||
0, //VK_MACRO
|
0, //VK_MACRO
|
||||||
0, //VK_MUTE
|
0, //VK_MUTE
|
||||||
@ -139,11 +139,4 @@ static const short keyCodes[] = {
|
|||||||
0, //VK_KPPLUSMINUS
|
0, //VK_KPPLUSMINUS
|
||||||
0x13, //VK_PAUSE
|
0x13, //VK_PAUSE
|
||||||
0, //VK_SCALE AL Compiz Scale (Expose)
|
0, //VK_SCALE AL Compiz Scale (Expose)
|
||||||
0, //KEY_KPCOMMA
|
|
||||||
0, //KEY_HANGEUL
|
|
||||||
0, //KEY_HANJA
|
|
||||||
0, //KEY_YEN
|
|
||||||
0x5B, //KEY_LEFTMETA
|
|
||||||
0x5C, //KEY_RIGHTMETA
|
|
||||||
0, //KEY_COMPOSE
|
|
||||||
};
|
};
|
||||||
|
@ -22,7 +22,6 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
struct mapping* mapping_parse(char* mapping) {
|
struct mapping* mapping_parse(char* mapping) {
|
||||||
@ -32,33 +31,25 @@ struct mapping* mapping_parse(char* mapping) {
|
|||||||
if (guid == NULL || name == NULL)
|
if (guid == NULL || name == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
struct mapping* map = calloc(sizeof(struct mapping), 1);
|
struct mapping* map = malloc(sizeof(struct mapping));
|
||||||
if (map == NULL) {
|
if (map == NULL) {
|
||||||
fprintf(stderr, "Not enough memory");
|
fprintf(stderr, "Not enough memory");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
strncpy(map->guid, guid, sizeof(map->guid) - 1);
|
strncpy(map->guid, guid, sizeof(map->guid));
|
||||||
strncpy(map->name, name, sizeof(map->name) - 1);
|
strncpy(map->name, name, sizeof(map->name));
|
||||||
|
memset(&map->abs_leftx, -1, sizeof(short) * 31);
|
||||||
/* Initialize all mapping indices to -1 to ensure they won't match anything */
|
|
||||||
memset(&map->abs_leftx, -1, offsetof(struct mapping, next) - offsetof(struct mapping, abs_leftx));
|
|
||||||
|
|
||||||
char* option;
|
char* option;
|
||||||
while ((option = strtok_r(NULL, ",", &strpoint)) != NULL) {
|
while ((option = strtok_r(NULL, ",", &strpoint)) != NULL) {
|
||||||
char *key = NULL, *orig_value = NULL;
|
char *key = NULL, *value = NULL;
|
||||||
int ret;
|
int ret;
|
||||||
if ((ret = sscanf(option, "%m[^:]:%ms", &key, &orig_value)) == 2) {
|
if ((ret = sscanf(option, "%m[^:]:%ms", &key, &value)) == 2) {
|
||||||
int int_value, direction_value;
|
int int_value, direction_value;
|
||||||
char *value = orig_value;
|
|
||||||
char flag = 0;
|
char flag = 0;
|
||||||
char half_axis = 0;
|
|
||||||
if (value[0] == '-' || value[0] == '+') {
|
|
||||||
half_axis = value[0];
|
|
||||||
value++;
|
|
||||||
}
|
|
||||||
if (strcmp("platform", key) == 0)
|
if (strcmp("platform", key) == 0)
|
||||||
strncpy(map->platform, value, sizeof(map->platform) - 1);
|
strncpy(map->platform, value, sizeof(map->platform));
|
||||||
else if (sscanf(value, "b%d", &int_value) == 1) {
|
else if (sscanf(value, "b%d", &int_value) == 1) {
|
||||||
if (strcmp("a", key) == 0)
|
if (strcmp("a", key) == 0)
|
||||||
map->btn_a = int_value;
|
map->btn_a = int_value;
|
||||||
@ -94,18 +85,6 @@ struct mapping* mapping_parse(char* mapping) {
|
|||||||
map->btn_lefttrigger = int_value;
|
map->btn_lefttrigger = int_value;
|
||||||
else if (strcmp("righttrigger", key) == 0)
|
else if (strcmp("righttrigger", key) == 0)
|
||||||
map->btn_righttrigger = int_value;
|
map->btn_righttrigger = int_value;
|
||||||
else if (strcmp("misc1", key) == 0)
|
|
||||||
map->btn_misc1 = int_value;
|
|
||||||
else if (strcmp("paddle1", key) == 0)
|
|
||||||
map->btn_paddle1 = int_value;
|
|
||||||
else if (strcmp("paddle2", key) == 0)
|
|
||||||
map->btn_paddle2 = int_value;
|
|
||||||
else if (strcmp("paddle3", key) == 0)
|
|
||||||
map->btn_paddle3 = int_value;
|
|
||||||
else if (strcmp("paddle4", key) == 0)
|
|
||||||
map->btn_paddle4 = int_value;
|
|
||||||
else if (strcmp("touchpad", key) == 0)
|
|
||||||
map->btn_touchpad = int_value;
|
|
||||||
} else if (sscanf(value, "a%d%c", &int_value, &flag) >= 1) {
|
} else if (sscanf(value, "a%d%c", &int_value, &flag) >= 1) {
|
||||||
if (strcmp("leftx", key) == 0) {
|
if (strcmp("leftx", key) == 0) {
|
||||||
map->abs_leftx = int_value;
|
map->abs_leftx = int_value;
|
||||||
@ -119,25 +98,10 @@ struct mapping* mapping_parse(char* mapping) {
|
|||||||
} else if (strcmp("righty", key) == 0) {
|
} else if (strcmp("righty", key) == 0) {
|
||||||
map->abs_righty = int_value;
|
map->abs_righty = int_value;
|
||||||
map->reverse_righty = flag == '~';
|
map->reverse_righty = flag == '~';
|
||||||
} else if (strcmp("lefttrigger", key) == 0) {
|
} else if (strcmp("lefttrigger", key) == 0)
|
||||||
map->abs_lefttrigger = int_value;
|
map->abs_lefttrigger = int_value;
|
||||||
map->halfaxis_lefttrigger = half_axis;
|
else if (strcmp("righttrigger", key) == 0)
|
||||||
} else if (strcmp("righttrigger", key) == 0) {
|
|
||||||
map->abs_righttrigger = int_value;
|
map->abs_righttrigger = int_value;
|
||||||
map->halfaxis_righttrigger = half_axis;
|
|
||||||
} else if (strcmp("dpright", key) == 0) {
|
|
||||||
map->abs_dpright = int_value;
|
|
||||||
map->halfaxis_dpright = half_axis;
|
|
||||||
} else if (strcmp("dpleft", key) == 0) {
|
|
||||||
map->abs_dpleft = int_value;
|
|
||||||
map->halfaxis_dpleft = half_axis;
|
|
||||||
} else if (strcmp("dpup", key) == 0) {
|
|
||||||
map->abs_dpup = int_value;
|
|
||||||
map->halfaxis_dpup = half_axis;
|
|
||||||
} else if (strcmp("dpdown", key) == 0) {
|
|
||||||
map->abs_dpdown = int_value;
|
|
||||||
map->halfaxis_dpdown = half_axis;
|
|
||||||
}
|
|
||||||
} else if (sscanf(value, "h%d.%d", &int_value, &direction_value) == 2) {
|
} else if (sscanf(value, "h%d.%d", &int_value, &direction_value) == 2) {
|
||||||
if (strcmp("dpright", key) == 0) {
|
if (strcmp("dpright", key) == 0) {
|
||||||
map->hat_dpright = int_value;
|
map->hat_dpright = int_value;
|
||||||
@ -152,8 +116,6 @@ struct mapping* mapping_parse(char* mapping) {
|
|||||||
map->hat_dpdown = int_value;
|
map->hat_dpdown = int_value;
|
||||||
map->hat_dir_dpdown = direction_value;
|
map->hat_dir_dpdown = direction_value;
|
||||||
}
|
}
|
||||||
} else if (strcmp("crc", key) == 0) {
|
|
||||||
/* CRC is not supported */
|
|
||||||
} else
|
} else
|
||||||
fprintf(stderr, "Can't map (%s)\n", option);
|
fprintf(stderr, "Can't map (%s)\n", option);
|
||||||
} else if (ret == 0 && option[0] != '\n')
|
} else if (ret == 0 && option[0] != '\n')
|
||||||
@ -162,8 +124,8 @@ struct mapping* mapping_parse(char* mapping) {
|
|||||||
if (key != NULL)
|
if (key != NULL)
|
||||||
free(key);
|
free(key);
|
||||||
|
|
||||||
if (orig_value != NULL)
|
if (value != NULL)
|
||||||
free(orig_value);
|
free(value);
|
||||||
}
|
}
|
||||||
map->guid[32] = '\0';
|
map->guid[32] = '\0';
|
||||||
map->name[256] = '\0';
|
map->name[256] = '\0';
|
||||||
@ -227,11 +189,5 @@ void mapping_print(struct mapping* map) {
|
|||||||
print_abs("righttrigger", map->abs_righttrigger);
|
print_abs("righttrigger", map->abs_righttrigger);
|
||||||
print_btn("lefttrigger", map->btn_lefttrigger);
|
print_btn("lefttrigger", map->btn_lefttrigger);
|
||||||
print_btn("righttrigger", map->btn_righttrigger);
|
print_btn("righttrigger", map->btn_righttrigger);
|
||||||
print_btn("misc1", map->btn_misc1);
|
|
||||||
print_btn("paddle1", map->btn_paddle1);
|
|
||||||
print_btn("paddle2", map->btn_paddle2);
|
|
||||||
print_btn("paddle3", map->btn_paddle3);
|
|
||||||
print_btn("paddle4", map->btn_paddle4);
|
|
||||||
print_btn("touchpad", map->btn_touchpad);
|
|
||||||
printf("platform:Linux\n");
|
printf("platform:Linux\n");
|
||||||
}
|
}
|
||||||
|
@ -28,31 +28,22 @@ struct mapping {
|
|||||||
|
|
||||||
bool reverse_leftx, reverse_lefty;
|
bool reverse_leftx, reverse_lefty;
|
||||||
bool reverse_rightx, reverse_righty;
|
bool reverse_rightx, reverse_righty;
|
||||||
char halfaxis_lefttrigger, halfaxis_righttrigger;
|
|
||||||
char halfaxis_dpright, halfaxis_dpleft;
|
|
||||||
char halfaxis_dpup, halfaxis_dpdown;
|
|
||||||
|
|
||||||
/* abs_leftx must be the first member of the list of mapping indices! */
|
|
||||||
short abs_leftx, abs_lefty;
|
short abs_leftx, abs_lefty;
|
||||||
short abs_rightx, abs_righty;
|
short abs_rightx, abs_righty;
|
||||||
|
|
||||||
short hat_dpright, hat_dpleft, hat_dpup, hat_dpdown;
|
short hat_dpright, hat_dpleft, hat_dpup, hat_dpdown;
|
||||||
short hat_dir_dpright, hat_dir_dpleft, hat_dir_dpup, hat_dir_dpdown;
|
short hat_dir_dpright, hat_dir_dpleft, hat_dir_dpup, hat_dir_dpdown;
|
||||||
short btn_dpup, btn_dpdown, btn_dpleft, btn_dpright;
|
short btn_dpup, btn_dpdown, btn_dpleft, btn_dpright;
|
||||||
short abs_dpright, abs_dpleft, abs_dpup, abs_dpdown;
|
|
||||||
|
|
||||||
short btn_a, btn_x, btn_y, btn_b;
|
short btn_a, btn_x, btn_y, btn_b;
|
||||||
short btn_back, btn_start, btn_guide;
|
short btn_back, btn_start, btn_guide;
|
||||||
short btn_leftstick, btn_rightstick;
|
short btn_leftstick, btn_rightstick;
|
||||||
short btn_leftshoulder, btn_rightshoulder;
|
short btn_leftshoulder, btn_rightshoulder;
|
||||||
short btn_misc1;
|
|
||||||
short btn_paddle1, btn_paddle2, btn_paddle3, btn_paddle4;
|
short abs_lefttrigger, abs_righttrigger;
|
||||||
short btn_touchpad;
|
|
||||||
|
|
||||||
short abs_lefttrigger, abs_righttrigger;
|
|
||||||
short btn_lefttrigger, btn_righttrigger;
|
short btn_lefttrigger, btn_righttrigger;
|
||||||
|
|
||||||
/* next must be the last member after the list of mapping indices! */
|
|
||||||
struct mapping* next;
|
struct mapping* next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
675
src/input/sdl.c
675
src/input/sdl.c
@ -26,406 +26,60 @@
|
|||||||
#define QUIT_KEY SDLK_q
|
#define QUIT_KEY SDLK_q
|
||||||
#define QUIT_BUTTONS (PLAY_FLAG|BACK_FLAG|LB_FLAG|RB_FLAG)
|
#define QUIT_BUTTONS (PLAY_FLAG|BACK_FLAG|LB_FLAG|RB_FLAG)
|
||||||
#define FULLSCREEN_KEY SDLK_f
|
#define FULLSCREEN_KEY SDLK_f
|
||||||
#define UNGRAB_KEY SDLK_z
|
|
||||||
|
|
||||||
static const int SDL_TO_LI_BUTTON_MAP[] = {
|
|
||||||
A_FLAG, B_FLAG, X_FLAG, Y_FLAG,
|
|
||||||
BACK_FLAG, SPECIAL_FLAG, PLAY_FLAG,
|
|
||||||
LS_CLK_FLAG, RS_CLK_FLAG,
|
|
||||||
LB_FLAG, RB_FLAG,
|
|
||||||
UP_FLAG, DOWN_FLAG, LEFT_FLAG, RIGHT_FLAG,
|
|
||||||
MISC_FLAG,
|
|
||||||
PADDLE1_FLAG, PADDLE2_FLAG, PADDLE3_FLAG, PADDLE4_FLAG,
|
|
||||||
TOUCHPAD_FLAG,
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct _GAMEPAD_STATE {
|
typedef struct _GAMEPAD_STATE {
|
||||||
unsigned char leftTrigger, rightTrigger;
|
char leftTrigger, rightTrigger;
|
||||||
short leftStickX, leftStickY;
|
short leftStickX, leftStickY;
|
||||||
short rightStickX, rightStickY;
|
short rightStickX, rightStickY;
|
||||||
int buttons;
|
int buttons;
|
||||||
SDL_JoystickID sdl_id;
|
SDL_JoystickID sdl_id;
|
||||||
SDL_GameController* controller;
|
|
||||||
#if !SDL_VERSION_ATLEAST(2, 0, 9)
|
|
||||||
SDL_Haptic* haptic;
|
SDL_Haptic* haptic;
|
||||||
int haptic_effect_id;
|
int haptic_effect_id;
|
||||||
#endif
|
|
||||||
short id;
|
short id;
|
||||||
bool initialized;
|
bool initialized;
|
||||||
} GAMEPAD_STATE, *PGAMEPAD_STATE;
|
} GAMEPAD_STATE, *PGAMEPAD_STATE;
|
||||||
|
|
||||||
// Limited by number of bits in activeGamepadMask
|
static GAMEPAD_STATE gamepads[4];
|
||||||
#define MAX_GAMEPADS 16
|
|
||||||
|
|
||||||
static GAMEPAD_STATE gamepads[MAX_GAMEPADS];
|
|
||||||
|
|
||||||
|
static int keyboard_modifiers;
|
||||||
static int activeGamepadMask = 0;
|
static int activeGamepadMask = 0;
|
||||||
|
|
||||||
int sdl_gamepads = 0;
|
int sdl_gamepads = 0;
|
||||||
|
|
||||||
#define VK_0 0x30
|
static PGAMEPAD_STATE get_gamepad(SDL_JoystickID sdl_id) {
|
||||||
#define VK_A 0x41
|
for (int i = 0;i<4;i++) {
|
||||||
|
|
||||||
// These are real Windows VK_* codes
|
|
||||||
#ifndef VK_F1
|
|
||||||
#define VK_F1 0x70
|
|
||||||
#define VK_F13 0x7C
|
|
||||||
#define VK_NUMPAD0 0x60
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int vk_for_sdl_scancode(SDL_Scancode scancode) {
|
|
||||||
// Set keycode. We explicitly use scancode here because GFE will try to correct
|
|
||||||
// for AZERTY layouts on the host but it depends on receiving VK_ values matching
|
|
||||||
// a QWERTY layout to work.
|
|
||||||
if (scancode >= SDL_SCANCODE_1 && scancode <= SDL_SCANCODE_9) {
|
|
||||||
// SDL defines SDL_SCANCODE_0 > SDL_SCANCODE_9, so we need to handle that manually
|
|
||||||
return (scancode - SDL_SCANCODE_1) + VK_0 + 1;
|
|
||||||
}
|
|
||||||
else if (scancode >= SDL_SCANCODE_A && scancode <= SDL_SCANCODE_Z) {
|
|
||||||
return (scancode - SDL_SCANCODE_A) + VK_A;
|
|
||||||
}
|
|
||||||
else if (scancode >= SDL_SCANCODE_F1 && scancode <= SDL_SCANCODE_F12) {
|
|
||||||
return (scancode - SDL_SCANCODE_F1) + VK_F1;
|
|
||||||
}
|
|
||||||
else if (scancode >= SDL_SCANCODE_F13 && scancode <= SDL_SCANCODE_F24) {
|
|
||||||
return (scancode - SDL_SCANCODE_F13) + VK_F13;
|
|
||||||
}
|
|
||||||
else if (scancode >= SDL_SCANCODE_KP_1 && scancode <= SDL_SCANCODE_KP_9) {
|
|
||||||
// SDL defines SDL_SCANCODE_KP_0 > SDL_SCANCODE_KP_9, so we need to handle that manually
|
|
||||||
return (scancode - SDL_SCANCODE_KP_1) + VK_NUMPAD0 + 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
switch (scancode) {
|
|
||||||
case SDL_SCANCODE_BACKSPACE:
|
|
||||||
return 0x08;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_TAB:
|
|
||||||
return 0x09;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_CLEAR:
|
|
||||||
return 0x0C;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_KP_ENTER:
|
|
||||||
case SDL_SCANCODE_RETURN:
|
|
||||||
return 0x0D;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_PAUSE:
|
|
||||||
return 0x13;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_CAPSLOCK:
|
|
||||||
return 0x14;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_ESCAPE:
|
|
||||||
return 0x1B;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_SPACE:
|
|
||||||
return 0x20;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_PAGEUP:
|
|
||||||
return 0x21;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_PAGEDOWN:
|
|
||||||
return 0x22;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_END:
|
|
||||||
return 0x23;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_HOME:
|
|
||||||
return 0x24;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_LEFT:
|
|
||||||
return 0x25;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_UP:
|
|
||||||
return 0x26;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_RIGHT:
|
|
||||||
return 0x27;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_DOWN:
|
|
||||||
return 0x28;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_SELECT:
|
|
||||||
return 0x29;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_EXECUTE:
|
|
||||||
return 0x2B;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_PRINTSCREEN:
|
|
||||||
return 0x2C;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_INSERT:
|
|
||||||
return 0x2D;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_DELETE:
|
|
||||||
return 0x2E;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_HELP:
|
|
||||||
return 0x2F;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_KP_0:
|
|
||||||
// See comment above about why we only handle SDL_SCANCODE_KP_0 here
|
|
||||||
return VK_NUMPAD0;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_0:
|
|
||||||
// See comment above about why we only handle SDL_SCANCODE_0 here
|
|
||||||
return VK_0;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_KP_MULTIPLY:
|
|
||||||
return 0x6A;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_KP_PLUS:
|
|
||||||
return 0x6B;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_KP_COMMA:
|
|
||||||
return 0x6C;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_KP_MINUS:
|
|
||||||
return 0x6D;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_KP_PERIOD:
|
|
||||||
return 0x6E;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_KP_DIVIDE:
|
|
||||||
return 0x6F;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_NUMLOCKCLEAR:
|
|
||||||
return 0x90;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_SCROLLLOCK:
|
|
||||||
return 0x91;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_LSHIFT:
|
|
||||||
return 0xA0;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_RSHIFT:
|
|
||||||
return 0xA1;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_LCTRL:
|
|
||||||
return 0xA2;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_RCTRL:
|
|
||||||
return 0xA3;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_LALT:
|
|
||||||
return 0xA4;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_RALT:
|
|
||||||
return 0xA5;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_LGUI:
|
|
||||||
return 0x5B;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_RGUI:
|
|
||||||
return 0x5C;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_APPLICATION:
|
|
||||||
return 0x5D;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_AC_BACK:
|
|
||||||
return 0xA6;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_AC_FORWARD:
|
|
||||||
return 0xA7;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_AC_REFRESH:
|
|
||||||
return 0xA8;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_AC_STOP:
|
|
||||||
return 0xA9;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_AC_SEARCH:
|
|
||||||
return 0xAA;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_AC_BOOKMARKS:
|
|
||||||
return 0xAB;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_AC_HOME:
|
|
||||||
return 0xAC;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_SEMICOLON:
|
|
||||||
return 0xBA;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_EQUALS:
|
|
||||||
return 0xBB;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_COMMA:
|
|
||||||
return 0xBC;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_MINUS:
|
|
||||||
return 0xBD;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_PERIOD:
|
|
||||||
return 0xBE;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_SLASH:
|
|
||||||
return 0xBF;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_GRAVE:
|
|
||||||
return 0xC0;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_LEFTBRACKET:
|
|
||||||
return 0xDB;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_BACKSLASH:
|
|
||||||
return 0xDC;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_RIGHTBRACKET:
|
|
||||||
return 0xDD;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_APOSTROPHE:
|
|
||||||
return 0xDE;
|
|
||||||
|
|
||||||
case SDL_SCANCODE_NONUSBACKSLASH:
|
|
||||||
return 0xE2;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void send_controller_arrival(PGAMEPAD_STATE state) {
|
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 18)
|
|
||||||
unsigned int supportedButtonFlags = 0;
|
|
||||||
unsigned short capabilities = 0;
|
|
||||||
unsigned char type = LI_CTYPE_UNKNOWN;
|
|
||||||
|
|
||||||
for (int i = 0; i < SDL_arraysize(SDL_TO_LI_BUTTON_MAP); i++) {
|
|
||||||
if (SDL_GameControllerHasButton(state->controller, (SDL_GameControllerButton)i)) {
|
|
||||||
supportedButtonFlags |= SDL_TO_LI_BUTTON_MAP[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SDL_GameControllerGetBindForAxis(state->controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT).bindType == SDL_CONTROLLER_BINDTYPE_AXIS ||
|
|
||||||
SDL_GameControllerGetBindForAxis(state->controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT).bindType == SDL_CONTROLLER_BINDTYPE_AXIS)
|
|
||||||
capabilities |= LI_CCAP_ANALOG_TRIGGERS;
|
|
||||||
if (SDL_GameControllerHasRumble(state->controller))
|
|
||||||
capabilities |= LI_CCAP_RUMBLE;
|
|
||||||
if (SDL_GameControllerHasRumbleTriggers(state->controller))
|
|
||||||
capabilities |= LI_CCAP_TRIGGER_RUMBLE;
|
|
||||||
if (SDL_GameControllerGetNumTouchpads(state->controller) > 0)
|
|
||||||
capabilities |= LI_CCAP_TOUCHPAD;
|
|
||||||
if (SDL_GameControllerHasSensor(state->controller, SDL_SENSOR_ACCEL))
|
|
||||||
capabilities |= LI_CCAP_ACCEL;
|
|
||||||
if (SDL_GameControllerHasSensor(state->controller, SDL_SENSOR_GYRO))
|
|
||||||
capabilities |= LI_CCAP_GYRO;
|
|
||||||
if (SDL_GameControllerHasLED(state->controller))
|
|
||||||
capabilities |= LI_CCAP_RGB_LED;
|
|
||||||
|
|
||||||
switch (SDL_GameControllerGetType(state->controller)) {
|
|
||||||
case SDL_CONTROLLER_TYPE_XBOX360:
|
|
||||||
case SDL_CONTROLLER_TYPE_XBOXONE:
|
|
||||||
type = LI_CTYPE_XBOX;
|
|
||||||
break;
|
|
||||||
case SDL_CONTROLLER_TYPE_PS3:
|
|
||||||
case SDL_CONTROLLER_TYPE_PS4:
|
|
||||||
case SDL_CONTROLLER_TYPE_PS5:
|
|
||||||
type = LI_CTYPE_PS;
|
|
||||||
break;
|
|
||||||
case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO:
|
|
||||||
#if SDL_VERSION_ATLEAST(2, 24, 0)
|
|
||||||
case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT:
|
|
||||||
case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT:
|
|
||||||
case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR:
|
|
||||||
#endif
|
|
||||||
type = LI_CTYPE_NINTENDO;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
LiSendControllerArrivalEvent(state->id, activeGamepadMask, type, supportedButtonFlags, capabilities);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static PGAMEPAD_STATE get_gamepad(SDL_JoystickID sdl_id, bool add) {
|
|
||||||
// See if a gamepad already exists
|
|
||||||
for (int i = 0;i<MAX_GAMEPADS;i++) {
|
|
||||||
if (gamepads[i].initialized && gamepads[i].sdl_id == sdl_id)
|
|
||||||
return &gamepads[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!add)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
for (int i = 0;i<MAX_GAMEPADS;i++) {
|
|
||||||
if (!gamepads[i].initialized) {
|
if (!gamepads[i].initialized) {
|
||||||
gamepads[i].sdl_id = sdl_id;
|
gamepads[i].sdl_id = sdl_id;
|
||||||
gamepads[i].id = i;
|
gamepads[i].id = i;
|
||||||
gamepads[i].initialized = true;
|
gamepads[i].initialized = true;
|
||||||
|
|
||||||
activeGamepadMask |= (1 << i);
|
activeGamepadMask |= (1 << i);
|
||||||
|
|
||||||
return &gamepads[i];
|
return &gamepads[i];
|
||||||
}
|
} else if (gamepads[i].sdl_id == sdl_id)
|
||||||
|
return &gamepads[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
return &gamepads[0];
|
return &gamepads[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
static void add_gamepad(int joystick_index) {
|
static void init_gamepad(int joystick_index) {
|
||||||
SDL_GameController* controller = SDL_GameControllerOpen(joystick_index);
|
if (SDL_IsGameController(joystick_index)) {
|
||||||
if (!controller) {
|
sdl_gamepads++;
|
||||||
fprintf(stderr, "Could not open gamecontroller %i: %s\n", joystick_index, SDL_GetError());
|
SDL_GameController* controller = SDL_GameControllerOpen(joystick_index);
|
||||||
return;
|
if (!controller) {
|
||||||
}
|
fprintf(stderr, "Could not open gamecontroller %i: %s\n", joystick_index, SDL_GetError());
|
||||||
|
return;
|
||||||
SDL_Joystick* joystick = SDL_GameControllerGetJoystick(controller);
|
|
||||||
SDL_JoystickID joystick_id = SDL_JoystickInstanceID(joystick);
|
|
||||||
|
|
||||||
// Check if we have already set up a state for this gamepad
|
|
||||||
PGAMEPAD_STATE state = get_gamepad(joystick_id, false);
|
|
||||||
if (state) {
|
|
||||||
// This was probably a gamepad added during initialization, so we've already
|
|
||||||
// got state set up. However, we still need to inform the host about it, since
|
|
||||||
// we couldn't do that during initialization (since we weren't connected yet).
|
|
||||||
send_controller_arrival(state);
|
|
||||||
|
|
||||||
SDL_GameControllerClose(controller);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new gamepad state
|
|
||||||
state = get_gamepad(joystick_id, true);
|
|
||||||
state->controller = controller;
|
|
||||||
|
|
||||||
#if !SDL_VERSION_ATLEAST(2, 0, 9)
|
|
||||||
state->haptic = SDL_HapticOpenFromJoystick(joystick);
|
|
||||||
if (haptic && (SDL_HapticQuery(state->haptic) & SDL_HAPTIC_LEFTRIGHT) == 0) {
|
|
||||||
SDL_HapticClose(state->haptic);
|
|
||||||
state->haptic = NULL;
|
|
||||||
}
|
|
||||||
state->haptic_effect_id = -1;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Send the controller arrival event to the host
|
|
||||||
send_controller_arrival(state);
|
|
||||||
|
|
||||||
sdl_gamepads++;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void remove_gamepad(SDL_JoystickID sdl_id) {
|
|
||||||
for (int i = 0;i<MAX_GAMEPADS;i++) {
|
|
||||||
if (gamepads[i].initialized && gamepads[i].sdl_id == sdl_id) {
|
|
||||||
#if !SDL_VERSION_ATLEAST(2, 0, 9)
|
|
||||||
if (gamepads[i].haptic_effect_id >= 0) {
|
|
||||||
SDL_HapticDestroyEffect(gamepads[i].haptic, gamepads[i].haptic_effect_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (gamepads[i].haptic) {
|
|
||||||
SDL_HapticClose(gamepads[i].haptic);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SDL_GameControllerClose(gamepads[i].controller);
|
|
||||||
|
|
||||||
// This will cause disconnection of the virtual controller on the host PC
|
|
||||||
activeGamepadMask &= ~(1 << i);
|
|
||||||
LiSendMultiControllerEvent(i, activeGamepadMask, 0, 0, 0, 0, 0, 0, 0);
|
|
||||||
|
|
||||||
memset(&gamepads[i], 0, sizeof(*gamepads));
|
|
||||||
sdl_gamepads--;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SDL_Joystick* joystick = SDL_GameControllerGetJoystick(controller);
|
||||||
|
SDL_Haptic* haptic = SDL_HapticOpenFromJoystick(joystick);
|
||||||
|
if (haptic && (SDL_HapticQuery(haptic) & SDL_HAPTIC_LEFTRIGHT) == 0) {
|
||||||
|
SDL_HapticClose(haptic);
|
||||||
|
haptic = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_JoystickID sdl_id = SDL_JoystickInstanceID(joystick);
|
||||||
|
PGAMEPAD_STATE state = get_gamepad(joystick_index);
|
||||||
|
state->haptic = haptic;
|
||||||
|
state->haptic_effect_id = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -433,41 +87,22 @@ void sdlinput_init(char* mappings) {
|
|||||||
memset(gamepads, 0, sizeof(gamepads));
|
memset(gamepads, 0, sizeof(gamepads));
|
||||||
|
|
||||||
SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
|
SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
|
||||||
#if !SDL_VERSION_ATLEAST(2, 0, 9)
|
|
||||||
SDL_InitSubSystem(SDL_INIT_HAPTIC);
|
SDL_InitSubSystem(SDL_INIT_HAPTIC);
|
||||||
#endif
|
|
||||||
SDL_GameControllerAddMappingsFromFile(mappings);
|
SDL_GameControllerAddMappingsFromFile(mappings);
|
||||||
|
|
||||||
// Add game controllers here to ensure an accurate count
|
for (int i = 0; i < SDL_NumJoysticks(); ++i)
|
||||||
// goes to the host when starting a new session.
|
init_gamepad(i);
|
||||||
for (int i = 0; i < SDL_NumJoysticks(); ++i) {
|
|
||||||
if (SDL_IsGameController(i))
|
|
||||||
add_gamepad(i);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int sdlinput_handle_event(SDL_Window* window, SDL_Event* event) {
|
int sdlinput_handle_event(SDL_Event* event) {
|
||||||
int button = 0;
|
int button = 0;
|
||||||
unsigned char touchEventType;
|
|
||||||
PGAMEPAD_STATE gamepad;
|
PGAMEPAD_STATE gamepad;
|
||||||
switch (event->type) {
|
switch (event->type) {
|
||||||
case SDL_MOUSEMOTION:
|
case SDL_MOUSEMOTION:
|
||||||
if (SDL_GetRelativeMouseMode())
|
LiSendMouseMoveEvent(event->motion.xrel, event->motion.yrel);
|
||||||
LiSendMouseMoveEvent(event->motion.xrel, event->motion.yrel);
|
|
||||||
else {
|
|
||||||
int w, h;
|
|
||||||
SDL_GetWindowSize(window, &w, &h);
|
|
||||||
LiSendMousePositionEvent(event->motion.x, event->motion.y, w, h);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case SDL_MOUSEWHEEL:
|
case SDL_MOUSEWHEEL:
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 18)
|
|
||||||
LiSendHighResHScrollEvent((short)(event->wheel.preciseX * 120)); // WHEEL_DELTA
|
|
||||||
LiSendHighResScrollEvent((short)(event->wheel.preciseY * 120)); // WHEEL_DELTA
|
|
||||||
#else
|
|
||||||
LiSendHScrollEvent(event->wheel.x);
|
|
||||||
LiSendScrollEvent(event->wheel.y);
|
LiSendScrollEvent(event->wheel.y);
|
||||||
#endif
|
|
||||||
break;
|
break;
|
||||||
case SDL_MOUSEBUTTONUP:
|
case SDL_MOUSEBUTTONUP:
|
||||||
case SDL_MOUSEBUTTONDOWN:
|
case SDL_MOUSEBUTTONDOWN:
|
||||||
@ -492,81 +127,78 @@ int sdlinput_handle_event(SDL_Window* window, SDL_Event* event) {
|
|||||||
if (button != 0)
|
if (button != 0)
|
||||||
LiSendMouseButtonEvent(event->type==SDL_MOUSEBUTTONDOWN?BUTTON_ACTION_PRESS:BUTTON_ACTION_RELEASE, button);
|
LiSendMouseButtonEvent(event->type==SDL_MOUSEBUTTONDOWN?BUTTON_ACTION_PRESS:BUTTON_ACTION_RELEASE, button);
|
||||||
|
|
||||||
return 0;
|
return SDL_MOUSE_GRAB;
|
||||||
case SDL_KEYDOWN:
|
case SDL_KEYDOWN:
|
||||||
case SDL_KEYUP:
|
case SDL_KEYUP:
|
||||||
button = vk_for_sdl_scancode(event->key.keysym.scancode);
|
button = event->key.keysym.sym;
|
||||||
|
if (button >= 0x21 && button <= 0x2f)
|
||||||
|
button = keyCodes1[button - 0x21];
|
||||||
|
else if (button >= 0x3a && button <= 0x40)
|
||||||
|
button = keyCodes2[button - 0x3a];
|
||||||
|
else if (button >= 0x5b && button <= 0x60)
|
||||||
|
button = keyCodes3[button - 0x5b];
|
||||||
|
else if (button >= 0x40000039 && button < 0x40000039 + sizeof(keyCodes4))
|
||||||
|
button = keyCodes4[button - 0x40000039];
|
||||||
|
else if (button >= 0x400000E0 && button <= 0x400000E7)
|
||||||
|
button = keyCodes5[button - 0x400000E0];
|
||||||
|
else if (button >= 0x61 && button <= 0x7a)
|
||||||
|
button -= 0x20;
|
||||||
|
else if (button == 0x7f)
|
||||||
|
button = 0x2e;
|
||||||
|
|
||||||
int modifiers = 0;
|
int modifier = 0;
|
||||||
if (event->key.keysym.mod & KMOD_CTRL) {
|
switch (event->key.keysym.sym) {
|
||||||
modifiers |= MODIFIER_CTRL;
|
case SDLK_RSHIFT:
|
||||||
}
|
case SDLK_LSHIFT:
|
||||||
if (event->key.keysym.mod & KMOD_ALT) {
|
modifier = MODIFIER_SHIFT;
|
||||||
modifiers |= MODIFIER_ALT;
|
break;
|
||||||
}
|
case SDLK_RALT:
|
||||||
if (event->key.keysym.mod & KMOD_SHIFT) {
|
case SDLK_LALT:
|
||||||
modifiers |= MODIFIER_SHIFT;
|
modifier = MODIFIER_ALT;
|
||||||
}
|
break;
|
||||||
if (event->key.keysym.mod & KMOD_GUI) {
|
case SDLK_RCTRL:
|
||||||
modifiers |= MODIFIER_META;
|
case SDLK_LCTRL:
|
||||||
|
modifier = MODIFIER_CTRL;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
LiSendKeyboardEvent(0x80 << 8 | button, event->type==SDL_KEYDOWN?KEY_ACTION_DOWN:KEY_ACTION_UP, modifiers);
|
if (modifier != 0) {
|
||||||
|
if (event->type==SDL_KEYDOWN)
|
||||||
|
keyboard_modifiers |= modifier;
|
||||||
|
else
|
||||||
|
keyboard_modifiers &= ~modifier;
|
||||||
|
}
|
||||||
|
|
||||||
// Quit the stream if all the required quit keys are down
|
// Quit the stream if all the required quit keys are down
|
||||||
if ((modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS && event->key.keysym.sym == QUIT_KEY && event->type==SDL_KEYUP)
|
if ((keyboard_modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS && event->key.keysym.sym == QUIT_KEY && event->type==SDL_KEYUP)
|
||||||
return SDL_QUIT_APPLICATION;
|
return SDL_QUIT_APPLICATION;
|
||||||
else if ((modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS && event->key.keysym.sym == FULLSCREEN_KEY && event->type==SDL_KEYUP)
|
else if ((keyboard_modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS && event->key.keysym.sym == FULLSCREEN_KEY && event->type==SDL_KEYUP)
|
||||||
return SDL_TOGGLE_FULLSCREEN;
|
return SDL_TOGGLE_FULLSCREEN;
|
||||||
else if ((modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS && event->key.keysym.sym == UNGRAB_KEY && event->type==SDL_KEYUP)
|
else if ((keyboard_modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS)
|
||||||
return SDL_GetRelativeMouseMode() ? SDL_MOUSE_UNGRAB : SDL_MOUSE_GRAB;
|
return SDL_MOUSE_UNGRAB;
|
||||||
break;
|
|
||||||
case SDL_FINGERDOWN:
|
|
||||||
case SDL_FINGERMOTION:
|
|
||||||
case SDL_FINGERUP:
|
|
||||||
switch (event->type) {
|
|
||||||
case SDL_FINGERDOWN:
|
|
||||||
touchEventType = LI_TOUCH_EVENT_DOWN;
|
|
||||||
break;
|
|
||||||
case SDL_FINGERMOTION:
|
|
||||||
touchEventType = LI_TOUCH_EVENT_MOVE;
|
|
||||||
break;
|
|
||||||
case SDL_FINGERUP:
|
|
||||||
touchEventType = LI_TOUCH_EVENT_UP;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return SDL_NOTHING;
|
|
||||||
}
|
|
||||||
|
|
||||||
// These are already window-relative normalized coordinates, so we just need to clamp them
|
LiSendKeyboardEvent(0x80 << 8 | button, event->type==SDL_KEYDOWN?KEY_ACTION_DOWN:KEY_ACTION_UP, keyboard_modifiers);
|
||||||
event->tfinger.x = SDL_max(SDL_min(1.0f, event->tfinger.x), 0.0f);
|
|
||||||
event->tfinger.y = SDL_max(SDL_min(1.0f, event->tfinger.y), 0.0f);
|
|
||||||
|
|
||||||
LiSendTouchEvent(touchEventType, event->tfinger.fingerId, event->tfinger.x, event->tfinger.y,
|
|
||||||
event->tfinger.pressure, 0.0f, 0.0f, LI_ROT_UNKNOWN);
|
|
||||||
break;
|
break;
|
||||||
case SDL_CONTROLLERAXISMOTION:
|
case SDL_CONTROLLERAXISMOTION:
|
||||||
gamepad = get_gamepad(event->caxis.which, false);
|
gamepad = get_gamepad(event->caxis.which);
|
||||||
if (!gamepad)
|
|
||||||
return SDL_NOTHING;
|
|
||||||
switch (event->caxis.axis) {
|
switch (event->caxis.axis) {
|
||||||
case SDL_CONTROLLER_AXIS_LEFTX:
|
case SDL_CONTROLLER_AXIS_LEFTX:
|
||||||
gamepad->leftStickX = event->caxis.value;
|
gamepad->leftStickX = event->caxis.value;
|
||||||
break;
|
break;
|
||||||
case SDL_CONTROLLER_AXIS_LEFTY:
|
case SDL_CONTROLLER_AXIS_LEFTY:
|
||||||
gamepad->leftStickY = -SDL_max(event->caxis.value, (short)-32767);
|
gamepad->leftStickY = -event->caxis.value - 1;
|
||||||
break;
|
break;
|
||||||
case SDL_CONTROLLER_AXIS_RIGHTX:
|
case SDL_CONTROLLER_AXIS_RIGHTX:
|
||||||
gamepad->rightStickX = event->caxis.value;
|
gamepad->rightStickX = event->caxis.value;
|
||||||
break;
|
break;
|
||||||
case SDL_CONTROLLER_AXIS_RIGHTY:
|
case SDL_CONTROLLER_AXIS_RIGHTY:
|
||||||
gamepad->rightStickY = -SDL_max(event->caxis.value, (short)-32767);
|
gamepad->rightStickY = -event->caxis.value - 1;
|
||||||
break;
|
break;
|
||||||
case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
|
case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
|
||||||
gamepad->leftTrigger = (unsigned char)(event->caxis.value * 255UL / 32767);
|
gamepad->leftTrigger = (event->caxis.value >> 8) + 127;
|
||||||
break;
|
break;
|
||||||
case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
|
case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
|
||||||
gamepad->rightTrigger = (unsigned char)(event->caxis.value * 255UL / 32767);
|
gamepad->rightTrigger = (event->caxis.value >> 8) + 127;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return SDL_NOTHING;
|
return SDL_NOTHING;
|
||||||
@ -575,86 +207,76 @@ int sdlinput_handle_event(SDL_Window* window, SDL_Event* event) {
|
|||||||
break;
|
break;
|
||||||
case SDL_CONTROLLERBUTTONDOWN:
|
case SDL_CONTROLLERBUTTONDOWN:
|
||||||
case SDL_CONTROLLERBUTTONUP:
|
case SDL_CONTROLLERBUTTONUP:
|
||||||
gamepad = get_gamepad(event->cbutton.which, false);
|
gamepad = get_gamepad(event->cbutton.which);
|
||||||
if (!gamepad)
|
switch (event->cbutton.button) {
|
||||||
|
case SDL_CONTROLLER_BUTTON_A:
|
||||||
|
button = A_FLAG;
|
||||||
|
break;
|
||||||
|
case SDL_CONTROLLER_BUTTON_B:
|
||||||
|
button = B_FLAG;
|
||||||
|
break;
|
||||||
|
case SDL_CONTROLLER_BUTTON_Y:
|
||||||
|
button = Y_FLAG;
|
||||||
|
break;
|
||||||
|
case SDL_CONTROLLER_BUTTON_X:
|
||||||
|
button = X_FLAG;
|
||||||
|
break;
|
||||||
|
case SDL_CONTROLLER_BUTTON_DPAD_UP:
|
||||||
|
button = UP_FLAG;
|
||||||
|
break;
|
||||||
|
case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
|
||||||
|
button = DOWN_FLAG;
|
||||||
|
break;
|
||||||
|
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
|
||||||
|
button = RIGHT_FLAG;
|
||||||
|
break;
|
||||||
|
case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
|
||||||
|
button = LEFT_FLAG;
|
||||||
|
break;
|
||||||
|
case SDL_CONTROLLER_BUTTON_BACK:
|
||||||
|
button = BACK_FLAG;
|
||||||
|
break;
|
||||||
|
case SDL_CONTROLLER_BUTTON_START:
|
||||||
|
button = PLAY_FLAG;
|
||||||
|
break;
|
||||||
|
case SDL_CONTROLLER_BUTTON_GUIDE:
|
||||||
|
button = SPECIAL_FLAG;
|
||||||
|
break;
|
||||||
|
case SDL_CONTROLLER_BUTTON_LEFTSTICK:
|
||||||
|
button = LS_CLK_FLAG;
|
||||||
|
break;
|
||||||
|
case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
|
||||||
|
button = RS_CLK_FLAG;
|
||||||
|
break;
|
||||||
|
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
|
||||||
|
button = LB_FLAG;
|
||||||
|
break;
|
||||||
|
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
|
||||||
|
button = RB_FLAG;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
return SDL_NOTHING;
|
return SDL_NOTHING;
|
||||||
if (event->cbutton.button >= SDL_arraysize(SDL_TO_LI_BUTTON_MAP))
|
}
|
||||||
return SDL_NOTHING;
|
|
||||||
|
|
||||||
if (event->type == SDL_CONTROLLERBUTTONDOWN)
|
if (event->type == SDL_CONTROLLERBUTTONDOWN)
|
||||||
gamepad->buttons |= SDL_TO_LI_BUTTON_MAP[event->cbutton.button];
|
gamepad->buttons |= button;
|
||||||
else
|
else
|
||||||
gamepad->buttons &= ~SDL_TO_LI_BUTTON_MAP[event->cbutton.button];
|
gamepad->buttons &= ~button;
|
||||||
|
|
||||||
if ((gamepad->buttons & QUIT_BUTTONS) == QUIT_BUTTONS)
|
if ((gamepad->buttons & QUIT_BUTTONS) == QUIT_BUTTONS)
|
||||||
return SDL_QUIT_APPLICATION;
|
return SDL_QUIT_APPLICATION;
|
||||||
|
|
||||||
LiSendMultiControllerEvent(gamepad->id, activeGamepadMask, gamepad->buttons, gamepad->leftTrigger, gamepad->rightTrigger, gamepad->leftStickX, gamepad->leftStickY, gamepad->rightStickX, gamepad->rightStickY);
|
LiSendMultiControllerEvent(gamepad->id, activeGamepadMask, gamepad->buttons, gamepad->leftTrigger, gamepad->rightTrigger, gamepad->leftStickX, gamepad->leftStickY, gamepad->rightStickX, gamepad->rightStickY);
|
||||||
break;
|
break;
|
||||||
case SDL_CONTROLLERDEVICEADDED:
|
|
||||||
add_gamepad(event->cdevice.which);
|
|
||||||
break;
|
|
||||||
case SDL_CONTROLLERDEVICEREMOVED:
|
|
||||||
remove_gamepad(event->cdevice.which);
|
|
||||||
break;
|
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 14)
|
|
||||||
case SDL_CONTROLLERSENSORUPDATE:
|
|
||||||
gamepad = get_gamepad(event->csensor.which, false);
|
|
||||||
if (!gamepad)
|
|
||||||
return SDL_NOTHING;
|
|
||||||
switch (event->csensor.sensor) {
|
|
||||||
case SDL_SENSOR_ACCEL:
|
|
||||||
LiSendControllerMotionEvent(gamepad->id, LI_MOTION_TYPE_ACCEL, event->csensor.data[0], event->csensor.data[1], event->csensor.data[2]);
|
|
||||||
break;
|
|
||||||
case SDL_SENSOR_GYRO:
|
|
||||||
// Convert rad/s to deg/s
|
|
||||||
LiSendControllerMotionEvent(gamepad->id, LI_MOTION_TYPE_GYRO,
|
|
||||||
event->csensor.data[0] * 57.2957795f,
|
|
||||||
event->csensor.data[1] * 57.2957795f,
|
|
||||||
event->csensor.data[2] * 57.2957795f);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SDL_CONTROLLERTOUCHPADDOWN:
|
|
||||||
case SDL_CONTROLLERTOUCHPADUP:
|
|
||||||
case SDL_CONTROLLERTOUCHPADMOTION:
|
|
||||||
gamepad = get_gamepad(event->ctouchpad.which, false);
|
|
||||||
if (!gamepad)
|
|
||||||
return SDL_NOTHING;
|
|
||||||
switch (event->type) {
|
|
||||||
case SDL_CONTROLLERTOUCHPADDOWN:
|
|
||||||
touchEventType = LI_TOUCH_EVENT_DOWN;
|
|
||||||
break;
|
|
||||||
case SDL_CONTROLLERTOUCHPADUP:
|
|
||||||
touchEventType = LI_TOUCH_EVENT_UP;
|
|
||||||
break;
|
|
||||||
case SDL_CONTROLLERTOUCHPADMOTION:
|
|
||||||
touchEventType = LI_TOUCH_EVENT_MOVE;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return SDL_NOTHING;
|
|
||||||
}
|
|
||||||
LiSendControllerTouchEvent(gamepad->id, touchEventType, event->ctouchpad.finger,
|
|
||||||
event->ctouchpad.x, event->ctouchpad.y, event->ctouchpad.pressure);
|
|
||||||
break;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return SDL_NOTHING;
|
return SDL_NOTHING;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sdlinput_rumble(unsigned short controller_id, unsigned short low_freq_motor, unsigned short high_freq_motor) {
|
void sdlinput_rumble(unsigned short controller_id, unsigned short low_freq_motor, unsigned short high_freq_motor) {
|
||||||
if (controller_id >= MAX_GAMEPADS)
|
if (controller_id >= 4)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
PGAMEPAD_STATE state = &gamepads[controller_id];
|
PGAMEPAD_STATE state = &gamepads[controller_id];
|
||||||
|
|
||||||
if (!state->initialized)
|
|
||||||
return;
|
|
||||||
|
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 9)
|
|
||||||
SDL_GameControllerRumble(state->controller, low_freq_motor, high_freq_motor, 30000);
|
|
||||||
#else
|
|
||||||
SDL_Haptic* haptic = state->haptic;
|
SDL_Haptic* haptic = state->haptic;
|
||||||
if (!haptic)
|
if (!haptic)
|
||||||
return;
|
return;
|
||||||
@ -677,45 +299,4 @@ void sdlinput_rumble(unsigned short controller_id, unsigned short low_freq_motor
|
|||||||
state->haptic_effect_id = SDL_HapticNewEffect(haptic, &effect);
|
state->haptic_effect_id = SDL_HapticNewEffect(haptic, &effect);
|
||||||
if (state->haptic_effect_id >= 0)
|
if (state->haptic_effect_id >= 0)
|
||||||
SDL_HapticRunEffect(haptic, state->haptic_effect_id, 1);
|
SDL_HapticRunEffect(haptic, state->haptic_effect_id, 1);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void sdlinput_rumble_triggers(unsigned short controller_id, unsigned short left_trigger, unsigned short right_trigger) {
|
|
||||||
PGAMEPAD_STATE state = &gamepads[controller_id];
|
|
||||||
|
|
||||||
if (!state->initialized)
|
|
||||||
return;
|
|
||||||
|
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 14)
|
|
||||||
SDL_GameControllerRumbleTriggers(state->controller, left_trigger, right_trigger, 30000);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void sdlinput_set_motion_event_state(unsigned short controller_id, unsigned char motion_type, unsigned short report_rate_hz) {
|
|
||||||
PGAMEPAD_STATE state = &gamepads[controller_id];
|
|
||||||
|
|
||||||
if (!state->initialized)
|
|
||||||
return;
|
|
||||||
|
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 14)
|
|
||||||
switch (motion_type) {
|
|
||||||
case LI_MOTION_TYPE_ACCEL:
|
|
||||||
SDL_GameControllerSetSensorEnabled(state->controller, SDL_SENSOR_ACCEL, report_rate_hz ? SDL_TRUE : SDL_FALSE);
|
|
||||||
break;
|
|
||||||
case LI_MOTION_TYPE_GYRO:
|
|
||||||
SDL_GameControllerSetSensorEnabled(state->controller, SDL_SENSOR_GYRO, report_rate_hz ? SDL_TRUE : SDL_FALSE);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void sdlinput_set_controller_led(unsigned short controller_id, unsigned char r, unsigned char g, unsigned char b) {
|
|
||||||
PGAMEPAD_STATE state = &gamepads[controller_id];
|
|
||||||
|
|
||||||
if (!state->initialized)
|
|
||||||
return;
|
|
||||||
|
|
||||||
#if SDL_VERSION_ATLEAST(2, 0, 14)
|
|
||||||
SDL_GameControllerSetLED(state->controller, r, g, b);
|
|
||||||
#endif
|
|
||||||
}
|
|
@ -22,9 +22,83 @@
|
|||||||
|
|
||||||
extern int sdl_gamepads;
|
extern int sdl_gamepads;
|
||||||
|
|
||||||
|
static const short keyCodes1[] = {
|
||||||
|
0, //SDLK_EXCLAIM
|
||||||
|
0, //SDLK_QUOTEDBL
|
||||||
|
0, //SDLK_HASH
|
||||||
|
0, //SDLK_DOLLAR
|
||||||
|
0, //SDLK_PERCENT
|
||||||
|
0, //SDLK_AMPERSAND
|
||||||
|
0xDE, //SDLK_QUOTE
|
||||||
|
0, //SDLK_LEFTPAREN
|
||||||
|
0, //SDLK_RIGHTPAREN
|
||||||
|
0, //SDLK_ASTERISK
|
||||||
|
0, //SDLK_PLUS
|
||||||
|
0xBC, //SDLK_COMMA
|
||||||
|
0xBD, //SDLK_MINUS
|
||||||
|
0xBE, //SDLK_PERIOD
|
||||||
|
0xBF, //SDLK_SLASH
|
||||||
|
};
|
||||||
|
|
||||||
|
static const short keyCodes2[] = {
|
||||||
|
0, //SDLK_COLON
|
||||||
|
0xBA, //SDLK_SEMICOLON
|
||||||
|
0, //SDLK_LESS
|
||||||
|
0xBB, //SDLK_EQUALS
|
||||||
|
0, //SDLK_GREATER
|
||||||
|
0, //SDLK_QUESTION
|
||||||
|
0, //SDLK_AT
|
||||||
|
};
|
||||||
|
|
||||||
|
static const short keyCodes3[] = {
|
||||||
|
0xDB, //SDLK_LEFTBRACKET
|
||||||
|
0xDC, //SDLK_BACKSLASH
|
||||||
|
0xDD, //SDLK_RIGHTBRACKET
|
||||||
|
0, //SDLK_CARET
|
||||||
|
0, //SDLK_UNDERSCORE
|
||||||
|
0xC0, //SDLK_BACKQUOTE
|
||||||
|
};
|
||||||
|
|
||||||
|
static const short keyCodes4[] = {
|
||||||
|
0x14, //SDLK_CAPSLOCK
|
||||||
|
0x70, //SDLK_F1
|
||||||
|
0x71, //SDLK_F2
|
||||||
|
0x72, //SDLK_F3
|
||||||
|
0x73, //SDLK_F4
|
||||||
|
0x74, //SDLK_F5
|
||||||
|
0x75, //SDLK_F6
|
||||||
|
0x76, //SDLK_F7
|
||||||
|
0x77, //SDLK_F8
|
||||||
|
0x78, //SDLK_F9
|
||||||
|
0x79, //SDLK_F10
|
||||||
|
0x7A, //SDLK_F11
|
||||||
|
0x7B, //SDLK_F12
|
||||||
|
0, //SDLK_PRINTSCREEN
|
||||||
|
0x91, //SDLK_SCROLLLOCK
|
||||||
|
0x13, //SDLK_PAUSE
|
||||||
|
0x9B, //SDLK_INSERT
|
||||||
|
0x24, //SDLK_HOME
|
||||||
|
0x21, //SDLK_PAGEUP
|
||||||
|
0, //Not used
|
||||||
|
0x23, //SDLK_END
|
||||||
|
0x22, //SDLK_PAGEDOWN
|
||||||
|
0x27, //SDLK_RIGHT
|
||||||
|
0x25, //SDLK_LEFT
|
||||||
|
0x28, //SDLK_DOWN
|
||||||
|
0x26, //SDLK_UP
|
||||||
|
};
|
||||||
|
|
||||||
|
static const short keyCodes5[] = {
|
||||||
|
0x11, //SDLK_LCTRL
|
||||||
|
0x10, //SDLK_LSHIFT
|
||||||
|
0x12, //SDLK_LALT
|
||||||
|
0x5B, //SDLK_LGUI
|
||||||
|
0x11, //SDLK_LRCTRL
|
||||||
|
0x10, //SDLK_RSHIFT
|
||||||
|
0x12, //SDLK_RALT
|
||||||
|
0x5C, //SDLK_RGUI
|
||||||
|
};
|
||||||
|
|
||||||
void sdlinput_init(char* mappings);
|
void sdlinput_init(char* mappings);
|
||||||
int sdlinput_handle_event(SDL_Window* window, SDL_Event* event);
|
int sdlinput_handle_event(SDL_Event* event);
|
||||||
void sdlinput_rumble(unsigned short controller_id, unsigned short low_freq_motor, unsigned short high_freq_motor);
|
void sdlinput_rumble(unsigned short controller_id, unsigned short low_freq_motor, unsigned short high_freq_motor);
|
||||||
void sdlinput_rumble_triggers(unsigned short controller_id, unsigned short left_trigger, unsigned short right_trigger);
|
|
||||||
void sdlinput_set_motion_event_state(unsigned short controller_id, unsigned char motion_type, unsigned short report_rate_hz);
|
|
||||||
void sdlinput_set_controller_led(unsigned short controller_id, unsigned char r, unsigned char g, unsigned char b);
|
|
||||||
|
@ -36,7 +36,7 @@ static struct mapping* defaultMappings;
|
|||||||
|
|
||||||
static struct udev *udev;
|
static struct udev *udev;
|
||||||
static struct udev_monitor *udev_mon;
|
static struct udev_monitor *udev_mon;
|
||||||
static int inputRotate;
|
static int udev_fd;
|
||||||
|
|
||||||
static int udev_handle(int fd) {
|
static int udev_handle(int fd) {
|
||||||
struct udev_device *dev = udev_monitor_receive_device(udev_mon);
|
struct udev_device *dev = udev_monitor_receive_device(udev_mon);
|
||||||
@ -46,7 +46,7 @@ static int udev_handle(int fd) {
|
|||||||
const char *devnode = udev_device_get_devnode(dev);
|
const char *devnode = udev_device_get_devnode(dev);
|
||||||
int id;
|
int id;
|
||||||
if (devnode != NULL && sscanf(devnode, "/dev/input/event%d", &id) == 1) {
|
if (devnode != NULL && sscanf(devnode, "/dev/input/event%d", &id) == 1) {
|
||||||
evdev_create(devnode, defaultMappings, debug, inputRotate);
|
evdev_create(devnode, defaultMappings, debug);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
udev_device_unref(dev);
|
udev_device_unref(dev);
|
||||||
@ -54,7 +54,7 @@ static int udev_handle(int fd) {
|
|||||||
return LOOP_OK;
|
return LOOP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void udev_init(bool autoload, struct mapping* mappings, bool verbose, int rotate) {
|
void udev_init(bool autoload, struct mapping* mappings, bool verbose) {
|
||||||
udev = udev_new();
|
udev = udev_new();
|
||||||
debug = verbose;
|
debug = verbose;
|
||||||
if (!udev) {
|
if (!udev) {
|
||||||
@ -76,7 +76,7 @@ void udev_init(bool autoload, struct mapping* mappings, bool verbose, int rotate
|
|||||||
const char *devnode = udev_device_get_devnode(dev);
|
const char *devnode = udev_device_get_devnode(dev);
|
||||||
int id;
|
int id;
|
||||||
if (devnode != NULL && sscanf(devnode, "/dev/input/event%d", &id) == 1) {
|
if (devnode != NULL && sscanf(devnode, "/dev/input/event%d", &id) == 1) {
|
||||||
evdev_create(devnode, mappings, verbose, rotate);
|
evdev_create(devnode, mappings, verbose);
|
||||||
}
|
}
|
||||||
udev_device_unref(dev);
|
udev_device_unref(dev);
|
||||||
}
|
}
|
||||||
@ -89,13 +89,11 @@ void udev_init(bool autoload, struct mapping* mappings, bool verbose, int rotate
|
|||||||
udev_monitor_enable_receiving(udev_mon);
|
udev_monitor_enable_receiving(udev_mon);
|
||||||
|
|
||||||
defaultMappings = mappings;
|
defaultMappings = mappings;
|
||||||
inputRotate = rotate;
|
|
||||||
|
|
||||||
loop_add_fd(udev_monitor_get_fd(udev_mon), &udev_handle, POLLIN);
|
int udev_fd = udev_monitor_get_fd(udev_mon);
|
||||||
|
loop_add_fd(udev_fd, &udev_handle, POLLIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
void udev_destroy() {
|
void evdev_destroy() {
|
||||||
loop_remove_fd(udev_monitor_get_fd(udev_mon));
|
|
||||||
udev_monitor_unref(udev_mon);
|
|
||||||
udev_unref(udev);
|
udev_unref(udev);
|
||||||
}
|
}
|
||||||
|
@ -19,5 +19,5 @@
|
|||||||
|
|
||||||
#include "mapping.h"
|
#include "mapping.h"
|
||||||
|
|
||||||
void udev_init(bool autoload, struct mapping* mappings, bool verbose, int rotate);
|
void udev_init(bool autoload, struct mapping* mappings, bool verbose);
|
||||||
void udev_destroy();
|
void evdev_destroy();
|
||||||
|
@ -111,12 +111,6 @@ static int x11_handler(int fd) {
|
|||||||
case Button5:
|
case Button5:
|
||||||
LiSendScrollEvent(-1);
|
LiSendScrollEvent(-1);
|
||||||
break;
|
break;
|
||||||
case 6:
|
|
||||||
LiSendHScrollEvent(-1);
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
LiSendHScrollEvent(1);
|
|
||||||
break;
|
|
||||||
case 8:
|
case 8:
|
||||||
button = BUTTON_X1;
|
button = BUTTON_X1;
|
||||||
break;
|
break;
|
||||||
|
11
src/loop.c
11
src/loop.c
@ -38,8 +38,7 @@ static int sigFd;
|
|||||||
|
|
||||||
static int loop_sig_handler(int fd) {
|
static int loop_sig_handler(int fd) {
|
||||||
struct signalfd_siginfo info;
|
struct signalfd_siginfo info;
|
||||||
if (read(fd, &info, sizeof(info)) != sizeof(info))
|
read(fd, &info, sizeof(info));
|
||||||
return LOOP_RETURN;
|
|
||||||
switch (info.ssi_signo) {
|
switch (info.ssi_signo) {
|
||||||
case SIGINT:
|
case SIGINT:
|
||||||
case SIGTERM:
|
case SIGTERM:
|
||||||
@ -74,15 +73,15 @@ void loop_add_fd(int fd, FdHandler handler, int events) {
|
|||||||
|
|
||||||
void loop_remove_fd(int fd) {
|
void loop_remove_fd(int fd) {
|
||||||
numFds--;
|
numFds--;
|
||||||
int fdindex = numFds;
|
int fdindex;
|
||||||
|
|
||||||
for (int i=0;i<=numFds;i++) {
|
for (int i=0;i<numFds;i++) {
|
||||||
if (fds[i].fd == fd) {
|
if (fds[i].fd == fd) {
|
||||||
fdindex = i;
|
fdindex = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fdindex != numFds && numFds > 0) {
|
if (fdindex != numFds && numFds > 0) {
|
||||||
memcpy(&fds[fdindex], &fds[numFds], sizeof(struct pollfd));
|
memcpy(&fds[fdindex], &fds[numFds], sizeof(struct pollfd));
|
||||||
memcpy(&fdHandlers[fdindex], &fdHandlers[numFds], sizeof(FdHandler));
|
memcpy(&fdHandlers[fdindex], &fdHandlers[numFds], sizeof(FdHandler));
|
||||||
|
191
src/main.c
191
src/main.c
@ -20,8 +20,8 @@
|
|||||||
#include "loop.h"
|
#include "loop.h"
|
||||||
#include "connection.h"
|
#include "connection.h"
|
||||||
#include "configuration.h"
|
#include "configuration.h"
|
||||||
#include "platform.h"
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
#include "platform.h"
|
||||||
#include "sdl.h"
|
#include "sdl.h"
|
||||||
|
|
||||||
#include "audio/audio.h"
|
#include "audio/audio.h"
|
||||||
@ -96,7 +96,7 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform sys
|
|||||||
gamepads += sdl_gamepads;
|
gamepads += sdl_gamepads;
|
||||||
#endif
|
#endif
|
||||||
int gamepad_mask = 0;
|
int gamepad_mask = 0;
|
||||||
for (int i = 0; i < gamepads; i++)
|
for (int i = 0; i < gamepads && i < 4; i++)
|
||||||
gamepad_mask = (gamepad_mask << 1) + 1;
|
gamepad_mask = (gamepad_mask << 1) + 1;
|
||||||
|
|
||||||
int ret = gs_start_app(server, &config->stream, appId, config->sops, config->localaudio, gamepad_mask);
|
int ret = gs_start_app(server, &config->stream, appId, config->sops, config->localaudio, gamepad_mask);
|
||||||
@ -104,9 +104,7 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform sys
|
|||||||
if (ret == GS_NOT_SUPPORTED_4K)
|
if (ret == GS_NOT_SUPPORTED_4K)
|
||||||
fprintf(stderr, "Server doesn't support 4K\n");
|
fprintf(stderr, "Server doesn't support 4K\n");
|
||||||
else if (ret == GS_NOT_SUPPORTED_MODE)
|
else if (ret == GS_NOT_SUPPORTED_MODE)
|
||||||
fprintf(stderr, "Server doesn't support %dx%d (%d fps) or remove --nounsupported option\n", config->stream.width, config->stream.height, config->stream.fps);
|
fprintf(stderr, "Server doesn't support %dx%d (%d fps) or try --unsupported option\n", config->stream.width, config->stream.height, config->stream.fps);
|
||||||
else if (ret == GS_NOT_SUPPORTED_SOPS_RESOLUTION)
|
|
||||||
fprintf(stderr, "Optimal Playable Settings isn't supported for the resolution %dx%d, use supported resolution or add --nosops option\n", config->stream.width, config->stream.height);
|
|
||||||
else if (ret == GS_ERROR)
|
else if (ret == GS_ERROR)
|
||||||
fprintf(stderr, "Gamestream error: %s\n", gs_error);
|
fprintf(stderr, "Gamestream error: %s\n", gs_error);
|
||||||
else
|
else
|
||||||
@ -118,22 +116,6 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform sys
|
|||||||
if (config->fullscreen)
|
if (config->fullscreen)
|
||||||
drFlags |= DISPLAY_FULLSCREEN;
|
drFlags |= DISPLAY_FULLSCREEN;
|
||||||
|
|
||||||
switch (config->rotate) {
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
case 90:
|
|
||||||
drFlags |= DISPLAY_ROTATE_90;
|
|
||||||
break;
|
|
||||||
case 180:
|
|
||||||
drFlags |= DISPLAY_ROTATE_180;
|
|
||||||
break;
|
|
||||||
case 270:
|
|
||||||
drFlags |= DISPLAY_ROTATE_270;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
printf("Ignoring invalid rotation value: %d\n", config->rotate);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config->debug_level > 0) {
|
if (config->debug_level > 0) {
|
||||||
printf("Stream %d x %d, %d fps, %d kbps\n", config->stream.width, config->stream.height, config->stream.fps, config->stream.bitrate);
|
printf("Stream %d x %d, %d fps, %d kbps\n", config->stream.width, config->stream.height, config->stream.fps, config->stream.bitrate);
|
||||||
connection_debug = true;
|
connection_debug = true;
|
||||||
@ -146,11 +128,9 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform sys
|
|||||||
LiStartConnection(&server->serverInfo, &config->stream, &connection_callbacks, platform_get_video(system), platform_get_audio(system, config->audio_device), NULL, drFlags, config->audio_device, 0);
|
LiStartConnection(&server->serverInfo, &config->stream, &connection_callbacks, platform_get_video(system), platform_get_audio(system, config->audio_device), NULL, drFlags, config->audio_device, 0);
|
||||||
|
|
||||||
if (IS_EMBEDDED(system)) {
|
if (IS_EMBEDDED(system)) {
|
||||||
if (!config->viewonly)
|
evdev_start();
|
||||||
evdev_start();
|
|
||||||
loop_main();
|
loop_main();
|
||||||
if (!config->viewonly)
|
evdev_stop();
|
||||||
evdev_stop();
|
|
||||||
}
|
}
|
||||||
#ifdef HAVE_SDL
|
#ifdef HAVE_SDL
|
||||||
else if (system == SDL)
|
else if (system == SDL)
|
||||||
@ -169,11 +149,7 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform sys
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void help() {
|
static void help() {
|
||||||
#ifdef GIT_BRANCH
|
|
||||||
printf("Moonlight Embedded %d.%d.%d-%s-%s\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, GIT_BRANCH, GIT_COMMIT_HASH);
|
|
||||||
#else
|
|
||||||
printf("Moonlight Embedded %d.%d.%d\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
|
printf("Moonlight Embedded %d.%d.%d\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
|
||||||
#endif
|
|
||||||
printf("Usage: moonlight [action] (options) [host]\n");
|
printf("Usage: moonlight [action] (options) [host]\n");
|
||||||
printf(" moonlight [configfile]\n");
|
printf(" moonlight [configfile]\n");
|
||||||
printf("\n Actions\n\n");
|
printf("\n Actions\n\n");
|
||||||
@ -195,26 +171,20 @@ static void help() {
|
|||||||
printf("\t-4k\t\t\tUse 3840x2160 resolution\n");
|
printf("\t-4k\t\t\tUse 3840x2160 resolution\n");
|
||||||
printf("\t-width <width>\t\tHorizontal resolution (default 1280)\n");
|
printf("\t-width <width>\t\tHorizontal resolution (default 1280)\n");
|
||||||
printf("\t-height <height>\tVertical resolution (default 720)\n");
|
printf("\t-height <height>\tVertical resolution (default 720)\n");
|
||||||
#ifdef HAVE_EMBEDDED
|
printf("\t-fps <fps>\t\tSpecify the fps to use (default -1)\n");
|
||||||
printf("\t-rotate <angle>\tRotate display: 0/90/180/270 (default 0)\n");
|
|
||||||
#endif
|
|
||||||
printf("\t-fps <fps>\t\tSpecify the fps to use (default 60)\n");
|
|
||||||
printf("\t-bitrate <bitrate>\tSpecify the bitrate in Kbps\n");
|
printf("\t-bitrate <bitrate>\tSpecify the bitrate in Kbps\n");
|
||||||
printf("\t-packetsize <size>\tSpecify the maximum packetsize in bytes\n");
|
printf("\t-packetsize <size>\tSpecify the maximum packetsize in bytes\n");
|
||||||
printf("\t-codec <codec>\t\tSelect used codec: auto/h264/h265/av1 (default auto)\n");
|
printf("\t-codec <codec>\t\tSelect used codec: auto/h264/h265 (default auto)\n");
|
||||||
printf("\t-hdr\t\tEnable HDR streaming (experimental, requires host and device support)\n");
|
printf("\t-remote\t\t\tEnable remote optimizations\n");
|
||||||
printf("\t-remote <yes/no/auto>\t\t\tEnable optimizations for WAN streaming (default auto)\n");
|
|
||||||
printf("\t-app <app>\t\tName of app to stream\n");
|
printf("\t-app <app>\t\tName of app to stream\n");
|
||||||
printf("\t-nosops\t\t\tDon't allow GFE to modify game settings\n");
|
printf("\t-nosops\t\t\tDon't allow GFE to modify game settings\n");
|
||||||
printf("\t-localaudio\t\tPlay audio locally on the host computer\n");
|
printf("\t-localaudio\t\tPlay audio locally\n");
|
||||||
printf("\t-surround <5.1/7.1>\t\tStream 5.1 or 7.1 surround sound\n");
|
printf("\t-surround\t\tStream 5.1 surround sound (requires GFE 2.7)\n");
|
||||||
printf("\t-keydir <directory>\tLoad encryption keys from directory\n");
|
printf("\t-keydir <directory>\tLoad encryption keys from directory\n");
|
||||||
printf("\t-mapping <file>\t\tUse <file> as gamepad mappings configuration file\n");
|
printf("\t-mapping <file>\t\tUse <file> as gamepad mappings configuration file\n");
|
||||||
printf("\t-platform <system>\tSpecify system used for audio, video and input: pi/imx/aml/rk/x11/x11_vdpau/sdl/fake (default auto)\n");
|
printf("\t-platform <system>\tSpecify system used for audio, video and input: pi/imx/aml/rk/x11/x11_vdpau/sdl/fake (default auto)\n");
|
||||||
printf("\t-nounsupported\t\tDon't stream if resolution is not officially supported by the server\n");
|
printf("\t-unsupported\t\tTry streaming if GFE version or options are unsupported\n");
|
||||||
printf("\t-quitappafter\t\tSend quit app request to remote after quitting session\n");
|
printf("\t-quitappafter\t\tSend quit app request to remote after quitting session\n");
|
||||||
printf("\t-viewonly\t\tDisable all input processing (view-only mode)\n");
|
|
||||||
printf("\t-nomouseemulation\t\tDisable gamepad mouse emulation support (long pressing Start button)\n");
|
|
||||||
#if defined(HAVE_SDL) || defined(HAVE_X11)
|
#if defined(HAVE_SDL) || defined(HAVE_X11)
|
||||||
printf("\n WM options (SDL and X11 only)\n\n");
|
printf("\n WM options (SDL and X11 only)\n\n");
|
||||||
printf("\t-windowed\t\tDisplay screen in a window\n");
|
printf("\t-windowed\t\tDisplay screen in a window\n");
|
||||||
@ -241,19 +211,19 @@ int main(int argc, char* argv[]) {
|
|||||||
|
|
||||||
if (config.action == NULL || strcmp("help", config.action) == 0)
|
if (config.action == NULL || strcmp("help", config.action) == 0)
|
||||||
help();
|
help();
|
||||||
|
|
||||||
if (config.debug_level > 0)
|
if (config.debug_level > 0)
|
||||||
printf("Moonlight Embedded %d.%d.%d (%s)\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, COMPILE_OPTIONS);
|
printf("Moonlight Embedded %d.%d.%d (%s)\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, COMPILE_OPTIONS);
|
||||||
|
|
||||||
if (strcmp("map", config.action) == 0) {
|
if (strcmp("map", config.action) == 0) {
|
||||||
if (config.inputsCount != 1) {
|
if (config.inputsCount != 1) {
|
||||||
printf("You need to specify one input device using -input.\n");
|
printf("You need to specify one input device using -input.\n");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
evdev_create(config.inputs[0], NULL, config.debug_level > 0, config.rotate);
|
evdev_create(config.inputs[0], NULL, config.debug_level > 0);
|
||||||
evdev_map(config.inputs[0]);
|
evdev_map(config.inputs[0]);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.address == NULL) {
|
if (config.address == NULL) {
|
||||||
@ -264,23 +234,23 @@ int main(int argc, char* argv[]) {
|
|||||||
}
|
}
|
||||||
config.address[0] = 0;
|
config.address[0] = 0;
|
||||||
printf("Searching for server...\n");
|
printf("Searching for server...\n");
|
||||||
gs_discover_server(config.address, &config.port);
|
gs_discover_server(config.address);
|
||||||
if (config.address[0] == 0) {
|
if (config.address[0] == 0) {
|
||||||
fprintf(stderr, "Autodiscovery failed. Specify an IP address next time.\n");
|
fprintf(stderr, "Autodiscovery failed. Specify an IP address next time.\n");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
char host_config_file[128];
|
char host_config_file[128];
|
||||||
sprintf(host_config_file, "hosts/%s.conf", config.address);
|
sprintf(host_config_file, "hosts/%s.conf", config.address);
|
||||||
if (access(host_config_file, R_OK) != -1)
|
if (access(host_config_file, R_OK) != -1)
|
||||||
config_file_parse(host_config_file, &config);
|
config_file_parse(host_config_file, &config);
|
||||||
|
|
||||||
SERVER_DATA server;
|
SERVER_DATA server;
|
||||||
printf("Connecting to %s...\n", config.address);
|
printf("Connect to %s...\n", config.address);
|
||||||
|
|
||||||
int ret;
|
int ret;
|
||||||
if ((ret = gs_init(&server, config.address, config.port, config.key_dir, config.debug_level, config.unsupported)) == GS_OUT_OF_MEMORY) {
|
if ((ret = gs_init(&server, config.address, config.key_dir, config.debug_level, config.unsupported)) == GS_OUT_OF_MEMORY) {
|
||||||
fprintf(stderr, "Not enough memory\n");
|
fprintf(stderr, "Not enough memory\n");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
} else if (ret == GS_ERROR) {
|
} else if (ret == GS_ERROR) {
|
||||||
@ -297,10 +267,8 @@ int main(int argc, char* argv[]) {
|
|||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.debug_level > 0) {
|
if (config.debug_level > 0)
|
||||||
printf("GPU: %s, GFE: %s (%s, %s)\n", server.gpuType, server.serverInfo.serverInfoGfeVersion, server.gsVersion, server.serverInfo.serverInfoAppVersion);
|
printf("NVIDIA %s, GFE %s (%s, %s)\n", server.gpuType, server.serverInfo.serverInfoGfeVersion, server.gsVersion, server.serverInfo.serverInfoAppVersion);
|
||||||
printf("Server codec flags: 0x%x\n", server.serverInfo.serverCodecModeSupport);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp("list", config.action) == 0) {
|
if (strcmp("list", config.action) == 0) {
|
||||||
pair_check(&server);
|
pair_check(&server);
|
||||||
@ -318,90 +286,57 @@ int main(int argc, char* argv[]) {
|
|||||||
fprintf(stderr, "You can't select a audio device for SDL\n");
|
fprintf(stderr, "You can't select a audio device for SDL\n");
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
|
config.stream.supportsHevc = config.codec != CODEC_H264 && (config.codec == CODEC_HEVC || platform_supports_hevc(system));
|
||||||
|
|
||||||
config.stream.supportedVideoFormats = VIDEO_FORMAT_H264;
|
if (IS_EMBEDDED(system)) {
|
||||||
if (config.codec == CODEC_HEVC || (config.codec == CODEC_UNSPECIFIED && platform_prefers_codec(system, CODEC_HEVC))) {
|
char* mapping_env = getenv("SDL_GAMECONTROLLERCONFIG");
|
||||||
config.stream.supportedVideoFormats |= VIDEO_FORMAT_H265;
|
if (config.mapping == NULL && mapping_env == NULL) {
|
||||||
if (config.hdr)
|
fprintf(stderr, "Please specify mapping file as default mapping could not be found.\n");
|
||||||
config.stream.supportedVideoFormats |= VIDEO_FORMAT_H265_MAIN10;
|
exit(-1);
|
||||||
}
|
}
|
||||||
if (config.codec == CODEC_AV1 || (config.codec == CODEC_UNSPECIFIED && platform_prefers_codec(system, CODEC_AV1))) {
|
|
||||||
config.stream.supportedVideoFormats |= VIDEO_FORMAT_AV1_MAIN8;
|
|
||||||
if (config.hdr)
|
|
||||||
config.stream.supportedVideoFormats |= VIDEO_FORMAT_AV1_MAIN10;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.hdr && !(config.stream.supportedVideoFormats & VIDEO_FORMAT_MASK_10BIT)) {
|
struct mapping* mappings = NULL;
|
||||||
fprintf(stderr, "HDR streaming requires HEVC or AV1 codec\n");
|
if (config.mapping != NULL)
|
||||||
exit(-1);
|
mappings = mapping_load(config.mapping, config.debug_level > 0);
|
||||||
}
|
|
||||||
|
|
||||||
|
if (mapping_env != NULL) {
|
||||||
|
struct mapping* map = mapping_parse(mapping_env);
|
||||||
|
map->next = mappings;
|
||||||
|
mappings = map;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=0;i<config.inputsCount;i++) {
|
||||||
|
if (config.debug_level > 0)
|
||||||
|
printf("Add input %s...\n", config.inputs[i]);
|
||||||
|
|
||||||
|
evdev_create(config.inputs[i], mappings, config.debug_level > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
udev_init(!inputAdded, mappings, config.debug_level > 0);
|
||||||
|
evdev_init();
|
||||||
|
rumble_handler = evdev_rumble;
|
||||||
|
#ifdef HAVE_LIBCEC
|
||||||
|
cec_init();
|
||||||
|
#endif /* HAVE_LIBCEC */
|
||||||
|
}
|
||||||
#ifdef HAVE_SDL
|
#ifdef HAVE_SDL
|
||||||
if (system == SDL)
|
else if (system == SDL) {
|
||||||
|
if (config.inputsCount > 0) {
|
||||||
|
fprintf(stderr, "You can't select input devices as SDL will automatically use all available controllers\n");
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
sdl_init(config.stream.width, config.stream.height, config.fullscreen);
|
sdl_init(config.stream.width, config.stream.height, config.fullscreen);
|
||||||
#endif
|
sdlinput_init(config.mapping);
|
||||||
|
rumble_handler = sdlinput_rumble;
|
||||||
if (config.viewonly) {
|
|
||||||
if (config.debug_level > 0)
|
|
||||||
printf("View-only mode enabled, no input will be sent to the host computer\n");
|
|
||||||
} else {
|
|
||||||
if (IS_EMBEDDED(system)) {
|
|
||||||
char* mapping_env = getenv("SDL_GAMECONTROLLERCONFIG");
|
|
||||||
if (config.mapping == NULL && mapping_env == NULL) {
|
|
||||||
fprintf(stderr, "Please specify mapping file as default mapping could not be found.\n");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct mapping* mappings = NULL;
|
|
||||||
if (config.mapping != NULL)
|
|
||||||
mappings = mapping_load(config.mapping, config.debug_level > 0);
|
|
||||||
|
|
||||||
if (mapping_env != NULL) {
|
|
||||||
struct mapping* map = mapping_parse(mapping_env);
|
|
||||||
map->next = mappings;
|
|
||||||
mappings = map;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i=0;i<config.inputsCount;i++) {
|
|
||||||
if (config.debug_level > 0)
|
|
||||||
printf("Adding input device %s...\n", config.inputs[i]);
|
|
||||||
|
|
||||||
evdev_create(config.inputs[i], mappings, config.debug_level > 0, config.rotate);
|
|
||||||
}
|
|
||||||
|
|
||||||
udev_init(!inputAdded, mappings, config.debug_level > 0, config.rotate);
|
|
||||||
evdev_init(config.mouse_emulation);
|
|
||||||
rumble_handler = evdev_rumble;
|
|
||||||
#ifdef HAVE_LIBCEC
|
|
||||||
cec_init();
|
|
||||||
#endif /* HAVE_LIBCEC */
|
|
||||||
}
|
|
||||||
#ifdef HAVE_SDL
|
|
||||||
else if (system == SDL) {
|
|
||||||
if (config.inputsCount > 0) {
|
|
||||||
fprintf(stderr, "You can't select input devices as SDL will automatically use all available controllers\n");
|
|
||||||
exit(-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
sdlinput_init(config.mapping);
|
|
||||||
rumble_handler = sdlinput_rumble;
|
|
||||||
rumble_triggers_handler = sdlinput_rumble_triggers;
|
|
||||||
set_motion_event_state_handler = sdlinput_set_motion_event_state;
|
|
||||||
set_controller_led_handler = sdlinput_set_controller_led;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
stream(&server, &config, system);
|
stream(&server, &config, system);
|
||||||
} else if (strcmp("pair", config.action) == 0) {
|
} else if (strcmp("pair", config.action) == 0) {
|
||||||
char pin[5];
|
char pin[5];
|
||||||
if (config.pin > 0 && config.pin <= 9999) {
|
sprintf(pin, "%d%d%d%d", (int)random() % 10, (int)random() % 10, (int)random() % 10, (int)random() % 10);
|
||||||
sprintf(pin, "%04d", config.pin);
|
|
||||||
} else {
|
|
||||||
sprintf(pin, "%d%d%d%d", (unsigned)random() % 10, (unsigned)random() % 10, (unsigned)random() % 10, (unsigned)random() % 10);
|
|
||||||
}
|
|
||||||
printf("Please enter the following PIN on the target PC: %s\n", pin);
|
printf("Please enter the following PIN on the target PC: %s\n", pin);
|
||||||
fflush(stdout);
|
|
||||||
if (gs_pair(&server, &pin[0]) != GS_OK) {
|
if (gs_pair(&server, &pin[0]) != GS_OK) {
|
||||||
fprintf(stderr, "Failed to pair to server: %s\n", gs_error);
|
fprintf(stderr, "Failed to pair to server: %s\n", gs_error);
|
||||||
} else {
|
} else {
|
||||||
|
@ -52,16 +52,9 @@ enum platform platform_check(char* name) {
|
|||||||
return PI;
|
return PI;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_MMAL
|
|
||||||
if (std || strcmp(name, "mmal") == 0) {
|
|
||||||
void *handle = dlopen("libmoonlight-mmal.so", RTLD_NOW | RTLD_GLOBAL);
|
|
||||||
if (handle != NULL && dlsym(RTLD_DEFAULT, "bcm_host_init") != NULL)
|
|
||||||
return MMAL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_AML
|
#ifdef HAVE_AML
|
||||||
if (std || strcmp(name, "aml") == 0) {
|
if (std || strcmp(name, "aml") == 0) {
|
||||||
void *handle = dlopen("libmoonlight-aml.so", RTLD_LAZY | RTLD_GLOBAL);
|
void *handle = dlopen("libmoonlight-aml.so", RTLD_NOW | RTLD_GLOBAL);
|
||||||
if (handle != NULL && access("/dev/amvideo", F_OK) != -1)
|
if (handle != NULL && access("/dev/amvideo", F_OK) != -1)
|
||||||
return AML;
|
return AML;
|
||||||
}
|
}
|
||||||
@ -87,18 +80,13 @@ enum platform platform_check(char* name) {
|
|||||||
if (init == INIT_VDPAU)
|
if (init == INIT_VDPAU)
|
||||||
return X11_VDPAU;
|
return X11_VDPAU;
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_SDL
|
|
||||||
return SDL;
|
|
||||||
#else
|
|
||||||
return X11;
|
return X11;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_SDL
|
#ifdef HAVE_SDL
|
||||||
if (std || strcmp(name, "sdl") == 0)
|
if (std || strcmp(name, "sdl") == 0)
|
||||||
return SDL;
|
return SDL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (strcmp(name, "fake") == 0)
|
if (strcmp(name, "fake") == 0)
|
||||||
return FAKE;
|
return FAKE;
|
||||||
|
|
||||||
@ -109,14 +97,13 @@ void platform_start(enum platform system) {
|
|||||||
switch (system) {
|
switch (system) {
|
||||||
#ifdef HAVE_AML
|
#ifdef HAVE_AML
|
||||||
case AML:
|
case AML:
|
||||||
write_bool("/sys/class/graphics/fb0/blank", true);
|
blank_fb("/sys/class/graphics/fb0/blank", true);
|
||||||
write_bool("/sys/class/graphics/fb1/blank", true);
|
blank_fb("/sys/class/graphics/fb1/blank", true);
|
||||||
write_bool("/sys/class/video/disable_video", false);
|
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
#if defined(HAVE_PI) || defined(HAVE_MMAL)
|
#ifdef HAVE_PI
|
||||||
case PI:
|
case PI:
|
||||||
write_bool("/sys/class/graphics/fb0/blank", true);
|
blank_fb("/sys/class/graphics/fb0/blank", true);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -126,13 +113,13 @@ void platform_stop(enum platform system) {
|
|||||||
switch (system) {
|
switch (system) {
|
||||||
#ifdef HAVE_AML
|
#ifdef HAVE_AML
|
||||||
case AML:
|
case AML:
|
||||||
write_bool("/sys/class/graphics/fb0/blank", false);
|
blank_fb("/sys/class/graphics/fb0/blank", false);
|
||||||
write_bool("/sys/class/graphics/fb1/blank", false);
|
blank_fb("/sys/class/graphics/fb1/blank", false);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
#if defined(HAVE_PI) || defined(HAVE_MMAL)
|
#ifdef HAVE_PI
|
||||||
case PI:
|
case PI:
|
||||||
write_bool("/sys/class/graphics/fb0/blank", false);
|
blank_fb("/sys/class/graphics/fb0/blank", false);
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -164,10 +151,6 @@ DECODER_RENDERER_CALLBACKS* platform_get_video(enum platform system) {
|
|||||||
case PI:
|
case PI:
|
||||||
return (PDECODER_RENDERER_CALLBACKS) dlsym(RTLD_DEFAULT, "decoder_callbacks_pi");
|
return (PDECODER_RENDERER_CALLBACKS) dlsym(RTLD_DEFAULT, "decoder_callbacks_pi");
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_MMAL
|
|
||||||
case MMAL:
|
|
||||||
return (PDECODER_RENDERER_CALLBACKS) dlsym(RTLD_DEFAULT, "decoder_callbacks_mmal");
|
|
||||||
#endif
|
|
||||||
#ifdef HAVE_AML
|
#ifdef HAVE_AML
|
||||||
case AML:
|
case AML:
|
||||||
return (PDECODER_RENDERER_CALLBACKS) dlsym(RTLD_DEFAULT, "decoder_callbacks_aml");
|
return (PDECODER_RENDERER_CALLBACKS) dlsym(RTLD_DEFAULT, "decoder_callbacks_aml");
|
||||||
@ -182,8 +165,6 @@ DECODER_RENDERER_CALLBACKS* platform_get_video(enum platform system) {
|
|||||||
|
|
||||||
AUDIO_RENDERER_CALLBACKS* platform_get_audio(enum platform system, char* audio_device) {
|
AUDIO_RENDERER_CALLBACKS* platform_get_audio(enum platform system, char* audio_device) {
|
||||||
switch (system) {
|
switch (system) {
|
||||||
case FAKE:
|
|
||||||
return NULL;
|
|
||||||
#ifdef HAVE_SDL
|
#ifdef HAVE_SDL
|
||||||
case SDL:
|
case SDL:
|
||||||
return &audio_callbacks_sdl;
|
return &audio_callbacks_sdl;
|
||||||
@ -192,7 +173,6 @@ AUDIO_RENDERER_CALLBACKS* platform_get_audio(enum platform system, char* audio_d
|
|||||||
case PI:
|
case PI:
|
||||||
if (audio_device == NULL || strcmp(audio_device, "local") == 0 || strcmp(audio_device, "hdmi") == 0)
|
if (audio_device == NULL || strcmp(audio_device, "local") == 0 || strcmp(audio_device, "hdmi") == 0)
|
||||||
return (PAUDIO_RENDERER_CALLBACKS) dlsym(RTLD_DEFAULT, "audio_callbacks_omx");
|
return (PAUDIO_RENDERER_CALLBACKS) dlsym(RTLD_DEFAULT, "audio_callbacks_omx");
|
||||||
// fall-through
|
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
#ifdef HAVE_PULSE
|
#ifdef HAVE_PULSE
|
||||||
@ -202,29 +182,15 @@ AUDIO_RENDERER_CALLBACKS* platform_get_audio(enum platform system, char* audio_d
|
|||||||
#ifdef HAVE_ALSA
|
#ifdef HAVE_ALSA
|
||||||
return &audio_callbacks_alsa;
|
return &audio_callbacks_alsa;
|
||||||
#endif
|
#endif
|
||||||
#ifdef __FreeBSD__
|
|
||||||
return &audio_callbacks_oss;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool platform_prefers_codec(enum platform system, enum codecs codec) {
|
bool platform_supports_hevc(enum platform system) {
|
||||||
switch (codec) {
|
switch (system) {
|
||||||
case CODEC_H264:
|
case AML:
|
||||||
// H.264 is always supported
|
case RK:
|
||||||
return true;
|
return true;
|
||||||
case CODEC_HEVC:
|
|
||||||
switch (system) {
|
|
||||||
case AML:
|
|
||||||
case RK:
|
|
||||||
case X11_VAAPI:
|
|
||||||
case X11_VDPAU:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
case CODEC_AV1:
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -233,8 +199,6 @@ char* platform_name(enum platform system) {
|
|||||||
switch(system) {
|
switch(system) {
|
||||||
case PI:
|
case PI:
|
||||||
return "Raspberry Pi (Broadcom)";
|
return "Raspberry Pi (Broadcom)";
|
||||||
case MMAL:
|
|
||||||
return "Raspberry Pi (Broadcom) MMAL";
|
|
||||||
case IMX:
|
case IMX:
|
||||||
return "i.MX6 (MXC Vivante)";
|
return "i.MX6 (MXC Vivante)";
|
||||||
case AML:
|
case AML:
|
||||||
|
@ -26,13 +26,12 @@
|
|||||||
|
|
||||||
#define IS_EMBEDDED(SYSTEM) SYSTEM != SDL
|
#define IS_EMBEDDED(SYSTEM) SYSTEM != SDL
|
||||||
|
|
||||||
enum platform { NONE, SDL, X11, X11_VDPAU, X11_VAAPI, PI, MMAL, IMX, AML, RK, FAKE };
|
enum platform { NONE, SDL, X11, X11_VDPAU, X11_VAAPI, PI, IMX, AML, RK, FAKE };
|
||||||
enum codecs { CODEC_UNSPECIFIED, CODEC_H264, CODEC_HEVC, CODEC_AV1 };
|
|
||||||
|
|
||||||
enum platform platform_check(char*);
|
enum platform platform_check(char*);
|
||||||
PDECODER_RENDERER_CALLBACKS platform_get_video(enum platform system);
|
PDECODER_RENDERER_CALLBACKS platform_get_video(enum platform system);
|
||||||
PAUDIO_RENDERER_CALLBACKS platform_get_audio(enum platform system, char* audio_device);
|
PAUDIO_RENDERER_CALLBACKS platform_get_audio(enum platform system, char* audio_device);
|
||||||
bool platform_prefers_codec(enum platform system, enum codecs codec);
|
bool platform_supports_hevc(enum platform system);
|
||||||
char* platform_name(enum platform system);
|
char* platform_name(enum platform system);
|
||||||
|
|
||||||
void platform_start(enum platform system);
|
void platform_start(enum platform system);
|
||||||
|
@ -50,6 +50,7 @@ void sdl_init(int width, int height, bool fullscreen) {
|
|||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SDL_SetRelativeMouseMode(SDL_TRUE);
|
||||||
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
||||||
if (!renderer) {
|
if (!renderer) {
|
||||||
printf("SDL_CreateRenderer failed: %s\n", SDL_GetError());
|
printf("SDL_CreateRenderer failed: %s\n", SDL_GetError());
|
||||||
@ -71,25 +72,19 @@ void sdl_init(int width, int height, bool fullscreen) {
|
|||||||
|
|
||||||
void sdl_loop() {
|
void sdl_loop() {
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
|
|
||||||
SDL_SetRelativeMouseMode(SDL_TRUE);
|
|
||||||
|
|
||||||
while(!done && SDL_WaitEvent(&event)) {
|
while(!done && SDL_WaitEvent(&event)) {
|
||||||
switch (sdlinput_handle_event(window, &event)) {
|
switch (sdlinput_handle_event(&event)) {
|
||||||
case SDL_QUIT_APPLICATION:
|
case SDL_QUIT_APPLICATION:
|
||||||
done = true;
|
done = true;
|
||||||
break;
|
break;
|
||||||
case SDL_TOGGLE_FULLSCREEN:
|
case SDL_TOGGLE_FULLSCREEN:
|
||||||
fullscreen_flags ^= SDL_WINDOW_FULLSCREEN;
|
fullscreen_flags ^= SDL_WINDOW_FULLSCREEN;
|
||||||
SDL_SetWindowFullscreen(window, fullscreen_flags);
|
SDL_SetWindowFullscreen(window, fullscreen_flags);
|
||||||
break;
|
|
||||||
case SDL_MOUSE_GRAB:
|
case SDL_MOUSE_GRAB:
|
||||||
SDL_ShowCursor(SDL_ENABLE);
|
|
||||||
SDL_SetRelativeMouseMode(SDL_TRUE);
|
SDL_SetRelativeMouseMode(SDL_TRUE);
|
||||||
break;
|
break;
|
||||||
case SDL_MOUSE_UNGRAB:
|
case SDL_MOUSE_UNGRAB:
|
||||||
SDL_SetRelativeMouseMode(SDL_FALSE);
|
SDL_SetRelativeMouseMode(SDL_FALSE);
|
||||||
SDL_ShowCursor(SDL_DISABLE);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
if (event.type == SDL_QUIT)
|
if (event.type == SDL_QUIT)
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
void sdl_init(int width, int height, bool fullscreen);
|
void sdl_init(int width, int height, bool fullscreen);
|
||||||
void sdl_loop();
|
void sdl_loop();
|
||||||
|
|
||||||
extern SDL_mutex *mutex;
|
SDL_mutex *mutex;
|
||||||
extern int sdlCurrentFrame, sdlNextFrame;
|
int sdlCurrentFrame, sdlNextFrame;
|
||||||
|
|
||||||
#endif /* HAVE_SDL */
|
#endif /* HAVE_SDL */
|
||||||
|
32
src/util.c
32
src/util.c
@ -24,43 +24,17 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
int write_bool(char *path, bool val) {
|
int blank_fb(char *path, bool clear) {
|
||||||
int fd = open(path, O_RDWR);
|
int fd = open(path, O_RDWR);
|
||||||
|
|
||||||
if(fd >= 0) {
|
if(fd >= 0) {
|
||||||
int ret = write(fd, val ? "1" : "0", 1);
|
int ret = write(fd, clear ? "1" : "0", 1);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
fprintf(stderr, "Failed to write %d to %s: %d\n", val ? 1 : 0, path, ret);
|
fprintf(stderr, "Failed to clear framebuffer %s: %d\n", path, ret);
|
||||||
|
|
||||||
close(fd);
|
close(fd);
|
||||||
return 0;
|
return 0;
|
||||||
} else
|
} else
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int read_file(char *path, char* output, int output_len) {
|
|
||||||
int fd = open(path, O_RDONLY);
|
|
||||||
|
|
||||||
if(fd >= 0) {
|
|
||||||
output_len = read(fd, output, output_len);
|
|
||||||
close(fd);
|
|
||||||
return output_len;
|
|
||||||
} else
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ensure_buf_size(void **buf, size_t *buf_size, size_t required_size) {
|
|
||||||
if (*buf_size >= required_size)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
*buf_size = required_size;
|
|
||||||
*buf = realloc(*buf, *buf_size);
|
|
||||||
if (!*buf) {
|
|
||||||
fprintf(stderr, "Failed to allocate %zu bytes\n", *buf_size);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
@ -18,9 +18,5 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
int write_bool(char *path, bool val);
|
int blank_fb(char *path, bool clear);
|
||||||
int read_file(char *path, char *output, int output_len);
|
|
||||||
bool ensure_buf_size(void **buf, size_t *buf_size, size_t required_size);
|
|
||||||
bool has_fast_aes(void);
|
|
||||||
|
183
src/video/aml.c
183
src/video/aml.c
@ -24,101 +24,48 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <codec.h>
|
#include <codec.h>
|
||||||
#include <errno.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#include <linux/videodev2.h>
|
|
||||||
|
|
||||||
#include "../util.h"
|
|
||||||
#include "video.h"
|
|
||||||
|
|
||||||
#define SYNC_OUTSIDE 0x02
|
#define SYNC_OUTSIDE 0x02
|
||||||
#define UCODE_IP_ONLY_PARAM 0x08
|
#define UCODE_IP_ONLY_PARAM 0x08
|
||||||
#define MAX_WRITE_ATTEMPTS 5
|
|
||||||
#define EAGAIN_SLEEP_TIME 2 * 1000
|
|
||||||
|
|
||||||
static codec_para_t codecParam = { 0 };
|
static codec_para_t codecParam = { 0 };
|
||||||
static pthread_t displayThread;
|
|
||||||
static int videoFd = -1;
|
|
||||||
static volatile bool done = false;
|
|
||||||
void *pkt_buf = NULL;
|
|
||||||
size_t pkt_buf_size = 0;
|
|
||||||
|
|
||||||
void* aml_display_thread(void* unused) {
|
|
||||||
while (!done) {
|
|
||||||
struct v4l2_buffer vbuf = { 0 };
|
|
||||||
vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
||||||
|
|
||||||
if (ioctl(videoFd, VIDIOC_DQBUF, &vbuf) < 0) {
|
|
||||||
if (errno == EAGAIN) {
|
|
||||||
usleep(500);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
fprintf(stderr, "VIDIOC_DQBUF failed: %d\n", errno);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ioctl(videoFd, VIDIOC_QBUF, &vbuf) < 0) {
|
|
||||||
fprintf(stderr, "VIDIOC_QBUF failed: %d\n", errno);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
printf("Display thread terminated\n");
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int aml_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
|
int aml_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
|
||||||
|
codecParam.stream_type = STREAM_TYPE_ES_VIDEO;
|
||||||
|
codecParam.has_video = 1;
|
||||||
|
codecParam.noblock = 0;
|
||||||
|
codecParam.am_sysinfo.param = 0;
|
||||||
|
|
||||||
codecParam.handle = -1;
|
switch (videoFormat) {
|
||||||
codecParam.cntl_handle = -1;
|
case VIDEO_FORMAT_H264:
|
||||||
codecParam.audio_utils_handle = -1;
|
if (width > 1920 || height > 1080) {
|
||||||
codecParam.sub_handle = -1;
|
codecParam.video_type = VFORMAT_H264_4K2K;
|
||||||
codecParam.has_video = 1;
|
codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_H264_4K2K;
|
||||||
codecParam.noblock = 0;
|
} else {
|
||||||
codecParam.stream_type = STREAM_TYPE_ES_VIDEO;
|
codecParam.video_type = VFORMAT_H264;
|
||||||
codecParam.am_sysinfo.param = 0;
|
codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_H264;
|
||||||
|
|
||||||
#ifdef STREAM_TYPE_FRAME
|
// Workaround for decoding special case of C1, 1080p, H264
|
||||||
codecParam.dec_mode = STREAM_TYPE_FRAME;
|
int major, minor;
|
||||||
#endif
|
struct utsname name;
|
||||||
|
uname(&name);
|
||||||
#ifdef FRAME_BASE_PATH_AMLVIDEO_AMVIDEO
|
int ret = sscanf(name.release, "%d.%d", &major, &minor);
|
||||||
codecParam.video_path = FRAME_BASE_PATH_AMLVIDEO_AMVIDEO;
|
if (!(major > 3 || (major == 3 && minor >= 14)) && width == 1920 && height == 1080)
|
||||||
#endif
|
codecParam.am_sysinfo.param = (void*) UCODE_IP_ONLY_PARAM;
|
||||||
|
}
|
||||||
if (videoFormat & VIDEO_FORMAT_MASK_H264) {
|
break;
|
||||||
if (width > 1920 || height > 1080) {
|
case VIDEO_FORMAT_H265:
|
||||||
codecParam.video_type = VFORMAT_H264_4K2K;
|
codecParam.video_type = VFORMAT_HEVC;
|
||||||
codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_H264_4K2K;
|
codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_HEVC;
|
||||||
} else {
|
break;
|
||||||
codecParam.video_type = VFORMAT_H264;
|
default:
|
||||||
codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_H264;
|
printf("Video format not supported\n");
|
||||||
|
return -1;
|
||||||
// Workaround for decoding special case of C1, 1080p, H264
|
|
||||||
int major, minor;
|
|
||||||
struct utsname name;
|
|
||||||
uname(&name);
|
|
||||||
int ret = sscanf(name.release, "%d.%d", &major, &minor);
|
|
||||||
if (ret == 2 && !(major > 3 || (major == 3 && minor >= 14)) && width == 1920 && height == 1080)
|
|
||||||
codecParam.am_sysinfo.param = (void*) UCODE_IP_ONLY_PARAM;
|
|
||||||
}
|
|
||||||
} else if (videoFormat & VIDEO_FORMAT_MASK_H265) {
|
|
||||||
codecParam.video_type = VFORMAT_HEVC;
|
|
||||||
codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_HEVC;
|
|
||||||
#ifdef CODEC_TAG_AV1
|
|
||||||
} else if (videoFormat & VIDEO_FORMAT_MASK_AV1) {
|
|
||||||
codecParam.video_type = VFORMAT_AV1;
|
|
||||||
codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_AV1;
|
|
||||||
#endif
|
|
||||||
} else {
|
|
||||||
printf("Video format not supported\n");
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
codecParam.am_sysinfo.width = width;
|
codecParam.am_sysinfo.width = width;
|
||||||
@ -137,83 +84,33 @@ int aml_setup(int videoFormat, int width, int height, int redrawRate, void* cont
|
|||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
char vfm_map[2048] = {};
|
|
||||||
char* eol;
|
|
||||||
if (read_file("/sys/class/vfm/map", vfm_map, sizeof(vfm_map) - 1) > 0 && (eol = strchr(vfm_map, '\n'))) {
|
|
||||||
*eol = 0;
|
|
||||||
|
|
||||||
// If amlvideo is in the pipeline, we must spawn a display thread
|
|
||||||
printf("VFM map: %s\n", vfm_map);
|
|
||||||
if (strstr(vfm_map, "amlvideo")) {
|
|
||||||
printf("Using display thread for amlvideo pipeline\n");
|
|
||||||
|
|
||||||
videoFd = open("/dev/video10", O_RDONLY | O_NONBLOCK);
|
|
||||||
if (videoFd < 0) {
|
|
||||||
fprintf(stderr, "Failed to open video device: %d\n", errno);
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_create(&displayThread, NULL, aml_display_thread, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ensure_buf_size(&pkt_buf, &pkt_buf_size, INITIAL_DECODER_BUFFER_SIZE);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void aml_cleanup() {
|
void aml_cleanup() {
|
||||||
if (videoFd >= 0) {
|
|
||||||
done = true;
|
|
||||||
pthread_join(displayThread, NULL);
|
|
||||||
close(videoFd);
|
|
||||||
}
|
|
||||||
|
|
||||||
codec_close(&codecParam);
|
codec_close(&codecParam);
|
||||||
free(pkt_buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int aml_submit_decode_unit(PDECODE_UNIT decodeUnit) {
|
int aml_submit_decode_unit(PDECODE_UNIT decodeUnit) {
|
||||||
|
int result = DR_OK;
|
||||||
ensure_buf_size(&pkt_buf, &pkt_buf_size, decodeUnit->fullLength);
|
|
||||||
|
|
||||||
int written = 0, length = 0, errCounter = 0, api;
|
|
||||||
PLENTRY entry = decodeUnit->bufferList;
|
PLENTRY entry = decodeUnit->bufferList;
|
||||||
do {
|
while (entry != NULL) {
|
||||||
memcpy(pkt_buf+length, entry->data, entry->length);
|
int api = codec_write(&codecParam, entry->data, entry->length);
|
||||||
length += entry->length;
|
if (api != entry->length) {
|
||||||
entry = entry->next;
|
fprintf(stderr, "codec_write error: %x\n", api);
|
||||||
} while (entry != NULL);
|
codec_reset(&codecParam);
|
||||||
|
result = DR_NEED_IDR;
|
||||||
codec_checkin_pts(&codecParam, decodeUnit->presentationTimeMs);
|
break;
|
||||||
while (length > 0) {
|
|
||||||
api = codec_write(&codecParam, pkt_buf+written, length);
|
|
||||||
if (api < 0) {
|
|
||||||
if (errno != EAGAIN) {
|
|
||||||
fprintf(stderr, "codec_write() error: %x %d\n", errno, api);
|
|
||||||
codec_reset(&codecParam);
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
if (++errCounter == MAX_WRITE_ATTEMPTS) {
|
|
||||||
fprintf(stderr, "codec_write() timeout\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
usleep(EAGAIN_SLEEP_TIME);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
written += api;
|
|
||||||
length -= api;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return length ? DR_NEED_IDR : DR_OK;
|
entry = entry->next;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
DECODER_RENDERER_CALLBACKS decoder_callbacks_aml = {
|
DECODER_RENDERER_CALLBACKS decoder_callbacks_aml = {
|
||||||
.setup = aml_setup,
|
.setup = aml_setup,
|
||||||
.cleanup = aml_cleanup,
|
.cleanup = aml_cleanup,
|
||||||
.submitDecodeUnit = aml_submit_decode_unit,
|
.submitDecodeUnit = aml_submit_decode_unit,
|
||||||
|
.capabilities = CAPABILITY_DIRECT_SUBMIT | CAPABILITY_SLICES_PER_FRAME(8),
|
||||||
// We may delay in aml_submit_decode_unit() for a while, so we can't set CAPABILITY_DIRECT_SUBMIT
|
|
||||||
.capabilities = CAPABILITY_REFERENCE_FRAME_INVALIDATION_HEVC,
|
|
||||||
};
|
};
|
||||||
|
@ -139,10 +139,11 @@ void egl_init(EGLNativeDisplayType native_display, NativeWindowType native_windo
|
|||||||
glGenBuffers(1, &ebo);
|
glGenBuffers(1, &ebo);
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
|
||||||
|
|
||||||
GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
|
GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
|
||||||
glShaderSource(vertex_shader, 1, &vertex_source, NULL);
|
glShaderSource(vertex_shader, 1, &vertex_source, NULL);
|
||||||
glCompileShader(vertex_shader);
|
glCompileShader(vertex_shader);
|
||||||
|
GLint maxLength = 0;
|
||||||
|
|
||||||
GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
|
GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||||
glShaderSource(fragment_shader, 1, &fragment_source, NULL);
|
glShaderSource(fragment_shader, 1, &fragment_source, NULL);
|
||||||
|
@ -32,8 +32,8 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
// General decoder and renderer state
|
// General decoder and renderer state
|
||||||
static AVPacket* pkt;
|
static AVPacket pkt;
|
||||||
static const AVCodec* decoder;
|
static AVCodec* decoder;
|
||||||
static AVCodecContext* decoder_ctx;
|
static AVCodecContext* decoder_ctx;
|
||||||
static AVFrame** dec_frames;
|
static AVFrame** dec_frames;
|
||||||
|
|
||||||
@ -53,79 +53,16 @@ int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer
|
|||||||
avcodec_register_all();
|
avcodec_register_all();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
pkt = av_packet_alloc();
|
av_init_packet(&pkt);
|
||||||
if (pkt == NULL) {
|
|
||||||
printf("Couldn't allocate packet\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ffmpeg_decoder = perf_lvl & VAAPI_ACCELERATION ? VAAPI : SOFTWARE;
|
ffmpeg_decoder = perf_lvl & VAAPI_ACCELERATION ? VAAPI : SOFTWARE;
|
||||||
|
switch (videoFormat) {
|
||||||
for (int try = 0; try < 6; try++) {
|
case VIDEO_FORMAT_H264:
|
||||||
if (videoFormat & VIDEO_FORMAT_MASK_H264) {
|
decoder = avcodec_find_decoder_by_name("h264");
|
||||||
if (ffmpeg_decoder == SOFTWARE) {
|
break;
|
||||||
if (try == 0) decoder = avcodec_find_decoder_by_name("h264_nvv4l2"); // Tegra
|
case VIDEO_FORMAT_H265:
|
||||||
if (try == 1) decoder = avcodec_find_decoder_by_name("h264_nvmpi"); // Tegra
|
decoder = avcodec_find_decoder_by_name("hevc");
|
||||||
if (try == 2) decoder = avcodec_find_decoder_by_name("h264_omx"); // VisionFive
|
break;
|
||||||
if (try == 3) decoder = avcodec_find_decoder_by_name("h264_v4l2m2m"); // Stateful V4L2
|
|
||||||
}
|
|
||||||
if (try == 4) decoder = avcodec_find_decoder_by_name("h264"); // Software and hwaccel
|
|
||||||
} else if (videoFormat & VIDEO_FORMAT_MASK_H265) {
|
|
||||||
if (ffmpeg_decoder == SOFTWARE) {
|
|
||||||
if (try == 0) decoder = avcodec_find_decoder_by_name("hevc_nvv4l2"); // Tegra
|
|
||||||
if (try == 1) decoder = avcodec_find_decoder_by_name("hevc_nvmpi"); // Tegra
|
|
||||||
if (try == 2) decoder = avcodec_find_decoder_by_name("hevc_omx"); // VisionFive
|
|
||||||
if (try == 3) decoder = avcodec_find_decoder_by_name("hevc_v4l2m2m"); // Stateful V4L2
|
|
||||||
}
|
|
||||||
if (try == 4) decoder = avcodec_find_decoder_by_name("hevc"); // Software and hwaccel
|
|
||||||
} else if (videoFormat & VIDEO_FORMAT_MASK_AV1) {
|
|
||||||
if (ffmpeg_decoder == SOFTWARE) {
|
|
||||||
if (try == 0) decoder = avcodec_find_decoder_by_name("libdav1d");
|
|
||||||
}
|
|
||||||
if (try == 1) decoder = avcodec_find_decoder_by_name("av1"); // Hwaccel
|
|
||||||
} else {
|
|
||||||
printf("Video format not supported\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip this decoder if it isn't compiled into FFmpeg
|
|
||||||
if (!decoder) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
decoder_ctx = avcodec_alloc_context3(decoder);
|
|
||||||
if (decoder_ctx == NULL) {
|
|
||||||
printf("Couldn't allocate context\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use low delay decoding
|
|
||||||
decoder_ctx->flags |= AV_CODEC_FLAG_LOW_DELAY;
|
|
||||||
|
|
||||||
// Allow display of corrupt frames and frames missing references
|
|
||||||
decoder_ctx->flags |= AV_CODEC_FLAG_OUTPUT_CORRUPT;
|
|
||||||
decoder_ctx->flags2 |= AV_CODEC_FLAG2_SHOW_ALL;
|
|
||||||
|
|
||||||
// Report decoding errors to allow us to request a key frame
|
|
||||||
decoder_ctx->err_recognition = AV_EF_EXPLODE;
|
|
||||||
|
|
||||||
if (perf_lvl & SLICE_THREADING) {
|
|
||||||
decoder_ctx->thread_type = FF_THREAD_SLICE;
|
|
||||||
decoder_ctx->thread_count = thread_count;
|
|
||||||
} else {
|
|
||||||
decoder_ctx->thread_count = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
decoder_ctx->width = width;
|
|
||||||
decoder_ctx->height = height;
|
|
||||||
decoder_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
|
|
||||||
|
|
||||||
int err = avcodec_open2(decoder_ctx, decoder, NULL);
|
|
||||||
if (err < 0) {
|
|
||||||
printf("Couldn't open codec: %s\n", decoder->name);
|
|
||||||
avcodec_free_context(&decoder_ctx);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (decoder == NULL) {
|
if (decoder == NULL) {
|
||||||
@ -133,7 +70,36 @@ int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Using FFmpeg decoder: %s\n", decoder->name);
|
decoder_ctx = avcodec_alloc_context3(decoder);
|
||||||
|
if (decoder_ctx == NULL) {
|
||||||
|
printf("Couldn't allocate context");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (perf_lvl & DISABLE_LOOP_FILTER)
|
||||||
|
// Skip the loop filter for performance reasons
|
||||||
|
decoder_ctx->skip_loop_filter = AVDISCARD_ALL;
|
||||||
|
|
||||||
|
if (perf_lvl & LOW_LATENCY_DECODE)
|
||||||
|
// Use low delay single threaded encoding
|
||||||
|
decoder_ctx->flags |= AV_CODEC_FLAG_LOW_DELAY;
|
||||||
|
|
||||||
|
if (perf_lvl & SLICE_THREADING)
|
||||||
|
decoder_ctx->thread_type = FF_THREAD_SLICE;
|
||||||
|
else
|
||||||
|
decoder_ctx->thread_type = FF_THREAD_FRAME;
|
||||||
|
|
||||||
|
decoder_ctx->thread_count = thread_count;
|
||||||
|
|
||||||
|
decoder_ctx->width = width;
|
||||||
|
decoder_ctx->height = height;
|
||||||
|
decoder_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
|
||||||
|
|
||||||
|
int err = avcodec_open2(decoder_ctx, decoder, NULL);
|
||||||
|
if (err < 0) {
|
||||||
|
printf("Couldn't open codec");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
dec_frames_cnt = buffer_count;
|
dec_frames_cnt = buffer_count;
|
||||||
dec_frames = malloc(buffer_count * sizeof(AVFrame*));
|
dec_frames = malloc(buffer_count * sizeof(AVFrame*));
|
||||||
@ -161,9 +127,10 @@ int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer
|
|||||||
// This function must be called after
|
// This function must be called after
|
||||||
// decoding is finished
|
// decoding is finished
|
||||||
void ffmpeg_destroy(void) {
|
void ffmpeg_destroy(void) {
|
||||||
av_packet_free(&pkt);
|
|
||||||
if (decoder_ctx) {
|
if (decoder_ctx) {
|
||||||
avcodec_free_context(&decoder_ctx);
|
avcodec_close(decoder_ctx);
|
||||||
|
av_free(decoder_ctx);
|
||||||
|
decoder_ctx = NULL;
|
||||||
}
|
}
|
||||||
if (dec_frames) {
|
if (dec_frames) {
|
||||||
for (int i = 0; i < dec_frames_cnt; i++) {
|
for (int i = 0; i < dec_frames_cnt; i++) {
|
||||||
@ -194,15 +161,15 @@ AVFrame* ffmpeg_get_frame(bool native_frame) {
|
|||||||
int ffmpeg_decode(unsigned char* indata, int inlen) {
|
int ffmpeg_decode(unsigned char* indata, int inlen) {
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
pkt->data = indata;
|
pkt.data = indata;
|
||||||
pkt->size = inlen;
|
pkt.size = inlen;
|
||||||
|
|
||||||
err = avcodec_send_packet(decoder_ctx, pkt);
|
err = avcodec_send_packet(decoder_ctx, &pkt);
|
||||||
if (err < 0) {
|
if (err < 0) {
|
||||||
char errorstring[512];
|
char errorstring[512];
|
||||||
av_strerror(err, errorstring, sizeof(errorstring));
|
av_strerror(err, errorstring, sizeof(errorstring));
|
||||||
fprintf(stderr, "Decode failed - %s\n", errorstring);
|
fprintf(stderr, "Decode failed - %s\n", errorstring);
|
||||||
}
|
}
|
||||||
|
|
||||||
return err < 0 ? err : 0;
|
return err < 0 ? err : 0;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* This file is part of Moonlight Embedded.
|
* This file is part of Moonlight Embedded.
|
||||||
*
|
*
|
||||||
* Based on Moonlight Pc implementation
|
* Based on Moonlight Pc implementation
|
||||||
*
|
*
|
||||||
* Moonlight is free software; you can redistribute it and/or modify
|
* Moonlight is free software; you can redistribute it and/or modify
|
||||||
@ -21,8 +21,18 @@
|
|||||||
|
|
||||||
#include <libavcodec/avcodec.h>
|
#include <libavcodec/avcodec.h>
|
||||||
|
|
||||||
// Enable multi-threaded decoding
|
// Disables the deblocking filter at the cost of image quality
|
||||||
|
#define DISABLE_LOOP_FILTER 0x1
|
||||||
|
// Uses the low latency decode flag (disables multithreading)
|
||||||
|
#define LOW_LATENCY_DECODE 0x2
|
||||||
|
// Threads process each slice, rather than each frame
|
||||||
#define SLICE_THREADING 0x4
|
#define SLICE_THREADING 0x4
|
||||||
|
// Uses nonstandard speedup tricks
|
||||||
|
#define FAST_DECODE 0x8
|
||||||
|
// Uses bilinear filtering instead of bicubic
|
||||||
|
#define BILINEAR_FILTERING 0x10
|
||||||
|
// Uses a faster bilinear filtering with lower image quality
|
||||||
|
#define FAST_BILINEAR_FILTERING 0x20
|
||||||
// Uses hardware acceleration
|
// Uses hardware acceleration
|
||||||
#define VDPAU_ACCELERATION 0x40
|
#define VDPAU_ACCELERATION 0x40
|
||||||
#define VAAPI_ACCELERATION 0x80
|
#define VAAPI_ACCELERATION 0x80
|
||||||
|
@ -46,7 +46,7 @@ static enum AVPixelFormat va_get_format(AVCodecContext* context, const enum AVPi
|
|||||||
fprintf(stderr, "Failed to initialize VAAPI frame context");
|
fprintf(stderr, "Failed to initialize VAAPI frame context");
|
||||||
return AV_PIX_FMT_NONE;
|
return AV_PIX_FMT_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
context->pix_fmt = AV_PIX_FMT_VAAPI;
|
context->pix_fmt = AV_PIX_FMT_VAAPI;
|
||||||
context->hw_device_ctx = device_ref;
|
context->hw_device_ctx = device_ref;
|
||||||
context->hw_frames_ctx = hw_ctx;
|
context->hw_frames_ctx = hw_ctx;
|
||||||
@ -65,7 +65,6 @@ int vaapi_init_lib() {
|
|||||||
int vaapi_init(AVCodecContext* decoder_ctx) {
|
int vaapi_init(AVCodecContext* decoder_ctx) {
|
||||||
decoder_ctx->get_format = va_get_format;
|
decoder_ctx->get_format = va_get_format;
|
||||||
decoder_ctx->get_buffer2 = va_get_buffer;
|
decoder_ctx->get_buffer2 = va_get_buffer;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void vaapi_queue(AVFrame* dec_frame, Window win, int width, int height) {
|
void vaapi_queue(AVFrame* dec_frame, Window win, int width, int height) {
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
#include <linux/v4l2-controls.h>
|
#include <linux/v4l2-controls.h>
|
||||||
#include <linux/videodev2.h>
|
#include <linux/videodev2.h>
|
||||||
|
|
||||||
#define MIN_FRAME_BUFFER_COUNT 18
|
#define MIN_FRAME_BUFFER_COUNT 18;
|
||||||
|
|
||||||
#define THRESHOLD 2
|
#define THRESHOLD 2
|
||||||
|
|
||||||
@ -139,11 +139,11 @@ static int decoder_renderer_setup(int videoFormat, int width, int height, int re
|
|||||||
}
|
}
|
||||||
|
|
||||||
close(fd_fb);
|
close(fd_fb);
|
||||||
|
|
||||||
int regfbcount = MIN_FRAME_BUFFER_COUNT + 2;
|
int regfbcount = MIN_FRAME_BUFFER_COUNT + 2;
|
||||||
int picWidth = ((width + 15) & ~15);
|
int picWidth = ((width + 15) & ~15);
|
||||||
int picHeight = ((height + 15) & ~15);
|
int picHeight = ((height + 15) & ~15);
|
||||||
|
|
||||||
char v4l_device[16];
|
char v4l_device[16];
|
||||||
sprintf(v4l_device, "/dev/video%d", 17);
|
sprintf(v4l_device, "/dev/video%d", 17);
|
||||||
fd = open(v4l_device, O_RDWR, 0);
|
fd = open(v4l_device, O_RDWR, 0);
|
||||||
@ -192,7 +192,7 @@ static int decoder_renderer_setup(int videoFormat, int width, int height, int re
|
|||||||
fprintf(stderr, "Not enough video buffers\n");
|
fprintf(stderr, "Not enough video buffers\n");
|
||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < regfbcount; i++) {
|
for (int i = 0; i < regfbcount; i++) {
|
||||||
struct v4l2_buffer buffer = {0};
|
struct v4l2_buffer buffer = {0};
|
||||||
struct vpu_buf *buf;
|
struct vpu_buf *buf;
|
||||||
@ -233,7 +233,7 @@ static int decoder_renderer_setup(int videoFormat, int width, int height, int re
|
|||||||
return -2;
|
return -2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vpu_setup(buffers, regfbcount, width, height);
|
vpu_setup(buffers, regfbcount, width, height);
|
||||||
|
|
||||||
if (pipe(pipefd) == -1 || pipe(clearpipefd) == -1) {
|
if (pipe(pipefd) == -1 || pipe(clearpipefd) == -1) {
|
||||||
|
@ -143,7 +143,7 @@ void vpu_setup(struct vpu_buf* buffers[], int bufferCount, int width, int height
|
|||||||
}
|
}
|
||||||
fb[i].bufMvCol = mvcol_md[i].phy_addr;
|
fb[i].bufMvCol = mvcol_md[i].phy_addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bufinfo.avcSliceBufInfo.bufferBase = slice_mem_desc.phy_addr;
|
bufinfo.avcSliceBufInfo.bufferBase = slice_mem_desc.phy_addr;
|
||||||
bufinfo.avcSliceBufInfo.bufferSize = phy_slicebuf_size;
|
bufinfo.avcSliceBufInfo.bufferSize = phy_slicebuf_size;
|
||||||
|
|
||||||
@ -210,7 +210,7 @@ bool vpu_decode(PDECODE_UNIT decodeUnit) {
|
|||||||
while (vpu_IsBusy()) {
|
while (vpu_IsBusy()) {
|
||||||
if (loop_id > 50) {
|
if (loop_id > 50) {
|
||||||
vpu_SWReset(handle, 0);
|
vpu_SWReset(handle, 0);
|
||||||
fprintf(stderr, "VPU busy timeout expired\n");
|
fprintf(stderr, "VPU is too long busy\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
vpu_WaitForInt(100);
|
vpu_WaitForInt(100);
|
||||||
@ -237,7 +237,7 @@ bool vpu_decode(PDECODE_UNIT decodeUnit) {
|
|||||||
fprintf(stderr, "Failed to decode frame\n");
|
fprintf(stderr, "Failed to decode frame\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
currentFrame = outinfo.indexFrameDisplay;
|
currentFrame = outinfo.indexFrameDisplay;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -249,7 +249,7 @@ void vpu_clear(int disp_clr_index) {
|
|||||||
void vpu_cleanup() {
|
void vpu_cleanup() {
|
||||||
IOFreePhyMem(&ps_mem_desc);
|
IOFreePhyMem(&ps_mem_desc);
|
||||||
IOFreePhyMem(&slice_mem_desc);
|
IOFreePhyMem(&slice_mem_desc);
|
||||||
|
|
||||||
IOFreeVirtMem(&mem_desc);
|
IOFreeVirtMem(&mem_desc);
|
||||||
IOFreePhyMem(&mem_desc);
|
IOFreePhyMem(&mem_desc);
|
||||||
vpu_UnInit();
|
vpu_UnInit();
|
||||||
|
293
src/video/mmal.c
293
src/video/mmal.c
@ -1,293 +0,0 @@
|
|||||||
|
|
||||||
/*
|
|
||||||
Copyright (c) 2012, Broadcom Europe Ltd
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
* Redistributions in binary form must reproduce the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer in the
|
|
||||||
documentation and/or other materials provided with the distribution.
|
|
||||||
* Neither the name of the copyright holder nor the
|
|
||||||
names of its contributors may be used to endorse or promote products
|
|
||||||
derived from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
||||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Video decode on Raspberry Pi using MMAL
|
|
||||||
// Based upon example code from the Raspberry Pi
|
|
||||||
|
|
||||||
#include "video.h"
|
|
||||||
|
|
||||||
#include <Limelight.h>
|
|
||||||
|
|
||||||
#include <sps.h>
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <bcm_host.h>
|
|
||||||
#include <interface/mmal/mmal.h>
|
|
||||||
#include <interface/mmal/util/mmal_default_components.h>
|
|
||||||
#include <interface/mmal/util/mmal_util_params.h>
|
|
||||||
#include <interface/mmal/util/mmal_util.h>
|
|
||||||
#include <interface/mmal/vc/mmal_vc_api.h>
|
|
||||||
#include <interface/vcos/vcos.h>
|
|
||||||
|
|
||||||
#define ALIGN(x, a) (((x)+(a)-1)&~((a)-1))
|
|
||||||
|
|
||||||
static VCOS_SEMAPHORE_T semaphore;
|
|
||||||
static MMAL_COMPONENT_T *decoder = NULL, *renderer = NULL;
|
|
||||||
static MMAL_POOL_T *pool_in = NULL, *pool_out = NULL;
|
|
||||||
|
|
||||||
static void input_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) {
|
|
||||||
mmal_buffer_header_release(buf);
|
|
||||||
|
|
||||||
if (port == decoder->input[0])
|
|
||||||
vcos_semaphore_post(&semaphore);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) {
|
|
||||||
if (buf->cmd == MMAL_EVENT_ERROR) {
|
|
||||||
MMAL_STATUS_T status = *(uint32_t *) buf->data;
|
|
||||||
fprintf(stderr, "Video decode error MMAL_EVENT_ERROR:%d\n", status);
|
|
||||||
}
|
|
||||||
|
|
||||||
mmal_buffer_header_release(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void output_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) {
|
|
||||||
if (mmal_port_send_buffer(renderer->input[0], buf) != MMAL_SUCCESS) {
|
|
||||||
fprintf(stderr, "Can't display decoded frame\n");
|
|
||||||
mmal_buffer_header_release(buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int decoder_renderer_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
|
|
||||||
if (videoFormat != VIDEO_FORMAT_H264) {
|
|
||||||
fprintf(stderr, "Video format not supported\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
bcm_host_init();
|
|
||||||
mmal_vc_init();
|
|
||||||
gs_sps_init(width, height);
|
|
||||||
|
|
||||||
vcos_semaphore_create(&semaphore, "video_decoder", 1);
|
|
||||||
if (mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &decoder) != MMAL_SUCCESS) {
|
|
||||||
fprintf(stderr, "Can't create decoder\n");
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
MMAL_ES_FORMAT_T *format_in = decoder->input[0]->format;
|
|
||||||
format_in->type = MMAL_ES_TYPE_VIDEO;
|
|
||||||
format_in->encoding = MMAL_ENCODING_H264;
|
|
||||||
format_in->es->video.width = ALIGN(width, 32);
|
|
||||||
format_in->es->video.height = ALIGN(height, 16);
|
|
||||||
format_in->es->video.crop.width = width;
|
|
||||||
format_in->es->video.crop.height = height;
|
|
||||||
format_in->es->video.frame_rate.num = redrawRate;
|
|
||||||
format_in->es->video.frame_rate.den = 1;
|
|
||||||
format_in->es->video.par.num = 1;
|
|
||||||
format_in->es->video.par.den = 1;
|
|
||||||
format_in->flags = MMAL_ES_FORMAT_FLAG_FRAMED;
|
|
||||||
|
|
||||||
if (mmal_port_format_commit(decoder->input[0]) != MMAL_SUCCESS) {
|
|
||||||
fprintf(stderr, "Can't commit input format to decoder\n");
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
|
|
||||||
decoder->input[0]->buffer_num = 5;
|
|
||||||
decoder->input[0]->buffer_size = INITIAL_DECODER_BUFFER_SIZE;
|
|
||||||
pool_in = mmal_port_pool_create(decoder->input[0], decoder->input[0]->buffer_num, decoder->output[0]->buffer_size);
|
|
||||||
|
|
||||||
MMAL_ES_FORMAT_T *format_out = decoder->output[0]->format;
|
|
||||||
format_out->encoding = MMAL_ENCODING_OPAQUE;
|
|
||||||
if (mmal_port_format_commit(decoder->output[0]) != MMAL_SUCCESS) {
|
|
||||||
fprintf(stderr, "Can't commit output format to decoder\n");
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
|
|
||||||
decoder->output[0]->buffer_num = 3;
|
|
||||||
decoder->output[0]->buffer_size = decoder->output[0]->buffer_size_recommended;
|
|
||||||
pool_out = mmal_port_pool_create(decoder->output[0], decoder->output[0]->buffer_num, decoder->output[0]->buffer_size);
|
|
||||||
|
|
||||||
if (mmal_port_enable(decoder->control, control_callback) != MMAL_SUCCESS) {
|
|
||||||
fprintf(stderr, "Can't enable control port\n");
|
|
||||||
return -4;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &renderer) != MMAL_SUCCESS) {
|
|
||||||
fprintf(stderr, "Can't create renderer\n");
|
|
||||||
return -5;
|
|
||||||
}
|
|
||||||
|
|
||||||
format_in = renderer->input[0]->format;
|
|
||||||
format_in->encoding = MMAL_ENCODING_OPAQUE;
|
|
||||||
format_in->es->video.width = width;
|
|
||||||
format_in->es->video.height = height;
|
|
||||||
format_in->es->video.crop.x = 0;
|
|
||||||
format_in->es->video.crop.y = 0;
|
|
||||||
format_in->es->video.crop.width = width;
|
|
||||||
format_in->es->video.crop.height = height;
|
|
||||||
if (mmal_port_format_commit(renderer->input[0]) != MMAL_SUCCESS) {
|
|
||||||
fprintf(stderr, "Can't set output format\n");
|
|
||||||
return -6;
|
|
||||||
}
|
|
||||||
|
|
||||||
MMAL_DISPLAYREGION_T param;
|
|
||||||
param.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
|
|
||||||
param.hdr.size = sizeof(MMAL_DISPLAYREGION_T);
|
|
||||||
param.set = MMAL_DISPLAY_SET_LAYER | MMAL_DISPLAY_SET_NUM | MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_TRANSFORM;
|
|
||||||
param.layer = 128;
|
|
||||||
param.display_num = 0;
|
|
||||||
param.fullscreen = true;
|
|
||||||
int displayRotation = drFlags & DISPLAY_ROTATE_MASK;
|
|
||||||
switch (displayRotation) {
|
|
||||||
case DISPLAY_ROTATE_90:
|
|
||||||
param.transform = MMAL_DISPLAY_ROT90;
|
|
||||||
break;
|
|
||||||
case DISPLAY_ROTATE_180:
|
|
||||||
param.transform = MMAL_DISPLAY_ROT180;
|
|
||||||
break;
|
|
||||||
case DISPLAY_ROTATE_270:
|
|
||||||
param.transform = MMAL_DISPLAY_ROT270;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
param.transform = MMAL_DISPLAY_ROT0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mmal_port_parameter_set(renderer->input[0], ¶m.hdr) != MMAL_SUCCESS) {
|
|
||||||
fprintf(stderr, "Can't set parameters\n");
|
|
||||||
return -7;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mmal_port_enable(renderer->control, control_callback) != MMAL_SUCCESS) {
|
|
||||||
fprintf(stderr, "Can't enable control port\n");
|
|
||||||
return -8;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mmal_component_enable(renderer) != MMAL_SUCCESS) {
|
|
||||||
fprintf(stderr, "Can't enable renderer\n");
|
|
||||||
return -9;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mmal_port_enable(renderer->input[0], input_callback) != MMAL_SUCCESS) {
|
|
||||||
fprintf(stderr, "Can't enable renderer input port\n");
|
|
||||||
return -10;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mmal_port_enable(decoder->input[0], input_callback) != MMAL_SUCCESS) {
|
|
||||||
fprintf(stderr, "Can't enable decoder input port\n");
|
|
||||||
return -11;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mmal_port_enable(decoder->output[0], output_callback) != MMAL_SUCCESS) {
|
|
||||||
fprintf(stderr, "Can't enable decoder output port\n");
|
|
||||||
return -12;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mmal_component_enable(decoder) != MMAL_SUCCESS) {
|
|
||||||
fprintf(stderr, "Can't enable decoder\n");
|
|
||||||
return -13;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void decoder_renderer_cleanup() {
|
|
||||||
if (decoder)
|
|
||||||
mmal_component_destroy(decoder);
|
|
||||||
|
|
||||||
if (renderer)
|
|
||||||
mmal_component_destroy(renderer);
|
|
||||||
|
|
||||||
if (pool_in)
|
|
||||||
mmal_pool_destroy(pool_in);
|
|
||||||
|
|
||||||
if (pool_out)
|
|
||||||
mmal_pool_destroy(pool_out);
|
|
||||||
|
|
||||||
vcos_semaphore_delete(&semaphore);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int decoder_renderer_submit_decode_unit(PDECODE_UNIT decodeUnit) {
|
|
||||||
MMAL_STATUS_T status;
|
|
||||||
MMAL_BUFFER_HEADER_T *buf = NULL;
|
|
||||||
PLENTRY entry = decodeUnit->bufferList;
|
|
||||||
bool first_entry = false;
|
|
||||||
|
|
||||||
while (entry != NULL) {
|
|
||||||
if (buf == NULL) {
|
|
||||||
vcos_semaphore_wait(&semaphore);
|
|
||||||
if ((buf = mmal_queue_get(pool_in->queue)) != NULL) {
|
|
||||||
buf->flags = 0;
|
|
||||||
buf->offset = 0;
|
|
||||||
buf->pts = buf->dts = MMAL_TIME_UNKNOWN;
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Video buffer full\n");
|
|
||||||
return DR_NEED_IDR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry->bufferType != BUFFER_TYPE_PICDATA)
|
|
||||||
buf->flags |= MMAL_BUFFER_HEADER_FLAG_CONFIG;
|
|
||||||
else if (!first_entry) {
|
|
||||||
buf->flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_START;
|
|
||||||
first_entry = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry->bufferType == BUFFER_TYPE_SPS)
|
|
||||||
gs_sps_fix(entry, GS_SPS_BITSTREAM_FIXUP, buf->data, &buf->length);
|
|
||||||
else {
|
|
||||||
if (entry->length + buf->length > buf->alloc_size) {
|
|
||||||
fprintf(stderr, "Video decoder buffer too small\n");
|
|
||||||
mmal_buffer_header_release(buf);
|
|
||||||
return DR_NEED_IDR;
|
|
||||||
}
|
|
||||||
memcpy(buf->data + buf->length, entry->data, entry->length);
|
|
||||||
buf->length += entry->length;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entry->bufferType != BUFFER_TYPE_PICDATA || entry->next == NULL || entry->next->bufferType != BUFFER_TYPE_PICDATA) {
|
|
||||||
buf->flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_END;
|
|
||||||
if ((status = mmal_port_send_buffer(decoder->input[0], buf)) != MMAL_SUCCESS) {
|
|
||||||
mmal_buffer_header_release(buf);
|
|
||||||
return DR_NEED_IDR;
|
|
||||||
}
|
|
||||||
buf = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
entry = entry->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Send available output buffers to decoder
|
|
||||||
while ((buf = mmal_queue_get(pool_out->queue))) {
|
|
||||||
if ((status = mmal_port_send_buffer(decoder->output[0], buf)) != MMAL_SUCCESS)
|
|
||||||
mmal_buffer_header_release(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
return DR_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
DECODER_RENDERER_CALLBACKS decoder_callbacks_mmal = {
|
|
||||||
.setup = decoder_renderer_setup,
|
|
||||||
.cleanup = decoder_renderer_cleanup,
|
|
||||||
.submitDecodeUnit = decoder_renderer_submit_decode_unit,
|
|
||||||
.capabilities = CAPABILITY_DIRECT_SUBMIT,
|
|
||||||
};
|
|
@ -28,8 +28,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
// Video decode on Raspberry Pi using OpenMAX IL though the ilcient helper library
|
// Video decode on Raspberry Pi using OpenMAX IL though the ilcient helper library
|
||||||
// Based upon video decode example from the Raspberry Pi firmware
|
// Based upon video decode example from the Raspberry Pi firmware
|
||||||
|
|
||||||
#include "video.h"
|
|
||||||
|
|
||||||
#include <Limelight.h>
|
#include <Limelight.h>
|
||||||
|
|
||||||
#include <sps.h>
|
#include <sps.h>
|
||||||
@ -41,11 +39,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|||||||
#include <ilclient.h>
|
#include <ilclient.h>
|
||||||
#include <bcm_host.h>
|
#include <bcm_host.h>
|
||||||
|
|
||||||
|
#define MAX_DECODE_UNIT_SIZE 262144
|
||||||
|
|
||||||
static TUNNEL_T tunnel[2];
|
static TUNNEL_T tunnel[2];
|
||||||
static COMPONENT_T *list[3];
|
static COMPONENT_T *list[3];
|
||||||
static ILCLIENT_T *client;
|
static ILCLIENT_T *client;
|
||||||
|
|
||||||
static COMPONENT_T *video_decode = NULL, *video_render = NULL;
|
static COMPONENT_T *video_decode = NULL, *video_scheduler = NULL, *video_render = NULL;
|
||||||
|
|
||||||
static int port_settings_changed;
|
static int port_settings_changed;
|
||||||
static int first_packet;
|
static int first_packet;
|
||||||
@ -60,6 +60,8 @@ static int decoder_renderer_setup(int videoFormat, int width, int height, int re
|
|||||||
gs_sps_init(width, height);
|
gs_sps_init(width, height);
|
||||||
|
|
||||||
OMX_VIDEO_PARAM_PORTFORMATTYPE format;
|
OMX_VIDEO_PARAM_PORTFORMATTYPE format;
|
||||||
|
OMX_TIME_CONFIG_CLOCKSTATETYPE cstate;
|
||||||
|
COMPONENT_T *clock = NULL;
|
||||||
|
|
||||||
memset(list, 0, sizeof(list));
|
memset(list, 0, sizeof(list));
|
||||||
memset(tunnel, 0, sizeof(tunnel));
|
memset(tunnel, 0, sizeof(tunnel));
|
||||||
@ -128,34 +130,18 @@ static int decoder_renderer_setup(int videoFormat, int width, int height, int re
|
|||||||
latencyTarget.nInterFactor = 500;
|
latencyTarget.nInterFactor = 500;
|
||||||
latencyTarget.nAdjCap = 20;
|
latencyTarget.nAdjCap = 20;
|
||||||
|
|
||||||
|
OMX_CONFIG_DISPLAYREGIONTYPE displayRegion;
|
||||||
|
displayRegion.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE);
|
||||||
|
displayRegion.nVersion.nVersion = OMX_VERSION;
|
||||||
|
displayRegion.nPortIndex = 90;
|
||||||
|
displayRegion.fullscreen = OMX_TRUE;
|
||||||
|
displayRegion.mode = OMX_DISPLAY_SET_FULLSCREEN;
|
||||||
|
|
||||||
if(OMX_SetParameter(ILC_GET_HANDLE(video_render), OMX_IndexConfigLatencyTarget, &latencyTarget) != OMX_ErrorNone) {
|
if(OMX_SetParameter(ILC_GET_HANDLE(video_render), OMX_IndexConfigLatencyTarget, &latencyTarget) != OMX_ErrorNone) {
|
||||||
fprintf(stderr, "Failed to set video render parameters\n");
|
fprintf(stderr, "Failed to set video render parameters\n");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
OMX_CONFIG_ROTATIONTYPE rotationType;
|
|
||||||
memset(&rotationType, 0, sizeof(OMX_CONFIG_ROTATIONTYPE));
|
|
||||||
rotationType.nSize = sizeof(OMX_CONFIG_ROTATIONTYPE);
|
|
||||||
rotationType.nVersion.nVersion = OMX_VERSION;
|
|
||||||
rotationType.nPortIndex = 90;
|
|
||||||
int displayRotation = drFlags & DISPLAY_ROTATE_MASK;
|
|
||||||
switch (displayRotation) {
|
|
||||||
case DISPLAY_ROTATE_90:
|
|
||||||
rotationType.nRotation = 90;
|
|
||||||
break;
|
|
||||||
case DISPLAY_ROTATE_180:
|
|
||||||
rotationType.nRotation = 180;
|
|
||||||
break;
|
|
||||||
case DISPLAY_ROTATE_270:
|
|
||||||
rotationType.nRotation = 270;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(OMX_SetParameter(ILC_GET_HANDLE(video_render), OMX_IndexConfigCommonRotate, &rotationType) != OMX_ErrorNone) {
|
|
||||||
fprintf(stderr, "Failed to set video rotation\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
OMX_PARAM_PORTDEFINITIONTYPE port;
|
OMX_PARAM_PORTDEFINITIONTYPE port;
|
||||||
|
|
||||||
memset(&port, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
|
memset(&port, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
|
||||||
@ -168,7 +154,7 @@ static int decoder_renderer_setup(int videoFormat, int width, int height, int re
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Increase the buffer size to fit the largest possible frame
|
// Increase the buffer size to fit the largest possible frame
|
||||||
port.nBufferSize = INITIAL_DECODER_BUFFER_SIZE;
|
port.nBufferSize = MAX_DECODE_UNIT_SIZE;
|
||||||
|
|
||||||
if(OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamPortDefinition, &port) == OMX_ErrorNone &&
|
if(OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamPortDefinition, &port) == OMX_ErrorNone &&
|
||||||
ilclient_enable_port_buffers(video_decode, 130, NULL, NULL, NULL) == 0) {
|
ilclient_enable_port_buffers(video_decode, 130, NULL, NULL, NULL) == 0) {
|
||||||
@ -186,6 +172,8 @@ static int decoder_renderer_setup(int videoFormat, int width, int height, int re
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void decoder_renderer_cleanup() {
|
static void decoder_renderer_cleanup() {
|
||||||
|
int status = 0;
|
||||||
|
|
||||||
OMX_BUFFERHEADERTYPE *buf;
|
OMX_BUFFERHEADERTYPE *buf;
|
||||||
if((buf = ilclient_get_input_buffer(video_decode, 130, 1)) == NULL){
|
if((buf = ilclient_get_input_buffer(video_decode, 130, 1)) == NULL){
|
||||||
fprintf(stderr, "Can't get video buffer\n");
|
fprintf(stderr, "Can't get video buffer\n");
|
||||||
|
505
src/video/rk.c
505
src/video/rk.c
@ -18,8 +18,7 @@
|
|||||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "video.h"
|
#include <Limelight.h>
|
||||||
#include "../util.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@ -28,6 +27,7 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -35,80 +35,24 @@
|
|||||||
#include <xf86drm.h>
|
#include <xf86drm.h>
|
||||||
#include <xf86drmMode.h>
|
#include <xf86drmMode.h>
|
||||||
#include <libdrm/drm_fourcc.h>
|
#include <libdrm/drm_fourcc.h>
|
||||||
#include <linux/videodev2.h>
|
|
||||||
|
|
||||||
#include <rockchip/rk_mpi.h>
|
#include <rockchip/rk_mpi.h>
|
||||||
|
|
||||||
#define MAX_FRAMES 3
|
#define READ_BUF_SIZE (SZ_1M)
|
||||||
#define RK_H264 0x7
|
#define MAX_FRAMES 16
|
||||||
#define RK_H265 0x1000004
|
#define RK_H264 7
|
||||||
#define RK_AV1 0x1000008
|
#define RK_H265 16777220
|
||||||
|
|
||||||
// Vendor-defined 10-bit format code used prior to 5.10
|
|
||||||
#ifndef DRM_FORMAT_NA12
|
|
||||||
#define DRM_FORMAT_NA12 fourcc_code('N', 'A', '1', '2')
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Upstreamed 10-bit format code used on 5.10+ kernels
|
|
||||||
#ifndef DRM_FORMAT_NV15
|
|
||||||
#define DRM_FORMAT_NV15 fourcc_code('N', 'V', '1', '5')
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Values for "Colorspace" connector property
|
|
||||||
#ifndef DRM_MODE_COLORIMETRY_DEFAULT
|
|
||||||
#define DRM_MODE_COLORIMETRY_DEFAULT 0
|
|
||||||
#endif
|
|
||||||
#ifndef DRM_MODE_COLORIMETRY_BT2020_RGB
|
|
||||||
#define DRM_MODE_COLORIMETRY_BT2020_RGB 9
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// HDR structs copied from linux include/linux/hdmi.h for older libdrm versions
|
|
||||||
struct rk_hdr_metadata_infoframe
|
|
||||||
{
|
|
||||||
uint8_t eotf;
|
|
||||||
uint8_t metadata_type;
|
|
||||||
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
uint16_t x, y;
|
|
||||||
} display_primaries[3];
|
|
||||||
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
uint16_t x, y;
|
|
||||||
} white_point;
|
|
||||||
|
|
||||||
uint16_t max_display_mastering_luminance;
|
|
||||||
uint16_t min_display_mastering_luminance;
|
|
||||||
|
|
||||||
uint16_t max_cll;
|
|
||||||
uint16_t max_fall;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct rk_hdr_output_metadata
|
|
||||||
{
|
|
||||||
uint32_t metadata_type;
|
|
||||||
|
|
||||||
union {
|
|
||||||
struct rk_hdr_metadata_infoframe hdmi_metadata_type1;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
void *pkt_buf = NULL;
|
void *pkt_buf = NULL;
|
||||||
size_t pkt_buf_size = 0;
|
|
||||||
int fd;
|
int fd;
|
||||||
int fb_id;
|
int fb_id;
|
||||||
uint32_t plane_id, crtc_id, conn_id, hdr_metadata_blob_id, pixel_format;
|
uint32_t plane_id, crtc_id;
|
||||||
int frm_eos;
|
int frm_eos;
|
||||||
int crtc_width;
|
int crtc_width;
|
||||||
int crtc_height;
|
int crtc_height;
|
||||||
RK_U32 frm_width;
|
RK_U32 frm_width;
|
||||||
RK_U32 frm_height;
|
RK_U32 frm_height;
|
||||||
int fb_x, fb_y, fb_width, fb_height;
|
int fb_x, fb_y, fb_width, fb_height;
|
||||||
bool atomic;
|
|
||||||
|
|
||||||
uint8_t last_colorspace = 0xFF;
|
|
||||||
bool last_hdr_state = false;
|
|
||||||
|
|
||||||
pthread_t tid_frame, tid_display;
|
pthread_t tid_frame, tid_display;
|
||||||
pthread_mutex_t mutex;
|
pthread_mutex_t mutex;
|
||||||
@ -121,12 +65,6 @@ drmModeRes *resources = NULL;
|
|||||||
drmModePlaneRes *plane_resources = NULL;
|
drmModePlaneRes *plane_resources = NULL;
|
||||||
drmModeCrtcPtr crtc = {0};
|
drmModeCrtcPtr crtc = {0};
|
||||||
|
|
||||||
drmModePropertyPtr hdr_metadata_prop = NULL;
|
|
||||||
|
|
||||||
drmModeAtomicReqPtr drm_request = NULL;
|
|
||||||
drmModePropertyPtr plane_props[32];
|
|
||||||
drmModePropertyPtr conn_props[32];
|
|
||||||
|
|
||||||
MppCtx mpi_ctx;
|
MppCtx mpi_ctx;
|
||||||
MppApi *mpi_api;
|
MppApi *mpi_api;
|
||||||
MppPacket mpi_packet;
|
MppPacket mpi_packet;
|
||||||
@ -138,57 +76,36 @@ struct {
|
|||||||
uint32_t handle;
|
uint32_t handle;
|
||||||
} frame_to_drm[MAX_FRAMES];
|
} frame_to_drm[MAX_FRAMES];
|
||||||
|
|
||||||
int set_property(uint32_t id, uint32_t type, drmModePropertyPtr *props, char *name, uint64_t value) {
|
|
||||||
while (*props) {
|
|
||||||
if (!strcasecmp(name, (*props)->name)) {
|
|
||||||
if (atomic)
|
|
||||||
return drmModeAtomicAddProperty(drm_request, id, (*props)->prop_id, value);
|
|
||||||
else
|
|
||||||
return drmModeObjectSetProperty(fd, id, type, (*props)->prop_id, value);
|
|
||||||
}
|
|
||||||
props++;
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(stderr, "Property '%s' not found\n", name);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *display_thread(void *param) {
|
void *display_thread(void *param) {
|
||||||
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
while (!frm_eos) {
|
while (!frm_eos) {
|
||||||
int _fb_id;
|
int _fb_id;
|
||||||
|
|
||||||
pthread_mutex_lock(&mutex);
|
ret = pthread_mutex_lock(&mutex);
|
||||||
|
assert(!ret);
|
||||||
while (fb_id == 0) {
|
while (fb_id == 0) {
|
||||||
pthread_cond_wait(&cond, &mutex);
|
pthread_cond_wait(&cond, &mutex);
|
||||||
|
assert(!ret);
|
||||||
if (fb_id == 0 && frm_eos) {
|
if (fb_id == 0 && frm_eos) {
|
||||||
pthread_mutex_unlock(&mutex);
|
ret = pthread_mutex_unlock(&mutex);
|
||||||
|
assert(!ret);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_fb_id = fb_id;
|
_fb_id = fb_id;
|
||||||
|
|
||||||
fb_id = 0;
|
fb_id = 0;
|
||||||
pthread_mutex_unlock(&mutex);
|
ret = pthread_mutex_unlock(&mutex);
|
||||||
|
assert(!ret);
|
||||||
|
|
||||||
if (atomic) {
|
// show DRM FB in overlay plane (auto vsynced/atomic !)
|
||||||
// We may need to modeset to apply colorspace changes when toggling HDR
|
ret = drmModeSetPlane(fd, plane_id, crtc_id, _fb_id, 0,
|
||||||
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "FB_ID", _fb_id);
|
fb_x, fb_y, fb_width, fb_height,
|
||||||
ret = drmModeAtomicCommit(fd, drm_request, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
|
0, 0, frm_width << 16, frm_height << 16);
|
||||||
if (ret) {
|
assert(!ret);
|
||||||
perror("drmModeAtomicCommit");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ret = drmModeSetPlane(fd, plane_id, crtc_id, _fb_id, 0,
|
|
||||||
fb_x, fb_y, fb_width, fb_height,
|
|
||||||
0, 0, frm_width << 16, frm_height << 16);
|
|
||||||
if (ret) {
|
|
||||||
perror("drmModeSetPlane");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void *frame_thread(void *param) {
|
void *frame_thread(void *param) {
|
||||||
@ -245,27 +162,25 @@ void *frame_thread(void *param) {
|
|||||||
|
|
||||||
// new DRM buffer
|
// new DRM buffer
|
||||||
struct drm_mode_create_dumb dmcd = {0};
|
struct drm_mode_create_dumb dmcd = {0};
|
||||||
dmcd.bpp = 8; // hor_stride is already adjusted for 10 vs 8 bit
|
dmcd.bpp = fmt == MPP_FMT_YUV420SP ? 8:10;
|
||||||
dmcd.width = hor_stride;
|
dmcd.width = hor_stride;
|
||||||
dmcd.height = ver_stride * 2; // documentation say not v*2/3 but v*2 (additional info included)
|
dmcd.height = ver_stride * 2; // documentation say not v*2/3 but v*2 (additional info included)
|
||||||
ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &dmcd);
|
do {
|
||||||
if (ret) {
|
ret = ioctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &dmcd);
|
||||||
perror("drmIoctl(DRM_IOCTL_MODE_CREATE_DUMB)");
|
} while (ret == -1 && (errno == EINTR || errno == EAGAIN));
|
||||||
exit(EXIT_FAILURE);
|
assert(!ret);
|
||||||
}
|
assert(dmcd.pitch == (fmt == MPP_FMT_YUV420SP?hor_stride:hor_stride * 10 / 8));
|
||||||
assert(dmcd.pitch == dmcd.width);
|
assert(dmcd.size == (fmt == MPP_FMT_YUV420SP?hor_stride:hor_stride * 10 / 8) * ver_stride * 2);
|
||||||
assert(dmcd.size == dmcd.pitch * dmcd.height);
|
|
||||||
frame_to_drm[i].handle = dmcd.handle;
|
frame_to_drm[i].handle = dmcd.handle;
|
||||||
|
|
||||||
// commit DRM buffer to frame group
|
// commit DRM buffer to frame group
|
||||||
struct drm_prime_handle dph = {0};
|
struct drm_prime_handle dph = {0};
|
||||||
dph.handle = dmcd.handle;
|
dph.handle = dmcd.handle;
|
||||||
dph.fd = -1;
|
dph.fd = -1;
|
||||||
ret = drmIoctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &dph);
|
do {
|
||||||
if (ret) {
|
ret = ioctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &dph);
|
||||||
perror("drmIoctl(DRM_IOCTL_PRIME_HANDLE_TO_FD)");
|
} while (ret == -1 && (errno == EINTR || errno == EAGAIN));
|
||||||
exit(EXIT_FAILURE);
|
assert(!ret);
|
||||||
}
|
|
||||||
MppBufferInfo info = {0};
|
MppBufferInfo info = {0};
|
||||||
info.type = MPP_BUFFER_TYPE_DRM;
|
info.type = MPP_BUFFER_TYPE_DRM;
|
||||||
info.size = dmcd.width * dmcd.height;
|
info.size = dmcd.width * dmcd.height;
|
||||||
@ -278,37 +193,17 @@ void *frame_thread(void *param) {
|
|||||||
uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
|
uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
|
||||||
handles[0] = frame_to_drm[i].handle;
|
handles[0] = frame_to_drm[i].handle;
|
||||||
offsets[0] = 0;
|
offsets[0] = 0;
|
||||||
pitches[0] = dmcd.pitch;
|
pitches[0] = hor_stride;
|
||||||
handles[1] = frame_to_drm[i].handle;
|
handles[1] = frame_to_drm[i].handle;
|
||||||
offsets[1] = pitches[0] * ver_stride;
|
offsets[1] = hor_stride * ver_stride;
|
||||||
pitches[1] = dmcd.pitch;
|
pitches[1] = hor_stride;
|
||||||
ret = drmModeAddFB2(fd, frm_width, frm_height, pixel_format, handles, pitches, offsets, &frame_to_drm[i].fb_id, 0);
|
ret = drmModeAddFB2(fd, frm_width, frm_height, fmt == MPP_FMT_YUV420SP ? DRM_FORMAT_NV12:DRM_FORMAT_NV12_10, handles, pitches, offsets, &frame_to_drm[i].fb_id, 0);
|
||||||
if (ret) {
|
assert(!ret);
|
||||||
perror("drmModeAddFB2");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// register external frame group
|
// register external frame group
|
||||||
ret = mpi_api->control(mpi_ctx, MPP_DEC_SET_EXT_BUF_GROUP, mpi_frm_grp);
|
ret = mpi_api->control(mpi_ctx, MPP_DEC_SET_EXT_BUF_GROUP, mpi_frm_grp);
|
||||||
ret = mpi_api->control(mpi_ctx, MPP_DEC_SET_INFO_CHANGE_READY, NULL);
|
ret = mpi_api->control(mpi_ctx, MPP_DEC_SET_INFO_CHANGE_READY, NULL);
|
||||||
|
|
||||||
// Set atomic properties for the plane prior to the first commit
|
|
||||||
if (atomic) {
|
|
||||||
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "CRTC_ID", crtc_id);
|
|
||||||
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "SRC_X", 0 << 16);
|
|
||||||
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "SRC_Y", 0 << 16);
|
|
||||||
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "SRC_W", frm_width << 16);
|
|
||||||
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "SRC_H", frm_height << 16);
|
|
||||||
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "CRTC_X", fb_x);
|
|
||||||
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "CRTC_Y", fb_y);
|
|
||||||
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "CRTC_W", fb_width);
|
|
||||||
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "CRTC_H", fb_height);
|
|
||||||
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "ZPOS", 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set properties on the connector
|
|
||||||
set_property(conn_id, DRM_MODE_OBJECT_CONNECTOR, conn_props, "allm_enable", 1); // HDMI ALLM (Game Mode)
|
|
||||||
set_property(conn_id, DRM_MODE_OBJECT_CONNECTOR, conn_props, "Colorspace", last_hdr_state ? DRM_MODE_COLORIMETRY_BT2020_RGB : DRM_MODE_COLORIMETRY_DEFAULT);
|
|
||||||
} else {
|
} else {
|
||||||
// regular frame received
|
// regular frame received
|
||||||
|
|
||||||
@ -323,10 +218,14 @@ void *frame_thread(void *param) {
|
|||||||
}
|
}
|
||||||
assert(i != MAX_FRAMES);
|
assert(i != MAX_FRAMES);
|
||||||
// send DRM FB to display thread
|
// send DRM FB to display thread
|
||||||
pthread_mutex_lock(&mutex);
|
ret = pthread_mutex_lock(&mutex);
|
||||||
|
assert(!ret);
|
||||||
fb_id = frame_to_drm[i].fb_id;
|
fb_id = frame_to_drm[i].fb_id;
|
||||||
pthread_cond_signal(&cond);
|
ret = pthread_cond_signal(&cond);
|
||||||
pthread_mutex_unlock(&mutex);
|
assert(!ret);
|
||||||
|
ret = pthread_mutex_unlock(&mutex);
|
||||||
|
assert(!ret);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, "Frame no buff\n");
|
fprintf(stderr, "Frame no buff\n");
|
||||||
}
|
}
|
||||||
@ -351,42 +250,29 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte
|
|||||||
int ret;
|
int ret;
|
||||||
int i;
|
int i;
|
||||||
int j;
|
int j;
|
||||||
int format;
|
int format = 0;
|
||||||
|
|
||||||
if (videoFormat & VIDEO_FORMAT_MASK_H264) {
|
switch (videoFormat) {
|
||||||
format = RK_H264;
|
case VIDEO_FORMAT_H264:
|
||||||
} else if (videoFormat & VIDEO_FORMAT_MASK_H265) {
|
format = RK_H264;
|
||||||
format = RK_H265;
|
break;
|
||||||
} else if (videoFormat & VIDEO_FORMAT_MASK_AV1) {
|
case VIDEO_FORMAT_H265:
|
||||||
format = RK_AV1;
|
format = RK_H265;
|
||||||
} else {
|
break;
|
||||||
fprintf(stderr, "Video format not supported\n");
|
default:
|
||||||
return -1;
|
fprintf(stderr, "Video format not supported\n");
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need atomic plane properties for HDR, but atomic seems to perform quite bad
|
|
||||||
// on RK3588 for some reason. The performance of commits seems to go down the longer
|
|
||||||
// the stream runs. We'll use the legacy API for non-HDR streams as a workaround.
|
|
||||||
atomic = !!(videoFormat & VIDEO_FORMAT_MASK_10BIT);
|
|
||||||
|
|
||||||
MppCodingType mpp_type = (MppCodingType)format;
|
MppCodingType mpp_type = (MppCodingType)format;
|
||||||
ret = mpp_check_support_format(MPP_CTX_DEC, mpp_type);
|
ret = mpp_check_support_format(MPP_CTX_DEC, mpp_type);
|
||||||
if (ret) {
|
assert(!ret);
|
||||||
fprintf(stderr, "Selected video format is not supported\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
|
fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
|
||||||
if (fd < 0) {
|
assert(fd >= 0);
|
||||||
fprintf(stderr, "Unable to open card0: %d\n", errno);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
resources = drmModeGetResources(fd);
|
resources = drmModeGetResources(fd);
|
||||||
if (!resources) {
|
assert(resources);
|
||||||
perror("drmModeGetResources");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// find active monitor
|
// find active monitor
|
||||||
for (i = 0; i < resources->count_connectors; ++i) {
|
for (i = 0; i < resources->count_connectors; ++i) {
|
||||||
@ -401,24 +287,6 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte
|
|||||||
}
|
}
|
||||||
assert(i < resources->count_connectors);
|
assert(i < resources->count_connectors);
|
||||||
|
|
||||||
conn_id = connector->connector_id;
|
|
||||||
|
|
||||||
{
|
|
||||||
drmModeObjectPropertiesPtr props = drmModeObjectGetProperties(fd, conn_id, DRM_MODE_OBJECT_CONNECTOR);
|
|
||||||
assert(props->count_props < sizeof(conn_props) / sizeof(conn_props[0]));
|
|
||||||
for (j = 0; j < props->count_props; j++) {
|
|
||||||
drmModePropertyPtr prop = drmModeGetProperty(fd, props->props[j]);
|
|
||||||
if (!prop) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!strcmp(prop->name, "HDR_OUTPUT_METADATA")) {
|
|
||||||
hdr_metadata_prop = prop;
|
|
||||||
}
|
|
||||||
conn_props[j] = prop;
|
|
||||||
}
|
|
||||||
drmModeFreeObjectProperties(props);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < resources->count_encoders; ++i) {
|
for (i = 0; i < resources->count_encoders; ++i) {
|
||||||
encoder = drmModeGetEncoder(fd, resources->encoders[i]);
|
encoder = drmModeGetEncoder(fd, resources->encoders[i]);
|
||||||
if (!encoder) {
|
if (!encoder) {
|
||||||
@ -434,10 +302,7 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte
|
|||||||
for (i = 0; i < resources->count_crtcs; ++i) {
|
for (i = 0; i < resources->count_crtcs; ++i) {
|
||||||
if (resources->crtcs[i] == encoder->crtc_id) {
|
if (resources->crtcs[i] == encoder->crtc_id) {
|
||||||
crtc = drmModeGetCrtc(fd, resources->crtcs[i]);
|
crtc = drmModeGetCrtc(fd, resources->crtcs[i]);
|
||||||
if (!crtc) {
|
assert(crtc);
|
||||||
perror("drmModeGetCrtc");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -448,25 +313,9 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte
|
|||||||
uint32_t crtc_bit = (1 << i);
|
uint32_t crtc_bit = (1 << i);
|
||||||
|
|
||||||
ret = drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
|
ret = drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
|
||||||
if (ret) {
|
assert(!ret);
|
||||||
perror("drmSetClientCap(DRM_CLIENT_CAP_UNIVERSAL_PLANES)");
|
|
||||||
}
|
|
||||||
if (atomic) {
|
|
||||||
ret = drmSetClientCap(fd, DRM_CLIENT_CAP_ATOMIC, 1);
|
|
||||||
if (ret) {
|
|
||||||
perror("drmSetClientCap(DRM_CLIENT_CAP_ATOMIC)");
|
|
||||||
atomic = false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
drm_request = drmModeAtomicAlloc();
|
|
||||||
assert(drm_request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
plane_resources = drmModeGetPlaneResources(fd);
|
plane_resources = drmModeGetPlaneResources(fd);
|
||||||
if (!plane_resources) {
|
assert(plane_resources);
|
||||||
perror("drmModeGetPlaneResources");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// search for OVERLAY (for active connector, unused, NV12 support)
|
// search for OVERLAY (for active connector, unused, NV12 support)
|
||||||
for (i = 0; i < plane_resources->count_planes; i++) {
|
for (i = 0; i < plane_resources->count_planes; i++) {
|
||||||
@ -475,19 +324,11 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (j = 0; j < ovr->count_formats; j++) {
|
for (j = 0; j < ovr->count_formats; j++) {
|
||||||
if (videoFormat & VIDEO_FORMAT_MASK_10BIT) {
|
if (ovr->formats[j] == DRM_FORMAT_NV12) {
|
||||||
// 10-bit formats use NA12 (vendor-defined) or NV15 (upstreamed in 5.10+)
|
|
||||||
if (ovr->formats[j] == DRM_FORMAT_NA12 || ovr->formats[j] == DRM_FORMAT_NV15) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (ovr->formats[j] == DRM_FORMAT_NV12) {
|
|
||||||
// 8-bit formats always use NV12
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (j < ovr->count_formats) {
|
if (j == ovr->count_formats) {
|
||||||
pixel_format = ovr->formats[j];
|
|
||||||
} else {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ((ovr->possible_crtcs & crtc_bit) && !ovr->crtc_id) {
|
if ((ovr->possible_crtcs & crtc_bit) && !ovr->crtc_id) {
|
||||||
@ -496,70 +337,37 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(props->count_props < sizeof(plane_props) / sizeof(plane_props[0]));
|
for (j = 0; j < props->count_props && !plane_id; j++) {
|
||||||
for (j = 0; j < props->count_props; j++) {
|
|
||||||
drmModePropertyPtr prop = drmModeGetProperty(fd, props->props[j]);
|
drmModePropertyPtr prop = drmModeGetProperty(fd, props->props[j]);
|
||||||
if (!prop) {
|
if (!prop) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
plane_props[j] = prop;
|
if (!strcmp(prop->name, "type") && props->prop_values[j] == DRM_PLANE_TYPE_OVERLAY) {
|
||||||
if (!strcmp(prop->name, "type") && (props->prop_values[j] == DRM_PLANE_TYPE_OVERLAY ||
|
|
||||||
props->prop_values[j] == DRM_PLANE_TYPE_CURSOR ||
|
|
||||||
props->prop_values[j] == DRM_PLANE_TYPE_PRIMARY)) {
|
|
||||||
plane_id = ovr->plane_id;
|
plane_id = ovr->plane_id;
|
||||||
}
|
}
|
||||||
|
drmModeFreeProperty(prop);
|
||||||
}
|
}
|
||||||
|
drmModeFreeObjectProperties(props);
|
||||||
if (plane_id) {
|
if (plane_id) {
|
||||||
drmModeFreeObjectProperties(props);
|
break;
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
for (j = 0; j < props->count_props; j++) {
|
|
||||||
drmModeFreeProperty(plane_props[j]);
|
|
||||||
plane_props[j] = NULL;
|
|
||||||
}
|
|
||||||
drmModeFreeObjectProperties(props);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
drmModeFreePlane(ovr);
|
drmModeFreePlane(ovr);
|
||||||
}
|
}
|
||||||
|
assert(plane_id);
|
||||||
if (!plane_id) {
|
|
||||||
fprintf(stderr, "Unable to find suitable plane\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// DRM defines rotation in degrees counter-clockwise while we define
|
|
||||||
// rotation in degrees clockwise, so we swap the 90 and 270 cases
|
|
||||||
int displayRotation = drFlags & DISPLAY_ROTATE_MASK;
|
|
||||||
switch (displayRotation) {
|
|
||||||
case DISPLAY_ROTATE_90:
|
|
||||||
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "rotation", DRM_MODE_ROTATE_270);
|
|
||||||
break;
|
|
||||||
case DISPLAY_ROTATE_180:
|
|
||||||
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "rotation", DRM_MODE_ROTATE_180);
|
|
||||||
break;
|
|
||||||
case DISPLAY_ROTATE_270:
|
|
||||||
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "rotation", DRM_MODE_ROTATE_90);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "rotation", DRM_MODE_ROTATE_0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// hide cursor by move in left lower corner
|
// hide cursor by move in left lower corner
|
||||||
drmModeMoveCursor(fd, crtc_id, 0, crtc_height);
|
drmModeMoveCursor(fd, crtc_id, 0, crtc_height);
|
||||||
|
|
||||||
// MPI SETUP
|
// MPI SETUP
|
||||||
|
|
||||||
ensure_buf_size(&pkt_buf, &pkt_buf_size, INITIAL_DECODER_BUFFER_SIZE);
|
pkt_buf = malloc(READ_BUF_SIZE);
|
||||||
ret = mpp_packet_init(&mpi_packet, pkt_buf, pkt_buf_size);
|
assert(pkt_buf);
|
||||||
|
ret = mpp_packet_init(&mpi_packet, pkt_buf, READ_BUF_SIZE);
|
||||||
assert(!ret);
|
assert(!ret);
|
||||||
|
|
||||||
ret = mpp_create(&mpi_ctx, &mpi_api);
|
ret = mpp_create(&mpi_ctx, &mpi_api);
|
||||||
if (ret) {
|
assert(!ret);
|
||||||
fprintf(stderr, "mpp_create() failed: %d\n", ret);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// decoder split mode (multi-data-input) need to be set before init
|
// decoder split mode (multi-data-input) need to be set before init
|
||||||
int param = 1;
|
int param = 1;
|
||||||
@ -567,21 +375,22 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte
|
|||||||
assert(!ret);
|
assert(!ret);
|
||||||
|
|
||||||
ret = mpp_init(mpi_ctx, MPP_CTX_DEC, mpp_type);
|
ret = mpp_init(mpi_ctx, MPP_CTX_DEC, mpp_type);
|
||||||
if (ret) {
|
assert(!ret);
|
||||||
fprintf(stderr, "mpp_init() failed: %d\n", ret);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set blocked read on Frame Thread
|
// set blocked read on Frame Thread
|
||||||
param = MPP_POLL_BLOCK;
|
param = MPP_POLL_BLOCK;
|
||||||
ret = mpi_api->control(mpi_ctx, MPP_SET_OUTPUT_BLOCK, ¶m);
|
ret = mpi_api->control(mpi_ctx, MPP_SET_OUTPUT_BLOCK, ¶m);
|
||||||
assert(!ret);
|
assert(!ret);
|
||||||
|
|
||||||
pthread_mutex_init(&mutex, NULL);
|
ret = pthread_mutex_init(&mutex, NULL);
|
||||||
pthread_cond_init(&cond, NULL);
|
assert(!ret);
|
||||||
|
ret = pthread_cond_init(&cond, NULL);
|
||||||
|
assert(!ret);
|
||||||
|
|
||||||
pthread_create(&tid_frame, NULL, frame_thread, NULL);
|
ret = pthread_create(&tid_frame, NULL, frame_thread, NULL);
|
||||||
pthread_create(&tid_display, NULL, display_thread, NULL);
|
assert(!ret);
|
||||||
|
ret = pthread_create(&tid_display, NULL, display_thread, NULL);
|
||||||
|
assert(!ret);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -592,19 +401,26 @@ void rk_cleanup() {
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
frm_eos = 1;
|
frm_eos = 1;
|
||||||
pthread_mutex_lock(&mutex);
|
ret = pthread_mutex_lock(&mutex);
|
||||||
pthread_cond_signal(&cond);
|
assert(!ret);
|
||||||
pthread_mutex_unlock(&mutex);
|
ret = pthread_cond_signal(&cond);
|
||||||
|
assert(!ret);
|
||||||
|
ret = pthread_mutex_unlock(&mutex);
|
||||||
|
assert(!ret);
|
||||||
|
|
||||||
pthread_join(tid_display, NULL);
|
ret = pthread_join(tid_display, NULL);
|
||||||
|
assert(!ret);
|
||||||
|
|
||||||
pthread_cond_destroy(&cond);
|
ret = pthread_cond_destroy(&cond);
|
||||||
pthread_mutex_destroy(&mutex);
|
assert(!ret);
|
||||||
|
ret = pthread_mutex_destroy(&mutex);
|
||||||
|
assert(!ret);
|
||||||
|
|
||||||
ret = mpi_api->reset(mpi_ctx);
|
ret = mpi_api->reset(mpi_ctx);
|
||||||
assert(!ret);
|
assert(!ret);
|
||||||
|
|
||||||
pthread_join(tid_frame, NULL);
|
ret = pthread_join(tid_frame, NULL);
|
||||||
|
assert(!ret);
|
||||||
|
|
||||||
if (mpi_frm_grp) {
|
if (mpi_frm_grp) {
|
||||||
ret = mpp_buffer_group_put(mpi_frm_grp);
|
ret = mpp_buffer_group_put(mpi_frm_grp);
|
||||||
@ -612,15 +428,13 @@ void rk_cleanup() {
|
|||||||
mpi_frm_grp = NULL;
|
mpi_frm_grp = NULL;
|
||||||
for (i = 0; i < MAX_FRAMES; i++) {
|
for (i = 0; i < MAX_FRAMES; i++) {
|
||||||
ret = drmModeRmFB(fd, frame_to_drm[i].fb_id);
|
ret = drmModeRmFB(fd, frame_to_drm[i].fb_id);
|
||||||
if (ret) {
|
assert(!ret);
|
||||||
perror("drmModeRmFB");
|
|
||||||
}
|
|
||||||
struct drm_mode_destroy_dumb dmdd = {0};
|
struct drm_mode_destroy_dumb dmdd = {0};
|
||||||
dmdd.handle = frame_to_drm[i].handle;
|
dmdd.handle = frame_to_drm[i].handle;
|
||||||
ret = drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dmdd);
|
do {
|
||||||
if (ret) {
|
ret = ioctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dmdd);
|
||||||
perror("drmIoctl(DRM_IOCTL_MODE_DESTROY_DUMB)");
|
} while (ret == -1 && (errno == EINTR || errno == EAGAIN));
|
||||||
}
|
assert(!ret);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -628,130 +442,37 @@ void rk_cleanup() {
|
|||||||
mpp_destroy(mpi_ctx);
|
mpp_destroy(mpi_ctx);
|
||||||
free(pkt_buf);
|
free(pkt_buf);
|
||||||
|
|
||||||
// Undo the connector-wide changes we performed
|
|
||||||
if (atomic)
|
|
||||||
drmModeAtomicSetCursor(drm_request, 0);
|
|
||||||
set_property(conn_id, DRM_MODE_OBJECT_CONNECTOR, conn_props, "HDR_OUTPUT_METADATA", 0);
|
|
||||||
set_property(conn_id, DRM_MODE_OBJECT_CONNECTOR, conn_props, "allm_enable", 0);
|
|
||||||
set_property(conn_id, DRM_MODE_OBJECT_CONNECTOR, conn_props, "Colorspace", DRM_MODE_COLORIMETRY_DEFAULT);
|
|
||||||
if (atomic)
|
|
||||||
drmModeAtomicCommit(fd, drm_request, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
|
|
||||||
|
|
||||||
if (hdr_metadata_blob_id) {
|
|
||||||
drmModeDestroyPropertyBlob(fd, hdr_metadata_blob_id);
|
|
||||||
hdr_metadata_blob_id = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (atomic)
|
|
||||||
drmModeAtomicFree(drm_request);
|
|
||||||
drmModeFreePlane(ovr);
|
drmModeFreePlane(ovr);
|
||||||
drmModeFreePlaneResources(plane_resources);
|
drmModeFreePlaneResources(plane_resources);
|
||||||
drmModeFreeEncoder(encoder);
|
drmModeFreeEncoder(encoder);
|
||||||
drmModeFreeConnector(connector);
|
drmModeFreeConnector(connector);
|
||||||
drmModeFreeCrtc(crtc);
|
drmModeFreeCrtc(crtc);
|
||||||
drmModeFreeResources(resources);
|
drmModeFreeResources(resources);
|
||||||
|
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
int rk_submit_decode_unit(PDECODE_UNIT decodeUnit) {
|
int rk_submit_decode_unit(PDECODE_UNIT decodeUnit) {
|
||||||
|
|
||||||
int result = DR_OK;
|
int result = DR_OK;
|
||||||
PLENTRY entry = decodeUnit->bufferList;
|
|
||||||
int length = 0;
|
|
||||||
|
|
||||||
if (ensure_buf_size(&pkt_buf, &pkt_buf_size, decodeUnit->fullLength)) {
|
if (decodeUnit->fullLength < READ_BUF_SIZE) {
|
||||||
// Buffer was reallocated, so update the mpp_packet accordingly
|
PLENTRY entry = decodeUnit->bufferList;
|
||||||
mpp_packet_set_data(mpi_packet, pkt_buf);
|
int length = 0;
|
||||||
mpp_packet_set_size(mpi_packet, pkt_buf_size);
|
while (entry != NULL) {
|
||||||
}
|
memcpy(pkt_buf+length, entry->data, entry->length);
|
||||||
|
length += entry->length;
|
||||||
while (entry != NULL) {
|
entry = entry->next;
|
||||||
memcpy(pkt_buf+length, entry->data, entry->length);
|
|
||||||
length += entry->length;
|
|
||||||
entry = entry->next;
|
|
||||||
}
|
|
||||||
|
|
||||||
mpp_packet_set_pos(mpi_packet, pkt_buf);
|
|
||||||
mpp_packet_set_length(mpi_packet, length);
|
|
||||||
|
|
||||||
if (last_hdr_state != decodeUnit->hdrActive) {
|
|
||||||
if (hdr_metadata_prop != NULL) {
|
|
||||||
int err;
|
|
||||||
|
|
||||||
if (hdr_metadata_blob_id) {
|
|
||||||
drmModeDestroyPropertyBlob(fd, hdr_metadata_blob_id);
|
|
||||||
hdr_metadata_blob_id = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (decodeUnit->hdrActive) {
|
|
||||||
struct rk_hdr_output_metadata outputMetadata;
|
|
||||||
SS_HDR_METADATA sunshineHdrMetadata;
|
|
||||||
|
|
||||||
// Sunshine will have HDR metadata but GFE will not
|
|
||||||
if (!LiGetHdrMetadata(&sunshineHdrMetadata)) {
|
|
||||||
memset(&sunshineHdrMetadata, 0, sizeof(sunshineHdrMetadata));
|
|
||||||
}
|
|
||||||
|
|
||||||
outputMetadata.metadata_type = 0; // HDMI_STATIC_METADATA_TYPE1
|
|
||||||
outputMetadata.hdmi_metadata_type1.eotf = 2; // SMPTE ST 2084
|
|
||||||
outputMetadata.hdmi_metadata_type1.metadata_type = 0; // Static Metadata Type 1
|
|
||||||
for (int i = 0; i < 3; i++) {
|
|
||||||
outputMetadata.hdmi_metadata_type1.display_primaries[i].x = sunshineHdrMetadata.displayPrimaries[i].x;
|
|
||||||
outputMetadata.hdmi_metadata_type1.display_primaries[i].y = sunshineHdrMetadata.displayPrimaries[i].y;
|
|
||||||
}
|
|
||||||
outputMetadata.hdmi_metadata_type1.white_point.x = sunshineHdrMetadata.whitePoint.x;
|
|
||||||
outputMetadata.hdmi_metadata_type1.white_point.y = sunshineHdrMetadata.whitePoint.y;
|
|
||||||
outputMetadata.hdmi_metadata_type1.max_display_mastering_luminance = sunshineHdrMetadata.maxDisplayLuminance;
|
|
||||||
outputMetadata.hdmi_metadata_type1.min_display_mastering_luminance = sunshineHdrMetadata.minDisplayLuminance;
|
|
||||||
outputMetadata.hdmi_metadata_type1.max_cll = sunshineHdrMetadata.maxContentLightLevel;
|
|
||||||
outputMetadata.hdmi_metadata_type1.max_fall = sunshineHdrMetadata.maxFrameAverageLightLevel;
|
|
||||||
|
|
||||||
err = drmModeCreatePropertyBlob(fd, &outputMetadata, sizeof(outputMetadata), &hdr_metadata_blob_id);
|
|
||||||
if (err < 0) {
|
|
||||||
hdr_metadata_blob_id = 0;
|
|
||||||
fprintf(stderr, "Failed to create HDR metadata blob: %d\n", errno);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err = drmModeObjectSetProperty(fd, conn_id, DRM_MODE_OBJECT_CONNECTOR, hdr_metadata_prop->prop_id, hdr_metadata_blob_id);
|
|
||||||
if (err < 0) {
|
|
||||||
fprintf(stderr, "Failed to set HDR metadata: %d\n", errno);
|
|
||||||
} else {
|
|
||||||
printf("Set display HDR mode: %s\n", decodeUnit->hdrActive ? "active" : "inactive");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "HDR_OUTPUT_METADATA property is not supported by your display/kernel. Do you have an HDR display connected?\n");
|
|
||||||
}
|
}
|
||||||
|
if (length) {
|
||||||
|
mpp_packet_set_pos(mpi_packet, pkt_buf);
|
||||||
|
mpp_packet_set_length(mpi_packet, length);
|
||||||
|
|
||||||
// Adjust plane EOTF property
|
while (MPP_OK != mpi_api->decode_put_packet(mpi_ctx, mpi_packet))
|
||||||
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "EOTF", decodeUnit->hdrActive ? 2 : 0); // PQ or SDR
|
;
|
||||||
}
|
|
||||||
|
|
||||||
if (last_colorspace != decodeUnit->colorspace) {
|
|
||||||
uint32_t v4l2_colorspace;
|
|
||||||
switch (decodeUnit->colorspace) {
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "Unknown frame colorspace: %d\n", decodeUnit->colorspace);
|
|
||||||
/* fall-through */
|
|
||||||
case COLORSPACE_REC_601:
|
|
||||||
v4l2_colorspace = V4L2_COLORSPACE_SMPTE170M;
|
|
||||||
break;
|
|
||||||
case COLORSPACE_REC_709:
|
|
||||||
v4l2_colorspace = V4L2_COLORSPACE_REC709;
|
|
||||||
break;
|
|
||||||
case COLORSPACE_REC_2020:
|
|
||||||
v4l2_colorspace = V4L2_COLORSPACE_BT2020;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "COLOR_SPACE", v4l2_colorspace);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
last_colorspace = decodeUnit->colorspace;
|
|
||||||
last_hdr_state = decodeUnit->hdrActive;
|
|
||||||
|
|
||||||
while (MPP_OK != mpi_api->decode_put_packet(mpi_ctx, mpi_packet));
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,6 @@
|
|||||||
#include "ffmpeg.h"
|
#include "ffmpeg.h"
|
||||||
|
|
||||||
#include "../sdl.h"
|
#include "../sdl.h"
|
||||||
#include "../util.h"
|
|
||||||
|
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
#include <SDL_thread.h>
|
#include <SDL_thread.h>
|
||||||
@ -29,18 +28,24 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#define SLICES_PER_FRAME 4
|
#define DECODER_BUFFER_SIZE 92*1024
|
||||||
|
|
||||||
static void* ffmpeg_buffer;
|
static char* ffmpeg_buffer;
|
||||||
static size_t ffmpeg_buffer_size;
|
|
||||||
|
|
||||||
static int sdl_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
|
static int sdl_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
|
||||||
if (ffmpeg_init(videoFormat, width, height, SLICE_THREADING, SDL_BUFFER_FRAMES, SLICES_PER_FRAME) < 0) {
|
int avc_flags = SLICE_THREADING;
|
||||||
|
|
||||||
|
if (ffmpeg_init(videoFormat, width, height, avc_flags, SDL_BUFFER_FRAMES, sysconf(_SC_NPROCESSORS_ONLN)) < 0) {
|
||||||
fprintf(stderr, "Couldn't initialize video decoding\n");
|
fprintf(stderr, "Couldn't initialize video decoding\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ensure_buf_size(&ffmpeg_buffer, &ffmpeg_buffer_size, INITIAL_DECODER_BUFFER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE);
|
ffmpeg_buffer = malloc(DECODER_BUFFER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE);
|
||||||
|
if (ffmpeg_buffer == NULL) {
|
||||||
|
fprintf(stderr, "Not enough memory\n");
|
||||||
|
ffmpeg_destroy();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -50,31 +55,36 @@ static void sdl_cleanup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int sdl_submit_decode_unit(PDECODE_UNIT decodeUnit) {
|
static int sdl_submit_decode_unit(PDECODE_UNIT decodeUnit) {
|
||||||
PLENTRY entry = decodeUnit->bufferList;
|
if (decodeUnit->fullLength < DECODER_BUFFER_SIZE) {
|
||||||
int length = 0;
|
PLENTRY entry = decodeUnit->bufferList;
|
||||||
|
int length = 0;
|
||||||
|
while (entry != NULL) {
|
||||||
|
memcpy(ffmpeg_buffer+length, entry->data, entry->length);
|
||||||
|
length += entry->length;
|
||||||
|
entry = entry->next;
|
||||||
|
}
|
||||||
|
ffmpeg_decode(ffmpeg_buffer, length);
|
||||||
|
|
||||||
ensure_buf_size(&ffmpeg_buffer, &ffmpeg_buffer_size, decodeUnit->fullLength + AV_INPUT_BUFFER_PADDING_SIZE);
|
if (SDL_LockMutex(mutex) == 0) {
|
||||||
|
AVFrame* frame = ffmpeg_get_frame(false);
|
||||||
|
if (frame != NULL) {
|
||||||
|
sdlNextFrame++;
|
||||||
|
|
||||||
while (entry != NULL) {
|
SDL_Event event;
|
||||||
memcpy(ffmpeg_buffer+length, entry->data, entry->length);
|
event.type = SDL_USEREVENT;
|
||||||
length += entry->length;
|
event.user.code = SDL_CODE_FRAME;
|
||||||
entry = entry->next;
|
event.user.data1 = &frame->data;
|
||||||
|
event.user.data2 = &frame->linesize;
|
||||||
|
SDL_PushEvent(&event);
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_UnlockMutex(mutex);
|
||||||
|
} else
|
||||||
|
fprintf(stderr, "Couldn't lock mutex\n");
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Video decode buffer too small");
|
||||||
|
exit(1);
|
||||||
}
|
}
|
||||||
ffmpeg_decode(ffmpeg_buffer, length);
|
|
||||||
|
|
||||||
SDL_LockMutex(mutex);
|
|
||||||
AVFrame* frame = ffmpeg_get_frame(false);
|
|
||||||
if (frame != NULL) {
|
|
||||||
sdlNextFrame++;
|
|
||||||
|
|
||||||
SDL_Event event;
|
|
||||||
event.type = SDL_USEREVENT;
|
|
||||||
event.user.code = SDL_CODE_FRAME;
|
|
||||||
event.user.data1 = &frame->data;
|
|
||||||
event.user.data2 = &frame->linesize;
|
|
||||||
SDL_PushEvent(&event);
|
|
||||||
}
|
|
||||||
SDL_UnlockMutex(mutex);
|
|
||||||
|
|
||||||
return DR_OK;
|
return DR_OK;
|
||||||
}
|
}
|
||||||
@ -83,5 +93,5 @@ DECODER_RENDERER_CALLBACKS decoder_callbacks_sdl = {
|
|||||||
.setup = sdl_setup,
|
.setup = sdl_setup,
|
||||||
.cleanup = sdl_cleanup,
|
.cleanup = sdl_cleanup,
|
||||||
.submitDecodeUnit = sdl_submit_decode_unit,
|
.submitDecodeUnit = sdl_submit_decode_unit,
|
||||||
.capabilities = CAPABILITY_SLICES_PER_FRAME(SLICES_PER_FRAME) | CAPABILITY_REFERENCE_FRAME_INVALIDATION_HEVC | CAPABILITY_DIRECT_SUBMIT,
|
.capabilities = CAPABILITY_SLICES_PER_FRAME(4) | CAPABILITY_REFERENCE_FRAME_INVALIDATION_AVC | CAPABILITY_REFERENCE_FRAME_INVALIDATION_HEVC | CAPABILITY_DIRECT_SUBMIT,
|
||||||
};
|
};
|
||||||
|
@ -24,17 +24,11 @@
|
|||||||
#define DISPLAY_FULLSCREEN 1
|
#define DISPLAY_FULLSCREEN 1
|
||||||
#define ENABLE_HARDWARE_ACCELERATION_1 2
|
#define ENABLE_HARDWARE_ACCELERATION_1 2
|
||||||
#define ENABLE_HARDWARE_ACCELERATION_2 4
|
#define ENABLE_HARDWARE_ACCELERATION_2 4
|
||||||
#define DISPLAY_ROTATE_MASK 24
|
|
||||||
#define DISPLAY_ROTATE_90 8
|
|
||||||
#define DISPLAY_ROTATE_180 16
|
|
||||||
#define DISPLAY_ROTATE_270 24
|
|
||||||
|
|
||||||
#define INIT_EGL 1
|
#define INIT_EGL 1
|
||||||
#define INIT_VDPAU 2
|
#define INIT_VDPAU 2
|
||||||
#define INIT_VAAPI 3
|
#define INIT_VAAPI 3
|
||||||
|
|
||||||
#define INITIAL_DECODER_BUFFER_SIZE (256*1024)
|
|
||||||
|
|
||||||
#ifdef HAVE_X11
|
#ifdef HAVE_X11
|
||||||
int x11_init(bool vdpau, bool vaapi);
|
int x11_init(bool vdpau, bool vaapi);
|
||||||
extern DECODER_RENDERER_CALLBACKS decoder_callbacks_x11;
|
extern DECODER_RENDERER_CALLBACKS decoder_callbacks_x11;
|
||||||
|
@ -26,7 +26,6 @@
|
|||||||
|
|
||||||
#include "../input/x11.h"
|
#include "../input/x11.h"
|
||||||
#include "../loop.h"
|
#include "../loop.h"
|
||||||
#include "../util.h"
|
|
||||||
|
|
||||||
#include <X11/Xatom.h>
|
#include <X11/Xatom.h>
|
||||||
#include <X11/Xutil.h>
|
#include <X11/Xutil.h>
|
||||||
@ -38,12 +37,11 @@
|
|||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <poll.h>
|
#include <poll.h>
|
||||||
|
|
||||||
|
#define DECODER_BUFFER_SIZE 92*1024
|
||||||
#define X11_VDPAU_ACCELERATION ENABLE_HARDWARE_ACCELERATION_1
|
#define X11_VDPAU_ACCELERATION ENABLE_HARDWARE_ACCELERATION_1
|
||||||
#define X11_VAAPI_ACCELERATION ENABLE_HARDWARE_ACCELERATION_2
|
#define X11_VAAPI_ACCELERATION ENABLE_HARDWARE_ACCELERATION_2
|
||||||
#define SLICES_PER_FRAME 4
|
|
||||||
|
|
||||||
static void* ffmpeg_buffer = NULL;
|
static char* ffmpeg_buffer = NULL;
|
||||||
static size_t ffmpeg_buffer_size = 0;
|
|
||||||
|
|
||||||
static Display *display = NULL;
|
static Display *display = NULL;
|
||||||
static Window window;
|
static Window window;
|
||||||
@ -59,7 +57,7 @@ static int frame_handle(int pipefd) {
|
|||||||
if (frame) {
|
if (frame) {
|
||||||
if (ffmpeg_decoder == SOFTWARE)
|
if (ffmpeg_decoder == SOFTWARE)
|
||||||
egl_draw(frame->data);
|
egl_draw(frame->data);
|
||||||
#ifdef HAVE_VAAPI
|
#ifdef HAVE_VAAPI
|
||||||
else if (ffmpeg_decoder == VAAPI)
|
else if (ffmpeg_decoder == VAAPI)
|
||||||
vaapi_queue(frame, window, display_width, display_height);
|
vaapi_queue(frame, window, display_width, display_height);
|
||||||
#endif
|
#endif
|
||||||
@ -83,7 +81,11 @@ int x11_init(bool vdpau, bool vaapi) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int x11_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
|
int x11_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
|
||||||
ensure_buf_size(&ffmpeg_buffer, &ffmpeg_buffer_size, INITIAL_DECODER_BUFFER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE);
|
ffmpeg_buffer = malloc(DECODER_BUFFER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE);
|
||||||
|
if (ffmpeg_buffer == NULL) {
|
||||||
|
fprintf(stderr, "Not enough memory\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!display) {
|
if (!display) {
|
||||||
fprintf(stderr, "Error: failed to open X display.\n");
|
fprintf(stderr, "Error: failed to open X display.\n");
|
||||||
@ -122,15 +124,13 @@ int x11_setup(int videoFormat, int width, int height, int redrawRate, void* cont
|
|||||||
}
|
}
|
||||||
XFlush(display);
|
XFlush(display);
|
||||||
|
|
||||||
int avc_flags;
|
int avc_flags = SLICE_THREADING;
|
||||||
if (drFlags & X11_VDPAU_ACCELERATION)
|
if (drFlags & X11_VDPAU_ACCELERATION)
|
||||||
avc_flags = VDPAU_ACCELERATION;
|
avc_flags |= VDPAU_ACCELERATION;
|
||||||
else if (drFlags & X11_VAAPI_ACCELERATION)
|
else if (drFlags & X11_VAAPI_ACCELERATION)
|
||||||
avc_flags = VAAPI_ACCELERATION;
|
avc_flags |= VAAPI_ACCELERATION;
|
||||||
else
|
|
||||||
avc_flags = SLICE_THREADING;
|
|
||||||
|
|
||||||
if (ffmpeg_init(videoFormat, width, height, avc_flags, 2, SLICES_PER_FRAME) < 0) {
|
if (ffmpeg_init(videoFormat, width, height, avc_flags, 2, 2) < 0) {
|
||||||
fprintf(stderr, "Couldn't initialize video decoding\n");
|
fprintf(stderr, "Couldn't initialize video decoding\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -164,23 +164,20 @@ void x11_cleanup() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int x11_submit_decode_unit(PDECODE_UNIT decodeUnit) {
|
int x11_submit_decode_unit(PDECODE_UNIT decodeUnit) {
|
||||||
PLENTRY entry = decodeUnit->bufferList;
|
if (decodeUnit->fullLength < DECODER_BUFFER_SIZE) {
|
||||||
int length = 0;
|
PLENTRY entry = decodeUnit->bufferList;
|
||||||
|
int length = 0;
|
||||||
ensure_buf_size(&ffmpeg_buffer, &ffmpeg_buffer_size, decodeUnit->fullLength + AV_INPUT_BUFFER_PADDING_SIZE);
|
while (entry != NULL) {
|
||||||
|
memcpy(ffmpeg_buffer+length, entry->data, entry->length);
|
||||||
while (entry != NULL) {
|
length += entry->length;
|
||||||
memcpy(ffmpeg_buffer+length, entry->data, entry->length);
|
entry = entry->next;
|
||||||
length += entry->length;
|
}
|
||||||
entry = entry->next;
|
ffmpeg_decode(ffmpeg_buffer, length);
|
||||||
|
AVFrame* frame = ffmpeg_get_frame(true);
|
||||||
|
if (frame != NULL)
|
||||||
|
write(pipefd[1], &frame, sizeof(void*));
|
||||||
}
|
}
|
||||||
|
|
||||||
ffmpeg_decode(ffmpeg_buffer, length);
|
|
||||||
|
|
||||||
AVFrame* frame = ffmpeg_get_frame(true);
|
|
||||||
if (frame != NULL)
|
|
||||||
write(pipefd[1], &frame, sizeof(void*));
|
|
||||||
|
|
||||||
return DR_OK;
|
return DR_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +185,7 @@ DECODER_RENDERER_CALLBACKS decoder_callbacks_x11 = {
|
|||||||
.setup = x11_setup,
|
.setup = x11_setup,
|
||||||
.cleanup = x11_cleanup,
|
.cleanup = x11_cleanup,
|
||||||
.submitDecodeUnit = x11_submit_decode_unit,
|
.submitDecodeUnit = x11_submit_decode_unit,
|
||||||
.capabilities = CAPABILITY_SLICES_PER_FRAME(SLICES_PER_FRAME) | CAPABILITY_REFERENCE_FRAME_INVALIDATION_HEVC | CAPABILITY_DIRECT_SUBMIT,
|
.capabilities = CAPABILITY_SLICES_PER_FRAME(4) | CAPABILITY_REFERENCE_FRAME_INVALIDATION_AVC | CAPABILITY_REFERENCE_FRAME_INVALIDATION_HEVC | CAPABILITY_DIRECT_SUBMIT,
|
||||||
};
|
};
|
||||||
|
|
||||||
DECODER_RENDERER_CALLBACKS decoder_callbacks_x11_vdpau = {
|
DECODER_RENDERER_CALLBACKS decoder_callbacks_x11_vdpau = {
|
||||||
@ -202,5 +199,5 @@ DECODER_RENDERER_CALLBACKS decoder_callbacks_x11_vaapi = {
|
|||||||
.setup = x11_setup_vaapi,
|
.setup = x11_setup_vaapi,
|
||||||
.cleanup = x11_cleanup,
|
.cleanup = x11_cleanup,
|
||||||
.submitDecodeUnit = x11_submit_decode_unit,
|
.submitDecodeUnit = x11_submit_decode_unit,
|
||||||
.capabilities = CAPABILITY_DIRECT_SUBMIT,
|
.capabilities = CAPABILITY_SLICES_PER_FRAME(4) | CAPABILITY_REFERENCE_FRAME_INVALIDATION_AVC | CAPABILITY_REFERENCE_FRAME_INVALIDATION_HEVC | CAPABILITY_DIRECT_SUBMIT,
|
||||||
};
|
};
|
||||||
|
1
third_party/SDL_GameControllerDB
vendored
1
third_party/SDL_GameControllerDB
vendored
@ -1 +0,0 @@
|
|||||||
Subproject commit ae51c9942f0d985b2161d6b22986f041fc98ce5c
|
|
896
third_party/h264bitstream/h264_stream.c
vendored
896
third_party/h264bitstream/h264_stream.c
vendored
@ -24,11 +24,16 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "bs.h"
|
#include "bs.h"
|
||||||
#include "h264_stream.h"
|
#include "h264_stream.h"
|
||||||
#include "h264_sei.h"
|
#include "h264_sei.h"
|
||||||
|
|
||||||
|
FILE* h264_dbgfile = NULL;
|
||||||
|
|
||||||
|
#define printf(...) fprintf((h264_dbgfile == NULL ? stdout : h264_dbgfile), __VA_ARGS__)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Calculate the log base 2 of the argument, rounded up.
|
Calculate the log base 2 of the argument, rounded up.
|
||||||
Zero or negative arguments return zero
|
Zero or negative arguments return zero
|
||||||
@ -549,7 +554,7 @@ void read_pic_parameter_set_rbsp(h264_stream_t* h, bs_t* b)
|
|||||||
if( 1 ) { have_more_data = more_rbsp_data(h, b); }
|
if( 1 ) { have_more_data = more_rbsp_data(h, b); }
|
||||||
if( 0 )
|
if( 0 )
|
||||||
{
|
{
|
||||||
have_more_data = pps->transform_8x8_mode_flag | pps->pic_scaling_matrix_present_flag | (pps->second_chroma_qp_index_offset != 0);
|
have_more_data = pps->transform_8x8_mode_flag | pps->pic_scaling_matrix_present_flag | pps->second_chroma_qp_index_offset != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( have_more_data )
|
if( have_more_data )
|
||||||
@ -1436,7 +1441,7 @@ void write_pic_parameter_set_rbsp(h264_stream_t* h, bs_t* b)
|
|||||||
if( 0 ) { have_more_data = more_rbsp_data(h, b); }
|
if( 0 ) { have_more_data = more_rbsp_data(h, b); }
|
||||||
if( 1 )
|
if( 1 )
|
||||||
{
|
{
|
||||||
have_more_data = pps->transform_8x8_mode_flag | pps->pic_scaling_matrix_present_flag | (pps->second_chroma_qp_index_offset != 0);
|
have_more_data = pps->transform_8x8_mode_flag | pps->pic_scaling_matrix_present_flag | pps->second_chroma_qp_index_offset != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if( have_more_data )
|
if( have_more_data )
|
||||||
@ -1894,3 +1899,890 @@ void write_dec_ref_pic_marking(h264_stream_t* h, bs_t* b)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void read_debug_seq_parameter_set_rbsp(h264_stream_t* h, bs_t* b);
|
||||||
|
void read_debug_scaling_list(bs_t* b, int* scalingList, int sizeOfScalingList, int* useDefaultScalingMatrixFlag );
|
||||||
|
void read_debug_vui_parameters(h264_stream_t* h, bs_t* b);
|
||||||
|
void read_debug_hrd_parameters(h264_stream_t* h, bs_t* b);
|
||||||
|
void read_debug_pic_parameter_set_rbsp(h264_stream_t* h, bs_t* b);
|
||||||
|
void read_debug_sei_rbsp(h264_stream_t* h, bs_t* b);
|
||||||
|
void read_debug_sei_message(h264_stream_t* h, bs_t* b);
|
||||||
|
void read_debug_access_unit_delimiter_rbsp(h264_stream_t* h, bs_t* b);
|
||||||
|
void read_debug_end_of_seq_rbsp(h264_stream_t* h, bs_t* b);
|
||||||
|
void read_debug_end_of_stream_rbsp(h264_stream_t* h, bs_t* b);
|
||||||
|
void read_debug_filler_data_rbsp(h264_stream_t* h, bs_t* b);
|
||||||
|
void read_debug_slice_layer_rbsp(h264_stream_t* h, bs_t* b);
|
||||||
|
void read_debug_rbsp_slice_trailing_bits(h264_stream_t* h, bs_t* b);
|
||||||
|
void read_debug_rbsp_trailing_bits(h264_stream_t* h, bs_t* b);
|
||||||
|
void read_debug_slice_header(h264_stream_t* h, bs_t* b);
|
||||||
|
void read_debug_ref_pic_list_reordering(h264_stream_t* h, bs_t* b);
|
||||||
|
void read_debug_pred_weight_table(h264_stream_t* h, bs_t* b);
|
||||||
|
void read_debug_dec_ref_pic_marking(h264_stream_t* h, bs_t* b);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//7.3.1 NAL unit syntax
|
||||||
|
int read_debug_nal_unit(h264_stream_t* h, uint8_t* buf, int size)
|
||||||
|
{
|
||||||
|
nal_t* nal = h->nal;
|
||||||
|
|
||||||
|
int nal_size = size;
|
||||||
|
int rbsp_size = size;
|
||||||
|
uint8_t* rbsp_buf = (uint8_t*)calloc(1, rbsp_size);
|
||||||
|
|
||||||
|
if( 1 )
|
||||||
|
{
|
||||||
|
int rc = nal_to_rbsp(buf, &nal_size, rbsp_buf, &rbsp_size);
|
||||||
|
|
||||||
|
if (rc < 0) { free(rbsp_buf); return -1; } // handle conversion error
|
||||||
|
}
|
||||||
|
|
||||||
|
if( 0 )
|
||||||
|
{
|
||||||
|
rbsp_size = size*3/4; // NOTE this may have to be slightly smaller (3/4 smaller, worst case) in order to be guaranteed to fit
|
||||||
|
}
|
||||||
|
|
||||||
|
bs_t* b = bs_new(rbsp_buf, rbsp_size);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); int forbidden_zero_bit = bs_read_u(b, 1); printf("forbidden_zero_bit: %d \n", forbidden_zero_bit);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); nal->nal_ref_idc = bs_read_u(b, 2); printf("nal->nal_ref_idc: %d \n", nal->nal_ref_idc);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); nal->nal_unit_type = bs_read_u(b, 5); printf("nal->nal_unit_type: %d \n", nal->nal_unit_type);
|
||||||
|
|
||||||
|
switch ( nal->nal_unit_type )
|
||||||
|
{
|
||||||
|
case NAL_UNIT_TYPE_CODED_SLICE_IDR:
|
||||||
|
case NAL_UNIT_TYPE_CODED_SLICE_NON_IDR:
|
||||||
|
case NAL_UNIT_TYPE_CODED_SLICE_AUX:
|
||||||
|
read_debug_slice_layer_rbsp(h, b);
|
||||||
|
break;
|
||||||
|
|
||||||
|
#ifdef HAVE_SEI
|
||||||
|
case NAL_UNIT_TYPE_SEI:
|
||||||
|
read_debug_sei_rbsp(h, b);
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
case NAL_UNIT_TYPE_SPS:
|
||||||
|
read_debug_seq_parameter_set_rbsp(h, b);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAL_UNIT_TYPE_PPS:
|
||||||
|
read_debug_pic_parameter_set_rbsp(h, b);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAL_UNIT_TYPE_AUD:
|
||||||
|
read_debug_access_unit_delimiter_rbsp(h, b);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAL_UNIT_TYPE_END_OF_SEQUENCE:
|
||||||
|
read_debug_end_of_seq_rbsp(h, b);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAL_UNIT_TYPE_END_OF_STREAM:
|
||||||
|
read_debug_end_of_stream_rbsp(h, b);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NAL_UNIT_TYPE_FILLER:
|
||||||
|
case NAL_UNIT_TYPE_SPS_EXT:
|
||||||
|
case NAL_UNIT_TYPE_UNSPECIFIED:
|
||||||
|
case NAL_UNIT_TYPE_CODED_SLICE_DATA_PARTITION_A:
|
||||||
|
case NAL_UNIT_TYPE_CODED_SLICE_DATA_PARTITION_B:
|
||||||
|
case NAL_UNIT_TYPE_CODED_SLICE_DATA_PARTITION_C:
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bs_overrun(b)) { bs_free(b); free(rbsp_buf); return -1; }
|
||||||
|
|
||||||
|
if( 0 )
|
||||||
|
{
|
||||||
|
// now get the actual size used
|
||||||
|
rbsp_size = bs_pos(b);
|
||||||
|
|
||||||
|
int rc = rbsp_to_nal(rbsp_buf, &rbsp_size, buf, &nal_size);
|
||||||
|
if (rc < 0) { bs_free(b); free(rbsp_buf); return -1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
bs_free(b);
|
||||||
|
free(rbsp_buf);
|
||||||
|
|
||||||
|
return nal_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//7.3.2.1 Sequence parameter set RBSP syntax
|
||||||
|
void read_debug_seq_parameter_set_rbsp(h264_stream_t* h, bs_t* b)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
sps_t* sps = h->sps;
|
||||||
|
if( 1 )
|
||||||
|
{
|
||||||
|
memset(sps, 0, sizeof(sps_t));
|
||||||
|
sps->chroma_format_idc = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->profile_idc = bs_read_u8(b); printf("sps->profile_idc: %d \n", sps->profile_idc);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->constraint_set0_flag = bs_read_u1(b); printf("sps->constraint_set0_flag: %d \n", sps->constraint_set0_flag);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->constraint_set1_flag = bs_read_u1(b); printf("sps->constraint_set1_flag: %d \n", sps->constraint_set1_flag);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->constraint_set2_flag = bs_read_u1(b); printf("sps->constraint_set2_flag: %d \n", sps->constraint_set2_flag);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->constraint_set3_flag = bs_read_u1(b); printf("sps->constraint_set3_flag: %d \n", sps->constraint_set3_flag);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->constraint_set4_flag = bs_read_u1(b); printf("sps->constraint_set4_flag: %d \n", sps->constraint_set4_flag);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->constraint_set5_flag = bs_read_u1(b); printf("sps->constraint_set5_flag: %d \n", sps->constraint_set5_flag);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); int reserved_zero_2bits = bs_read_u(b, 2); printf("reserved_zero_2bits: %d \n", reserved_zero_2bits);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->level_idc = bs_read_u8(b); printf("sps->level_idc: %d \n", sps->level_idc);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->seq_parameter_set_id = bs_read_ue(b); printf("sps->seq_parameter_set_id: %d \n", sps->seq_parameter_set_id);
|
||||||
|
|
||||||
|
if( sps->profile_idc == 100 || sps->profile_idc == 110 ||
|
||||||
|
sps->profile_idc == 122 || sps->profile_idc == 144 )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->chroma_format_idc = bs_read_ue(b); printf("sps->chroma_format_idc: %d \n", sps->chroma_format_idc);
|
||||||
|
if( sps->chroma_format_idc == 3 )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->residual_colour_transform_flag = bs_read_u1(b); printf("sps->residual_colour_transform_flag: %d \n", sps->residual_colour_transform_flag);
|
||||||
|
}
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->bit_depth_luma_minus8 = bs_read_ue(b); printf("sps->bit_depth_luma_minus8: %d \n", sps->bit_depth_luma_minus8);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->bit_depth_chroma_minus8 = bs_read_ue(b); printf("sps->bit_depth_chroma_minus8: %d \n", sps->bit_depth_chroma_minus8);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->qpprime_y_zero_transform_bypass_flag = bs_read_u1(b); printf("sps->qpprime_y_zero_transform_bypass_flag: %d \n", sps->qpprime_y_zero_transform_bypass_flag);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->seq_scaling_matrix_present_flag = bs_read_u1(b); printf("sps->seq_scaling_matrix_present_flag: %d \n", sps->seq_scaling_matrix_present_flag);
|
||||||
|
if( sps->seq_scaling_matrix_present_flag )
|
||||||
|
{
|
||||||
|
for( i = 0; i < 8; i++ )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->seq_scaling_list_present_flag[ i ] = bs_read_u1(b); printf("sps->seq_scaling_list_present_flag[ i ]: %d \n", sps->seq_scaling_list_present_flag[ i ]);
|
||||||
|
if( sps->seq_scaling_list_present_flag[ i ] )
|
||||||
|
{
|
||||||
|
if( i < 6 )
|
||||||
|
{
|
||||||
|
read_debug_scaling_list( b, sps->ScalingList4x4[ i ], 16,
|
||||||
|
&( sps->UseDefaultScalingMatrix4x4Flag[ i ] ) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
read_debug_scaling_list( b, sps->ScalingList8x8[ i - 6 ], 64,
|
||||||
|
&( sps->UseDefaultScalingMatrix8x8Flag[ i - 6 ] ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->log2_max_frame_num_minus4 = bs_read_ue(b); printf("sps->log2_max_frame_num_minus4: %d \n", sps->log2_max_frame_num_minus4);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->pic_order_cnt_type = bs_read_ue(b); printf("sps->pic_order_cnt_type: %d \n", sps->pic_order_cnt_type);
|
||||||
|
if( sps->pic_order_cnt_type == 0 )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->log2_max_pic_order_cnt_lsb_minus4 = bs_read_ue(b); printf("sps->log2_max_pic_order_cnt_lsb_minus4: %d \n", sps->log2_max_pic_order_cnt_lsb_minus4);
|
||||||
|
}
|
||||||
|
else if( sps->pic_order_cnt_type == 1 )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->delta_pic_order_always_zero_flag = bs_read_u1(b); printf("sps->delta_pic_order_always_zero_flag: %d \n", sps->delta_pic_order_always_zero_flag);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->offset_for_non_ref_pic = bs_read_se(b); printf("sps->offset_for_non_ref_pic: %d \n", sps->offset_for_non_ref_pic);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->offset_for_top_to_bottom_field = bs_read_se(b); printf("sps->offset_for_top_to_bottom_field: %d \n", sps->offset_for_top_to_bottom_field);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->num_ref_frames_in_pic_order_cnt_cycle = bs_read_ue(b); printf("sps->num_ref_frames_in_pic_order_cnt_cycle: %d \n", sps->num_ref_frames_in_pic_order_cnt_cycle);
|
||||||
|
for( i = 0; i < sps->num_ref_frames_in_pic_order_cnt_cycle; i++ )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->offset_for_ref_frame[ i ] = bs_read_se(b); printf("sps->offset_for_ref_frame[ i ]: %d \n", sps->offset_for_ref_frame[ i ]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->num_ref_frames = bs_read_ue(b); printf("sps->num_ref_frames: %d \n", sps->num_ref_frames);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->gaps_in_frame_num_value_allowed_flag = bs_read_u1(b); printf("sps->gaps_in_frame_num_value_allowed_flag: %d \n", sps->gaps_in_frame_num_value_allowed_flag);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->pic_width_in_mbs_minus1 = bs_read_ue(b); printf("sps->pic_width_in_mbs_minus1: %d \n", sps->pic_width_in_mbs_minus1);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->pic_height_in_map_units_minus1 = bs_read_ue(b); printf("sps->pic_height_in_map_units_minus1: %d \n", sps->pic_height_in_map_units_minus1);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->frame_mbs_only_flag = bs_read_u1(b); printf("sps->frame_mbs_only_flag: %d \n", sps->frame_mbs_only_flag);
|
||||||
|
if( !sps->frame_mbs_only_flag )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->mb_adaptive_frame_field_flag = bs_read_u1(b); printf("sps->mb_adaptive_frame_field_flag: %d \n", sps->mb_adaptive_frame_field_flag);
|
||||||
|
}
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->direct_8x8_inference_flag = bs_read_u1(b); printf("sps->direct_8x8_inference_flag: %d \n", sps->direct_8x8_inference_flag);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->frame_cropping_flag = bs_read_u1(b); printf("sps->frame_cropping_flag: %d \n", sps->frame_cropping_flag);
|
||||||
|
if( sps->frame_cropping_flag )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->frame_crop_left_offset = bs_read_ue(b); printf("sps->frame_crop_left_offset: %d \n", sps->frame_crop_left_offset);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->frame_crop_right_offset = bs_read_ue(b); printf("sps->frame_crop_right_offset: %d \n", sps->frame_crop_right_offset);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->frame_crop_top_offset = bs_read_ue(b); printf("sps->frame_crop_top_offset: %d \n", sps->frame_crop_top_offset);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->frame_crop_bottom_offset = bs_read_ue(b); printf("sps->frame_crop_bottom_offset: %d \n", sps->frame_crop_bottom_offset);
|
||||||
|
}
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui_parameters_present_flag = bs_read_u1(b); printf("sps->vui_parameters_present_flag: %d \n", sps->vui_parameters_present_flag);
|
||||||
|
if( sps->vui_parameters_present_flag )
|
||||||
|
{
|
||||||
|
read_debug_vui_parameters(h, b);
|
||||||
|
}
|
||||||
|
read_debug_rbsp_trailing_bits(h, b);
|
||||||
|
|
||||||
|
if( 1 )
|
||||||
|
{
|
||||||
|
memcpy(h->sps_table[sps->seq_parameter_set_id], h->sps, sizeof(sps_t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//7.3.2.1.1 Scaling list syntax
|
||||||
|
void read_debug_scaling_list(bs_t* b, int* scalingList, int sizeOfScalingList, int* useDefaultScalingMatrixFlag )
|
||||||
|
{
|
||||||
|
// NOTE need to be able to set useDefaultScalingMatrixFlag when reading, hence passing as pointer
|
||||||
|
int lastScale = 8;
|
||||||
|
int nextScale = 8;
|
||||||
|
int delta_scale;
|
||||||
|
for( int j = 0; j < sizeOfScalingList; j++ )
|
||||||
|
{
|
||||||
|
if( nextScale != 0 )
|
||||||
|
{
|
||||||
|
if( 0 )
|
||||||
|
{
|
||||||
|
nextScale = scalingList[ j ];
|
||||||
|
if (useDefaultScalingMatrixFlag[0]) { nextScale = 0; }
|
||||||
|
delta_scale = (nextScale - lastScale) % 256 ;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); delta_scale = bs_read_se(b); printf("delta_scale: %d \n", delta_scale);
|
||||||
|
|
||||||
|
if( 1 )
|
||||||
|
{
|
||||||
|
nextScale = ( lastScale + delta_scale + 256 ) % 256;
|
||||||
|
useDefaultScalingMatrixFlag[0] = ( j == 0 && nextScale == 0 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( 1 )
|
||||||
|
{
|
||||||
|
scalingList[ j ] = ( nextScale == 0 ) ? lastScale : nextScale;
|
||||||
|
}
|
||||||
|
lastScale = scalingList[ j ];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Appendix E.1.1 VUI parameters syntax
|
||||||
|
void read_debug_vui_parameters(h264_stream_t* h, bs_t* b)
|
||||||
|
{
|
||||||
|
sps_t* sps = h->sps;
|
||||||
|
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.aspect_ratio_info_present_flag = bs_read_u1(b); printf("sps->vui.aspect_ratio_info_present_flag: %d \n", sps->vui.aspect_ratio_info_present_flag);
|
||||||
|
if( sps->vui.aspect_ratio_info_present_flag )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.aspect_ratio_idc = bs_read_u8(b); printf("sps->vui.aspect_ratio_idc: %d \n", sps->vui.aspect_ratio_idc);
|
||||||
|
if( sps->vui.aspect_ratio_idc == SAR_Extended )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.sar_width = bs_read_u(b, 16); printf("sps->vui.sar_width: %d \n", sps->vui.sar_width);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.sar_height = bs_read_u(b, 16); printf("sps->vui.sar_height: %d \n", sps->vui.sar_height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.overscan_info_present_flag = bs_read_u1(b); printf("sps->vui.overscan_info_present_flag: %d \n", sps->vui.overscan_info_present_flag);
|
||||||
|
if( sps->vui.overscan_info_present_flag )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.overscan_appropriate_flag = bs_read_u1(b); printf("sps->vui.overscan_appropriate_flag: %d \n", sps->vui.overscan_appropriate_flag);
|
||||||
|
}
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.video_signal_type_present_flag = bs_read_u1(b); printf("sps->vui.video_signal_type_present_flag: %d \n", sps->vui.video_signal_type_present_flag);
|
||||||
|
if( sps->vui.video_signal_type_present_flag )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.video_format = bs_read_u(b, 3); printf("sps->vui.video_format: %d \n", sps->vui.video_format);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.video_full_range_flag = bs_read_u1(b); printf("sps->vui.video_full_range_flag: %d \n", sps->vui.video_full_range_flag);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.colour_description_present_flag = bs_read_u1(b); printf("sps->vui.colour_description_present_flag: %d \n", sps->vui.colour_description_present_flag);
|
||||||
|
if( sps->vui.colour_description_present_flag )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.colour_primaries = bs_read_u8(b); printf("sps->vui.colour_primaries: %d \n", sps->vui.colour_primaries);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.transfer_characteristics = bs_read_u8(b); printf("sps->vui.transfer_characteristics: %d \n", sps->vui.transfer_characteristics);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.matrix_coefficients = bs_read_u8(b); printf("sps->vui.matrix_coefficients: %d \n", sps->vui.matrix_coefficients);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.chroma_loc_info_present_flag = bs_read_u1(b); printf("sps->vui.chroma_loc_info_present_flag: %d \n", sps->vui.chroma_loc_info_present_flag);
|
||||||
|
if( sps->vui.chroma_loc_info_present_flag )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.chroma_sample_loc_type_top_field = bs_read_ue(b); printf("sps->vui.chroma_sample_loc_type_top_field: %d \n", sps->vui.chroma_sample_loc_type_top_field);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.chroma_sample_loc_type_bottom_field = bs_read_ue(b); printf("sps->vui.chroma_sample_loc_type_bottom_field: %d \n", sps->vui.chroma_sample_loc_type_bottom_field);
|
||||||
|
}
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.timing_info_present_flag = bs_read_u1(b); printf("sps->vui.timing_info_present_flag: %d \n", sps->vui.timing_info_present_flag);
|
||||||
|
if( sps->vui.timing_info_present_flag )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.num_units_in_tick = bs_read_u(b, 32); printf("sps->vui.num_units_in_tick: %d \n", sps->vui.num_units_in_tick);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.time_scale = bs_read_u(b, 32); printf("sps->vui.time_scale: %d \n", sps->vui.time_scale);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.fixed_frame_rate_flag = bs_read_u1(b); printf("sps->vui.fixed_frame_rate_flag: %d \n", sps->vui.fixed_frame_rate_flag);
|
||||||
|
}
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.nal_hrd_parameters_present_flag = bs_read_u1(b); printf("sps->vui.nal_hrd_parameters_present_flag: %d \n", sps->vui.nal_hrd_parameters_present_flag);
|
||||||
|
if( sps->vui.nal_hrd_parameters_present_flag )
|
||||||
|
{
|
||||||
|
read_debug_hrd_parameters(h, b);
|
||||||
|
}
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.vcl_hrd_parameters_present_flag = bs_read_u1(b); printf("sps->vui.vcl_hrd_parameters_present_flag: %d \n", sps->vui.vcl_hrd_parameters_present_flag);
|
||||||
|
if( sps->vui.vcl_hrd_parameters_present_flag )
|
||||||
|
{
|
||||||
|
read_debug_hrd_parameters(h, b);
|
||||||
|
}
|
||||||
|
if( sps->vui.nal_hrd_parameters_present_flag || sps->vui.vcl_hrd_parameters_present_flag )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.low_delay_hrd_flag = bs_read_u1(b); printf("sps->vui.low_delay_hrd_flag: %d \n", sps->vui.low_delay_hrd_flag);
|
||||||
|
}
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.pic_struct_present_flag = bs_read_u1(b); printf("sps->vui.pic_struct_present_flag: %d \n", sps->vui.pic_struct_present_flag);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.bitstream_restriction_flag = bs_read_u1(b); printf("sps->vui.bitstream_restriction_flag: %d \n", sps->vui.bitstream_restriction_flag);
|
||||||
|
if( sps->vui.bitstream_restriction_flag )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.motion_vectors_over_pic_boundaries_flag = bs_read_u1(b); printf("sps->vui.motion_vectors_over_pic_boundaries_flag: %d \n", sps->vui.motion_vectors_over_pic_boundaries_flag);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.max_bytes_per_pic_denom = bs_read_ue(b); printf("sps->vui.max_bytes_per_pic_denom: %d \n", sps->vui.max_bytes_per_pic_denom);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.max_bits_per_mb_denom = bs_read_ue(b); printf("sps->vui.max_bits_per_mb_denom: %d \n", sps->vui.max_bits_per_mb_denom);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.log2_max_mv_length_horizontal = bs_read_ue(b); printf("sps->vui.log2_max_mv_length_horizontal: %d \n", sps->vui.log2_max_mv_length_horizontal);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.log2_max_mv_length_vertical = bs_read_ue(b); printf("sps->vui.log2_max_mv_length_vertical: %d \n", sps->vui.log2_max_mv_length_vertical);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.num_reorder_frames = bs_read_ue(b); printf("sps->vui.num_reorder_frames: %d \n", sps->vui.num_reorder_frames);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.max_dec_frame_buffering = bs_read_ue(b); printf("sps->vui.max_dec_frame_buffering: %d \n", sps->vui.max_dec_frame_buffering);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//Appendix E.1.2 HRD parameters syntax
|
||||||
|
void read_debug_hrd_parameters(h264_stream_t* h, bs_t* b)
|
||||||
|
{
|
||||||
|
sps_t* sps = h->sps;
|
||||||
|
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->hrd.cpb_cnt_minus1 = bs_read_ue(b); printf("sps->hrd.cpb_cnt_minus1: %d \n", sps->hrd.cpb_cnt_minus1);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->hrd.bit_rate_scale = bs_read_u(b, 4); printf("sps->hrd.bit_rate_scale: %d \n", sps->hrd.bit_rate_scale);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->hrd.cpb_size_scale = bs_read_u(b, 4); printf("sps->hrd.cpb_size_scale: %d \n", sps->hrd.cpb_size_scale);
|
||||||
|
for( int SchedSelIdx = 0; SchedSelIdx <= sps->hrd.cpb_cnt_minus1; SchedSelIdx++ )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->hrd.bit_rate_value_minus1[ SchedSelIdx ] = bs_read_ue(b); printf("sps->hrd.bit_rate_value_minus1[ SchedSelIdx ]: %d \n", sps->hrd.bit_rate_value_minus1[ SchedSelIdx ]);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->hrd.cpb_size_value_minus1[ SchedSelIdx ] = bs_read_ue(b); printf("sps->hrd.cpb_size_value_minus1[ SchedSelIdx ]: %d \n", sps->hrd.cpb_size_value_minus1[ SchedSelIdx ]);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->hrd.cbr_flag[ SchedSelIdx ] = bs_read_u1(b); printf("sps->hrd.cbr_flag[ SchedSelIdx ]: %d \n", sps->hrd.cbr_flag[ SchedSelIdx ]);
|
||||||
|
}
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->hrd.initial_cpb_removal_delay_length_minus1 = bs_read_u(b, 5); printf("sps->hrd.initial_cpb_removal_delay_length_minus1: %d \n", sps->hrd.initial_cpb_removal_delay_length_minus1);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->hrd.cpb_removal_delay_length_minus1 = bs_read_u(b, 5); printf("sps->hrd.cpb_removal_delay_length_minus1: %d \n", sps->hrd.cpb_removal_delay_length_minus1);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->hrd.dpb_output_delay_length_minus1 = bs_read_u(b, 5); printf("sps->hrd.dpb_output_delay_length_minus1: %d \n", sps->hrd.dpb_output_delay_length_minus1);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->hrd.time_offset_length = bs_read_u(b, 5); printf("sps->hrd.time_offset_length: %d \n", sps->hrd.time_offset_length);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
UNIMPLEMENTED
|
||||||
|
//7.3.2.1.2 Sequence parameter set extension RBSP syntax
|
||||||
|
int read_debug_seq_parameter_set_extension_rbsp(bs_t* b, sps_ext_t* sps_ext) {
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); seq_parameter_set_id = bs_read_ue(b); printf("seq_parameter_set_id: %d \n", seq_parameter_set_id);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); aux_format_idc = bs_read_ue(b); printf("aux_format_idc: %d \n", aux_format_idc);
|
||||||
|
if( aux_format_idc != 0 ) {
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); bit_depth_aux_minus8 = bs_read_ue(b); printf("bit_depth_aux_minus8: %d \n", bit_depth_aux_minus8);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); alpha_incr_flag = bs_read_u1(b); printf("alpha_incr_flag: %d \n", alpha_incr_flag);
|
||||||
|
alpha_opaque_value = bs_read_debug_u(v);
|
||||||
|
alpha_transparent_value = bs_read_debug_u(v);
|
||||||
|
}
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); additional_extension_flag = bs_read_u1(b); printf("additional_extension_flag: %d \n", additional_extension_flag);
|
||||||
|
read_debug_rbsp_trailing_bits();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
//7.3.2.2 Picture parameter set RBSP syntax
|
||||||
|
void read_debug_pic_parameter_set_rbsp(h264_stream_t* h, bs_t* b)
|
||||||
|
{
|
||||||
|
pps_t* pps = h->pps;
|
||||||
|
if( 1 )
|
||||||
|
{
|
||||||
|
memset(pps, 0, sizeof(pps_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->pic_parameter_set_id = bs_read_ue(b); printf("pps->pic_parameter_set_id: %d \n", pps->pic_parameter_set_id);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->seq_parameter_set_id = bs_read_ue(b); printf("pps->seq_parameter_set_id: %d \n", pps->seq_parameter_set_id);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->entropy_coding_mode_flag = bs_read_u1(b); printf("pps->entropy_coding_mode_flag: %d \n", pps->entropy_coding_mode_flag);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->pic_order_present_flag = bs_read_u1(b); printf("pps->pic_order_present_flag: %d \n", pps->pic_order_present_flag);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->num_slice_groups_minus1 = bs_read_ue(b); printf("pps->num_slice_groups_minus1: %d \n", pps->num_slice_groups_minus1);
|
||||||
|
|
||||||
|
if( pps->num_slice_groups_minus1 > 0 )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->slice_group_map_type = bs_read_ue(b); printf("pps->slice_group_map_type: %d \n", pps->slice_group_map_type);
|
||||||
|
if( pps->slice_group_map_type == 0 )
|
||||||
|
{
|
||||||
|
for( int i_group = 0; i_group <= pps->num_slice_groups_minus1; i_group++ )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->run_length_minus1[ i_group ] = bs_read_ue(b); printf("pps->run_length_minus1[ i_group ]: %d \n", pps->run_length_minus1[ i_group ]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( pps->slice_group_map_type == 2 )
|
||||||
|
{
|
||||||
|
for( int i_group = 0; i_group < pps->num_slice_groups_minus1; i_group++ )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->top_left[ i_group ] = bs_read_ue(b); printf("pps->top_left[ i_group ]: %d \n", pps->top_left[ i_group ]);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->bottom_right[ i_group ] = bs_read_ue(b); printf("pps->bottom_right[ i_group ]: %d \n", pps->bottom_right[ i_group ]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if( pps->slice_group_map_type == 3 ||
|
||||||
|
pps->slice_group_map_type == 4 ||
|
||||||
|
pps->slice_group_map_type == 5 )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->slice_group_change_direction_flag = bs_read_u1(b); printf("pps->slice_group_change_direction_flag: %d \n", pps->slice_group_change_direction_flag);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->slice_group_change_rate_minus1 = bs_read_ue(b); printf("pps->slice_group_change_rate_minus1: %d \n", pps->slice_group_change_rate_minus1);
|
||||||
|
}
|
||||||
|
else if( pps->slice_group_map_type == 6 )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->pic_size_in_map_units_minus1 = bs_read_ue(b); printf("pps->pic_size_in_map_units_minus1: %d \n", pps->pic_size_in_map_units_minus1);
|
||||||
|
for( int i = 0; i <= pps->pic_size_in_map_units_minus1; i++ )
|
||||||
|
{
|
||||||
|
int v = intlog2( pps->num_slice_groups_minus1 + 1 );
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->slice_group_id[ i ] = bs_read_u(b, v); printf("pps->slice_group_id[ i ]: %d \n", pps->slice_group_id[ i ]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->num_ref_idx_l0_active_minus1 = bs_read_ue(b); printf("pps->num_ref_idx_l0_active_minus1: %d \n", pps->num_ref_idx_l0_active_minus1);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->num_ref_idx_l1_active_minus1 = bs_read_ue(b); printf("pps->num_ref_idx_l1_active_minus1: %d \n", pps->num_ref_idx_l1_active_minus1);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->weighted_pred_flag = bs_read_u1(b); printf("pps->weighted_pred_flag: %d \n", pps->weighted_pred_flag);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->weighted_bipred_idc = bs_read_u(b, 2); printf("pps->weighted_bipred_idc: %d \n", pps->weighted_bipred_idc);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->pic_init_qp_minus26 = bs_read_se(b); printf("pps->pic_init_qp_minus26: %d \n", pps->pic_init_qp_minus26);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->pic_init_qs_minus26 = bs_read_se(b); printf("pps->pic_init_qs_minus26: %d \n", pps->pic_init_qs_minus26);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->chroma_qp_index_offset = bs_read_se(b); printf("pps->chroma_qp_index_offset: %d \n", pps->chroma_qp_index_offset);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->deblocking_filter_control_present_flag = bs_read_u1(b); printf("pps->deblocking_filter_control_present_flag: %d \n", pps->deblocking_filter_control_present_flag);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->constrained_intra_pred_flag = bs_read_u1(b); printf("pps->constrained_intra_pred_flag: %d \n", pps->constrained_intra_pred_flag);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->redundant_pic_cnt_present_flag = bs_read_u1(b); printf("pps->redundant_pic_cnt_present_flag: %d \n", pps->redundant_pic_cnt_present_flag);
|
||||||
|
|
||||||
|
int have_more_data = 0;
|
||||||
|
if( 1 ) { have_more_data = more_rbsp_data(h, b); }
|
||||||
|
if( 0 )
|
||||||
|
{
|
||||||
|
have_more_data = pps->transform_8x8_mode_flag | pps->pic_scaling_matrix_present_flag | pps->second_chroma_qp_index_offset != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( have_more_data )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->transform_8x8_mode_flag = bs_read_u1(b); printf("pps->transform_8x8_mode_flag: %d \n", pps->transform_8x8_mode_flag);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->pic_scaling_matrix_present_flag = bs_read_u1(b); printf("pps->pic_scaling_matrix_present_flag: %d \n", pps->pic_scaling_matrix_present_flag);
|
||||||
|
if( pps->pic_scaling_matrix_present_flag )
|
||||||
|
{
|
||||||
|
for( int i = 0; i < 6 + 2* pps->transform_8x8_mode_flag; i++ )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->pic_scaling_list_present_flag[ i ] = bs_read_u1(b); printf("pps->pic_scaling_list_present_flag[ i ]: %d \n", pps->pic_scaling_list_present_flag[ i ]);
|
||||||
|
if( pps->pic_scaling_list_present_flag[ i ] )
|
||||||
|
{
|
||||||
|
if( i < 6 )
|
||||||
|
{
|
||||||
|
read_debug_scaling_list( b, pps->ScalingList4x4[ i ], 16,
|
||||||
|
&( pps->UseDefaultScalingMatrix4x4Flag[ i ] ) );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
read_debug_scaling_list( b, pps->ScalingList8x8[ i - 6 ], 64,
|
||||||
|
&( pps->UseDefaultScalingMatrix8x8Flag[ i - 6 ] ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->second_chroma_qp_index_offset = bs_read_se(b); printf("pps->second_chroma_qp_index_offset: %d \n", pps->second_chroma_qp_index_offset);
|
||||||
|
}
|
||||||
|
read_debug_rbsp_trailing_bits(h, b);
|
||||||
|
|
||||||
|
if( 1 )
|
||||||
|
{
|
||||||
|
memcpy(h->pps, h->pps_table[pps->pic_parameter_set_id], sizeof(pps_t));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_SEI
|
||||||
|
//7.3.2.3 Supplemental enhancement information RBSP syntax
|
||||||
|
void read_debug_sei_rbsp(h264_stream_t* h, bs_t* b)
|
||||||
|
{
|
||||||
|
if( 1 )
|
||||||
|
{
|
||||||
|
for( int i = 0; i < h->num_seis; i++ )
|
||||||
|
{
|
||||||
|
sei_free(h->seis[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
h->num_seis = 0;
|
||||||
|
do {
|
||||||
|
h->num_seis++;
|
||||||
|
h->seis = (sei_t**)realloc(h->seis, h->num_seis * sizeof(sei_t*));
|
||||||
|
h->seis[h->num_seis - 1] = sei_new();
|
||||||
|
h->sei = h->seis[h->num_seis - 1];
|
||||||
|
read_debug_sei_message(h, b);
|
||||||
|
} while( more_rbsp_data(h, b) );
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if( 0 )
|
||||||
|
{
|
||||||
|
for (int i = 0; i < h->num_seis; i++)
|
||||||
|
{
|
||||||
|
h->sei = h->seis[i];
|
||||||
|
read_debug_sei_message(h, b);
|
||||||
|
}
|
||||||
|
h->sei = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
read_debug_rbsp_trailing_bits(h, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
//7.3.2.3.1 Supplemental enhancement information message syntax
|
||||||
|
void read_debug_sei_message(h264_stream_t* h, bs_t* b)
|
||||||
|
{
|
||||||
|
if( 0 )
|
||||||
|
{
|
||||||
|
_write_ff_coded_number(b, h->sei->payloadType);
|
||||||
|
_write_ff_coded_number(b, h->sei->payloadSize);
|
||||||
|
}
|
||||||
|
if( 1 )
|
||||||
|
{
|
||||||
|
h->sei->payloadType = _read_ff_coded_number(b);
|
||||||
|
h->sei->payloadSize = _read_ff_coded_number(b);
|
||||||
|
}
|
||||||
|
read_debug_sei_payload( h, b, h->sei->payloadType, h->sei->payloadSize );
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//7.3.2.4 Access unit delimiter RBSP syntax
|
||||||
|
void read_debug_access_unit_delimiter_rbsp(h264_stream_t* h, bs_t* b)
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); h->aud->primary_pic_type = bs_read_u(b, 3); printf("h->aud->primary_pic_type: %d \n", h->aud->primary_pic_type);
|
||||||
|
read_debug_rbsp_trailing_bits(h, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
//7.3.2.5 End of sequence RBSP syntax
|
||||||
|
void read_debug_end_of_seq_rbsp(h264_stream_t* h, bs_t* b)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//7.3.2.6 End of stream RBSP syntax
|
||||||
|
void read_debug_end_of_stream_rbsp(h264_stream_t* h, bs_t* b)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//7.3.2.7 Filler data RBSP syntax
|
||||||
|
void read_debug_filler_data_rbsp(h264_stream_t* h, bs_t* b)
|
||||||
|
{
|
||||||
|
while( bs_next_bits(b, 8) == 0xFF )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); int ff_byte = bs_read_u(b, 8); printf("ff_byte: %d \n", ff_byte);
|
||||||
|
}
|
||||||
|
read_debug_rbsp_trailing_bits(h, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
//7.3.2.8 Slice layer without partitioning RBSP syntax
|
||||||
|
void read_debug_slice_layer_rbsp(h264_stream_t* h, bs_t* b)
|
||||||
|
{
|
||||||
|
read_debug_slice_header(h, b);
|
||||||
|
slice_data_rbsp_t* slice_data = h->slice_data;
|
||||||
|
|
||||||
|
if ( slice_data != NULL )
|
||||||
|
{
|
||||||
|
if ( slice_data->rbsp_buf != NULL ) free( slice_data->rbsp_buf );
|
||||||
|
uint8_t *sptr = b->p + (!!b->bits_left); // CABAC-specific: skip alignment bits, if there are any
|
||||||
|
slice_data->rbsp_size = b->end - sptr;
|
||||||
|
|
||||||
|
slice_data->rbsp_buf = (uint8_t*)malloc(slice_data->rbsp_size);
|
||||||
|
memcpy( slice_data->rbsp_buf, sptr, slice_data->rbsp_size );
|
||||||
|
// ugly hack: since next NALU starts at byte border, we are going to be padded by trailing_bits;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME should read or skip data
|
||||||
|
//slice_data( ); /* all categories of slice_data( ) syntax */
|
||||||
|
read_debug_rbsp_slice_trailing_bits(h, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// UNIMPLEMENTED
|
||||||
|
//7.3.2.9.1 Slice data partition A RBSP syntax
|
||||||
|
slice_data_partition_a_layer_rbsp( ) {
|
||||||
|
read_debug_slice_header( ); // only category 2
|
||||||
|
slice_id = bs_read_debug_ue(b)
|
||||||
|
read_debug_slice_data( ); // only category 2
|
||||||
|
read_debug_rbsp_slice_trailing_bits( ); // only category 2
|
||||||
|
}
|
||||||
|
|
||||||
|
//7.3.2.9.2 Slice data partition B RBSP syntax
|
||||||
|
slice_data_partition_b_layer_rbsp( ) {
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); slice_id = bs_read_ue(b); printf("slice_id: %d \n", slice_id); // only category 3
|
||||||
|
if( redundant_pic_cnt_present_flag )
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); redundant_pic_cnt = bs_read_ue(b); printf("redundant_pic_cnt: %d \n", redundant_pic_cnt);
|
||||||
|
read_debug_slice_data( ); // only category 3
|
||||||
|
read_debug_rbsp_slice_trailing_bits( ); // only category 3
|
||||||
|
}
|
||||||
|
|
||||||
|
//7.3.2.9.3 Slice data partition C RBSP syntax
|
||||||
|
slice_data_partition_c_layer_rbsp( ) {
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); slice_id = bs_read_ue(b); printf("slice_id: %d \n", slice_id); // only category 4
|
||||||
|
if( redundant_pic_cnt_present_flag )
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); redundant_pic_cnt = bs_read_ue(b); printf("redundant_pic_cnt: %d \n", redundant_pic_cnt);
|
||||||
|
read_debug_slice_data( ); // only category 4
|
||||||
|
rbsp_slice_trailing_bits( ); // only category 4
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
//7.3.2.10 RBSP slice trailing bits syntax
|
||||||
|
void read_debug_rbsp_slice_trailing_bits(h264_stream_t* h, bs_t* b)
|
||||||
|
{
|
||||||
|
read_debug_rbsp_trailing_bits(h, b);
|
||||||
|
if( h->pps->entropy_coding_mode_flag )
|
||||||
|
{
|
||||||
|
while( more_rbsp_trailing_data(h, b) )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); int cabac_zero_word = bs_read_u(b, 16); printf("cabac_zero_word: %d \n", cabac_zero_word);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//7.3.2.11 RBSP trailing bits syntax
|
||||||
|
void read_debug_rbsp_trailing_bits(h264_stream_t* h, bs_t* b)
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); int rbsp_stop_one_bit = bs_read_u(b, 1); printf("rbsp_stop_one_bit: %d \n", rbsp_stop_one_bit);
|
||||||
|
|
||||||
|
while( !bs_byte_aligned(b) )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); int rbsp_alignment_zero_bit = bs_read_u(b, 1); printf("rbsp_alignment_zero_bit: %d \n", rbsp_alignment_zero_bit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//7.3.3 Slice header syntax
|
||||||
|
void read_debug_slice_header(h264_stream_t* h, bs_t* b)
|
||||||
|
{
|
||||||
|
slice_header_t* sh = h->sh;
|
||||||
|
if( 1 )
|
||||||
|
{
|
||||||
|
memset(sh, 0, sizeof(slice_header_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
nal_t* nal = h->nal;
|
||||||
|
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->first_mb_in_slice = bs_read_ue(b); printf("sh->first_mb_in_slice: %d \n", sh->first_mb_in_slice);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->slice_type = bs_read_ue(b); printf("sh->slice_type: %d \n", sh->slice_type);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pic_parameter_set_id = bs_read_ue(b); printf("sh->pic_parameter_set_id: %d \n", sh->pic_parameter_set_id);
|
||||||
|
|
||||||
|
// TODO check existence, otherwise fail
|
||||||
|
pps_t* pps = h->pps;
|
||||||
|
sps_t* sps = h->sps;
|
||||||
|
memcpy(h->pps_table[sh->pic_parameter_set_id], h->pps, sizeof(pps_t));
|
||||||
|
memcpy(h->sps_table[pps->seq_parameter_set_id], h->sps, sizeof(sps_t));
|
||||||
|
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->frame_num = bs_read_u(b, sps->log2_max_frame_num_minus4 + 4 ); printf("sh->frame_num: %d \n", sh->frame_num); // was u(v)
|
||||||
|
if( !sps->frame_mbs_only_flag )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->field_pic_flag = bs_read_u1(b); printf("sh->field_pic_flag: %d \n", sh->field_pic_flag);
|
||||||
|
if( sh->field_pic_flag )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->bottom_field_flag = bs_read_u1(b); printf("sh->bottom_field_flag: %d \n", sh->bottom_field_flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( nal->nal_unit_type == 5 )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->idr_pic_id = bs_read_ue(b); printf("sh->idr_pic_id: %d \n", sh->idr_pic_id);
|
||||||
|
}
|
||||||
|
if( sps->pic_order_cnt_type == 0 )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pic_order_cnt_lsb = bs_read_u(b, sps->log2_max_pic_order_cnt_lsb_minus4 + 4 ); printf("sh->pic_order_cnt_lsb: %d \n", sh->pic_order_cnt_lsb); // was u(v)
|
||||||
|
if( pps->pic_order_present_flag && !sh->field_pic_flag )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->delta_pic_order_cnt_bottom = bs_read_se(b); printf("sh->delta_pic_order_cnt_bottom: %d \n", sh->delta_pic_order_cnt_bottom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( sps->pic_order_cnt_type == 1 && !sps->delta_pic_order_always_zero_flag )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->delta_pic_order_cnt[ 0 ] = bs_read_se(b); printf("sh->delta_pic_order_cnt[ 0 ]: %d \n", sh->delta_pic_order_cnt[ 0 ]);
|
||||||
|
if( pps->pic_order_present_flag && !sh->field_pic_flag )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->delta_pic_order_cnt[ 1 ] = bs_read_se(b); printf("sh->delta_pic_order_cnt[ 1 ]: %d \n", sh->delta_pic_order_cnt[ 1 ]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( pps->redundant_pic_cnt_present_flag )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->redundant_pic_cnt = bs_read_ue(b); printf("sh->redundant_pic_cnt: %d \n", sh->redundant_pic_cnt);
|
||||||
|
}
|
||||||
|
if( is_slice_type( sh->slice_type, SH_SLICE_TYPE_B ) )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->direct_spatial_mv_pred_flag = bs_read_u1(b); printf("sh->direct_spatial_mv_pred_flag: %d \n", sh->direct_spatial_mv_pred_flag);
|
||||||
|
}
|
||||||
|
if( is_slice_type( sh->slice_type, SH_SLICE_TYPE_P ) || is_slice_type( sh->slice_type, SH_SLICE_TYPE_SP ) || is_slice_type( sh->slice_type, SH_SLICE_TYPE_B ) )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->num_ref_idx_active_override_flag = bs_read_u1(b); printf("sh->num_ref_idx_active_override_flag: %d \n", sh->num_ref_idx_active_override_flag);
|
||||||
|
if( sh->num_ref_idx_active_override_flag )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->num_ref_idx_l0_active_minus1 = bs_read_ue(b); printf("sh->num_ref_idx_l0_active_minus1: %d \n", sh->num_ref_idx_l0_active_minus1); // FIXME does this modify the pps?
|
||||||
|
if( is_slice_type( sh->slice_type, SH_SLICE_TYPE_B ) )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->num_ref_idx_l1_active_minus1 = bs_read_ue(b); printf("sh->num_ref_idx_l1_active_minus1: %d \n", sh->num_ref_idx_l1_active_minus1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
read_debug_ref_pic_list_reordering(h, b);
|
||||||
|
if( ( pps->weighted_pred_flag && ( is_slice_type( sh->slice_type, SH_SLICE_TYPE_P ) || is_slice_type( sh->slice_type, SH_SLICE_TYPE_SP ) ) ) ||
|
||||||
|
( pps->weighted_bipred_idc == 1 && is_slice_type( sh->slice_type, SH_SLICE_TYPE_B ) ) )
|
||||||
|
{
|
||||||
|
read_debug_pred_weight_table(h, b);
|
||||||
|
}
|
||||||
|
if( nal->nal_ref_idc != 0 )
|
||||||
|
{
|
||||||
|
read_debug_dec_ref_pic_marking(h, b);
|
||||||
|
}
|
||||||
|
if( pps->entropy_coding_mode_flag && ! is_slice_type( sh->slice_type, SH_SLICE_TYPE_I ) && ! is_slice_type( sh->slice_type, SH_SLICE_TYPE_SI ) )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->cabac_init_idc = bs_read_ue(b); printf("sh->cabac_init_idc: %d \n", sh->cabac_init_idc);
|
||||||
|
}
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->slice_qp_delta = bs_read_se(b); printf("sh->slice_qp_delta: %d \n", sh->slice_qp_delta);
|
||||||
|
if( is_slice_type( sh->slice_type, SH_SLICE_TYPE_SP ) || is_slice_type( sh->slice_type, SH_SLICE_TYPE_SI ) )
|
||||||
|
{
|
||||||
|
if( is_slice_type( sh->slice_type, SH_SLICE_TYPE_SP ) )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->sp_for_switch_flag = bs_read_u1(b); printf("sh->sp_for_switch_flag: %d \n", sh->sp_for_switch_flag);
|
||||||
|
}
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->slice_qs_delta = bs_read_se(b); printf("sh->slice_qs_delta: %d \n", sh->slice_qs_delta);
|
||||||
|
}
|
||||||
|
if( pps->deblocking_filter_control_present_flag )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->disable_deblocking_filter_idc = bs_read_ue(b); printf("sh->disable_deblocking_filter_idc: %d \n", sh->disable_deblocking_filter_idc);
|
||||||
|
if( sh->disable_deblocking_filter_idc != 1 )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->slice_alpha_c0_offset_div2 = bs_read_se(b); printf("sh->slice_alpha_c0_offset_div2: %d \n", sh->slice_alpha_c0_offset_div2);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->slice_beta_offset_div2 = bs_read_se(b); printf("sh->slice_beta_offset_div2: %d \n", sh->slice_beta_offset_div2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( pps->num_slice_groups_minus1 > 0 &&
|
||||||
|
pps->slice_group_map_type >= 3 && pps->slice_group_map_type <= 5)
|
||||||
|
{
|
||||||
|
int v = intlog2( pps->pic_size_in_map_units_minus1 + pps->slice_group_change_rate_minus1 + 1 );
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->slice_group_change_cycle = bs_read_u(b, v); printf("sh->slice_group_change_cycle: %d \n", sh->slice_group_change_cycle); // FIXME add 2?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//7.3.3.1 Reference picture list reordering syntax
|
||||||
|
void read_debug_ref_pic_list_reordering(h264_stream_t* h, bs_t* b)
|
||||||
|
{
|
||||||
|
slice_header_t* sh = h->sh;
|
||||||
|
// FIXME should be an array
|
||||||
|
|
||||||
|
if( ! is_slice_type( sh->slice_type, SH_SLICE_TYPE_I ) && ! is_slice_type( sh->slice_type, SH_SLICE_TYPE_SI ) )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->rplr.ref_pic_list_reordering_flag_l0 = bs_read_u1(b); printf("sh->rplr.ref_pic_list_reordering_flag_l0: %d \n", sh->rplr.ref_pic_list_reordering_flag_l0);
|
||||||
|
if( sh->rplr.ref_pic_list_reordering_flag_l0 )
|
||||||
|
{
|
||||||
|
int n = -1;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
n++;
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->rplr.reorder_l0.reordering_of_pic_nums_idc[ n ] = bs_read_ue(b); printf("sh->rplr.reorder_l0.reordering_of_pic_nums_idc[ n ]: %d \n", sh->rplr.reorder_l0.reordering_of_pic_nums_idc[ n ]);
|
||||||
|
if( sh->rplr.reorder_l0.reordering_of_pic_nums_idc[ n ] == 0 ||
|
||||||
|
sh->rplr.reorder_l0.reordering_of_pic_nums_idc[ n ] == 1 )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->rplr.reorder_l0.abs_diff_pic_num_minus1[ n ] = bs_read_ue(b); printf("sh->rplr.reorder_l0.abs_diff_pic_num_minus1[ n ]: %d \n", sh->rplr.reorder_l0.abs_diff_pic_num_minus1[ n ]);
|
||||||
|
}
|
||||||
|
else if( sh->rplr.reorder_l0.reordering_of_pic_nums_idc[ n ] == 2 )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->rplr.reorder_l0.long_term_pic_num[ n ] = bs_read_ue(b); printf("sh->rplr.reorder_l0.long_term_pic_num[ n ]: %d \n", sh->rplr.reorder_l0.long_term_pic_num[ n ]);
|
||||||
|
}
|
||||||
|
} while( sh->rplr.reorder_l0.reordering_of_pic_nums_idc[ n ] != 3 && ! bs_eof(b) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( is_slice_type( sh->slice_type, SH_SLICE_TYPE_B ) )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->rplr.ref_pic_list_reordering_flag_l1 = bs_read_u1(b); printf("sh->rplr.ref_pic_list_reordering_flag_l1: %d \n", sh->rplr.ref_pic_list_reordering_flag_l1);
|
||||||
|
if( sh->rplr.ref_pic_list_reordering_flag_l1 )
|
||||||
|
{
|
||||||
|
int n = -1;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
n++;
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->rplr.reorder_l1.reordering_of_pic_nums_idc[ n ] = bs_read_ue(b); printf("sh->rplr.reorder_l1.reordering_of_pic_nums_idc[ n ]: %d \n", sh->rplr.reorder_l1.reordering_of_pic_nums_idc[ n ]);
|
||||||
|
if( sh->rplr.reorder_l1.reordering_of_pic_nums_idc[ n ] == 0 ||
|
||||||
|
sh->rplr.reorder_l1.reordering_of_pic_nums_idc[ n ] == 1 )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->rplr.reorder_l1.abs_diff_pic_num_minus1[ n ] = bs_read_ue(b); printf("sh->rplr.reorder_l1.abs_diff_pic_num_minus1[ n ]: %d \n", sh->rplr.reorder_l1.abs_diff_pic_num_minus1[ n ]);
|
||||||
|
}
|
||||||
|
else if( sh->rplr.reorder_l1.reordering_of_pic_nums_idc[ n ] == 2 )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->rplr.reorder_l1.long_term_pic_num[ n ] = bs_read_ue(b); printf("sh->rplr.reorder_l1.long_term_pic_num[ n ]: %d \n", sh->rplr.reorder_l1.long_term_pic_num[ n ]);
|
||||||
|
}
|
||||||
|
} while( sh->rplr.reorder_l1.reordering_of_pic_nums_idc[ n ] != 3 && ! bs_eof(b) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//7.3.3.2 Prediction weight table syntax
|
||||||
|
void read_debug_pred_weight_table(h264_stream_t* h, bs_t* b)
|
||||||
|
{
|
||||||
|
slice_header_t* sh = h->sh;
|
||||||
|
sps_t* sps = h->sps;
|
||||||
|
pps_t* pps = h->pps;
|
||||||
|
|
||||||
|
int i, j;
|
||||||
|
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.luma_log2_weight_denom = bs_read_ue(b); printf("sh->pwt.luma_log2_weight_denom: %d \n", sh->pwt.luma_log2_weight_denom);
|
||||||
|
if( sps->chroma_format_idc != 0 )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.chroma_log2_weight_denom = bs_read_ue(b); printf("sh->pwt.chroma_log2_weight_denom: %d \n", sh->pwt.chroma_log2_weight_denom);
|
||||||
|
}
|
||||||
|
for( i = 0; i <= pps->num_ref_idx_l0_active_minus1; i++ )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.luma_weight_l0_flag[i] = bs_read_u1(b); printf("sh->pwt.luma_weight_l0_flag[i]: %d \n", sh->pwt.luma_weight_l0_flag[i]);
|
||||||
|
if( sh->pwt.luma_weight_l0_flag[i] )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.luma_weight_l0[ i ] = bs_read_se(b); printf("sh->pwt.luma_weight_l0[ i ]: %d \n", sh->pwt.luma_weight_l0[ i ]);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.luma_offset_l0[ i ] = bs_read_se(b); printf("sh->pwt.luma_offset_l0[ i ]: %d \n", sh->pwt.luma_offset_l0[ i ]);
|
||||||
|
}
|
||||||
|
if ( sps->chroma_format_idc != 0 )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.chroma_weight_l0_flag[i] = bs_read_u1(b); printf("sh->pwt.chroma_weight_l0_flag[i]: %d \n", sh->pwt.chroma_weight_l0_flag[i]);
|
||||||
|
if( sh->pwt.chroma_weight_l0_flag[i] )
|
||||||
|
{
|
||||||
|
for( j =0; j < 2; j++ )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.chroma_weight_l0[ i ][ j ] = bs_read_se(b); printf("sh->pwt.chroma_weight_l0[ i ][ j ]: %d \n", sh->pwt.chroma_weight_l0[ i ][ j ]);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.chroma_offset_l0[ i ][ j ] = bs_read_se(b); printf("sh->pwt.chroma_offset_l0[ i ][ j ]: %d \n", sh->pwt.chroma_offset_l0[ i ][ j ]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if( is_slice_type( sh->slice_type, SH_SLICE_TYPE_B ) )
|
||||||
|
{
|
||||||
|
for( i = 0; i <= pps->num_ref_idx_l1_active_minus1; i++ )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.luma_weight_l1_flag[i] = bs_read_u1(b); printf("sh->pwt.luma_weight_l1_flag[i]: %d \n", sh->pwt.luma_weight_l1_flag[i]);
|
||||||
|
if( sh->pwt.luma_weight_l1_flag[i] )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.luma_weight_l1[ i ] = bs_read_se(b); printf("sh->pwt.luma_weight_l1[ i ]: %d \n", sh->pwt.luma_weight_l1[ i ]);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.luma_offset_l1[ i ] = bs_read_se(b); printf("sh->pwt.luma_offset_l1[ i ]: %d \n", sh->pwt.luma_offset_l1[ i ]);
|
||||||
|
}
|
||||||
|
if( sps->chroma_format_idc != 0 )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.chroma_weight_l1_flag[i] = bs_read_u1(b); printf("sh->pwt.chroma_weight_l1_flag[i]: %d \n", sh->pwt.chroma_weight_l1_flag[i]);
|
||||||
|
if( sh->pwt.chroma_weight_l1_flag[i] )
|
||||||
|
{
|
||||||
|
for( j = 0; j < 2; j++ )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.chroma_weight_l1[ i ][ j ] = bs_read_se(b); printf("sh->pwt.chroma_weight_l1[ i ][ j ]: %d \n", sh->pwt.chroma_weight_l1[ i ][ j ]);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.chroma_offset_l1[ i ][ j ] = bs_read_se(b); printf("sh->pwt.chroma_offset_l1[ i ][ j ]: %d \n", sh->pwt.chroma_offset_l1[ i ][ j ]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//7.3.3.3 Decoded reference picture marking syntax
|
||||||
|
void read_debug_dec_ref_pic_marking(h264_stream_t* h, bs_t* b)
|
||||||
|
{
|
||||||
|
slice_header_t* sh = h->sh;
|
||||||
|
// FIXME should be an array
|
||||||
|
|
||||||
|
if( h->nal->nal_unit_type == 5 )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->drpm.no_output_of_prior_pics_flag = bs_read_u1(b); printf("sh->drpm.no_output_of_prior_pics_flag: %d \n", sh->drpm.no_output_of_prior_pics_flag);
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->drpm.long_term_reference_flag = bs_read_u1(b); printf("sh->drpm.long_term_reference_flag: %d \n", sh->drpm.long_term_reference_flag);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->drpm.adaptive_ref_pic_marking_mode_flag = bs_read_u1(b); printf("sh->drpm.adaptive_ref_pic_marking_mode_flag: %d \n", sh->drpm.adaptive_ref_pic_marking_mode_flag);
|
||||||
|
if( sh->drpm.adaptive_ref_pic_marking_mode_flag )
|
||||||
|
{
|
||||||
|
int n = -1;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
n++;
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->drpm.memory_management_control_operation[ n ] = bs_read_ue(b); printf("sh->drpm.memory_management_control_operation[ n ]: %d \n", sh->drpm.memory_management_control_operation[ n ]);
|
||||||
|
if( sh->drpm.memory_management_control_operation[ n ] == 1 ||
|
||||||
|
sh->drpm.memory_management_control_operation[ n ] == 3 )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->drpm.difference_of_pic_nums_minus1[ n ] = bs_read_ue(b); printf("sh->drpm.difference_of_pic_nums_minus1[ n ]: %d \n", sh->drpm.difference_of_pic_nums_minus1[ n ]);
|
||||||
|
}
|
||||||
|
if(sh->drpm.memory_management_control_operation[ n ] == 2 )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->drpm.long_term_pic_num[ n ] = bs_read_ue(b); printf("sh->drpm.long_term_pic_num[ n ]: %d \n", sh->drpm.long_term_pic_num[ n ]);
|
||||||
|
}
|
||||||
|
if( sh->drpm.memory_management_control_operation[ n ] == 3 ||
|
||||||
|
sh->drpm.memory_management_control_operation[ n ] == 6 )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->drpm.long_term_frame_idx[ n ] = bs_read_ue(b); printf("sh->drpm.long_term_frame_idx[ n ]: %d \n", sh->drpm.long_term_frame_idx[ n ]);
|
||||||
|
}
|
||||||
|
if( sh->drpm.memory_management_control_operation[ n ] == 4 )
|
||||||
|
{
|
||||||
|
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->drpm.max_long_term_frame_idx_plus1[ n ] = bs_read_ue(b); printf("sh->drpm.max_long_term_frame_idx_plus1[ n ]: %d \n", sh->drpm.max_long_term_frame_idx_plus1[ n ]);
|
||||||
|
}
|
||||||
|
} while( sh->drpm.memory_management_control_operation[ n ] != 0 && ! bs_eof(b) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
2
third_party/moonlight-common-c
vendored
2
third_party/moonlight-common-c
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 8af4562af672dd6b9ed28553ead172984fd9a683
|
Subproject commit b70f072090f734cc35fd163ce9d39e2d22ceba57
|
Loading…
x
Reference in New Issue
Block a user