Compare commits

...

145 Commits

Author SHA1 Message Date
Cameron Gutman
a6bf7154a7 Reduce MAX_FRAMES for RK backend
See b1d111b5f7 (r146373202)
2024-09-08 00:32:30 -05:00
Mingjie Shen
274d3db34d fix unbounded write of sprintf
Buffer write operations that do not control the length of data written
may overflow. Fix by replacing sprintf() with snprintf().
2024-03-25 00:11:49 -05:00
Cameron Gutman
014af67397 Fix build warnings 2024-02-19 22:01:31 -06:00
Cameron Gutman
eb4a202358 Version 2.7.0 2024-02-19 21:29:05 -06:00
Cameron Gutman
bac5360494 Provide better errors when RK renderer fails 2024-02-19 20:56:55 -06:00
Cameron Gutman
0e12282311 Remove a bunch of useless asserts 2024-02-19 20:49:25 -06:00
Cameron Gutman
883498d642 Replace SDL key handling with Moonlight Qt code
The existing code had a bunch of incorrectly mapped keys and was using
keysym instead of scancode which causes issues with non-US keyboards.
2024-02-19 16:16:02 -06:00
Cameron Gutman
55b49221d8 Link util.c into the platform libraries 2024-02-19 15:58:32 -06:00
Cameron Gutman
454f67b0ed Move CPU detection code into a separate file 2024-02-19 15:56:49 -06:00
Cameron Gutman
2f03600bae Ignore CRCs in SDL mappings 2024-02-19 15:45:17 -06:00
Cameron Gutman
fc904d2dac Treat devices as gamepads if they have a hat instead of an analog stick
Fixes #880
2024-02-18 14:56:57 -06:00
Cameron Gutman
d6c9650a32 Add rotation support for Rockchip
Fixes #878
2024-02-18 14:48:13 -06:00
Cameron Gutman
a9d6f17d5e Replace ioctl() retry loops with drmIoctl() 2024-02-18 14:30:59 -06:00
Cameron Gutman
ca4c517510 Update moonlight-common-c 2024-02-18 00:48:26 -06:00
Cameron Gutman
3b1b2ab51d Replace FindLibUUID.cmake with modified version from CMake project 2024-02-18 00:25:53 -06:00
Cameron Gutman
b533c52edd Add missing CMake include 2024-02-18 00:18:41 -06:00
Cameron Gutman
d89bd8d20f Update SDL_GameControllerDB 2024-02-17 23:56:23 -06:00
Cameron Gutman
8125d2194b Fix __builtin_cpu_supports(aes) on GCC 9 and earlier 2024-02-17 13:23:06 -06:00
Cameron Gutman
8fb2c72ca1 Update moonlight-common-c with RTSP encryption 2024-02-17 13:18:40 -06:00
Cameron Gutman
9255f774f2 Add fast AES detection for RISC-V using hwprobe() syscall 2024-01-23 02:46:05 -06:00
Cameron Gutman
d3d79c3224 Fix HAVE_GETAUXVAL codepath 2024-01-19 19:02:07 -06:00
Cameron Gutman
b08a04c378 Update moonlight-common-c with finalized encryption changes 2024-01-19 18:58:57 -06:00
amazingfate
5bd2799209 Support cursor plane on Rockchip platforms 2024-01-16 00:49:16 -06:00
Cameron Gutman
0712e05aab Opt in to video encryption on CPUs with good AES perf 2024-01-15 17:35:59 -06:00
Cameron Gutman
02cddf762b Update moonlight-common-c with new encryption support 2024-01-15 17:33:30 -06:00
armin-25689
810ef140cb style: make the code more prescriptive 2024-01-01 12:41:51 -06:00
armin-25689
50a7454bc1 build: use CMAKE_ARGS to control instead for FreeBSD 2024-01-01 12:41:51 -06:00
armin-25689
a610eddd97 build: change <endian.h> header file and __s32 type for FreeBSD 2024-01-01 12:41:51 -06:00
armin-25689
b3fb22d427 fix: it took so long time to connect to the Sunshine host in the FreeBSD
Disable reuse feature for FreeBSD to prevent excessive time from being spent connecting to the Sunshine host
2024-01-01 12:41:51 -06:00
armin-25689
3840b1409e feat: add oss audio callback for FreeBSD
OSS is the native sound system of the FreeBSD.It will be used on the x11_vaapi platform.It was added to reduce moonlight's dependency on FreeBSD.
2024-01-01 12:41:51 -06:00
Cameron Gutman
3b72f5195b Update moonlight-common-c with new bitrate logic 2023-12-03 22:26:22 -06:00
Cameron Gutman
000d9da4a0 Don't hardcode the signature length 2023-11-29 23:48:09 -06:00
Cameron Gutman
2aba7a10e7 Version 2.6.2 2023-11-03 01:08:34 -05:00
Cameron Gutman
0131274dce Update moonlight-common-c to fix multi-homed host bug 2023-11-03 00:56:23 -05:00
Cameron Gutman
dc03235a62 Fix several build warnings 2023-10-29 19:56:18 +00:00
Cameron Gutman
c007eabf50 Update moonlight-common-c to fix input reliability/performance issues 2023-10-25 22:55:38 -05:00
Cameron Gutman
d578fd8c7d Version 2.6.1 2023-10-07 21:12:37 -05:00
Cameron Gutman
807f9027c7 Update moonlight-common-c 2023-10-07 21:09:44 -05:00
Cameron Gutman
36c1636f3c Version 2.6.0 2023-09-01 18:40:56 -05:00
Cameron Gutman
19442743a6 Fix busy looping forever after an evdev device goes away 2023-09-01 18:07:25 -05:00
Cameron Gutman
74714e6fb1 Cleanup udev code a bit 2023-09-01 18:06:42 -05:00
Cameron Gutman
b1d111b5f7 Only use atomic API for HDR streams on RK backend 2023-08-31 21:32:12 -05:00
Cameron Gutman
eb46891f4c Add AV1 support for the AML backend 2023-08-31 19:39:53 -05:00
Cameron Gutman
c883b6786f Allow modesets to handle SDR/HDR transition 2023-08-31 18:51:32 -05:00
Cameron Gutman
861cdbc014 Set Colorspace connector property in RK backend 2023-08-31 18:15:25 -05:00
Cameron Gutman
41fddcf5ef Fix error check 2023-08-31 17:55:50 -05:00
Cameron Gutman
d1db1d5231 Add error handling for some RK init failures 2023-08-31 17:53:38 -05:00
Cameron Gutman
8ef8891969 Add AV1 support to Rockchip backend 2023-08-31 17:52:40 -05:00
Cameron Gutman
642b498aee Fix supportedVideoFormats value for HEVC/AV1 2023-08-31 17:51:51 -05:00
Cameron Gutman
68234081f8 Implement touch events for SDL backend 2023-08-30 00:03:49 -05:00
Cameron Gutman
3904c82ee9 Fix build with old SDL versions 2023-08-30 00:02:09 -05:00
Cameron Gutman
bf14d37cd5 Fix codec autoselection 2023-08-29 22:43:00 -05:00
Cameron Gutman
b1b3e274b3 Fix AVCodecContext leak 2023-08-29 22:37:18 -05:00
Cameron Gutman
93f5dc6cae Send controller arrival from SDL backend 2023-08-29 22:31:00 -05:00
Cameron Gutman
e15466e909 Send controller arrival from evdev backend 2023-08-29 21:45:55 -05:00
Cameron Gutman
859965a5a6 Support new controller features for SDL backend 2023-08-29 21:19:36 -05:00
Cameron Gutman
0deeb94655 Add support for extended Sunshine button flags 2023-08-29 20:47:31 -05:00
Cameron Gutman
c158833258 Try all decoders not just the first one that was compiled in 2023-08-29 20:47:09 -05:00
Cameron Gutman
ab53f149f2 Update moonlight-common-c with Sunshine extensions 2023-08-29 20:46:35 -05:00
Cameron Gutman
606e6d1181 Update SDL_GameControllerDB 2023-08-21 21:57:27 -05:00
Cameron Gutman
6538d855d8 Work around broken APT config on AppVeyor Ubuntu2004 image 2023-07-15 01:18:45 -05:00
Cameron Gutman
0169cedb27 Allow up to 16 gamepads with Sunshine hosts 2023-06-30 20:55:20 -05:00
Cameron Gutman
5bb47ce8b6 Update automatic bitrate logic to match Qt client 2023-03-12 16:23:33 -05:00
Cameron Gutman
0d0728f3c4 Set HDR metadata using legacy DRM API 2023-03-12 16:11:23 -05:00
Cameron Gutman
a585072ff8 Just log if atomic commit fails 2023-03-12 16:04:12 -05:00
Cameron Gutman
dc786e3033 Don't assert if the previous commit is still in progress 2023-03-12 14:50:05 -05:00
Cameron Gutman
dede55c7f9 Allow modesets for HDR entry/exit 2023-03-12 14:46:19 -05:00
Cameron Gutman
a328a53d59 Fix handling of 10-bit YUV buffers on newer RK devices 2023-03-12 14:20:32 -05:00
Cameron Gutman
6ea4a768e4 Support both NA12 and NV15 with runtime selection 2023-03-12 13:53:19 -05:00
Cameron Gutman
91022ea3bb Add HDR metadata struct for old libdrm headers 2023-03-12 13:44:30 -05:00
Cameron Gutman
6310edb0bd Set HDR metadata and HDMI ALLM for RK backend 2023-03-12 13:16:25 -05:00
Cameron Gutman
c779c1970d Change RK backend to use DRM atomic API 2023-03-12 12:58:51 -05:00
Cameron Gutman
1caf24beab Only use HEVC by default for accelerated X11 platforms 2023-03-06 21:43:50 -06:00
Cameron Gutman
f486c8e356 Only prefer SDL over unaccelerated X11 2023-03-06 21:43:02 -06:00
Cameron Gutman
5edf4d3dc7 Print FFmpeg decoder name 2023-03-06 21:26:18 -06:00
Cameron Gutman
0b80cc197a Try various other non-hwaccel FFmpeg decoders 2023-03-06 21:24:49 -06:00
Cameron Gutman
21eb932e52 Prefer SDL over X11 and enable HEVC for X11/SDL 2023-03-06 21:16:35 -06:00
Cameron Gutman
db52ac901f Update SDL_GameControllerDB 2023-03-05 23:53:46 -06:00
Cameron Gutman
2e0a014496 Update moonlight-common-c 2023-03-05 23:49:05 -06:00
Cameron Gutman
6ab37d0036 Accept both primary and overlay planes on Rockchip platforms
Fixes #861
2023-03-05 23:48:10 -06:00
Cameron Gutman
a9f8a77b66 Use upstreamed fourcc code for 10-bit NV12 on Rockchip 2023-03-05 23:46:34 -06:00
Cameron Gutman
a8a4e8908e
Fix copy-pasta 2023-03-04 12:41:08 -06:00
Cameron Gutman
b8442bfa95
Remove GFE-specific references from readme 2023-03-04 12:38:54 -06:00
Cameron Gutman
4c58ac9f39 Update SDL_GameControllerDB 2023-02-21 23:46:57 -06:00
Cameron Gutman
8384a243b2 Modernize HTTPS launch/resume for Sunshine 2023-02-21 23:46:31 -06:00
Cameron Gutman
32ebb00292 Add horizontal scrolling support with Sunshine 2023-02-21 23:31:34 -06:00
Cristi Mitrana
7988594e9e correct the video output rotation option
Should be `rotate` and not `rotation`.
2023-02-15 01:44:43 -06:00
Cameron Gutman
00c28f53d8 Further bugfixes and improvements for AML decoder 2022-12-04 14:49:35 -06:00
TheChoconut
a8d5f7e2e7 Implement ensure_buf_size in aml, lower eagain wait time 2022-12-04 14:30:54 -06:00
TheChoconut
4cb30a2a46 set disable_video to false on startup 2022-12-04 14:30:54 -06:00
TheChoconut
858c884099 update amlogic decoder 2022-12-04 14:30:54 -06:00
TheChoconut
5c23f0494d lazy load libamcodec, only require libamcodec.so 2022-12-04 14:30:54 -06:00
Cameron Gutman
e7c611bb1e Update SDL_GameControllerDB 2022-11-29 19:21:40 -06:00
Cameron Gutman
62007b912e Fix ensure_buf_size() usage in RK backend 2022-11-20 20:53:56 -06:00
Cameron Gutman
1feeab9c71 Fix more warnings 2022-11-20 20:41:21 -06:00
Cameron Gutman
c985b9ea0d Fix unchecked return value in aml 2022-11-20 19:56:04 -06:00
Cameron Gutman
66bc1b2904 Remove debug code in h264bitstream 2022-11-20 19:53:30 -06:00
Cameron Gutman
32e87994cd Consolidate and improve decode buffer handling 2022-11-20 19:48:36 -06:00
Cameron Gutman
098f53cd0b Fix several build warnings and minor bugs 2022-11-20 19:34:19 -06:00
Cameron Gutman
56f84ab662 Update moonlight-common-c with audio crash fix 2022-11-09 00:10:47 -06:00
Cameron Gutman
c3325a8887 Don't print error if zero samples are decoded 2022-11-09 00:10:03 -06:00
Cameron Gutman
f16dc469af Make blank_fb() into a more generic write_bool() function 2022-11-08 21:45:27 -06:00
Cameron Gutman
b2192eda25 Spawn a display thread if amlvideo is in the pipeline 2022-11-08 21:14:21 -06:00
Cameron Gutman
b26d747e95 Parse HDR option from moonlight.conf 2022-11-08 19:42:30 -06:00
Cameron Gutman
c2f21b955d Implement foundation for HDR support
Likely not functional for any decoder yet
2022-11-08 19:40:03 -06:00
Cameron Gutman
28ace51874 Add missing errno.h 2022-11-01 01:42:46 -05:00
Cameron Gutman
a9302d02f5 Fix handling of partial writes and EAGAIN in Amlogic decoder
Fixes spurious codec resets due to EAGAIN errors at 4K during complex scenes.
2022-11-01 00:35:25 -05:00
Cameron Gutman
30b563a2fc Tune ALSA parameters to avoid underruns
Fixes frequent underruns on Vero 4K
2022-11-01 00:11:08 -05:00
Cameron Gutman
cb4b5d55b5 Improve ALSA underrun error recovery
Fixes lack of audio on Vero 4K
2022-10-31 22:19:34 -05:00
Cameron Gutman
58958ca32a Update moonlight-common-c with improved loss recovery 2022-10-31 00:04:26 -05:00
Cameron Gutman
f021439d1b Allow a pairing attempt even if the PC is busy
Pairing while busy doesn't work with GFE but works with Sunshine
2022-10-12 22:28:30 -05:00
Cameron Gutman
13390594f5 Update SDL_GameControllerDB 2022-10-05 00:04:28 -05:00
Cameron Gutman
7ab48fb6d4 Use RFI for HEVC only
It is unstable on H.264 streams
2022-10-05 04:54:54 +00:00
Cameron Gutman
254f41686b Update moonlight-common-c with GFE 3.26 and HEVC RFI fixes 2022-10-05 04:53:01 +00:00
Cameron Gutman
758dc68958 Version 2.5.3 2022-09-23 22:59:19 -05:00
Cameron Gutman
0a9cadb729 Remove unused variable 2022-09-23 22:52:53 -05:00
Cameron Gutman
0325a3b88c Add support for custom ports with Sunshine 2022-09-23 22:48:43 -05:00
Cameron Gutman
3f00f25a39 Store port information in SERVER_DATA 2022-09-23 22:12:03 -05:00
Cameron Gutman
3859949b0f Fix amcodec detection on OSMC 2022-09-23 21:44:22 -05:00
Cameron Gutman
42980d09ee Remove semicolon from MIN_FRAME_BUFFER_COUNT definition 2022-09-23 21:31:52 -05:00
Cameron Gutman
60c4b514af Fix some grammatical errors 2022-09-23 21:30:44 -05:00
Cameron Gutman
9de64d25b9 Update SDL_GameControllerDB 2022-09-23 21:06:48 -05:00
Cameron Gutman
20a02acb07 Fix decoding errors on some devices when streaming from GFE 3.26 2022-09-22 23:30:21 -05:00
Cameron Gutman
b7cf7a130b Fix mishandling of IDR frames with a SEI or AUD NAL 2022-09-12 00:02:50 -05:00
Cameron Gutman
1b95f027a2 Fix handling of 3 byte Annex B start sequences 2022-09-05 22:53:25 +00:00
Cameron Gutman
4ebd3fb8ba Update SDL_GameControllerDB 2022-08-10 19:33:36 -05:00
kkoshelev
b9703e7a1e
Add --pin argument for easier pairing using frontends (#853)
* Add --pin flag to make pair easier for frontends

* Fix lint errors
2022-06-28 20:22:07 -05:00
Cameron Gutman
543dc087fc Increase decode buffer size to match Pi/MMAL decoders 2022-06-26 14:22:54 -05:00
Cameron Gutman
5fe7b36b40 Reduce use of magic numbers 2022-04-21 23:31:04 -05:00
Cameron Gutman
d74cc63038 Fix stack buffer overflow in pairing process 2022-04-21 23:06:31 -05:00
Cameron Gutman
039040e247 Replace usage of functions deprecated in OpenSSL 3.0 2022-04-21 23:04:23 -05:00
Cameron Gutman
22f75b74f9 Fix const warnings with FFmpeg 5.0 2022-04-21 22:13:21 -05:00
Cameron Gutman
6c215e47bf Update SDL_GameControllerDB 2022-03-15 23:19:54 -05:00
Cameron Gutman
44623c4a5e Update moonlight-common-c 2022-03-15 23:19:16 -05:00
Maciej Bogusz
5449b521aa Warn about invalid rotation values 2022-01-11 19:39:59 -06:00
Maciej Bogusz
786d4a66ec Add rotation in the sample configuration file 2022-01-11 19:39:59 -06:00
Laurent Camarasa
8323eeb23c Bugfix : let the program terminate if connection is lost when using SDL 2022-01-10 01:25:51 -06:00
Cameron Gutman
f871b663b1 Add absolute mouse mode support when ungrabbed 2022-01-09 21:09:48 -06:00
Cameron Gutman
23e6854a84 Switch ungrab key combo to Ctrl+Alt+Shift+Z like Moonlight Qt 2022-01-09 19:57:52 -06:00
Cameron Gutman
fe5dd11893 Add high resolution scrolling with SDL 2.0.18+ 2022-01-09 19:54:25 -06:00
Cameron Gutman
fbf6a2e2f7 Use new rumble API on SDL 2.0.9+ 2022-01-09 19:50:48 -06:00
Cameron Gutman
5761f533ab Grab mouse only once streaming has started 2022-01-09 19:41:56 -06:00
Cameron Gutman
bbdd7e5b24 Implement controller hotplugging for SDL 2022-01-09 19:41:35 -06:00
Cameron Gutman
634a0eee15 Fix finding libraries via pkg-config on Buster 2021-12-28 18:50:41 -06:00
Cameron Gutman
18fd1637a6 Prefer pkg-config for finding MMAL libraries 2021-12-28 18:07:16 -06:00
60 changed files with 2273 additions and 1854 deletions

View File

@ -1,10 +1,14 @@
cmake_minimum_required(VERSION 3.1)
project(moonlight-embedded VERSION 2.5.2 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)
@ -13,7 +17,6 @@ set(MOONLIGHT_DEFINITIONS)
find_package(ALSA)
find_package(Opus REQUIRED)
find_package(Broadcom-OMX)
find_package(Broadcom-MMAL)
find_package(Freescale)
find_package(Amlogic)
find_package(Rockchip)
@ -51,6 +54,11 @@ 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)
set(SOFTWARE_FOUND FALSE)
@ -81,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)
@ -92,7 +116,7 @@ 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})
@ -103,26 +127,26 @@ 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(BROADCOM-MMAL_FOUND)
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})
@ -131,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})
@ -187,12 +211,12 @@ if (PULSE_FOUND)
target_link_libraries(moonlight ${PULSE_LIBRARIES})
endif()
if (AMLOGIC_FOUND OR BROADCOM-OMX_FOUND OR BROADCOM-MMAL_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-OMX_FOUND AND NOT BROADCOM-MMAL_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()

View File

@ -2,30 +2,14 @@
[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/uaph3i3lfu7gl7m7/branch/master?svg=true)](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.

View File

@ -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'

View File

@ -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})

