mirror of
https://github.com/moonlight-stream/moonlight-embedded.git
synced 2025-07-01 15:25:35 +00:00
Compare commits
154 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a6bf7154a7 | ||
|
274d3db34d | ||
|
014af67397 | ||
|
eb4a202358 | ||
|
bac5360494 | ||
|
0e12282311 | ||
|
883498d642 | ||
|
55b49221d8 | ||
|
454f67b0ed | ||
|
2f03600bae | ||
|
fc904d2dac | ||
|
d6c9650a32 | ||
|
a9d6f17d5e | ||
|
ca4c517510 | ||
|
3b1b2ab51d | ||
|
b533c52edd | ||
|
d89bd8d20f | ||
|
8125d2194b | ||
|
8fb2c72ca1 | ||
|
9255f774f2 | ||
|
d3d79c3224 | ||
|
b08a04c378 | ||
|
5bd2799209 | ||
|
0712e05aab | ||
|
02cddf762b | ||
|
810ef140cb | ||
|
50a7454bc1 | ||
|
a610eddd97 | ||
|
b3fb22d427 | ||
|
3840b1409e | ||
|
3b72f5195b | ||
|
000d9da4a0 | ||
|
2aba7a10e7 | ||
|
0131274dce | ||
|
dc03235a62 | ||
|
c007eabf50 | ||
|
d578fd8c7d | ||
|
807f9027c7 | ||
|
36c1636f3c | ||
|
19442743a6 | ||
|
74714e6fb1 | ||
|
b1d111b5f7 | ||
|
eb46891f4c | ||
|
c883b6786f | ||
|
861cdbc014 | ||
|
41fddcf5ef | ||
|
d1db1d5231 | ||
|
8ef8891969 | ||
|
642b498aee | ||
|
68234081f8 | ||
|
3904c82ee9 | ||
|
bf14d37cd5 | ||
|
b1b3e274b3 | ||
|
93f5dc6cae | ||
|
e15466e909 | ||
|
859965a5a6 | ||
|
0deeb94655 | ||
|
c158833258 | ||
|
ab53f149f2 | ||
|
606e6d1181 | ||
|
6538d855d8 | ||
|
0169cedb27 | ||
|
5bb47ce8b6 | ||
|
0d0728f3c4 | ||
|
a585072ff8 | ||
|
dc786e3033 | ||
|
dede55c7f9 | ||
|
a328a53d59 | ||
|
6ea4a768e4 | ||
|
91022ea3bb | ||
|
6310edb0bd | ||
|
c779c1970d | ||
|
1caf24beab | ||
|
f486c8e356 | ||
|
5edf4d3dc7 | ||
|
0b80cc197a | ||
|
21eb932e52 | ||
|
db52ac901f | ||
|
2e0a014496 | ||
|
6ab37d0036 | ||
|
a9f8a77b66 | ||
|
a8a4e8908e | ||
|
b8442bfa95 | ||
|
4c58ac9f39 | ||
|
8384a243b2 | ||
|
32ebb00292 | ||
|
7988594e9e | ||
|
00c28f53d8 | ||
|
a8d5f7e2e7 | ||
|
4cb30a2a46 | ||
|
858c884099 | ||
|
5c23f0494d | ||
|
e7c611bb1e | ||
|
62007b912e | ||
|
1feeab9c71 | ||
|
c985b9ea0d | ||
|
66bc1b2904 | ||
|
32e87994cd | ||
|
098f53cd0b | ||
|
56f84ab662 | ||
|
c3325a8887 | ||
|
f16dc469af | ||
|
b2192eda25 | ||
|
b26d747e95 | ||
|
c2f21b955d | ||
|
28ace51874 | ||
|
a9302d02f5 | ||
|
30b563a2fc | ||
|
cb4b5d55b5 | ||
|
58958ca32a | ||
|
f021439d1b | ||
|
13390594f5 | ||
|
7ab48fb6d4 | ||
|
254f41686b | ||
|
758dc68958 | ||
|
0a9cadb729 | ||
|
0325a3b88c | ||
|
3f00f25a39 | ||
|
3859949b0f | ||
|
42980d09ee | ||
|
60c4b514af | ||
|
9de64d25b9 | ||
|
20a02acb07 | ||
|
b7cf7a130b | ||
|
1b95f027a2 | ||
|
4ebd3fb8ba | ||
|
b9703e7a1e | ||
|
543dc087fc | ||
|
5fe7b36b40 | ||
|
d74cc63038 | ||
|
039040e247 | ||
|
22f75b74f9 | ||
|
6c215e47bf | ||
|
44623c4a5e | ||
|
5449b521aa | ||
|
786d4a66ec | ||
|
8323eeb23c | ||
|
f871b663b1 | ||
|
23e6854a84 | ||
|
fe5dd11893 | ||
|
fbf6a2e2f7 | ||
|
5761f533ab | ||
|
bbdd7e5b24 | ||
|
634a0eee15 | ||
|
18fd1637a6 | ||
|
807565de8b | ||
|
1d32f894f7 | ||
|
89030f0701 | ||
|
ba8a23725d | ||
|
81322a2b91 | ||
|
d58d8f70e8 | ||
|
76b0de3566 | ||
|
bf7149e75d | ||
|
87613b3176 |
@ -1,10 +1,14 @@
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
project(moonlight-embedded VERSION 2.5.1 LANGUAGES C)
|
||||
cmake_minimum_required(VERSION 3.6)
|
||||
project(moonlight-embedded VERSION 2.7.0 LANGUAGES C)
|
||||
SET(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
|
||||
SET(CMAKE_C_STANDARD 99)
|
||||
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)
|
||||
list(APPEND SRC_LIST ./src/input/evdev.c ./src/input/mapping.c ./src/input/udev.c)
|
||||
|
||||
@ -12,25 +16,48 @@ set(MOONLIGHT_DEFINITIONS)
|
||||
|
||||
find_package(ALSA)
|
||||
find_package(Opus REQUIRED)
|
||||
find_package(Broadcom)
|
||||
find_package(Broadcom-OMX)
|
||||
find_package(Freescale)
|
||||
find_package(Amlogic)
|
||||
find_package(Rockchip)
|
||||
|
||||
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(UDEV REQUIRED libudev)
|
||||
pkg_check_modules(SDL sdl2>=2.0.4)
|
||||
pkg_check_modules(AVCODEC libavcodec)
|
||||
pkg_check_modules(AVUTIL libavutil)
|
||||
pkg_check_modules(XLIB x11)
|
||||
pkg_check_modules(VDPAU vdpau)
|
||||
pkg_check_modules(LIBVA libva)
|
||||
pkg_check_modules(LIBVA_X11 libva-x11)
|
||||
pkg_check_modules(PULSE libpulse-simple)
|
||||
pkg_check_modules(CEC libcec>=4)
|
||||
pkg_check_modules(EGL egl)
|
||||
pkg_check_modules(GLES glesv2)
|
||||
if (ENABLE_SDL)
|
||||
pkg_check_modules(SDL sdl2>=2.0.4)
|
||||
endif()
|
||||
if (ENABLE_FFMPEG)
|
||||
pkg_check_modules(AVCODEC libavcodec)
|
||||
pkg_check_modules(AVUTIL libavutil)
|
||||
pkg_check_modules(VDPAU vdpau)
|
||||
pkg_check_modules(LIBVA libva)
|
||||
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(VA_ACCEL_FOUND FALSE)
|
||||
@ -62,6 +89,22 @@ add_executable(moonlight ${SRC_LIST})
|
||||
target_link_libraries(moonlight m)
|
||||
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)
|
||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_LIBCEC)
|
||||
list(APPEND MOONLIGHT_OPTIONS CEC)
|
||||
@ -73,35 +116,37 @@ endif()
|
||||
if(AMLOGIC_FOUND)
|
||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_AML)
|
||||
list(APPEND MOONLIGHT_OPTIONS AML)
|
||||
add_library(moonlight-aml SHARED ./src/video/aml.c ${ILCLIENT_SRC_LIST})
|
||||
add_library(moonlight-aml SHARED ./src/video/aml.c ./src/util.c ${ILCLIENT_SRC_LIST})
|
||||
target_include_directories(moonlight-aml PRIVATE ${AMLOGIC_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR})
|
||||
target_link_libraries(moonlight-aml gamestream ${AMLOGIC_LIBRARIES})
|
||||
set_property(TARGET moonlight-aml PROPERTY COMPILE_DEFINITIONS ${AMLOGIC_DEFINITIONS})
|
||||
install(TARGETS moonlight-aml DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
endif()
|
||||
|
||||
if(BROADCOM_FOUND)
|
||||
if(BROADCOM-OMX_FOUND)
|
||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_PI)
|
||||
list(APPEND MOONLIGHT_OPTIONS PI)
|
||||
aux_source_directory(./third_party/ilclient ILCLIENT_SRC_LIST)
|
||||
add_library(moonlight-pi SHARED ./src/video/pi.c ./src/audio/omx.c ${ILCLIENT_SRC_LIST})
|
||||
add_library(moonlight-pi SHARED ./src/video/pi.c ./src/audio/omx.c ./src/util.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_link_libraries(moonlight-pi gamestream ${BROADCOM_OMX_LIBRARIES} ${OPUS_LIBRARY})
|
||||
set_property(TARGET moonlight-pi PROPERTY COMPILE_DEFINITIONS ${BROADCOM_OMX_DEFINITIONS})
|
||||
install(TARGETS moonlight-pi DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
endif()
|
||||
|
||||
if(MMAL_FOUND)
|
||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_MMAL)
|
||||
list(APPEND MOONLIGHT_OPTIONS MMAL)
|
||||
add_library(moonlight-mmal SHARED ./src/video/mmal.c)
|
||||
target_include_directories(moonlight-mmal PRIVATE ${BROADCOM_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR})
|
||||
target_link_libraries(moonlight-mmal gamestream ${BROADCOM_MMAL_LIBRARIES})
|
||||
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)
|
||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_IMX)
|
||||
list(APPEND MOONLIGHT_OPTIONS IMX)
|
||||
add_library(moonlight-imx SHARED ./src/video/imx.c ./src/video/imx_vpu.c)
|
||||
add_library(moonlight-imx SHARED ./src/video/imx.c ./src/video/imx_vpu.c ./src/util.c)
|
||||
target_include_directories(moonlight-imx PRIVATE ${FREESCALE_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR})
|
||||
target_link_libraries(moonlight-imx gamestream ${FREESCALE_LIBRARIES})
|
||||
install(TARGETS moonlight-imx DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
@ -110,7 +155,7 @@ endif()
|
||||
if(ROCKCHIP_FOUND)
|
||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_ROCKCHIP)
|
||||
list(APPEND MOONLIGHT_OPTIONS ROCKCHIP)
|
||||
add_library(moonlight-rk SHARED ./src/video/rk.c)
|
||||
add_library(moonlight-rk SHARED ./src/video/rk.c ./src/util.c)
|
||||
target_include_directories(moonlight-rk PRIVATE ${ROCKCHIP_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR})
|
||||
target_link_libraries(moonlight-rk gamestream ${ROCKCHIP_LIBRARIES})
|
||||
set_property(TARGET moonlight-rk PROPERTY COMPILE_DEFINITIONS ${ROCKCHIP_DEFINITIONS})
|
||||
@ -166,12 +211,12 @@ if (PULSE_FOUND)
|
||||
target_link_libraries(moonlight ${PULSE_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if (AMLOGIC_FOUND OR BROADCOM_FOUND OR FREESCALE_FOUND OR ROCKCHIP_FOUND OR X11_FOUND)
|
||||
if (AMLOGIC_FOUND OR BROADCOM-OMX_FOUND OR MMAL_FOUND OR FREESCALE_FOUND OR ROCKCHIP_FOUND OR X11_FOUND)
|
||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_EMBEDDED)
|
||||
list(APPEND MOONLIGHT_OPTIONS EMBEDDED)
|
||||
endif()
|
||||
|
||||
if(NOT AMLOGIC_FOUND AND NOT BROADCOM_FOUND AND NOT FREESCALE_FOUND AND NOT ROCKCHIP_FOUND AND NOT SOFTWARE_FOUND)
|
||||
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)
|
||||
message(FATAL_ERROR "No video output available")
|
||||
endif()
|
||||
|
||||
|
20
README.md
20
README.md
@ -2,30 +2,14 @@
|
||||
|
||||
[](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
|
||||
|
||||
More information about installing and runnning Moonlight Embedded is available on the [wiki](https://github.com/moonlight-stream/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
|
||||
|
||||
Please check the wiki and old bug reports before submitting a new bug report.
|
||||
|
@ -12,7 +12,7 @@ environment:
|
||||
BUILD_TARGET: raspbian
|
||||
|
||||
install:
|
||||
- 'sudo apt update'
|
||||
- 'sudo apt update || true'
|
||||
- 'sudo apt install -y $PACKAGES'
|
||||
- '[ "$BUILD_TARGET" != raspbian ] || docker run --rm --privileged multiarch/qemu-user-static --reset -p yes'
|
||||
|
||||
@ -21,7 +21,7 @@ before_build:
|
||||
|
||||
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.sh $APPVEYOR_REPO_COMMIT"; 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'
|
||||
|
@ -1,29 +1,17 @@
|
||||
find_path(AMLOGIC_INCLUDE_DIR
|
||||
NAMES codec.h
|
||||
DOC "Amlogic include directory"
|
||||
PATHS /usr/local/include/amcodec /usr/include/amcodec /usr/include/)
|
||||
PATHS /usr/local/include/amcodec /usr/osmc/include/amcodec /usr/include/amcodec /usr/include/)
|
||||
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
|
||||
NAMES libamcodec.so
|
||||
DOC "Path to Amlogic Video Codec Library"
|
||||
PATHS /usr/lib/aml_libs /usr/local/lib /usr/lib)
|
||||
PATHS /usr/lib/aml_libs /usr/osmc/lib /usr/local/lib /usr/lib)
|
||||
mark_as_advanced(AMCODEC_LIBRARY)
|
||||
|
||||
include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Amlogic DEFAULT_MSG AMLOGIC_INCLUDE_DIR AMCODEC_LIBRARY AMADEC_LIBRARY AMAVUTILS_LIBRARY)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Amlogic DEFAULT_MSG AMLOGIC_INCLUDE_DIR AMCODEC_LIBRARY)
|
||||
|
||||
set(AMLOGIC_LIBRARIES ${AMCODEC_LIBRARY} ${AMADEC_LIBRARY} ${AMAVUTILS_LIBRARY})
|
||||
set(AMLOGIC_LIBRARIES ${AMCODEC_LIBRARY})
|
||||
set(AMLOGIC_INCLUDE_DIRS ${AMLOGIC_INCLUDE_DIR})
|
||||
|
@ -28,28 +28,9 @@ find_library(BCM_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(Broadcom DEFAULT_MSG BROADCOM_INCLUDE_DIR VCOS_LIBRARY VCHIQ_LIBRARY OPENMAXIL_LIBRARY BCM_HOST_LIBRARY)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Broadcom-OMX 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_MMAL_LIBRARIES ${BCM_HOST_LIBRARY} ${VCOS_LIBRARY} ${MMAL_CORE_LIBRARY} ${MMAL_UTIL_LIBRARY} ${MMAL_VC_CLIENT_LIBRARY})
|
||||
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)
|
@ -1,51 +1,77 @@
|
||||
# - Try to find LIBUUID
|
||||
# Find LIBUUID headers, libraries and the answer to all questions.
|
||||
# CMake - Cross Platform Makefile Generator
|
||||
# Copyright 2000-2024 Kitware, Inc. and Contributors
|
||||
# All rights reserved.
|
||||
#
|
||||
# LIBUUID_FOUND True if libuuid got found
|
||||
# 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.
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See
|
||||
# https://cmake.org/licensing for details.
|
||||
#
|
||||
#[=======================================================================[.rst:
|
||||
FindLibUUID
|
||||
------------
|
||||
|
||||
INCLUDE( FindPkgConfig )
|
||||
Find LibUUID include directory and library.
|
||||
|
||||
IF ( LibUuid_FIND_REQUIRED )
|
||||
SET( _pkgconfig_REQUIRED "REQUIRED" )
|
||||
ELSE( LibUuid_FIND_REQUIRED )
|
||||
SET( _pkgconfig_REQUIRED "" )
|
||||
ENDIF ( LibUuid_FIND_REQUIRED )
|
||||
Imported Targets
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
IF ( LIBUUID_MIN_VERSION )
|
||||
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 )
|
||||
An :ref:`imported target <Imported targets>` named
|
||||
``LibUUID::LibUUID`` is provided if LibUUID has been found.
|
||||
|
||||
Result Variables
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
IF( NOT LIBUUID_FOUND AND NOT PKG_CONFIG_FOUND )
|
||||
FIND_PATH( LIBUUID_INCLUDE_DIRS uuid/uuid.h )
|
||||
FIND_LIBRARY( LIBUUID_LIBRARIES uuid)
|
||||
This module defines the following variables:
|
||||
|
||||
# Report results
|
||||
IF ( LIBUUID_LIBRARIES AND LIBUUID_INCLUDE_DIRS )
|
||||
SET( LIBUUID_FOUND 1 )
|
||||
IF ( NOT LIBUUID_FIND_QUIETLY )
|
||||
MESSAGE( STATUS "Found libuuid: ${LIBUUID_LIBRARIES}" )
|
||||
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 )
|
||||
``LibUUID_FOUND``
|
||||
True if LibUUID was found, false otherwise.
|
||||
``LibUUID_INCLUDE_DIRS``
|
||||
Include directories needed to include LibUUID headers.
|
||||
``LibUUID_LIBRARIES``
|
||||
Libraries needed to link to LibUUID.
|
||||
|
||||
MARK_AS_ADVANCED( LIBUUID_LIBRARIES LIBUUID_INCLUDE_DIRS )
|
||||
Cache Variables
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
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()
|
||||
|
41
cmake/FindMMAL.cmake
Normal file
41
cmake/FindMMAL.cmake
Normal file
@ -0,0 +1,41 @@
|
||||
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})
|
@ -99,9 +99,9 @@ By default, 1392 is used on LAN and 1024 on WAN.
|
||||
=item B<-codec> [I<CODEC>]
|
||||
|
||||
Select codec to use.
|
||||
Can be 'auto', 'h264', 'h265' or 'hevc'.
|
||||
Not all video decoders do support H.265/HEVC.
|
||||
Will still use H.264 if server doesn't support HEVC.
|
||||
Can be 'auto', 'h264', 'h265', 'hevc', or 'av1'.
|
||||
Not all video decoders support H.265/HEVC or AV1.
|
||||
Will still use H.264 if server doesn't support HEVC or AV1.
|
||||
|
||||
=item B<-remote> [I<yes/no/auto>]
|
||||
|
||||
|
@ -23,9 +23,9 @@ target_link_libraries(gamestream moonlight-common)
|
||||
set_target_properties(gamestream 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_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(gamestream ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS})
|
||||
|
||||
|
@ -48,11 +48,16 @@
|
||||
|
||||
static char unique_id[UNIQUEID_CHARS+1];
|
||||
static X509 *cert;
|
||||
static char cert_hex[4096];
|
||||
static char cert_hex[8192];
|
||||
static EVP_PKEY *privateKey;
|
||||
|
||||
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) {
|
||||
char buffer[PATH_MAX];
|
||||
char* p = buffer;
|
||||
@ -87,16 +92,16 @@ static int load_unique_id(const char* keyDirectory) {
|
||||
snprintf(uniqueFilePath, PATH_MAX, "%s/%s", keyDirectory, UNIQUE_FILE_NAME);
|
||||
|
||||
FILE *fd = fopen(uniqueFilePath, "r");
|
||||
if (fd == NULL) {
|
||||
if (fd == NULL || fread(unique_id, UNIQUEID_CHARS, 1, fd) != UNIQUEID_CHARS) {
|
||||
snprintf(unique_id,UNIQUEID_CHARS+1,"0123456789ABCDEF");
|
||||
|
||||
if (fd)
|
||||
fclose(fd);
|
||||
fd = fopen(uniqueFilePath, "w");
|
||||
if (fd == NULL)
|
||||
return GS_FAILED;
|
||||
|
||||
fwrite(unique_id, UNIQUEID_CHARS, 1, fd);
|
||||
} else {
|
||||
fread(unique_id, UNIQUEID_CHARS, 1, fd);
|
||||
}
|
||||
fclose(fd);
|
||||
unique_id[UNIQUEID_CHARS] = 0;
|
||||
@ -159,109 +164,129 @@ static int load_cert(const char* keyDirectory) {
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
static int load_server_status(PSERVER_DATA server) {
|
||||
|
||||
static int load_serverinfo(PSERVER_DATA server, bool https) {
|
||||
uuid_t uuid;
|
||||
char uuid_str[37];
|
||||
|
||||
int ret;
|
||||
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) {
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
i = 0;
|
||||
do {
|
||||
char *pairedText = NULL;
|
||||
char *currentGameText = NULL;
|
||||
char *stateText = NULL;
|
||||
char *serverCodecModeSupportText = NULL;
|
||||
/* Fetch the HTTPS port if we don't have one yet */
|
||||
if (!server->httpsPort) {
|
||||
ret = load_serverinfo(server, false);
|
||||
if (ret != GS_OK)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = GS_INVALID;
|
||||
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse(uuid, uuid_str);
|
||||
|
||||
// Modern GFE versions don't allow serverinfo to be fetched over HTTPS if the client
|
||||
// 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);
|
||||
// Modern GFE versions don't allow serverinfo to be fetched over HTTPS if the client
|
||||
// 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.
|
||||
ret = GS_INVALID;
|
||||
for (i = 0; i < 2 && ret != GS_OK; i++) {
|
||||
ret = load_serverinfo(server, i == 0);
|
||||
}
|
||||
|
||||
if (ret == GS_OK && !server->unsupported) {
|
||||
if (server->serverMajorVersion > MAX_SUPPORTED_GFE_VERSION) {
|
||||
@ -283,6 +308,12 @@ static void bytes_to_hex(unsigned char *in, char *out, size_t len) {
|
||||
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) {
|
||||
int result = GS_FAILED;
|
||||
|
||||
@ -350,18 +381,42 @@ static bool verifySignature(const char *data, int dataLength, char *signature, i
|
||||
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 ret = GS_OK;
|
||||
char url[4096];
|
||||
uuid_t uuid;
|
||||
char uuid_str[37];
|
||||
char uuid_str[UUID_STRLEN];
|
||||
PHTTP_DATA data = http_create_data();
|
||||
if (data == NULL)
|
||||
return GS_OUT_OF_MEMORY;
|
||||
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse(uuid, uuid_str);
|
||||
snprintf(url, sizeof(url), "http://%s:47989/unpair?uniqueid=%s&uuid=%s", server->serverInfo.address, unique_id, uuid_str);
|
||||
snprintf(url, sizeof(url), "http://%s:%u/unpair?uniqueid=%s&uuid=%s", server->serverInfo.address, server->httpPort, unique_id, uuid_str);
|
||||
ret = http_request(url, data);
|
||||
|
||||
http_free_data(data);
|
||||
@ -371,29 +426,32 @@ int gs_unpair(PSERVER_DATA server) {
|
||||
int gs_pair(PSERVER_DATA server, char* pin) {
|
||||
int ret = GS_OK;
|
||||
char* result = NULL;
|
||||
char url[4096];
|
||||
size_t url_max_len = 16384;
|
||||
char* url = malloc(url_max_len);
|
||||
uuid_t uuid;
|
||||
char uuid_str[37];
|
||||
char uuid_str[UUID_STRLEN];
|
||||
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) {
|
||||
gs_error = "Already paired";
|
||||
return GS_WRONG_STATE;
|
||||
}
|
||||
|
||||
if (server->currentGame != 0) {
|
||||
gs_error = "The computer is currently in a game. You must close the game before pairing";
|
||||
return GS_WRONG_STATE;
|
||||
ret = GS_WRONG_STATE;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
unsigned char salt_data[16];
|
||||
char salt_hex[33];
|
||||
RAND_bytes(salt_data, 16);
|
||||
bytes_to_hex(salt_data, salt_hex, 16);
|
||||
char salt_hex[SIZEOF_AS_HEX_STR(salt_data)];
|
||||
RAND_bytes(salt_data, sizeof(salt_data));
|
||||
bytes_to_hex(salt_data, salt_hex, sizeof(salt_data));
|
||||
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse(uuid, uuid_str);
|
||||
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);
|
||||
PHTTP_DATA data = http_create_data();
|
||||
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);
|
||||
data = http_create_data();
|
||||
if (data == NULL)
|
||||
return GS_OUT_OF_MEMORY;
|
||||
else if ((ret = http_request(url, data)) != GS_OK)
|
||||
@ -415,43 +473,33 @@ int gs_pair(PSERVER_DATA server, char* pin) {
|
||||
if ((ret = xml_search(data->memory, data->size, "plaincert", &result)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (strlen(result)/2 > 8191) {
|
||||
gs_error = "Server certificate too big";
|
||||
ret = GS_FAILED;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
char plaincert[8192];
|
||||
for (int count = 0; count < strlen(result); count += 2) {
|
||||
sscanf(&result[count], "%2hhx", &plaincert[count / 2]);
|
||||
}
|
||||
plaincert[strlen(result)/2] = '\0';
|
||||
size_t plaincertlen = strlen(result)/2;
|
||||
plaincert = malloc(plaincertlen + 1);
|
||||
hex_to_bytes(result, plaincert, plaincertlen*2);
|
||||
plaincert[plaincertlen] = 0;
|
||||
|
||||
unsigned char salt_pin[20];
|
||||
unsigned char aes_key_hash[32];
|
||||
AES_KEY enc_key, dec_key;
|
||||
memcpy(salt_pin, salt_data, 16);
|
||||
memcpy(salt_pin+16, pin, 4);
|
||||
unsigned char salt_pin[sizeof(salt_data) + 4];
|
||||
unsigned char aes_key[32]; // Must fit SHA256
|
||||
memcpy(salt_pin, salt_data, sizeof(salt_data));
|
||||
memcpy(salt_pin+sizeof(salt_data), pin, 4);
|
||||
|
||||
int hash_length = server->serverMajorVersion >= 7 ? 32 : 20;
|
||||
if (server->serverMajorVersion >= 7)
|
||||
SHA256(salt_pin, 20, aes_key_hash);
|
||||
SHA256(salt_pin, sizeof(salt_pin), aes_key);
|
||||
else
|
||||
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);
|
||||
SHA1(salt_pin, sizeof(salt_pin), aes_key);
|
||||
|
||||
unsigned char challenge_data[16];
|
||||
unsigned char challenge_enc[16];
|
||||
char challenge_hex[33];
|
||||
RAND_bytes(challenge_data, 16);
|
||||
AES_encrypt(challenge_data, challenge_enc, &enc_key);
|
||||
bytes_to_hex(challenge_enc, challenge_hex, 16);
|
||||
unsigned char challenge_enc[sizeof(challenge_data)];
|
||||
char challenge_hex[SIZEOF_AS_HEX_STR(challenge_enc)];
|
||||
RAND_bytes(challenge_data, sizeof(challenge_data));
|
||||
encrypt(challenge_data, sizeof(challenge_data), aes_key, challenge_enc);
|
||||
bytes_to_hex(challenge_enc, challenge_hex, sizeof(challenge_enc));
|
||||
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse(uuid, uuid_str);
|
||||
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);
|
||||
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);
|
||||
if ((ret = http_request(url, data)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
@ -475,42 +523,42 @@ int gs_pair(PSERVER_DATA server, char* pin) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
char challenge_response_data_enc[48];
|
||||
char challenge_response_data[48];
|
||||
for (int count = 0; count < strlen(result); count += 2) {
|
||||
sscanf(&result[count], "%2hhx", &challenge_response_data_enc[count / 2]);
|
||||
char challenge_response_data_enc[64];
|
||||
char challenge_response_data[sizeof(challenge_response_data_enc)];
|
||||
|
||||
if (strlen(result) / 2 > sizeof(challenge_response_data_enc)) {
|
||||
gs_error = "Server challenge response too big";
|
||||
ret = GS_FAILED;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 48; i += 16) {
|
||||
AES_decrypt(&challenge_response_data_enc[i], &challenge_response_data[i], &dec_key);
|
||||
}
|
||||
hex_to_bytes(result, challenge_response_data_enc, strlen(result));
|
||||
decrypt(challenge_response_data_enc, sizeof(challenge_response_data_enc), aes_key, challenge_response_data);
|
||||
|
||||
char client_secret_data[16];
|
||||
RAND_bytes(client_secret_data, 16);
|
||||
RAND_bytes(client_secret_data, sizeof(client_secret_data));
|
||||
|
||||
const ASN1_BIT_STRING *asnSignature;
|
||||
X509_get0_signature(&asnSignature, NULL, cert);
|
||||
|
||||
char challenge_response[16 + 256 + 16];
|
||||
challenge_response = malloc(16 + asnSignature->length + sizeof(client_secret_data));
|
||||
char challenge_response_hash[32];
|
||||
char challenge_response_hash_enc[32];
|
||||
char challenge_response_hex[65];
|
||||
char challenge_response_hash_enc[sizeof(challenge_response_hash)];
|
||||
char challenge_response_hex[SIZEOF_AS_HEX_STR(challenge_response_hash_enc)];
|
||||
memcpy(challenge_response, challenge_response_data + hash_length, 16);
|
||||
memcpy(challenge_response + 16, asnSignature->data, 256);
|
||||
memcpy(challenge_response + 16 + 256, client_secret_data, 16);
|
||||
memcpy(challenge_response + 16, asnSignature->data, asnSignature->length);
|
||||
memcpy(challenge_response + 16 + asnSignature->length, client_secret_data, sizeof(client_secret_data));
|
||||
if (server->serverMajorVersion >= 7)
|
||||
SHA256(challenge_response, 16 + 256 + 16, challenge_response_hash);
|
||||
SHA256(challenge_response, 16 + asnSignature->length + sizeof(client_secret_data), challenge_response_hash);
|
||||
else
|
||||
SHA1(challenge_response, 16 + 256 + 16, challenge_response_hash);
|
||||
SHA1(challenge_response, 16 + asnSignature->length + sizeof(client_secret_data), challenge_response_hash);
|
||||
|
||||
for (int i = 0; i < 32; i += 16) {
|
||||
AES_encrypt(&challenge_response_hash[i], &challenge_response_hash_enc[i], &enc_key);
|
||||
}
|
||||
bytes_to_hex(challenge_response_hash_enc, challenge_response_hex, 32);
|
||||
encrypt(challenge_response_hash, sizeof(challenge_response_hash), aes_key, challenge_response_hash_enc);
|
||||
bytes_to_hex(challenge_response_hash_enc, challenge_response_hex, sizeof(challenge_response_hash_enc));
|
||||
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse(uuid, uuid_str);
|
||||
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);
|
||||
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);
|
||||
if ((ret = http_request(url, data)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
@ -534,12 +582,15 @@ int gs_pair(PSERVER_DATA server, char* pin) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
char pairing_secret[16 + 256];
|
||||
for (int count = 0; count < strlen(result); count += 2) {
|
||||
sscanf(&result[count], "%2hhx", &pairing_secret[count / 2]);
|
||||
size_t pairing_secret_len = strlen(result) / 2;
|
||||
if (pairing_secret_len <= 16) {
|
||||
ret = GS_INVALID;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!verifySignature(pairing_secret, 16, pairing_secret+16, 256, plaincert)) {
|
||||
pairing_secret = malloc(pairing_secret_len);
|
||||
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";
|
||||
ret = GS_FAILED;
|
||||
goto cleanup;
|
||||
@ -547,21 +598,21 @@ int gs_pair(PSERVER_DATA server, char* pin) {
|
||||
|
||||
unsigned char *signature = NULL;
|
||||
size_t s_len;
|
||||
if (sign_it(client_secret_data, 16, &signature, &s_len, privateKey) != GS_OK) {
|
||||
if (sign_it(client_secret_data, sizeof(client_secret_data), &signature, &s_len, privateKey) != GS_OK) {
|
||||
gs_error = "Failed to sign data";
|
||||
ret = GS_FAILED;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
char client_pairing_secret[16 + 256];
|
||||
char client_pairing_secret_hex[(16 + 256) * 2 + 1];
|
||||
memcpy(client_pairing_secret, client_secret_data, 16);
|
||||
memcpy(client_pairing_secret + 16, signature, 256);
|
||||
bytes_to_hex(client_pairing_secret, client_pairing_secret_hex, 16 + 256);
|
||||
client_pairing_secret = malloc(sizeof(client_secret_data) + s_len);
|
||||
client_pairing_secret_hex = malloc(LEN_AS_HEX_STR(sizeof(client_secret_data) + s_len));
|
||||
memcpy(client_pairing_secret, client_secret_data, sizeof(client_secret_data));
|
||||
memcpy(client_pairing_secret + sizeof(client_secret_data), signature, s_len);
|
||||
bytes_to_hex(client_pairing_secret, client_pairing_secret_hex, sizeof(client_secret_data) + s_len);
|
||||
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse(uuid, uuid_str);
|
||||
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);
|
||||
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);
|
||||
if ((ret = http_request(url, data)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
@ -580,7 +631,7 @@ int gs_pair(PSERVER_DATA server, char* pin) {
|
||||
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse(uuid, 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);
|
||||
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);
|
||||
if ((ret = http_request(url, data)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
@ -603,11 +654,23 @@ int gs_pair(PSERVER_DATA server, char* pin) {
|
||||
if (ret != GS_OK)
|
||||
gs_unpair(server);
|
||||
|
||||
if (result != NULL)
|
||||
free(result);
|
||||
free(url);
|
||||
free(plaincert);
|
||||
free(challenge_response);
|
||||
free(pairing_secret);
|
||||
free(client_pairing_secret);
|
||||
free(client_pairing_secret_hex);
|
||||
free(result);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@ -615,14 +678,14 @@ int gs_applist(PSERVER_DATA server, PAPP_LIST *list) {
|
||||
int ret = GS_OK;
|
||||
char url[4096];
|
||||
uuid_t uuid;
|
||||
char uuid_str[37];
|
||||
char uuid_str[UUID_STRLEN];
|
||||
PHTTP_DATA data = http_create_data();
|
||||
if (data == NULL)
|
||||
return GS_OUT_OF_MEMORY;
|
||||
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse(uuid, uuid_str);
|
||||
snprintf(url, sizeof(url), "https://%s:47984/applist?uniqueid=%s&uuid=%s", server->serverInfo.address, unique_id, uuid_str);
|
||||
snprintf(url, sizeof(url), "https://%s:%u/applist?uniqueid=%s&uuid=%s", server->serverInfo.address, server->httpsPort, unique_id, uuid_str);
|
||||
if (http_request(url, data) != GS_OK)
|
||||
ret = GS_IO_ERROR;
|
||||
else if (xml_status(data->memory, data->size) == GS_ERROR)
|
||||
@ -638,14 +701,12 @@ int gs_start_app(PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, b
|
||||
int ret = GS_OK;
|
||||
uuid_t uuid;
|
||||
char* result = NULL;
|
||||
char uuid_str[37];
|
||||
char uuid_str[UUID_STRLEN];
|
||||
|
||||
PDISPLAY_MODE mode = server->modes;
|
||||
bool correct_mode = false;
|
||||
bool supported_resolution = false;
|
||||
while (mode != NULL) {
|
||||
if (mode->width == config->width && mode->height == config->height) {
|
||||
supported_resolution = true;
|
||||
if (mode->refresh == config->fps)
|
||||
correct_mode = true;
|
||||
}
|
||||
@ -656,39 +717,34 @@ int gs_start_app(PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, b
|
||||
if (!correct_mode && !server->unsupported)
|
||||
return GS_NOT_SUPPORTED_MODE;
|
||||
|
||||
if (config->height >= 2160 && !server->supports4K)
|
||||
return GS_NOT_SUPPORTED_4K;
|
||||
RAND_bytes(config->remoteInputAesKey, sizeof(config->remoteInputAesKey));
|
||||
memset(config->remoteInputAesIv, 0, sizeof(config->remoteInputAesIv));
|
||||
|
||||
RAND_bytes(config->remoteInputAesKey, 16);
|
||||
memset(config->remoteInputAesIv, 0, 16);
|
||||
// GFE somehow doesn't like this totally legit random number, so we have to generate one
|
||||
RAND_bytes(config->remoteInputAesIv, 4);
|
||||
|
||||
srand(time(NULL));
|
||||
char url[4096];
|
||||
u_int32_t rikeyid = 0;
|
||||
memcpy(&rikeyid, config->remoteInputAesIv, 4);
|
||||
RAND_bytes(config->remoteInputAesIv, sizeof(rikeyid));
|
||||
memcpy(&rikeyid, config->remoteInputAesIv, sizeof(rikeyid));
|
||||
rikeyid = htonl(rikeyid);
|
||||
char rikey_hex[33];
|
||||
bytes_to_hex(config->remoteInputAesKey, rikey_hex, 16);
|
||||
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();
|
||||
if (data == NULL)
|
||||
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_unparse(uuid, uuid_str);
|
||||
int surround_info = SURROUNDAUDIOINFO_FROM_AUDIO_CONFIGURATION(config->audioConfiguration);
|
||||
if (server->currentGame == 0) {
|
||||
// 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 = config->fps > 60 ? 0 : config->fps;
|
||||
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, fps, sops, rikey_hex, rikeyid, localaudio, surround_info, gamepad_mask, gamepad_mask);
|
||||
} else
|
||||
snprintf(url, sizeof(url), "https://%s:47984/resume?uniqueid=%s&uuid=%s&rikey=%s&rikeyid=%d&surroundAudioInfo=%d", server->serverInfo.address, unique_id, uuid_str, rikey_hex, rikeyid, surround_info);
|
||||
|
||||
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",
|
||||
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,
|
||||
(config->supportedVideoFormats & VIDEO_FORMAT_MASK_10BIT) ? "&hdrMode=1&clientHdrCapVersion=0&clientHdrCapSupportedFlagsInUint32=0&clientHdrCapMetaDataId=NV_STATIC_METADATA_TYPE_1&clientHdrCapDisplayData=0x0x0x0x0x0x0x0x0x0x0" : "",
|
||||
LiGetLaunchUrlQueryParameters());
|
||||
if ((ret = http_request(url, data)) == GS_OK)
|
||||
server->currentGame = appId;
|
||||
else
|
||||
@ -696,7 +752,8 @@ int gs_start_app(PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, b
|
||||
|
||||
if ((ret = xml_status(data->memory, data->size) != GS_OK))
|
||||
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;
|
||||
|
||||
if (!strcmp(result, "0")) {
|
||||
@ -724,7 +781,7 @@ int gs_quit_app(PSERVER_DATA server) {
|
||||
int ret = GS_OK;
|
||||
char url[4096];
|
||||
uuid_t uuid;
|
||||
char uuid_str[37];
|
||||
char uuid_str[UUID_STRLEN];
|
||||
char* result = NULL;
|
||||
PHTTP_DATA data = http_create_data();
|
||||
if (data == NULL)
|
||||
@ -732,7 +789,7 @@ int gs_quit_app(PSERVER_DATA server) {
|
||||
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse(uuid, uuid_str);
|
||||
snprintf(url, sizeof(url), "https://%s:47984/cancel?uniqueid=%s&uuid=%s", server->serverInfo.address, unique_id, uuid_str);
|
||||
snprintf(url, sizeof(url), "https://%s:%u/cancel?uniqueid=%s&uuid=%s", server->serverInfo.address, server->httpsPort, unique_id, uuid_str);
|
||||
if ((ret = http_request(url, data)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
@ -754,7 +811,7 @@ int gs_quit_app(PSERVER_DATA server) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
int gs_init(PSERVER_DATA server, char *address, const char *keyDirectory, int log_level, bool unsupported) {
|
||||
int gs_init(PSERVER_DATA server, char *address, unsigned short httpPort, const char *keyDirectory, int log_level, bool unsupported) {
|
||||
mkdirtree(keyDirectory);
|
||||
if (load_unique_id(keyDirectory) != GS_OK)
|
||||
return GS_FAILED;
|
||||
@ -767,5 +824,7 @@ int gs_init(PSERVER_DATA server, char *address, const char *keyDirectory, int lo
|
||||
LiInitializeServerInformation(&server->serverInfo);
|
||||
server->serverInfo.address = address;
|
||||
server->unsupported = unsupported;
|
||||
server->httpPort = httpPort ? httpPort : 47989;
|
||||
server->httpsPort = 0; /* Populated by load_server_status() */
|
||||
return load_server_status(server);
|
||||
}
|
||||
|
@ -31,16 +31,18 @@
|
||||
typedef struct _SERVER_DATA {
|
||||
char* gpuType;
|
||||
bool paired;
|
||||
bool supports4K;
|
||||
bool unsupported;
|
||||
bool isNvidiaSoftware;
|
||||
int currentGame;
|
||||
int serverMajorVersion;
|
||||
char* gsVersion;
|
||||
PDISPLAY_MODE modes;
|
||||
SERVER_INFORMATION serverInfo;
|
||||
unsigned short httpPort;
|
||||
unsigned short httpsPort;
|
||||
} SERVER_DATA, *PSERVER_DATA;
|
||||
|
||||
int gs_init(PSERVER_DATA server, char* address, const char *keyDirectory, int logLevel, bool unsupported);
|
||||
int gs_init(PSERVER_DATA server, char* address, unsigned short httpPort, 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_applist(PSERVER_DATA server, PAPP_LIST *app_list);
|
||||
int gs_unpair(PSERVER_DATA server);
|
||||
|
@ -33,6 +33,11 @@
|
||||
|
||||
static AvahiSimplePoll *simple_poll = NULL;
|
||||
|
||||
struct cb_ctx {
|
||||
char* address;
|
||||
unsigned short* port;
|
||||
};
|
||||
|
||||
static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {
|
||||
if (state == AVAHI_CLIENT_FAILURE) {
|
||||
gs_error = "Server connection failure";
|
||||
@ -43,12 +48,14 @@ 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) {
|
||||
if (event == AVAHI_RESOLVER_FOUND) {
|
||||
if (userdata != NULL) {
|
||||
avahi_address_snprint(userdata, AVAHI_ADDRESS_STR_MAX, address);
|
||||
struct cb_ctx* ctx = userdata;
|
||||
avahi_address_snprint(ctx->address, AVAHI_ADDRESS_STR_MAX, address);
|
||||
*ctx->port = port;
|
||||
avahi_simple_poll_quit(simple_poll);
|
||||
} else {
|
||||
char strAddress[AVAHI_ADDRESS_STR_MAX];
|
||||
avahi_address_snprint(strAddress, sizeof(strAddress), address);
|
||||
printf(" %s (%s)\n", host_name, strAddress);
|
||||
printf(" %s (%s:%u)\n", host_name, strAddress, port);
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,7 +80,7 @@ static void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, Avah
|
||||
}
|
||||
}
|
||||
|
||||
void gs_discover_server(char* dest) {
|
||||
void gs_discover_server(char* dest, unsigned short* port) {
|
||||
AvahiClient *client = NULL;
|
||||
AvahiServiceBrowser *sb = NULL;
|
||||
|
||||
@ -89,7 +96,10 @@ void gs_discover_server(char* dest) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET, "_nvstream._tcp", NULL, 0, browse_callback, dest))) {
|
||||
struct cb_ctx ctx;
|
||||
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";
|
||||
goto cleanup;
|
||||
}
|
||||
|
@ -21,4 +21,4 @@
|
||||
|
||||
#define MAX_ADDRESS_SIZE 40
|
||||
|
||||
void gs_discover_server(char* dest);
|
||||
void gs_discover_server(char* dest, unsigned short* port);
|
||||
|
@ -26,9 +26,6 @@
|
||||
|
||||
static CURL *curl;
|
||||
|
||||
static const char *pCertFile = "./client.pem";
|
||||
static const char *pKeyFile = "./key.pem";
|
||||
|
||||
static bool debug;
|
||||
|
||||
static size_t _write_curl(void *contents, size_t size, size_t nmemb, void *userp)
|
||||
@ -54,10 +51,10 @@ int http_init(const char* keyDirectory, int logLevel) {
|
||||
return GS_FAILED;
|
||||
|
||||
char certificateFilePath[4096];
|
||||
sprintf(certificateFilePath, "%s/%s", keyDirectory, CERTIFICATE_FILE_NAME);
|
||||
snprintf(certificateFilePath, sizeof(certificateFilePath), "%s/%s", keyDirectory, CERTIFICATE_FILE_NAME);
|
||||
|
||||
char keyFilePath[4096];
|
||||
sprintf(&keyFilePath[0], "%s/%s", keyDirectory, KEY_FILE_NAME);
|
||||
snprintf(keyFilePath, sizeof(keyFilePath), "%s/%s", keyDirectory, KEY_FILE_NAME);
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
|
||||
curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1L);
|
||||
@ -76,6 +73,9 @@ int http_init(const char* keyDirectory, int logLevel) {
|
||||
int http_request(char* url, PHTTP_DATA data) {
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, data);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
#ifdef __FreeBSD__
|
||||
curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1);
|
||||
#endif
|
||||
|
||||
if (debug)
|
||||
printf("Request %s\n", url);
|
||||
|
@ -39,7 +39,6 @@ CERT_KEY_PAIR mkcert_generate() {
|
||||
EVP_PKEY *pkey = NULL;
|
||||
PKCS12 *p12 = NULL;
|
||||
|
||||
CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
|
||||
bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
|
||||
|
||||
OpenSSL_add_all_algorithms();
|
||||
@ -81,71 +80,47 @@ void mkcert_save(const char* certFile, const char* p12File, const char* keyPairF
|
||||
}
|
||||
|
||||
int mkcert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int years) {
|
||||
X509 *x;
|
||||
EVP_PKEY *pk;
|
||||
RSA *rsa;
|
||||
X509_NAME *name = NULL;
|
||||
EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
|
||||
EVP_PKEY_keygen_init(ctx);
|
||||
EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits);
|
||||
|
||||
if (*pkeyp == NULL) {
|
||||
if ((pk=EVP_PKEY_new()) == NULL) {
|
||||
abort();
|
||||
return(0);
|
||||
}
|
||||
} else {
|
||||
pk = *pkeyp;
|
||||
}
|
||||
// pk must be initialized on input
|
||||
EVP_PKEY *pk = NULL;;
|
||||
EVP_PKEY_keygen(ctx, &pk);
|
||||
|
||||
if (*x509p == NULL) {
|
||||
if ((x = X509_new()) == NULL) {
|
||||
goto err;
|
||||
}
|
||||
} else {
|
||||
x = *x509p;
|
||||
}
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
|
||||
if ((rsa = RSA_new()) == NULL)
|
||||
goto err;
|
||||
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));
|
||||
|
||||
BIGNUM* bne = BN_new();
|
||||
if (bne == NULL) {
|
||||
abort();
|
||||
goto err;
|
||||
}
|
||||
X509_gmtime_adj(before, 0);
|
||||
X509_gmtime_adj(after, 60 * 60 * 24 * 365 * years);
|
||||
|
||||
BN_set_word(bne, RSA_F4);
|
||||
if (RSA_generate_key_ex(rsa, bits, bne, NULL) == 0) {
|
||||
abort();
|
||||
goto err;
|
||||
}
|
||||
X509_set1_notBefore(cert, before);
|
||||
X509_set1_notAfter(cert, after);
|
||||
|
||||
if (!EVP_PKEY_assign_RSA(pk, rsa)) {
|
||||
abort();
|
||||
goto err;
|
||||
}
|
||||
ASN1_STRING_free(before);
|
||||
ASN1_STRING_free(after);
|
||||
#endif
|
||||
|
||||
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);
|
||||
X509_set_pubkey(cert, 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* 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);
|
||||
|
||||
/* Its self signed so set the issuer name to be the same as the
|
||||
* subject.
|
||||
*/
|
||||
X509_set_issuer_name(x, name);
|
||||
|
||||
if (!X509_sign(x, pk, EVP_sha256())) {
|
||||
if (!X509_sign(cert, pk, EVP_sha256())) {
|
||||
goto err;
|
||||
}
|
||||
|
||||
*x509p = x;
|
||||
*x509p = cert;
|
||||
*pkeyp = pk;
|
||||
|
||||
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) {
|
||||
const char naluHeader[] = {0x00, 0x00, 0x00, 0x01};
|
||||
int start_len = sps->data[2] == 0x01 ? 3 : 4;
|
||||
|
||||
read_nal_unit(h264_stream, sps->data+4, sps->length-4);
|
||||
read_nal_unit(h264_stream, sps->data+start_len, sps->length-start_len);
|
||||
|
||||
// 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
|
||||
@ -76,8 +76,8 @@ void gs_sps_fix(PLENTRY sps, int flags, uint8_t* out_buf, uint32_t* out_offset)
|
||||
h264_stream->sps->vui.max_bits_per_mb_denom = 1;
|
||||
}
|
||||
|
||||
memcpy(out_buf+*out_offset, naluHeader, 4);
|
||||
*out_offset += 4;
|
||||
memcpy(out_buf+*out_offset, sps->data, start_len);
|
||||
*out_offset += start_len;
|
||||
|
||||
*out_offset += write_nal_unit(h264_stream, out_buf+*out_offset, 128);
|
||||
}
|
||||
|
@ -25,8 +25,6 @@
|
||||
|
||||
#define STATUS_OK 200
|
||||
|
||||
static XML_Parser parser;
|
||||
|
||||
struct xml_query {
|
||||
char *memory;
|
||||
size_t size;
|
||||
|
@ -7,6 +7,10 @@
|
||||
#height = 720
|
||||
#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
|
||||
## Set to -1 to enable default
|
||||
## 20Mbps (20000) for 1080p (60 fps)
|
||||
|
@ -57,8 +57,8 @@ static int alsa_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGUR
|
||||
|
||||
snd_pcm_hw_params_t *hw_params;
|
||||
snd_pcm_sw_params_t *sw_params;
|
||||
snd_pcm_uframes_t period_size = samplesPerFrame * FRAME_BUFFER;
|
||||
snd_pcm_uframes_t buffer_size = 2 * period_size;
|
||||
snd_pcm_uframes_t period_size = (opusConfig->sampleRate * 20) / 1000; // 20 ms period
|
||||
snd_pcm_uframes_t buffer_size = 3 * period_size; // 60 ms buffer
|
||||
unsigned int sampleRate = opusConfig->sampleRate;
|
||||
|
||||
char* audio_device = (char*) context;
|
||||
@ -84,7 +84,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_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_start_threshold(handle, sw_params, 1));
|
||||
CHECK_RETURN(snd_pcm_sw_params_set_start_threshold(handle, sw_params, period_size));
|
||||
CHECK_RETURN(snd_pcm_sw_params(handle, sw_params));
|
||||
snd_pcm_sw_params_free(sw_params);
|
||||
|
||||
@ -115,14 +115,17 @@ static void alsa_renderer_decode_and_play_sample(char* data, int length) {
|
||||
int decodeLen = opus_multistream_decode(decoder, data, length, pcmBuffer, samplesPerFrame, 0);
|
||||
if (decodeLen > 0) {
|
||||
int rc = snd_pcm_writei(handle, pcmBuffer, decodeLen);
|
||||
if (rc == -EPIPE)
|
||||
snd_pcm_recover(handle, rc, 1);
|
||||
if (rc < 0) {
|
||||
rc = snd_pcm_recover(handle, rc, 0);
|
||||
if (rc == 0)
|
||||
rc = snd_pcm_writei(handle, pcmBuffer, decodeLen);
|
||||
}
|
||||
|
||||
if (rc<0)
|
||||
printf("Alsa error from writei: %d\n", rc);
|
||||
else if (decodeLen != rc)
|
||||
printf("Alsa shortm write, write %d frames\n", rc);
|
||||
} else {
|
||||
} else if (decodeLen < 0) {
|
||||
printf("Opus error from decode: %d\n", decodeLen);
|
||||
}
|
||||
}
|
||||
|
@ -21,8 +21,6 @@
|
||||
|
||||
#include <Limelight.h>
|
||||
|
||||
#define FRAME_BUFFER 12
|
||||
|
||||
#ifdef HAVE_ALSA
|
||||
extern AUDIO_RENDERER_CALLBACKS audio_callbacks_alsa;
|
||||
#endif
|
||||
@ -33,3 +31,6 @@ extern AUDIO_RENDERER_CALLBACKS audio_callbacks_sdl;
|
||||
extern AUDIO_RENDERER_CALLBACKS audio_callbacks_pulse;
|
||||
bool audio_pulse_init(char* audio_device);
|
||||
#endif
|
||||
#ifdef __FreeBSD__
|
||||
extern AUDIO_RENDERER_CALLBACKS audio_callbacks_oss;
|
||||
#endif
|
||||
|
@ -34,7 +34,7 @@ static int channelCount;
|
||||
static int samplesPerFrame;
|
||||
|
||||
static int omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int arFlags) {
|
||||
int rc, error;
|
||||
int rc;
|
||||
OMX_ERRORTYPE err;
|
||||
unsigned char omxMapping[AUDIO_CONFIGURATION_MAX_CHANNEL_COUNT];
|
||||
char* componentName = "audio_render";
|
||||
@ -203,7 +203,7 @@ static void omx_renderer_decode_and_play_sample(char* data, int length) {
|
||||
if (r != OMX_ErrorNone) {
|
||||
fprintf(stderr, "Empty buffer error\n");
|
||||
}
|
||||
} else {
|
||||
} else if (decodeLen < 0) {
|
||||
printf("Opus error from decode: %d\n", decodeLen);
|
||||
}
|
||||
}
|
||||
|
105
src/audio/oss.c
Normal file
105
src/audio/oss.c
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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
|
@ -101,7 +101,7 @@ static void pulse_renderer_decode_and_play_sample(char* data, int length) {
|
||||
|
||||
if (rc<0)
|
||||
printf("Pulseaudio error: %s\n", pa_strerror(error));
|
||||
} else {
|
||||
} else if (decodeLen < 0) {
|
||||
printf("Opus error from decode: %d\n", decodeLen);
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ static void sdl_renderer_decode_and_play_sample(char* data, int length) {
|
||||
int decodeLen = opus_multistream_decode(decoder, data, length, pcmBuffer, samplesPerFrame, 0);
|
||||
if (decodeLen > 0) {
|
||||
SDL_QueueAudio(dev, pcmBuffer, decodeLen * channelCount * sizeof(short));
|
||||
} else {
|
||||
} else if (decodeLen < 0) {
|
||||
printf("Opus error from decode: %d\n", decodeLen);
|
||||
}
|
||||
}
|
||||
|
77
src/config.c
77
src/config.c
@ -17,8 +17,10 @@
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "platform.h"
|
||||
#include "config.h"
|
||||
#include "util.h"
|
||||
#include "cpu.h"
|
||||
|
||||
#include "input/evdev.h"
|
||||
#include "audio/audio.h"
|
||||
@ -72,6 +74,9 @@ static struct option long_options[] = {
|
||||
{"verbose", 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},
|
||||
};
|
||||
|
||||
@ -222,6 +227,8 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) {
|
||||
config->codec = CODEC_H264;
|
||||
else if (strcasecmp(value, "h265") == 0 || strcasecmp(value, "hevc") == 0)
|
||||
config->codec = CODEC_HEVC;
|
||||
else if (strcasecmp(value, "av1") == 0)
|
||||
config->codec = CODEC_AV1;
|
||||
break;
|
||||
case 'y':
|
||||
config->unsupported = false;
|
||||
@ -244,6 +251,15 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) {
|
||||
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:
|
||||
if (config->action == NULL)
|
||||
config->action = value;
|
||||
@ -332,21 +348,20 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) {
|
||||
config->stream.packetSize = 1392;
|
||||
config->stream.streamingRemotely = STREAM_CFG_AUTO;
|
||||
config->stream.audioConfiguration = AUDIO_CONFIGURATION_STEREO;
|
||||
config->stream.supportsHevc = false;
|
||||
config->stream.encryptionFlags = ENCFLG_AUDIO;
|
||||
config->stream.supportedVideoFormats = SCM_H264;
|
||||
|
||||
#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")) {
|
||||
config->stream.encryptionFlags = ENCFLG_NONE;
|
||||
printf("Disabling audio encryption on low performance CPU\n");
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
#endif
|
||||
|
||||
config->debug_level = 0;
|
||||
config->platform = "auto";
|
||||
@ -364,6 +379,9 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) {
|
||||
config->mouse_emulation = true;
|
||||
config->rotate = 0;
|
||||
config->codec = CODEC_UNSPECIFIED;
|
||||
config->hdr = false;
|
||||
config->pin = 0;
|
||||
config->port = 47989;
|
||||
|
||||
config->inputsCount = 0;
|
||||
config->mapping = get_path("gamecontrollerdb.txt", getenv("XDG_DATA_DIRS"));
|
||||
@ -381,7 +399,7 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) {
|
||||
} else {
|
||||
int option_index = 0;
|
||||
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:xy4", long_options, &option_index)) != -1) {
|
||||
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) {
|
||||
parse_argument(c, optarg, config);
|
||||
}
|
||||
}
|
||||
@ -393,19 +411,32 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) {
|
||||
struct passwd *pw = getpwuid(getuid());
|
||||
const char *dir;
|
||||
if ((dir = getenv("XDG_CACHE_DIR")) != NULL)
|
||||
sprintf(config->key_dir, "%s" MOONLIGHT_PATH, dir);
|
||||
snprintf(config->key_dir, sizeof(config->key_dir), "%s" MOONLIGHT_PATH, dir);
|
||||
else if ((dir = getenv("HOME")) != NULL)
|
||||
sprintf(config->key_dir, "%s" DEFAULT_CACHE_DIR MOONLIGHT_PATH, dir);
|
||||
snprintf(config->key_dir, sizeof(config->key_dir), "%s" DEFAULT_CACHE_DIR MOONLIGHT_PATH, dir);
|
||||
else
|
||||
sprintf(config->key_dir, "%s" DEFAULT_CACHE_DIR MOONLIGHT_PATH, pw->pw_dir);
|
||||
snprintf(config->key_dir, sizeof(config->key_dir), "%s" DEFAULT_CACHE_DIR MOONLIGHT_PATH, pw->pw_dir);
|
||||
}
|
||||
|
||||
if (config->stream.bitrate == -1) {
|
||||
if (config->stream.height >= 1080 && config->stream.fps >= 60)
|
||||
config->stream.bitrate = 20000;
|
||||
else if (config->stream.height >= 1080 || config->stream.fps >= 60)
|
||||
config->stream.bitrate = 10000;
|
||||
else
|
||||
config->stream.bitrate = 5000;
|
||||
// This table prefers 16:10 resolutions because they are
|
||||
// only slightly more pixels than the 16:9 equivalents, so
|
||||
// we don't want to bump those 16:10 resolutions up to the
|
||||
// next 16:9 slot.
|
||||
|
||||
if (config->stream.width * config->stream.height <= 640 * 360) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,8 +23,6 @@
|
||||
|
||||
#define MAX_INPUTS 6
|
||||
|
||||
enum codecs { CODEC_UNSPECIFIED, CODEC_H264, CODEC_HEVC };
|
||||
|
||||
typedef struct _CONFIGURATION {
|
||||
STREAM_CONFIGURATION stream;
|
||||
int debug_level;
|
||||
@ -47,6 +45,9 @@ typedef struct _CONFIGURATION {
|
||||
char* inputs[MAX_INPUTS];
|
||||
int inputsCount;
|
||||
enum codecs codec;
|
||||
bool hdr;
|
||||
int pin;
|
||||
unsigned short port;
|
||||
} CONFIGURATION, *PCONFIGURATION;
|
||||
|
||||
extern bool inputAdded;
|
||||
|
@ -23,13 +23,21 @@
|
||||
#include <stdarg.h>
|
||||
#include <signal.h>
|
||||
|
||||
#ifdef HAVE_SDL
|
||||
#include <SDL.h>
|
||||
#endif
|
||||
|
||||
pthread_t main_thread_id = 0;
|
||||
bool connection_debug;
|
||||
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");
|
||||
@ -48,6 +56,12 @@ static void connection_terminated(int errorCode) {
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef HAVE_SDL
|
||||
SDL_Event event;
|
||||
event.type = SDL_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
#endif
|
||||
|
||||
if (main_thread_id != 0)
|
||||
pthread_kill(main_thread_id, SIGTERM);
|
||||
}
|
||||
@ -64,6 +78,21 @@ static void rumble(unsigned short controllerNumber, unsigned short lowFreqMotor,
|
||||
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) {
|
||||
switch (status) {
|
||||
case CONN_STATUS_OKAY:
|
||||
@ -83,5 +112,9 @@ CONNECTION_LISTENER_CALLBACKS connection_callbacks = {
|
||||
.connectionTerminated = connection_terminated,
|
||||
.logMessage = connection_log_message,
|
||||
.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,3 +26,6 @@ extern CONNECTION_LISTENER_CALLBACKS connection_callbacks;
|
||||
extern pthread_t main_thread_id;
|
||||
extern bool connection_debug;
|
||||
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
Normal file
121
src/cpu.c
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* 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
Normal file
21
src/cpu.h
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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);
|
@ -90,7 +90,7 @@ void cec_init() {
|
||||
g_iface.init_video_standalone(g_iface.connection);
|
||||
|
||||
cec_adapter devices[10];
|
||||
int8_t iDevicesFound = g_iface.find_adapters(g_iface.connection, devices, sizeof(devices) / sizeof(devices), NULL);
|
||||
int8_t iDevicesFound = g_iface.find_adapters(g_iface.connection, devices, sizeof(devices) / sizeof(devices[0]), NULL);
|
||||
|
||||
if (iDevicesFound <= 0) {
|
||||
fprintf(stderr, "No CEC devices found\n");
|
||||
|
@ -38,7 +38,11 @@
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#ifdef __linux__
|
||||
#include <endian.h>
|
||||
#else
|
||||
#include <sys/endian.h>
|
||||
#endif
|
||||
#include <math.h>
|
||||
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
@ -66,14 +70,19 @@ struct input_device {
|
||||
int hats_state[3][2];
|
||||
int fd;
|
||||
char modifiers;
|
||||
__s32 mouseDeltaX, mouseDeltaY, mouseScroll;
|
||||
#ifdef __linux__
|
||||
__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;
|
||||
int haptic_effect_id;
|
||||
int buttonFlags;
|
||||
char leftTrigger, rightTrigger;
|
||||
unsigned char leftTrigger, rightTrigger;
|
||||
short leftStickX, leftStickY;
|
||||
short rightStickX, rightStickY;
|
||||
bool gamepadModified;
|
||||
@ -105,6 +114,9 @@ static const int hat_constants[3][3] = {{HAT_UP | HAT_LEFT, HAT_UP, HAT_UP | HAT
|
||||
// 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 int numDevices = 0;
|
||||
static int assignedControllerIds = 0;
|
||||
@ -167,6 +179,10 @@ static void evdev_remove(int devindex) {
|
||||
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)
|
||||
memcpy(&devices[devindex], &devices[numDevices], sizeof(struct input_device));
|
||||
}
|
||||
@ -189,7 +205,7 @@ 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;
|
||||
}
|
||||
|
||||
static char evdev_convert_value_byte(struct input_event *ev, struct input_device *dev, struct input_abs_parms *parms, char halfaxis) {
|
||||
static unsigned char evdev_convert_value_byte(struct input_event *ev, struct input_device *dev, struct input_abs_parms *parms, char halfaxis) {
|
||||
if (parms->max == 0 && parms->min == 0) {
|
||||
fprintf(stderr, "Axis not found: %d\n", ev->code);
|
||||
return 0;
|
||||
@ -240,12 +256,70 @@ void *HandleMouseEmulation(void* param)
|
||||
deltaY = pow((float)rawY / 32767.0f * MOUSE_EMULATION_MOTION_MULTIPLIER, 3);
|
||||
|
||||
// Enforce deadzones
|
||||
deltaX = abs(deltaX) > MOUSE_EMULATION_DEADZONE ? deltaX - MOUSE_EMULATION_DEADZONE : 0;
|
||||
deltaY = abs(deltaY) > MOUSE_EMULATION_DEADZONE ? deltaY - MOUSE_EMULATION_DEADZONE : 0;
|
||||
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) {
|
||||
@ -271,13 +345,17 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev)
|
||||
dev->mouseDeltaX = 0;
|
||||
dev->mouseDeltaY = 0;
|
||||
}
|
||||
if (dev->mouseScroll != 0) {
|
||||
LiSendScrollEvent(dev->mouseScroll);
|
||||
dev->mouseScroll = 0;
|
||||
if (dev->mouseVScroll != 0) {
|
||||
LiSendScrollEvent(dev->mouseVScroll);
|
||||
dev->mouseVScroll = 0;
|
||||
}
|
||||
if (dev->mouseHScroll != 0) {
|
||||
LiSendHScrollEvent(dev->mouseHScroll);
|
||||
dev->mouseHScroll = 0;
|
||||
}
|
||||
if (dev->gamepadModified) {
|
||||
if (dev->controllerId < 0) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int i = 0; i < MAX_GAMEPADS; i++) {
|
||||
if ((assignedControllerIds & (1 << i)) == 0) {
|
||||
assignedControllerIds |= (1 << i);
|
||||
dev->controllerId = i;
|
||||
@ -288,6 +366,9 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev)
|
||||
//Use id 0 when too many gamepads are connected
|
||||
if (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.
|
||||
if (dev->mouseEmulation == false)
|
||||
@ -337,7 +418,7 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev)
|
||||
LiSendKeyboardEvent(code, ev->value?KEY_ACTION_DOWN:KEY_ACTION_UP, dev->modifiers);
|
||||
} else {
|
||||
int mouseCode = 0;
|
||||
short gamepadCode = 0;
|
||||
int gamepadCode = 0;
|
||||
int index = dev->key_map[ev->code];
|
||||
|
||||
switch (ev->code) {
|
||||
@ -411,6 +492,18 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev)
|
||||
gamepadCode = BACK_FLAG;
|
||||
else if (index == dev->map->btn_guide)
|
||||
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) {
|
||||
@ -481,8 +574,11 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev)
|
||||
case REL_Y:
|
||||
dev->mouseDeltaY = ev->value;
|
||||
break;
|
||||
case REL_HWHEEL:
|
||||
dev->mouseHScroll = ev->value;
|
||||
break;
|
||||
case REL_WHEEL:
|
||||
dev->mouseScroll = ev->value;
|
||||
dev->mouseVScroll = ev->value;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@ -740,8 +836,10 @@ void evdev_create(const char* device, struct mapping* mappings, bool verbose, in
|
||||
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_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) ||
|
||||
@ -922,9 +1020,9 @@ void evdev_map(char* device) {
|
||||
for (int i = 0; i < 16; i++)
|
||||
buf += sprintf(buf, "%02x", ((unsigned char*) guid)[i]);
|
||||
|
||||
struct mapping map;
|
||||
strncpy(map.name, libevdev_get_name(evdev), sizeof(map.name));
|
||||
strncpy(map.guid, str_guid, sizeof(map.guid));
|
||||
struct mapping map = {0};
|
||||
strncpy(map.name, name, sizeof(map.name) - 1);
|
||||
strncpy(map.guid, str_guid, sizeof(map.guid) - 1);
|
||||
|
||||
libevdev_free(evdev);
|
||||
close(fd);
|
||||
|
@ -38,8 +38,8 @@ struct mapping* mapping_parse(char* mapping) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
strncpy(map->guid, guid, sizeof(map->guid));
|
||||
strncpy(map->name, name, sizeof(map->name));
|
||||
strncpy(map->guid, guid, sizeof(map->guid) - 1);
|
||||
strncpy(map->name, name, sizeof(map->name) - 1);
|
||||
|
||||
/* 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));
|
||||
@ -58,7 +58,7 @@ struct mapping* mapping_parse(char* mapping) {
|
||||
value++;
|
||||
}
|
||||
if (strcmp("platform", key) == 0)
|
||||
strncpy(map->platform, value, sizeof(map->platform));
|
||||
strncpy(map->platform, value, sizeof(map->platform) - 1);
|
||||
else if (sscanf(value, "b%d", &int_value) == 1) {
|
||||
if (strcmp("a", key) == 0)
|
||||
map->btn_a = int_value;
|
||||
@ -94,6 +94,18 @@ struct mapping* mapping_parse(char* mapping) {
|
||||
map->btn_lefttrigger = int_value;
|
||||
else if (strcmp("righttrigger", key) == 0)
|
||||
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) {
|
||||
if (strcmp("leftx", key) == 0) {
|
||||
map->abs_leftx = int_value;
|
||||
@ -140,6 +152,8 @@ struct mapping* mapping_parse(char* mapping) {
|
||||
map->hat_dpdown = int_value;
|
||||
map->hat_dir_dpdown = direction_value;
|
||||
}
|
||||
} else if (strcmp("crc", key) == 0) {
|
||||
/* CRC is not supported */
|
||||
} else
|
||||
fprintf(stderr, "Can't map (%s)\n", option);
|
||||
} else if (ret == 0 && option[0] != '\n')
|
||||
@ -213,5 +227,11 @@ void mapping_print(struct mapping* map) {
|
||||
print_abs("righttrigger", map->abs_righttrigger);
|
||||
print_btn("lefttrigger", map->btn_lefttrigger);
|
||||
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");
|
||||
}
|
||||
|
@ -28,9 +28,9 @@ struct mapping {
|
||||
|
||||
bool reverse_leftx, reverse_lefty;
|
||||
bool reverse_rightx, reverse_righty;
|
||||
bool halfaxis_lefttrigger, halfaxis_righttrigger;
|
||||
bool halfaxis_dpright, halfaxis_dpleft;
|
||||
bool halfaxis_dpup, halfaxis_dpdown;
|
||||
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;
|
||||
@ -45,6 +45,9 @@ struct mapping {
|
||||
short btn_back, btn_start, btn_guide;
|
||||
short btn_leftstick, btn_rightstick;
|
||||
short btn_leftshoulder, btn_rightshoulder;
|
||||
short btn_misc1;
|
||||
short btn_paddle1, btn_paddle2, btn_paddle3, btn_paddle4;
|
||||
short btn_touchpad;
|
||||
|
||||
short abs_lefttrigger, abs_righttrigger;
|
||||
short btn_lefttrigger, btn_righttrigger;
|
||||
|
669
src/input/sdl.c
669
src/input/sdl.c
@ -26,60 +26,406 @@
|
||||
#define QUIT_KEY SDLK_q
|
||||
#define QUIT_BUTTONS (PLAY_FLAG|BACK_FLAG|LB_FLAG|RB_FLAG)
|
||||
#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 {
|
||||
char leftTrigger, rightTrigger;
|
||||
unsigned char leftTrigger, rightTrigger;
|
||||
short leftStickX, leftStickY;
|
||||
short rightStickX, rightStickY;
|
||||
int buttons;
|
||||
SDL_JoystickID sdl_id;
|
||||
SDL_GameController* controller;
|
||||
#if !SDL_VERSION_ATLEAST(2, 0, 9)
|
||||
SDL_Haptic* haptic;
|
||||
int haptic_effect_id;
|
||||
#endif
|
||||
short id;
|
||||
bool initialized;
|
||||
} GAMEPAD_STATE, *PGAMEPAD_STATE;
|
||||
|
||||
static GAMEPAD_STATE gamepads[4];
|
||||
// Limited by number of bits in activeGamepadMask
|
||||
#define MAX_GAMEPADS 16
|
||||
|
||||
static GAMEPAD_STATE gamepads[MAX_GAMEPADS];
|
||||
|
||||
static int keyboard_modifiers;
|
||||
static int activeGamepadMask = 0;
|
||||
|
||||
int sdl_gamepads = 0;
|
||||
|
||||
static PGAMEPAD_STATE get_gamepad(SDL_JoystickID sdl_id) {
|
||||
for (int i = 0;i<4;i++) {
|
||||
#define VK_0 0x30
|
||||
#define VK_A 0x41
|
||||
|
||||
// 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) {
|
||||
gamepads[i].sdl_id = sdl_id;
|
||||
gamepads[i].id = i;
|
||||
gamepads[i].initialized = true;
|
||||
|
||||
activeGamepadMask |= (1 << i);
|
||||
|
||||
return &gamepads[i];
|
||||
} else if (gamepads[i].sdl_id == sdl_id)
|
||||
return &gamepads[i];
|
||||
}
|
||||
}
|
||||
|
||||
return &gamepads[0];
|
||||
}
|
||||
|
||||
static void init_gamepad(int joystick_index) {
|
||||
if (SDL_IsGameController(joystick_index)) {
|
||||
sdl_gamepads++;
|
||||
SDL_GameController* controller = SDL_GameControllerOpen(joystick_index);
|
||||
if (!controller) {
|
||||
fprintf(stderr, "Could not open gamecontroller %i: %s\n", joystick_index, SDL_GetError());
|
||||
return;
|
||||
}
|
||||
static void add_gamepad(int joystick_index) {
|
||||
SDL_GameController* controller = SDL_GameControllerOpen(joystick_index);
|
||||
if (!controller) {
|
||||
fprintf(stderr, "Could not open gamecontroller %i: %s\n", joystick_index, SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
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_Joystick* joystick = SDL_GameControllerGetJoystick(controller);
|
||||
SDL_JoystickID joystick_id = SDL_JoystickInstanceID(joystick);
|
||||
|
||||
SDL_JoystickID sdl_id = SDL_JoystickInstanceID(joystick);
|
||||
PGAMEPAD_STATE state = get_gamepad(joystick_index);
|
||||
state->haptic = haptic;
|
||||
state->haptic_effect_id = -1;
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,22 +433,41 @@ void sdlinput_init(char* mappings) {
|
||||
memset(gamepads, 0, sizeof(gamepads));
|
||||
|
||||
SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
|
||||
#if !SDL_VERSION_ATLEAST(2, 0, 9)
|
||||
SDL_InitSubSystem(SDL_INIT_HAPTIC);
|
||||
#endif
|
||||
SDL_GameControllerAddMappingsFromFile(mappings);
|
||||
|
||||
for (int i = 0; i < SDL_NumJoysticks(); ++i)
|
||||
init_gamepad(i);
|
||||
// Add game controllers here to ensure an accurate count
|
||||
// goes to the host when starting a new session.
|
||||
for (int i = 0; i < SDL_NumJoysticks(); ++i) {
|
||||
if (SDL_IsGameController(i))
|
||||
add_gamepad(i);
|
||||
}
|
||||
}
|
||||
|
||||
int sdlinput_handle_event(SDL_Event* event) {
|
||||
int sdlinput_handle_event(SDL_Window* window, SDL_Event* event) {
|
||||
int button = 0;
|
||||
unsigned char touchEventType;
|
||||
PGAMEPAD_STATE gamepad;
|
||||
switch (event->type) {
|
||||
case SDL_MOUSEMOTION:
|
||||
LiSendMouseMoveEvent(event->motion.xrel, event->motion.yrel);
|
||||
if (SDL_GetRelativeMouseMode())
|
||||
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;
|
||||
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);
|
||||
#endif
|
||||
break;
|
||||
case SDL_MOUSEBUTTONUP:
|
||||
case SDL_MOUSEBUTTONDOWN:
|
||||
@ -127,64 +492,63 @@ int sdlinput_handle_event(SDL_Event* event) {
|
||||
if (button != 0)
|
||||
LiSendMouseButtonEvent(event->type==SDL_MOUSEBUTTONDOWN?BUTTON_ACTION_PRESS:BUTTON_ACTION_RELEASE, button);
|
||||
|
||||
return SDL_MOUSE_GRAB;
|
||||
return 0;
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
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;
|
||||
button = vk_for_sdl_scancode(event->key.keysym.scancode);
|
||||
|
||||
int modifier = 0;
|
||||
switch (event->key.keysym.sym) {
|
||||
case SDLK_RSHIFT:
|
||||
case SDLK_LSHIFT:
|
||||
modifier = MODIFIER_SHIFT;
|
||||
break;
|
||||
case SDLK_RALT:
|
||||
case SDLK_LALT:
|
||||
modifier = MODIFIER_ALT;
|
||||
break;
|
||||
case SDLK_RCTRL:
|
||||
case SDLK_LCTRL:
|
||||
modifier = MODIFIER_CTRL;
|
||||
break;
|
||||
case SDLK_RGUI:
|
||||
case SDLK_LGUI:
|
||||
modifier = MODIFIER_META;
|
||||
break;
|
||||
int modifiers = 0;
|
||||
if (event->key.keysym.mod & KMOD_CTRL) {
|
||||
modifiers |= MODIFIER_CTRL;
|
||||
}
|
||||
if (event->key.keysym.mod & KMOD_ALT) {
|
||||
modifiers |= MODIFIER_ALT;
|
||||
}
|
||||
if (event->key.keysym.mod & KMOD_SHIFT) {
|
||||
modifiers |= MODIFIER_SHIFT;
|
||||
}
|
||||
if (event->key.keysym.mod & KMOD_GUI) {
|
||||
modifiers |= MODIFIER_META;
|
||||
}
|
||||
|
||||
if (modifier != 0) {
|
||||
if (event->type==SDL_KEYDOWN)
|
||||
keyboard_modifiers |= modifier;
|
||||
else
|
||||
keyboard_modifiers &= ~modifier;
|
||||
}
|
||||
LiSendKeyboardEvent(0x80 << 8 | button, event->type==SDL_KEYDOWN?KEY_ACTION_DOWN:KEY_ACTION_UP, modifiers);
|
||||
|
||||
// Quit the stream if all the required quit keys are down
|
||||
if ((keyboard_modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS && event->key.keysym.sym == QUIT_KEY && event->type==SDL_KEYUP)
|
||||
if ((modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS && event->key.keysym.sym == QUIT_KEY && event->type==SDL_KEYUP)
|
||||
return SDL_QUIT_APPLICATION;
|
||||
else if ((keyboard_modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS && event->key.keysym.sym == FULLSCREEN_KEY && event->type==SDL_KEYUP)
|
||||
else if ((modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS && event->key.keysym.sym == FULLSCREEN_KEY && event->type==SDL_KEYUP)
|
||||
return SDL_TOGGLE_FULLSCREEN;
|
||||
else if ((keyboard_modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS)
|
||||
return SDL_MOUSE_UNGRAB;
|
||||
else if ((modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS && event->key.keysym.sym == UNGRAB_KEY && event->type==SDL_KEYUP)
|
||||
return SDL_GetRelativeMouseMode() ? SDL_MOUSE_UNGRAB : SDL_MOUSE_GRAB;
|
||||
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;
|
||||
}
|
||||
|
||||
LiSendKeyboardEvent(0x80 << 8 | button, event->type==SDL_KEYDOWN?KEY_ACTION_DOWN:KEY_ACTION_UP, keyboard_modifiers);
|
||||
// These are already window-relative normalized coordinates, so we just need to clamp them
|
||||
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;
|
||||
case SDL_CONTROLLERAXISMOTION:
|
||||
gamepad = get_gamepad(event->caxis.which);
|
||||
gamepad = get_gamepad(event->caxis.which, false);
|
||||
if (!gamepad)
|
||||
return SDL_NOTHING;
|
||||
switch (event->caxis.axis) {
|
||||
case SDL_CONTROLLER_AXIS_LEFTX:
|
||||
gamepad->leftStickX = event->caxis.value;
|
||||
@ -211,76 +575,86 @@ int sdlinput_handle_event(SDL_Event* event) {
|
||||
break;
|
||||
case SDL_CONTROLLERBUTTONDOWN:
|
||||
case SDL_CONTROLLERBUTTONUP:
|
||||
gamepad = get_gamepad(event->cbutton.which);
|
||||
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:
|
||||
gamepad = get_gamepad(event->cbutton.which, false);
|
||||
if (!gamepad)
|
||||
return SDL_NOTHING;
|
||||
}
|
||||
if (event->cbutton.button >= SDL_arraysize(SDL_TO_LI_BUTTON_MAP))
|
||||
return SDL_NOTHING;
|
||||
|
||||
if (event->type == SDL_CONTROLLERBUTTONDOWN)
|
||||
gamepad->buttons |= button;
|
||||
gamepad->buttons |= SDL_TO_LI_BUTTON_MAP[event->cbutton.button];
|
||||
else
|
||||
gamepad->buttons &= ~button;
|
||||
gamepad->buttons &= ~SDL_TO_LI_BUTTON_MAP[event->cbutton.button];
|
||||
|
||||
if ((gamepad->buttons & QUIT_BUTTONS) == QUIT_BUTTONS)
|
||||
return SDL_QUIT_APPLICATION;
|
||||
|
||||
LiSendMultiControllerEvent(gamepad->id, activeGamepadMask, gamepad->buttons, gamepad->leftTrigger, gamepad->rightTrigger, gamepad->leftStickX, gamepad->leftStickY, gamepad->rightStickX, gamepad->rightStickY);
|
||||
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;
|
||||
}
|
||||
|
||||
void sdlinput_rumble(unsigned short controller_id, unsigned short low_freq_motor, unsigned short high_freq_motor) {
|
||||
if (controller_id >= 4)
|
||||
if (controller_id >= MAX_GAMEPADS)
|
||||
return;
|
||||
|
||||
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;
|
||||
if (!haptic)
|
||||
return;
|
||||
@ -303,4 +677,45 @@ void sdlinput_rumble(unsigned short controller_id, unsigned short low_freq_motor
|
||||
state->haptic_effect_id = SDL_HapticNewEffect(haptic, &effect);
|
||||
if (state->haptic_effect_id >= 0)
|
||||
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,83 +22,9 @@
|
||||
|
||||
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
|
||||
0x2D, //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[] = {
|
||||
0xA2, //SDLK_LCTRL
|
||||
0xA0, //SDLK_LSHIFT
|
||||
0xA4, //SDLK_LALT
|
||||
0x5B, //SDLK_LGUI
|
||||
0xA3, //SDLK_RCTRL
|
||||
0xA1, //SDLK_RSHIFT
|
||||
0xA5, //SDLK_RALT
|
||||
0x5C, //SDLK_RGUI
|
||||
};
|
||||
|
||||
void sdlinput_init(char* mappings);
|
||||
int sdlinput_handle_event(SDL_Event* event);
|
||||
int sdlinput_handle_event(SDL_Window* window, SDL_Event* event);
|
||||
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,6 @@ static struct mapping* defaultMappings;
|
||||
|
||||
static struct udev *udev;
|
||||
static struct udev_monitor *udev_mon;
|
||||
static int udev_fd;
|
||||
static int inputRotate;
|
||||
|
||||
static int udev_handle(int fd) {
|
||||
@ -92,10 +91,11 @@ void udev_init(bool autoload, struct mapping* mappings, bool verbose, int rotate
|
||||
defaultMappings = mappings;
|
||||
inputRotate = rotate;
|
||||
|
||||
int udev_fd = udev_monitor_get_fd(udev_mon);
|
||||
loop_add_fd(udev_fd, &udev_handle, POLLIN);
|
||||
loop_add_fd(udev_monitor_get_fd(udev_mon), &udev_handle, POLLIN);
|
||||
}
|
||||
|
||||
void evdev_destroy() {
|
||||
void udev_destroy() {
|
||||
loop_remove_fd(udev_monitor_get_fd(udev_mon));
|
||||
udev_monitor_unref(udev_mon);
|
||||
udev_unref(udev);
|
||||
}
|
||||
|
@ -20,4 +20,4 @@
|
||||
#include "mapping.h"
|
||||
|
||||
void udev_init(bool autoload, struct mapping* mappings, bool verbose, int rotate);
|
||||
void evdev_destroy();
|
||||
void udev_destroy();
|
||||
|
@ -111,6 +111,12 @@ static int x11_handler(int fd) {
|
||||
case Button5:
|
||||
LiSendScrollEvent(-1);
|
||||
break;
|
||||
case 6:
|
||||
LiSendHScrollEvent(-1);
|
||||
break;
|
||||
case 7:
|
||||
LiSendHScrollEvent(1);
|
||||
break;
|
||||
case 8:
|
||||
button = BUTTON_X1;
|
||||
break;
|
||||
|
@ -38,7 +38,8 @@ static int sigFd;
|
||||
|
||||
static int loop_sig_handler(int fd) {
|
||||
struct signalfd_siginfo info;
|
||||
read(fd, &info, sizeof(info));
|
||||
if (read(fd, &info, sizeof(info)) != sizeof(info))
|
||||
return LOOP_RETURN;
|
||||
switch (info.ssi_signo) {
|
||||
case SIGINT:
|
||||
case SIGTERM:
|
||||
@ -73,9 +74,9 @@ void loop_add_fd(int fd, FdHandler handler, int events) {
|
||||
|
||||
void loop_remove_fd(int fd) {
|
||||
numFds--;
|
||||
int fdindex;
|
||||
int fdindex = numFds;
|
||||
|
||||
for (int i=0;i<numFds;i++) {
|
||||
for (int i=0;i<=numFds;i++) {
|
||||
if (fds[i].fd == fd) {
|
||||
fdindex = i;
|
||||
break;
|
||||
|
55
src/main.c
55
src/main.c
@ -20,8 +20,8 @@
|
||||
#include "loop.h"
|
||||
#include "connection.h"
|
||||
#include "configuration.h"
|
||||
#include "config.h"
|
||||
#include "platform.h"
|
||||
#include "config.h"
|
||||
#include "sdl.h"
|
||||
|
||||
#include "audio/audio.h"
|
||||
@ -96,7 +96,7 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform sys
|
||||
gamepads += sdl_gamepads;
|
||||
#endif
|
||||
int gamepad_mask = 0;
|
||||
for (int i = 0; i < gamepads && i < 4; i++)
|
||||
for (int i = 0; i < gamepads; i++)
|
||||
gamepad_mask = (gamepad_mask << 1) + 1;
|
||||
|
||||
int ret = gs_start_app(server, &config->stream, appId, config->sops, config->localaudio, gamepad_mask);
|
||||
@ -119,6 +119,8 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform sys
|
||||
drFlags |= DISPLAY_FULLSCREEN;
|
||||
|
||||
switch (config->rotate) {
|
||||
case 0:
|
||||
break;
|
||||
case 90:
|
||||
drFlags |= DISPLAY_ROTATE_90;
|
||||
break;
|
||||
@ -128,6 +130,8 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform sys
|
||||
case 270:
|
||||
drFlags |= DISPLAY_ROTATE_270;
|
||||
break;
|
||||
default:
|
||||
printf("Ignoring invalid rotation value: %d\n", config->rotate);
|
||||
}
|
||||
|
||||
if (config->debug_level > 0) {
|
||||
@ -191,13 +195,14 @@ static void help() {
|
||||
printf("\t-4k\t\t\tUse 3840x2160 resolution\n");
|
||||
printf("\t-width <width>\t\tHorizontal resolution (default 1280)\n");
|
||||
printf("\t-height <height>\tVertical resolution (default 720)\n");
|
||||
#if defined(HAVE_PI) | defined(HAVE_MMAL)
|
||||
printf("\t-rotate <height>\tRotate display: 0/90/180/270 (default 0)\n");
|
||||
#ifdef HAVE_EMBEDDED
|
||||
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-packetsize <size>\tSpecify the maximum packetsize in bytes\n");
|
||||
printf("\t-codec <codec>\t\tSelect used codec: auto/h264/h265 (default auto)\n");
|
||||
printf("\t-codec <codec>\t\tSelect used codec: auto/h264/h265/av1 (default auto)\n");
|
||||
printf("\t-hdr\t\tEnable HDR streaming (experimental, requires host and device support)\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-nosops\t\t\tDon't allow GFE to modify game settings\n");
|
||||
@ -259,7 +264,7 @@ int main(int argc, char* argv[]) {
|
||||
}
|
||||
config.address[0] = 0;
|
||||
printf("Searching for server...\n");
|
||||
gs_discover_server(config.address);
|
||||
gs_discover_server(config.address, &config.port);
|
||||
if (config.address[0] == 0) {
|
||||
fprintf(stderr, "Autodiscovery failed. Specify an IP address next time.\n");
|
||||
exit(-1);
|
||||
@ -272,10 +277,10 @@ int main(int argc, char* argv[]) {
|
||||
config_file_parse(host_config_file, &config);
|
||||
|
||||
SERVER_DATA server;
|
||||
printf("Connect to %s...\n", config.address);
|
||||
printf("Connecting to %s...\n", config.address);
|
||||
|
||||
int ret;
|
||||
if ((ret = gs_init(&server, config.address, config.key_dir, config.debug_level, config.unsupported)) == GS_OUT_OF_MEMORY) {
|
||||
if ((ret = gs_init(&server, config.address, config.port, config.key_dir, config.debug_level, config.unsupported)) == GS_OUT_OF_MEMORY) {
|
||||
fprintf(stderr, "Not enough memory\n");
|
||||
exit(-1);
|
||||
} else if (ret == GS_ERROR) {
|
||||
@ -292,8 +297,10 @@ int main(int argc, char* argv[]) {
|
||||
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("Server codec flags: 0x%x\n", server.serverInfo.serverCodecModeSupport);
|
||||
}
|
||||
|
||||
if (strcmp("list", config.action) == 0) {
|
||||
pair_check(&server);
|
||||
@ -311,7 +318,23 @@ int main(int argc, char* argv[]) {
|
||||
fprintf(stderr, "You can't select a audio device for SDL\n");
|
||||
exit(-1);
|
||||
}
|
||||
config.stream.supportsHevc = config.codec != CODEC_H264 && (config.codec == CODEC_HEVC || platform_supports_hevc(system));
|
||||
|
||||
config.stream.supportedVideoFormats = VIDEO_FORMAT_H264;
|
||||
if (config.codec == CODEC_HEVC || (config.codec == CODEC_UNSPECIFIED && platform_prefers_codec(system, CODEC_HEVC))) {
|
||||
config.stream.supportedVideoFormats |= VIDEO_FORMAT_H265;
|
||||
if (config.hdr)
|
||||
config.stream.supportedVideoFormats |= VIDEO_FORMAT_H265_MAIN10;
|
||||
}
|
||||
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)) {
|
||||
fprintf(stderr, "HDR streaming requires HEVC or AV1 codec\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
#ifdef HAVE_SDL
|
||||
if (system == SDL)
|
||||
@ -341,7 +364,7 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
for (int i=0;i<config.inputsCount;i++) {
|
||||
if (config.debug_level > 0)
|
||||
printf("Add input %s...\n", config.inputs[i]);
|
||||
printf("Adding input device %s...\n", config.inputs[i]);
|
||||
|
||||
evdev_create(config.inputs[i], mappings, config.debug_level > 0, config.rotate);
|
||||
}
|
||||
@ -362,6 +385,9 @@ int main(int argc, char* argv[]) {
|
||||
|
||||
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
|
||||
}
|
||||
@ -369,8 +395,13 @@ int main(int argc, char* argv[]) {
|
||||
stream(&server, &config, system);
|
||||
} else if (strcmp("pair", config.action) == 0) {
|
||||
char pin[5];
|
||||
sprintf(pin, "%d%d%d%d", (int)random() % 10, (int)random() % 10, (int)random() % 10, (int)random() % 10);
|
||||
if (config.pin > 0 && config.pin <= 9999) {
|
||||
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);
|
||||
fflush(stdout);
|
||||
if (gs_pair(&server, &pin[0]) != GS_OK) {
|
||||
fprintf(stderr, "Failed to pair to server: %s\n", gs_error);
|
||||
} else {
|
||||
|
@ -61,7 +61,7 @@ enum platform platform_check(char* name) {
|
||||
#endif
|
||||
#ifdef HAVE_AML
|
||||
if (std || strcmp(name, "aml") == 0) {
|
||||
void *handle = dlopen("libmoonlight-aml.so", RTLD_NOW | RTLD_GLOBAL);
|
||||
void *handle = dlopen("libmoonlight-aml.so", RTLD_LAZY | RTLD_GLOBAL);
|
||||
if (handle != NULL && access("/dev/amvideo", F_OK) != -1)
|
||||
return AML;
|
||||
}
|
||||
@ -87,13 +87,18 @@ enum platform platform_check(char* name) {
|
||||
if (init == INIT_VDPAU)
|
||||
return X11_VDPAU;
|
||||
#endif
|
||||
#ifdef HAVE_SDL
|
||||
return SDL;
|
||||
#else
|
||||
return X11;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#ifdef HAVE_SDL
|
||||
if (std || strcmp(name, "sdl") == 0)
|
||||
return SDL;
|
||||
#endif
|
||||
|
||||
if (strcmp(name, "fake") == 0)
|
||||
return FAKE;
|
||||
|
||||
@ -104,13 +109,14 @@ void platform_start(enum platform system) {
|
||||
switch (system) {
|
||||
#ifdef HAVE_AML
|
||||
case AML:
|
||||
blank_fb("/sys/class/graphics/fb0/blank", true);
|
||||
blank_fb("/sys/class/graphics/fb1/blank", true);
|
||||
write_bool("/sys/class/graphics/fb0/blank", true);
|
||||
write_bool("/sys/class/graphics/fb1/blank", true);
|
||||
write_bool("/sys/class/video/disable_video", false);
|
||||
break;
|
||||
#endif
|
||||
#if defined(HAVE_PI) | defined(HAVE_MMAL)
|
||||
#if defined(HAVE_PI) || defined(HAVE_MMAL)
|
||||
case PI:
|
||||
blank_fb("/sys/class/graphics/fb0/blank", true);
|
||||
write_bool("/sys/class/graphics/fb0/blank", true);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
@ -120,13 +126,13 @@ void platform_stop(enum platform system) {
|
||||
switch (system) {
|
||||
#ifdef HAVE_AML
|
||||
case AML:
|
||||
blank_fb("/sys/class/graphics/fb0/blank", false);
|
||||
blank_fb("/sys/class/graphics/fb1/blank", false);
|
||||
write_bool("/sys/class/graphics/fb0/blank", false);
|
||||
write_bool("/sys/class/graphics/fb1/blank", false);
|
||||
break;
|
||||
#endif
|
||||
#if defined(HAVE_PI) | defined(HAVE_MMAL)
|
||||
#if defined(HAVE_PI) || defined(HAVE_MMAL)
|
||||
case PI:
|
||||
blank_fb("/sys/class/graphics/fb0/blank", false);
|
||||
write_bool("/sys/class/graphics/fb0/blank", false);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
@ -196,15 +202,29 @@ AUDIO_RENDERER_CALLBACKS* platform_get_audio(enum platform system, char* audio_d
|
||||
#ifdef HAVE_ALSA
|
||||
return &audio_callbacks_alsa;
|
||||
#endif
|
||||
#ifdef __FreeBSD__
|
||||
return &audio_callbacks_oss;
|
||||
#endif
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool platform_supports_hevc(enum platform system) {
|
||||
switch (system) {
|
||||
case AML:
|
||||
case RK:
|
||||
bool platform_prefers_codec(enum platform system, enum codecs codec) {
|
||||
switch (codec) {
|
||||
case CODEC_H264:
|
||||
// H.264 is always supported
|
||||
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;
|
||||
}
|
||||
|
@ -27,11 +27,12 @@
|
||||
#define IS_EMBEDDED(SYSTEM) SYSTEM != SDL
|
||||
|
||||
enum platform { NONE, SDL, X11, X11_VDPAU, X11_VAAPI, PI, MMAL, IMX, AML, RK, FAKE };
|
||||
enum codecs { CODEC_UNSPECIFIED, CODEC_H264, CODEC_HEVC, CODEC_AV1 };
|
||||
|
||||
enum platform platform_check(char*);
|
||||
PDECODER_RENDERER_CALLBACKS platform_get_video(enum platform system);
|
||||
PAUDIO_RENDERER_CALLBACKS platform_get_audio(enum platform system, char* audio_device);
|
||||
bool platform_supports_hevc(enum platform system);
|
||||
bool platform_prefers_codec(enum platform system, enum codecs codec);
|
||||
char* platform_name(enum platform system);
|
||||
|
||||
void platform_start(enum platform system);
|
||||
|
@ -50,7 +50,6 @@ void sdl_init(int width, int height, bool fullscreen) {
|
||||
exit(1);
|
||||
}
|
||||
|
||||
SDL_SetRelativeMouseMode(SDL_TRUE);
|
||||
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
||||
if (!renderer) {
|
||||
printf("SDL_CreateRenderer failed: %s\n", SDL_GetError());
|
||||
@ -72,19 +71,25 @@ void sdl_init(int width, int height, bool fullscreen) {
|
||||
|
||||
void sdl_loop() {
|
||||
SDL_Event event;
|
||||
|
||||
SDL_SetRelativeMouseMode(SDL_TRUE);
|
||||
|
||||
while(!done && SDL_WaitEvent(&event)) {
|
||||
switch (sdlinput_handle_event(&event)) {
|
||||
switch (sdlinput_handle_event(window, &event)) {
|
||||
case SDL_QUIT_APPLICATION:
|
||||
done = true;
|
||||
break;
|
||||
case SDL_TOGGLE_FULLSCREEN:
|
||||
fullscreen_flags ^= SDL_WINDOW_FULLSCREEN;
|
||||
SDL_SetWindowFullscreen(window, fullscreen_flags);
|
||||
break;
|
||||
case SDL_MOUSE_GRAB:
|
||||
SDL_ShowCursor(SDL_ENABLE);
|
||||
SDL_SetRelativeMouseMode(SDL_TRUE);
|
||||
break;
|
||||
case SDL_MOUSE_UNGRAB:
|
||||
SDL_SetRelativeMouseMode(SDL_FALSE);
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
break;
|
||||
default:
|
||||
if (event.type == SDL_QUIT)
|
||||
|
20
src/util.c
20
src/util.c
@ -24,14 +24,15 @@
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int blank_fb(char *path, bool clear) {
|
||||
int write_bool(char *path, bool val) {
|
||||
int fd = open(path, O_RDWR);
|
||||
|
||||
if(fd >= 0) {
|
||||
int ret = write(fd, clear ? "1" : "0", 1);
|
||||
int ret = write(fd, val ? "1" : "0", 1);
|
||||
if (ret < 0)
|
||||
fprintf(stderr, "Failed to clear framebuffer %s: %d\n", path, ret);
|
||||
fprintf(stderr, "Failed to write %d to %s: %d\n", val ? 1 : 0, path, ret);
|
||||
|
||||
close(fd);
|
||||
return 0;
|
||||
@ -50,3 +51,16 @@ int read_file(char *path, char* output, int output_len) {
|
||||
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,6 +18,9 @@
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
|
||||
int blank_fb(char *path, bool clear);
|
||||
int write_bool(char *path, bool val);
|
||||
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,48 +24,101 @@
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ioctl.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 UCODE_IP_ONLY_PARAM 0x08
|
||||
#define MAX_WRITE_ATTEMPTS 5
|
||||
#define EAGAIN_SLEEP_TIME 2 * 1000
|
||||
|
||||
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) {
|
||||
codecParam.stream_type = STREAM_TYPE_ES_VIDEO;
|
||||
codecParam.has_video = 1;
|
||||
codecParam.noblock = 0;
|
||||
codecParam.am_sysinfo.param = 0;
|
||||
|
||||
switch (videoFormat) {
|
||||
case VIDEO_FORMAT_H264:
|
||||
if (width > 1920 || height > 1080) {
|
||||
codecParam.video_type = VFORMAT_H264_4K2K;
|
||||
codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_H264_4K2K;
|
||||
} else {
|
||||
codecParam.video_type = VFORMAT_H264;
|
||||
codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_H264;
|
||||
codecParam.handle = -1;
|
||||
codecParam.cntl_handle = -1;
|
||||
codecParam.audio_utils_handle = -1;
|
||||
codecParam.sub_handle = -1;
|
||||
codecParam.has_video = 1;
|
||||
codecParam.noblock = 0;
|
||||
codecParam.stream_type = STREAM_TYPE_ES_VIDEO;
|
||||
codecParam.am_sysinfo.param = 0;
|
||||
|
||||
// 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 (!(major > 3 || (major == 3 && minor >= 14)) && width == 1920 && height == 1080)
|
||||
codecParam.am_sysinfo.param = (void*) UCODE_IP_ONLY_PARAM;
|
||||
}
|
||||
break;
|
||||
case VIDEO_FORMAT_H265:
|
||||
codecParam.video_type = VFORMAT_HEVC;
|
||||
codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_HEVC;
|
||||
break;
|
||||
default:
|
||||
printf("Video format not supported\n");
|
||||
return -1;
|
||||
#ifdef STREAM_TYPE_FRAME
|
||||
codecParam.dec_mode = STREAM_TYPE_FRAME;
|
||||
#endif
|
||||
|
||||
#ifdef FRAME_BASE_PATH_AMLVIDEO_AMVIDEO
|
||||
codecParam.video_path = FRAME_BASE_PATH_AMLVIDEO_AMVIDEO;
|
||||
#endif
|
||||
|
||||
if (videoFormat & VIDEO_FORMAT_MASK_H264) {
|
||||
if (width > 1920 || height > 1080) {
|
||||
codecParam.video_type = VFORMAT_H264_4K2K;
|
||||
codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_H264_4K2K;
|
||||
} else {
|
||||
codecParam.video_type = VFORMAT_H264;
|
||||
codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_H264;
|
||||
|
||||
// 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;
|
||||
@ -84,33 +137,83 @@ int aml_setup(int videoFormat, int width, int height, int redrawRate, void* cont
|
||||
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;
|
||||
}
|
||||
|
||||
void aml_cleanup() {
|
||||
if (videoFd >= 0) {
|
||||
done = true;
|
||||
pthread_join(displayThread, NULL);
|
||||
close(videoFd);
|
||||
}
|
||||
|
||||
codec_close(&codecParam);
|
||||
free(pkt_buf);
|
||||
}
|
||||
|
||||
int aml_submit_decode_unit(PDECODE_UNIT decodeUnit) {
|
||||
int result = DR_OK;
|
||||
PLENTRY entry = decodeUnit->bufferList;
|
||||
while (entry != NULL) {
|
||||
int api = codec_write(&codecParam, entry->data, entry->length);
|
||||
if (api != entry->length) {
|
||||
fprintf(stderr, "codec_write error: %x\n", api);
|
||||
codec_reset(&codecParam);
|
||||
result = DR_NEED_IDR;
|
||||
break;
|
||||
}
|
||||
|
||||
ensure_buf_size(&pkt_buf, &pkt_buf_size, decodeUnit->fullLength);
|
||||
|
||||
int written = 0, length = 0, errCounter = 0, api;
|
||||
PLENTRY entry = decodeUnit->bufferList;
|
||||
do {
|
||||
memcpy(pkt_buf+length, entry->data, entry->length);
|
||||
length += entry->length;
|
||||
entry = entry->next;
|
||||
} while (entry != NULL);
|
||||
|
||||
codec_checkin_pts(&codecParam, decodeUnit->presentationTimeMs);
|
||||
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 result;
|
||||
|
||||
return length ? DR_NEED_IDR : DR_OK;
|
||||
}
|
||||
|
||||
DECODER_RENDERER_CALLBACKS decoder_callbacks_aml = {
|
||||
.setup = aml_setup,
|
||||
.cleanup = aml_cleanup,
|
||||
.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,
|
||||
};
|
||||
|
@ -143,7 +143,6 @@ void egl_init(EGLNativeDisplayType native_display, NativeWindowType native_windo
|
||||
GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
|
||||
glShaderSource(vertex_shader, 1, &vertex_source, NULL);
|
||||
glCompileShader(vertex_shader);
|
||||
GLint maxLength = 0;
|
||||
|
||||
GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||
glShaderSource(fragment_shader, 1, &fragment_source, NULL);
|
||||
|
@ -33,7 +33,7 @@
|
||||
|
||||
// General decoder and renderer state
|
||||
static AVPacket* pkt;
|
||||
static AVCodec* decoder;
|
||||
static const AVCodec* decoder;
|
||||
static AVCodecContext* decoder_ctx;
|
||||
static AVFrame** dec_frames;
|
||||
|
||||
@ -60,13 +60,72 @@ int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer
|
||||
}
|
||||
|
||||
ffmpeg_decoder = perf_lvl & VAAPI_ACCELERATION ? VAAPI : SOFTWARE;
|
||||
switch (videoFormat) {
|
||||
case VIDEO_FORMAT_H264:
|
||||
decoder = avcodec_find_decoder_by_name("h264");
|
||||
break;
|
||||
case VIDEO_FORMAT_H265:
|
||||
decoder = avcodec_find_decoder_by_name("hevc");
|
||||
break;
|
||||
|
||||
for (int try = 0; try < 6; try++) {
|
||||
if (videoFormat & VIDEO_FORMAT_MASK_H264) {
|
||||
if (ffmpeg_decoder == SOFTWARE) {
|
||||
if (try == 0) decoder = avcodec_find_decoder_by_name("h264_nvv4l2"); // Tegra
|
||||
if (try == 1) decoder = avcodec_find_decoder_by_name("h264_nvmpi"); // Tegra
|
||||
if (try == 2) decoder = avcodec_find_decoder_by_name("h264_omx"); // VisionFive
|
||||
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) {
|
||||
@ -74,38 +133,7 @@ int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer
|
||||
return -1;
|
||||
}
|
||||
|
||||
decoder_ctx = avcodec_alloc_context3(decoder);
|
||||
if (decoder_ctx == NULL) {
|
||||
printf("Couldn't allocate context");
|
||||
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");
|
||||
return err;
|
||||
}
|
||||
printf("Using FFmpeg decoder: %s\n", decoder->name);
|
||||
|
||||
dec_frames_cnt = buffer_count;
|
||||
dec_frames = malloc(buffer_count * sizeof(AVFrame*));
|
||||
@ -135,9 +163,7 @@ int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer
|
||||
void ffmpeg_destroy(void) {
|
||||
av_packet_free(&pkt);
|
||||
if (decoder_ctx) {
|
||||
avcodec_close(decoder_ctx);
|
||||
av_free(decoder_ctx);
|
||||
decoder_ctx = NULL;
|
||||
avcodec_free_context(&decoder_ctx);
|
||||
}
|
||||
if (dec_frames) {
|
||||
for (int i = 0; i < dec_frames_cnt; i++) {
|
||||
|
@ -65,6 +65,7 @@ int vaapi_init_lib() {
|
||||
int vaapi_init(AVCodecContext* decoder_ctx) {
|
||||
decoder_ctx->get_format = va_get_format;
|
||||
decoder_ctx->get_buffer2 = va_get_buffer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void vaapi_queue(AVFrame* dec_frame, Window win, int width, int height) {
|
||||
|
@ -42,7 +42,7 @@
|
||||
#include <linux/v4l2-controls.h>
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#define MIN_FRAME_BUFFER_COUNT 18;
|
||||
#define MIN_FRAME_BUFFER_COUNT 18
|
||||
|
||||
#define THRESHOLD 2
|
||||
|
||||
|
@ -210,7 +210,7 @@ bool vpu_decode(PDECODE_UNIT decodeUnit) {
|
||||
while (vpu_IsBusy()) {
|
||||
if (loop_id > 50) {
|
||||
vpu_SWReset(handle, 0);
|
||||
fprintf(stderr, "VPU is too long busy\n");
|
||||
fprintf(stderr, "VPU busy timeout expired\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
vpu_WaitForInt(100);
|
||||
|
@ -48,8 +48,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <interface/mmal/vc/mmal_vc_api.h>
|
||||
#include <interface/vcos/vcos.h>
|
||||
|
||||
#define MAX_DECODE_UNIT_SIZE 262144
|
||||
|
||||
#define ALIGN(x, a) (((x)+(a)-1)&~((a)-1))
|
||||
|
||||
static VCOS_SEMAPHORE_T semaphore;
|
||||
@ -80,8 +78,6 @@ static void output_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) {
|
||||
}
|
||||
|
||||
static int decoder_renderer_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
|
||||
MMAL_STATUS_T status;
|
||||
|
||||
if (videoFormat != VIDEO_FORMAT_H264) {
|
||||
fprintf(stderr, "Video format not supported\n");
|
||||
return -1;
|
||||
@ -116,7 +112,7 @@ static int decoder_renderer_setup(int videoFormat, int width, int height, int re
|
||||
}
|
||||
|
||||
decoder->input[0]->buffer_num = 5;
|
||||
decoder->input[0]->buffer_size = MAX_DECODE_UNIT_SIZE;
|
||||
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;
|
||||
|
@ -41,13 +41,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#include <ilclient.h>
|
||||
#include <bcm_host.h>
|
||||
|
||||
#define MAX_DECODE_UNIT_SIZE 262144
|
||||
|
||||
static TUNNEL_T tunnel[2];
|
||||
static COMPONENT_T *list[3];
|
||||
static ILCLIENT_T *client;
|
||||
|
||||
static COMPONENT_T *video_decode = NULL, *video_scheduler = NULL, *video_render = NULL;
|
||||
static COMPONENT_T *video_decode = NULL, *video_render = NULL;
|
||||
|
||||
static int port_settings_changed;
|
||||
static int first_packet;
|
||||
@ -62,8 +60,6 @@ static int decoder_renderer_setup(int videoFormat, int width, int height, int re
|
||||
gs_sps_init(width, height);
|
||||
|
||||
OMX_VIDEO_PARAM_PORTFORMATTYPE format;
|
||||
OMX_TIME_CONFIG_CLOCKSTATETYPE cstate;
|
||||
COMPONENT_T *clock = NULL;
|
||||
|
||||
memset(list, 0, sizeof(list));
|
||||
memset(tunnel, 0, sizeof(tunnel));
|
||||
@ -132,13 +128,6 @@ static int decoder_renderer_setup(int videoFormat, int width, int height, int re
|
||||
latencyTarget.nInterFactor = 500;
|
||||
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) {
|
||||
fprintf(stderr, "Failed to set video render parameters\n");
|
||||
exit(EXIT_FAILURE);
|
||||
@ -179,7 +168,7 @@ static int decoder_renderer_setup(int videoFormat, int width, int height, int re
|
||||
}
|
||||
|
||||
// Increase the buffer size to fit the largest possible frame
|
||||
port.nBufferSize = MAX_DECODE_UNIT_SIZE;
|
||||
port.nBufferSize = INITIAL_DECODER_BUFFER_SIZE;
|
||||
|
||||
if(OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamPortDefinition, &port) == OMX_ErrorNone &&
|
||||
ilclient_enable_port_buffers(video_decode, 130, NULL, NULL, NULL) == 0) {
|
||||
@ -197,8 +186,6 @@ static int decoder_renderer_setup(int videoFormat, int width, int height, int re
|
||||
}
|
||||
|
||||
static void decoder_renderer_cleanup() {
|
||||
int status = 0;
|
||||
|
||||
OMX_BUFFERHEADERTYPE *buf;
|
||||
if((buf = ilclient_get_input_buffer(video_decode, 130, 1)) == NULL){
|
||||
fprintf(stderr, "Can't get video buffer\n");
|
||||
|
513
src/video/rk.c
513
src/video/rk.c
@ -18,7 +18,8 @@
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <Limelight.h>
|
||||
#include "video.h"
|
||||
#include "../util.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -27,7 +28,6 @@
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
@ -35,28 +35,80 @@
|
||||
#include <xf86drm.h>
|
||||
#include <xf86drmMode.h>
|
||||
#include <libdrm/drm_fourcc.h>
|
||||
#include <linux/videodev2.h>
|
||||
|
||||
#include <rockchip/rk_mpi.h>
|
||||
|
||||
#define READ_BUF_SIZE 0x00100000
|
||||
#define MAX_FRAMES 16
|
||||
#define RK_H264 7
|
||||
#define RK_H265 16777220
|
||||
#define MAX_FRAMES 3
|
||||
#define RK_H264 0x7
|
||||
#define RK_H265 0x1000004
|
||||
#define RK_AV1 0x1000008
|
||||
|
||||
#ifndef DRM_FORMAT_NV12_10
|
||||
#define DRM_FORMAT_NV12_10 fourcc_code('N', 'A', '1', '2')
|
||||
// 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;
|
||||
size_t pkt_buf_size = 0;
|
||||
int fd;
|
||||
int fb_id;
|
||||
uint32_t plane_id, crtc_id;
|
||||
uint32_t plane_id, crtc_id, conn_id, hdr_metadata_blob_id, pixel_format;
|
||||
int frm_eos;
|
||||
int crtc_width;
|
||||
int crtc_height;
|
||||
RK_U32 frm_width;
|
||||
RK_U32 frm_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_mutex_t mutex;
|
||||
@ -69,6 +121,12 @@ drmModeRes *resources = NULL;
|
||||
drmModePlaneRes *plane_resources = NULL;
|
||||
drmModeCrtcPtr crtc = {0};
|
||||
|
||||
drmModePropertyPtr hdr_metadata_prop = NULL;
|
||||
|
||||
drmModeAtomicReqPtr drm_request = NULL;
|
||||
drmModePropertyPtr plane_props[32];
|
||||
drmModePropertyPtr conn_props[32];
|
||||
|
||||
MppCtx mpi_ctx;
|
||||
MppApi *mpi_api;
|
||||
MppPacket mpi_packet;
|
||||
@ -80,36 +138,57 @@ struct {
|
||||
uint32_t handle;
|
||||
} 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) {
|
||||
|
||||
int ret;
|
||||
|
||||
while (!frm_eos) {
|
||||
int _fb_id;
|
||||
|
||||
ret = pthread_mutex_lock(&mutex);
|
||||
assert(!ret);
|
||||
pthread_mutex_lock(&mutex);
|
||||
while (fb_id == 0) {
|
||||
pthread_cond_wait(&cond, &mutex);
|
||||
assert(!ret);
|
||||
if (fb_id == 0 && frm_eos) {
|
||||
ret = pthread_mutex_unlock(&mutex);
|
||||
assert(!ret);
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
_fb_id = fb_id;
|
||||
|
||||
fb_id = 0;
|
||||
ret = pthread_mutex_unlock(&mutex);
|
||||
assert(!ret);
|
||||
pthread_mutex_unlock(&mutex);
|
||||
|
||||
// show DRM FB in overlay plane (auto vsynced/atomic !)
|
||||
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);
|
||||
assert(!ret);
|
||||
if (atomic) {
|
||||
// We may need to modeset to apply colorspace changes when toggling HDR
|
||||
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "FB_ID", _fb_id);
|
||||
ret = drmModeAtomicCommit(fd, drm_request, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
|
||||
if (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) {
|
||||
@ -166,25 +245,27 @@ void *frame_thread(void *param) {
|
||||
|
||||
// new DRM buffer
|
||||
struct drm_mode_create_dumb dmcd = {0};
|
||||
dmcd.bpp = fmt == MPP_FMT_YUV420SP ? 8:10;
|
||||
dmcd.bpp = 8; // hor_stride is already adjusted for 10 vs 8 bit
|
||||
dmcd.width = hor_stride;
|
||||
dmcd.height = ver_stride * 2; // documentation say not v*2/3 but v*2 (additional info included)
|
||||
do {
|
||||
ret = ioctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &dmcd);
|
||||
} while (ret == -1 && (errno == EINTR || errno == EAGAIN));
|
||||
assert(!ret);
|
||||
assert(dmcd.pitch == (fmt == MPP_FMT_YUV420SP?hor_stride:hor_stride * 10 / 8));
|
||||
assert(dmcd.size == (fmt == MPP_FMT_YUV420SP?hor_stride:hor_stride * 10 / 8) * ver_stride * 2);
|
||||
ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &dmcd);
|
||||
if (ret) {
|
||||
perror("drmIoctl(DRM_IOCTL_MODE_CREATE_DUMB)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
assert(dmcd.pitch == dmcd.width);
|
||||
assert(dmcd.size == dmcd.pitch * dmcd.height);
|
||||
frame_to_drm[i].handle = dmcd.handle;
|
||||
|
||||
// commit DRM buffer to frame group
|
||||
struct drm_prime_handle dph = {0};
|
||||
dph.handle = dmcd.handle;
|
||||
dph.fd = -1;
|
||||
do {
|
||||
ret = ioctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &dph);
|
||||
} while (ret == -1 && (errno == EINTR || errno == EAGAIN));
|
||||
assert(!ret);
|
||||
ret = drmIoctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &dph);
|
||||
if (ret) {
|
||||
perror("drmIoctl(DRM_IOCTL_PRIME_HANDLE_TO_FD)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
MppBufferInfo info = {0};
|
||||
info.type = MPP_BUFFER_TYPE_DRM;
|
||||
info.size = dmcd.width * dmcd.height;
|
||||
@ -197,17 +278,37 @@ void *frame_thread(void *param) {
|
||||
uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
|
||||
handles[0] = frame_to_drm[i].handle;
|
||||
offsets[0] = 0;
|
||||
pitches[0] = hor_stride;
|
||||
pitches[0] = dmcd.pitch;
|
||||
handles[1] = frame_to_drm[i].handle;
|
||||
offsets[1] = hor_stride * ver_stride;
|
||||
pitches[1] = hor_stride;
|
||||
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);
|
||||
assert(!ret);
|
||||
offsets[1] = pitches[0] * ver_stride;
|
||||
pitches[1] = dmcd.pitch;
|
||||
ret = drmModeAddFB2(fd, frm_width, frm_height, pixel_format, handles, pitches, offsets, &frame_to_drm[i].fb_id, 0);
|
||||
if (ret) {
|
||||
perror("drmModeAddFB2");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
// 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_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 {
|
||||
// regular frame received
|
||||
|
||||
@ -222,14 +323,10 @@ void *frame_thread(void *param) {
|
||||
}
|
||||
assert(i != MAX_FRAMES);
|
||||
// send DRM FB to display thread
|
||||
ret = pthread_mutex_lock(&mutex);
|
||||
assert(!ret);
|
||||
pthread_mutex_lock(&mutex);
|
||||
fb_id = frame_to_drm[i].fb_id;
|
||||
ret = pthread_cond_signal(&cond);
|
||||
assert(!ret);
|
||||
ret = pthread_mutex_unlock(&mutex);
|
||||
assert(!ret);
|
||||
|
||||
pthread_cond_signal(&cond);
|
||||
pthread_mutex_unlock(&mutex);
|
||||
} else {
|
||||
fprintf(stderr, "Frame no buff\n");
|
||||
}
|
||||
@ -254,29 +351,42 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte
|
||||
int ret;
|
||||
int i;
|
||||
int j;
|
||||
int format = 0;
|
||||
int format;
|
||||
|
||||
switch (videoFormat) {
|
||||
case VIDEO_FORMAT_H264:
|
||||
format = RK_H264;
|
||||
break;
|
||||
case VIDEO_FORMAT_H265:
|
||||
format = RK_H265;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Video format not supported\n");
|
||||
return -1;
|
||||
if (videoFormat & VIDEO_FORMAT_MASK_H264) {
|
||||
format = RK_H264;
|
||||
} else if (videoFormat & VIDEO_FORMAT_MASK_H265) {
|
||||
format = RK_H265;
|
||||
} else if (videoFormat & VIDEO_FORMAT_MASK_AV1) {
|
||||
format = RK_AV1;
|
||||
} else {
|
||||
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;
|
||||
ret = mpp_check_support_format(MPP_CTX_DEC, mpp_type);
|
||||
assert(!ret);
|
||||
if (ret) {
|
||||
fprintf(stderr, "Selected video format is not supported\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
|
||||
assert(fd >= 0);
|
||||
if (fd < 0) {
|
||||
fprintf(stderr, "Unable to open card0: %d\n", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
resources = drmModeGetResources(fd);
|
||||
assert(resources);
|
||||
if (!resources) {
|
||||
perror("drmModeGetResources");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// find active monitor
|
||||
for (i = 0; i < resources->count_connectors; ++i) {
|
||||
@ -291,6 +401,24 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte
|
||||
}
|
||||
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) {
|
||||
encoder = drmModeGetEncoder(fd, resources->encoders[i]);
|
||||
if (!encoder) {
|
||||
@ -306,7 +434,10 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte
|
||||
for (i = 0; i < resources->count_crtcs; ++i) {
|
||||
if (resources->crtcs[i] == encoder->crtc_id) {
|
||||
crtc = drmModeGetCrtc(fd, resources->crtcs[i]);
|
||||
assert(crtc);
|
||||
if (!crtc) {
|
||||
perror("drmModeGetCrtc");
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -317,9 +448,25 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte
|
||||
uint32_t crtc_bit = (1 << i);
|
||||
|
||||
ret = drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
|
||||
assert(!ret);
|
||||
if (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);
|
||||
assert(plane_resources);
|
||||
if (!plane_resources) {
|
||||
perror("drmModeGetPlaneResources");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// search for OVERLAY (for active connector, unused, NV12 support)
|
||||
for (i = 0; i < plane_resources->count_planes; i++) {
|
||||
@ -328,11 +475,19 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte
|
||||
continue;
|
||||
}
|
||||
for (j = 0; j < ovr->count_formats; j++) {
|
||||
if (ovr->formats[j] == DRM_FORMAT_NV12) {
|
||||
if (videoFormat & VIDEO_FORMAT_MASK_10BIT) {
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
if (j == ovr->count_formats) {
|
||||
if (j < ovr->count_formats) {
|
||||
pixel_format = ovr->formats[j];
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
if ((ovr->possible_crtcs & crtc_bit) && !ovr->crtc_id) {
|
||||
@ -341,37 +496,70 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte
|
||||
continue;
|
||||
}
|
||||
|
||||
for (j = 0; j < props->count_props && !plane_id; j++) {
|
||||
assert(props->count_props < sizeof(plane_props) / sizeof(plane_props[0]));
|
||||
for (j = 0; j < props->count_props; j++) {
|
||||
drmModePropertyPtr prop = drmModeGetProperty(fd, props->props[j]);
|
||||
if (!prop) {
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(prop->name, "type") && props->prop_values[j] == DRM_PLANE_TYPE_OVERLAY) {
|
||||
plane_props[j] = prop;
|
||||
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;
|
||||
}
|
||||
drmModeFreeProperty(prop);
|
||||
}
|
||||
drmModeFreeObjectProperties(props);
|
||||
if (plane_id) {
|
||||
break;
|
||||
drmModeFreeObjectProperties(props);
|
||||
break;
|
||||
} else {
|
||||
for (j = 0; j < props->count_props; j++) {
|
||||
drmModeFreeProperty(plane_props[j]);
|
||||
plane_props[j] = NULL;
|
||||
}
|
||||
drmModeFreeObjectProperties(props);
|
||||
}
|
||||
}
|
||||
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
|
||||
drmModeMoveCursor(fd, crtc_id, 0, crtc_height);
|
||||
|
||||
// MPI SETUP
|
||||
|
||||
pkt_buf = malloc(READ_BUF_SIZE);
|
||||
assert(pkt_buf);
|
||||
ret = mpp_packet_init(&mpi_packet, pkt_buf, READ_BUF_SIZE);
|
||||
ensure_buf_size(&pkt_buf, &pkt_buf_size, INITIAL_DECODER_BUFFER_SIZE);
|
||||
ret = mpp_packet_init(&mpi_packet, pkt_buf, pkt_buf_size);
|
||||
assert(!ret);
|
||||
|
||||
ret = mpp_create(&mpi_ctx, &mpi_api);
|
||||
assert(!ret);
|
||||
if (ret) {
|
||||
fprintf(stderr, "mpp_create() failed: %d\n", ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// decoder split mode (multi-data-input) need to be set before init
|
||||
int param = 1;
|
||||
@ -379,22 +567,21 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte
|
||||
assert(!ret);
|
||||
|
||||
ret = mpp_init(mpi_ctx, MPP_CTX_DEC, mpp_type);
|
||||
assert(!ret);
|
||||
if (ret) {
|
||||
fprintf(stderr, "mpp_init() failed: %d\n", ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// set blocked read on Frame Thread
|
||||
param = MPP_POLL_BLOCK;
|
||||
ret = mpi_api->control(mpi_ctx, MPP_SET_OUTPUT_BLOCK, ¶m);
|
||||
assert(!ret);
|
||||
|
||||
ret = pthread_mutex_init(&mutex, NULL);
|
||||
assert(!ret);
|
||||
ret = pthread_cond_init(&cond, NULL);
|
||||
assert(!ret);
|
||||
pthread_mutex_init(&mutex, NULL);
|
||||
pthread_cond_init(&cond, NULL);
|
||||
|
||||
ret = pthread_create(&tid_frame, NULL, frame_thread, NULL);
|
||||
assert(!ret);
|
||||
ret = pthread_create(&tid_display, NULL, display_thread, NULL);
|
||||
assert(!ret);
|
||||
pthread_create(&tid_frame, NULL, frame_thread, NULL);
|
||||
pthread_create(&tid_display, NULL, display_thread, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -405,26 +592,19 @@ void rk_cleanup() {
|
||||
int ret;
|
||||
|
||||
frm_eos = 1;
|
||||
ret = pthread_mutex_lock(&mutex);
|
||||
assert(!ret);
|
||||
ret = pthread_cond_signal(&cond);
|
||||
assert(!ret);
|
||||
ret = pthread_mutex_unlock(&mutex);
|
||||
assert(!ret);
|
||||
pthread_mutex_lock(&mutex);
|
||||
pthread_cond_signal(&cond);
|
||||
pthread_mutex_unlock(&mutex);
|
||||
|
||||
ret = pthread_join(tid_display, NULL);
|
||||
assert(!ret);
|
||||
pthread_join(tid_display, NULL);
|
||||
|
||||
ret = pthread_cond_destroy(&cond);
|
||||
assert(!ret);
|
||||
ret = pthread_mutex_destroy(&mutex);
|
||||
assert(!ret);
|
||||
pthread_cond_destroy(&cond);
|
||||
pthread_mutex_destroy(&mutex);
|
||||
|
||||
ret = mpi_api->reset(mpi_ctx);
|
||||
assert(!ret);
|
||||
|
||||
ret = pthread_join(tid_frame, NULL);
|
||||
assert(!ret);
|
||||
pthread_join(tid_frame, NULL);
|
||||
|
||||
if (mpi_frm_grp) {
|
||||
ret = mpp_buffer_group_put(mpi_frm_grp);
|
||||
@ -432,13 +612,15 @@ void rk_cleanup() {
|
||||
mpi_frm_grp = NULL;
|
||||
for (i = 0; i < MAX_FRAMES; i++) {
|
||||
ret = drmModeRmFB(fd, frame_to_drm[i].fb_id);
|
||||
assert(!ret);
|
||||
if (ret) {
|
||||
perror("drmModeRmFB");
|
||||
}
|
||||
struct drm_mode_destroy_dumb dmdd = {0};
|
||||
dmdd.handle = frame_to_drm[i].handle;
|
||||
do {
|
||||
ret = ioctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dmdd);
|
||||
} while (ret == -1 && (errno == EINTR || errno == EAGAIN));
|
||||
assert(!ret);
|
||||
ret = drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dmdd);
|
||||
if (ret) {
|
||||
perror("drmIoctl(DRM_IOCTL_MODE_DESTROY_DUMB)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -446,37 +628,130 @@ void rk_cleanup() {
|
||||
mpp_destroy(mpi_ctx);
|
||||
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);
|
||||
drmModeFreePlaneResources(plane_resources);
|
||||
drmModeFreeEncoder(encoder);
|
||||
drmModeFreeConnector(connector);
|
||||
drmModeFreeCrtc(crtc);
|
||||
drmModeFreeResources(resources);
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
int rk_submit_decode_unit(PDECODE_UNIT decodeUnit) {
|
||||
|
||||
int result = DR_OK;
|
||||
PLENTRY entry = decodeUnit->bufferList;
|
||||
int length = 0;
|
||||
|
||||
if (decodeUnit->fullLength < READ_BUF_SIZE) {
|
||||
PLENTRY entry = decodeUnit->bufferList;
|
||||
int length = 0;
|
||||
while (entry != NULL) {
|
||||
memcpy(pkt_buf+length, entry->data, entry->length);
|
||||
length += entry->length;
|
||||
entry = entry->next;
|
||||
}
|
||||
if (length) {
|
||||
mpp_packet_set_pos(mpi_packet, pkt_buf);
|
||||
mpp_packet_set_length(mpi_packet, length);
|
||||
|
||||
while (MPP_OK != mpi_api->decode_put_packet(mpi_ctx, mpi_packet))
|
||||
;
|
||||
|
||||
}
|
||||
if (ensure_buf_size(&pkt_buf, &pkt_buf_size, decodeUnit->fullLength)) {
|
||||
// Buffer was reallocated, so update the mpp_packet accordingly
|
||||
mpp_packet_set_data(mpi_packet, pkt_buf);
|
||||
mpp_packet_set_size(mpi_packet, pkt_buf_size);
|
||||
}
|
||||
|
||||
while (entry != NULL) {
|
||||
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");
|
||||
}
|
||||
|
||||
// Adjust plane EOTF property
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "ffmpeg.h"
|
||||
|
||||
#include "../sdl.h"
|
||||
#include "../util.h"
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_thread.h>
|
||||
@ -28,10 +29,10 @@
|
||||
#include <unistd.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define DECODER_BUFFER_SIZE 92*1024
|
||||
#define SLICES_PER_FRAME 4
|
||||
|
||||
static char* ffmpeg_buffer;
|
||||
static void* ffmpeg_buffer;
|
||||
static size_t ffmpeg_buffer_size;
|
||||
|
||||
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) {
|
||||
@ -39,12 +40,7 @@ static int sdl_setup(int videoFormat, int width, int height, int redrawRate, voi
|
||||
return -1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
ensure_buf_size(&ffmpeg_buffer, &ffmpeg_buffer_size, INITIAL_DECODER_BUFFER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -54,36 +50,31 @@ static void sdl_cleanup() {
|
||||
}
|
||||
|
||||
static int sdl_submit_decode_unit(PDECODE_UNIT decodeUnit) {
|
||||
if (decodeUnit->fullLength < DECODER_BUFFER_SIZE) {
|
||||
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);
|
||||
PLENTRY entry = decodeUnit->bufferList;
|
||||
int length = 0;
|
||||
|
||||
if (SDL_LockMutex(mutex) == 0) {
|
||||
AVFrame* frame = ffmpeg_get_frame(false);
|
||||
if (frame != NULL) {
|
||||
sdlNextFrame++;
|
||||
ensure_buf_size(&ffmpeg_buffer, &ffmpeg_buffer_size, decodeUnit->fullLength + AV_INPUT_BUFFER_PADDING_SIZE);
|
||||
|
||||
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);
|
||||
} else
|
||||
fprintf(stderr, "Couldn't lock mutex\n");
|
||||
} else {
|
||||
fprintf(stderr, "Video decode buffer too small");
|
||||
exit(1);
|
||||
while (entry != NULL) {
|
||||
memcpy(ffmpeg_buffer+length, entry->data, entry->length);
|
||||
length += entry->length;
|
||||
entry = entry->next;
|
||||
}
|
||||
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;
|
||||
}
|
||||
@ -92,5 +83,5 @@ DECODER_RENDERER_CALLBACKS decoder_callbacks_sdl = {
|
||||
.setup = sdl_setup,
|
||||
.cleanup = sdl_cleanup,
|
||||
.submitDecodeUnit = sdl_submit_decode_unit,
|
||||
.capabilities = CAPABILITY_SLICES_PER_FRAME(SLICES_PER_FRAME) | CAPABILITY_REFERENCE_FRAME_INVALIDATION_AVC | CAPABILITY_DIRECT_SUBMIT,
|
||||
.capabilities = CAPABILITY_SLICES_PER_FRAME(SLICES_PER_FRAME) | CAPABILITY_REFERENCE_FRAME_INVALIDATION_HEVC | CAPABILITY_DIRECT_SUBMIT,
|
||||
};
|
||||
|
@ -33,6 +33,8 @@
|
||||
#define INIT_VDPAU 2
|
||||
#define INIT_VAAPI 3
|
||||
|
||||
#define INITIAL_DECODER_BUFFER_SIZE (256*1024)
|
||||
|
||||
#ifdef HAVE_X11
|
||||
int x11_init(bool vdpau, bool vaapi);
|
||||
extern DECODER_RENDERER_CALLBACKS decoder_callbacks_x11;
|
||||
|
@ -26,6 +26,7 @@
|
||||
|
||||
#include "../input/x11.h"
|
||||
#include "../loop.h"
|
||||
#include "../util.h"
|
||||
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xutil.h>
|
||||
@ -37,12 +38,12 @@
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
|
||||
#define DECODER_BUFFER_SIZE 92*1024
|
||||
#define X11_VDPAU_ACCELERATION ENABLE_HARDWARE_ACCELERATION_1
|
||||
#define X11_VAAPI_ACCELERATION ENABLE_HARDWARE_ACCELERATION_2
|
||||
#define SLICES_PER_FRAME 4
|
||||
|
||||
static char* ffmpeg_buffer = NULL;
|
||||
static void* ffmpeg_buffer = NULL;
|
||||
static size_t ffmpeg_buffer_size = 0;
|
||||
|
||||
static Display *display = NULL;
|
||||
static Window window;
|
||||
@ -82,11 +83,7 @@ int x11_init(bool vdpau, bool vaapi) {
|
||||
}
|
||||
|
||||
int x11_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
|
||||
ffmpeg_buffer = malloc(DECODER_BUFFER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE);
|
||||
if (ffmpeg_buffer == NULL) {
|
||||
fprintf(stderr, "Not enough memory\n");
|
||||
return -1;
|
||||
}
|
||||
ensure_buf_size(&ffmpeg_buffer, &ffmpeg_buffer_size, INITIAL_DECODER_BUFFER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE);
|
||||
|
||||
if (!display) {
|
||||
fprintf(stderr, "Error: failed to open X display.\n");
|
||||
@ -167,20 +164,23 @@ void x11_cleanup() {
|
||||
}
|
||||
|
||||
int x11_submit_decode_unit(PDECODE_UNIT decodeUnit) {
|
||||
if (decodeUnit->fullLength < DECODER_BUFFER_SIZE) {
|
||||
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);
|
||||
AVFrame* frame = ffmpeg_get_frame(true);
|
||||
if (frame != NULL)
|
||||
write(pipefd[1], &frame, sizeof(void*));
|
||||
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);
|
||||
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*));
|
||||
|
||||
return DR_OK;
|
||||
}
|
||||
|
||||
@ -188,7 +188,7 @@ DECODER_RENDERER_CALLBACKS decoder_callbacks_x11 = {
|
||||
.setup = x11_setup,
|
||||
.cleanup = x11_cleanup,
|
||||
.submitDecodeUnit = x11_submit_decode_unit,
|
||||
.capabilities = CAPABILITY_SLICES_PER_FRAME(SLICES_PER_FRAME) | CAPABILITY_REFERENCE_FRAME_INVALIDATION_AVC | CAPABILITY_DIRECT_SUBMIT,
|
||||
.capabilities = CAPABILITY_SLICES_PER_FRAME(SLICES_PER_FRAME) | CAPABILITY_REFERENCE_FRAME_INVALIDATION_HEVC | CAPABILITY_DIRECT_SUBMIT,
|
||||
};
|
||||
|
||||
DECODER_RENDERER_CALLBACKS decoder_callbacks_x11_vdpau = {
|
||||
|
2
third_party/SDL_GameControllerDB
vendored
2
third_party/SDL_GameControllerDB
vendored
@ -1 +1 @@
|
||||
Subproject commit 68d3f1e5d5e204a5975834c5a6cae572bb075445
|
||||
Subproject commit ae51c9942f0d985b2161d6b22986f041fc98ce5c
|
896
third_party/h264bitstream/h264_stream.c
vendored
896
third_party/h264bitstream/h264_stream.c
vendored
@ -24,16 +24,11 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "bs.h"
|
||||
#include "h264_stream.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.
|
||||
Zero or negative arguments return zero
|
||||
@ -554,7 +549,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( 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 )
|
||||
@ -1441,7 +1436,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( 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 )
|
||||
@ -1899,890 +1894,3 @@ 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 8abc371fb4c970b7cfae0860789e98fbb90ed231
|
||||
Subproject commit 8af4562af672dd6b9ed28553ead172984fd9a683
|
Loading…
x
Reference in New Issue
Block a user