View File

@ -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()

View File

@ -2,7 +2,7 @@ find_path(BROADCOM_INCLUDE_DIR
NAMES bcm_host.h
DOC "Broadcom include directory"
PATHS /opt/vc/include)
mark_as_advanced(BCM_INCLUDE_DIR)
mark_as_advanced(BROADCOM_INCLUDE_DIR)
find_library(VCOS_LIBRARY
NAMES libvcos.so
@ -35,7 +35,7 @@ find_library(MMAL_VC_CLIENT_LIBRARY
mark_as_advanced(MMAL_VC_CLIENT_LIBRARY)
include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Broadcom-MMAL DEFAULT_MSG BROADCOM_INCLUDE_DIR VCOS_LIBRARY MMAL_CORE_LIBRARY MMAL_UTIL_LIBRARY MMAL_VC_CLIENT_LIBRARY BCM_HOST_LIBRARY)
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(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(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})

View File

@ -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>]

View File

@ -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})

View File

@ -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", &currentGameText) != 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", &currentGameText) != 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);
}

View File

@ -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);

View File

@ -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;
}

View File

@ -21,4 +21,4 @@
#define MAX_ADDRESS_SIZE 40
void gs_discover_server(char* dest);
void gs_discover_server(char* dest, unsigned short* port);

View File

@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -25,8 +25,6 @@
#define STATUS_OK 200
static XML_Parser parser;
struct xml_query {
char *memory;
size_t size;

View File

@ -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)

View File

@ -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);
}
}

View File

@ -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

View File

@ -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
View 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

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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));
}
}
}

View File

@ -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;

View File

@ -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,
};

View File

@ -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
View 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
View 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);

View File

@ -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");

View File

@ -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,8 +70,13 @@ 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;
@ -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));
}
@ -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);

View File

@ -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");
}

View File

@ -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;

View File

@ -26,6 +26,18 @@
#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 {
unsigned char leftTrigger, rightTrigger;
@ -33,53 +45,387 @@ typedef struct _GAMEPAD_STATE {
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
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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,7 +395,11 @@ 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) {

View File

@ -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;
}

View File

@ -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);

View File

@ -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)

View File

@ -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;
}

View File

@ -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);

View File

@ -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,
};

View File

@ -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);

View File

@ -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++) {

View File

@ -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) {

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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");

View File

@ -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, &param);
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;
}

View File

@ -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,
};

View File

@ -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;

View File

@ -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 = {

@ -1 +1 @@
Subproject commit aecb6b0df26c20f83868111ae4c8b66649c59e69
Subproject commit ae51c9942f0d985b2161d6b22986f041fc98ce5c

View File

@ -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) );
}
}
}

@ -1 +1 @@
Subproject commit 8abc371fb4c970b7cfae0860789e98fbb90ed231
Subproject commit 8af4562af672dd6b9ed28553ead172984fd9a683