Compare commits

...

247 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
Cameron Gutman
807565de8b Version 2.5.2 2021-12-20 00:24:48 +00:00
Cameron Gutman
1d32f894f7 Update SDL_GameControllerDB 2021-12-19 16:24:01 -06:00
Cameron Gutman
89030f0701 Fix Raspbian CI build 2021-12-19 16:21:58 -06:00
Cameron Gutman
ba8a23725d Fix build on Bullseye 2021-12-19 16:11:00 -06:00
Cameron Gutman
81322a2b91 Remove redundant match expression 2021-09-18 09:25:56 -05:00
TheChoconut
d58d8f70e8
Added options for optional dependencies. (#829) 2021-09-18 09:22:40 -05:00
Cameron Gutman
76b0de3566 Triggers should be unsigned types 2021-09-16 01:28:09 -05:00
Cameron Gutman
bf7149e75d Fix type of halfaxis mapping values 2021-09-16 01:19:08 -05:00
TheChoconut
87613b3176 Flush pair code for external scripts 2021-08-21 08:26:58 -05:00
Cameron Gutman
4164dc512f Version 2.5.1 2021-08-08 12:08:08 -05:00
Cameron Gutman
81a49e4564 Add parameter to disable gamepad mouse emulation 2021-08-08 12:03:56 -05:00
Cameron Gutman
3611f1dd89 Fix null termination issue if /proc/cpuinfo was over 4KB 2021-08-08 11:47:03 -05:00
Cameron Gutman
4b41692fd8 Fix handling of buttons outside the normal joystick range
See aa03b9d7af

Fixes #788
Fixes #696
Fixes #675
Fixes #693
Fixes #642
2021-08-08 11:26:07 -05:00
Cameron Gutman
d45567b5ee Revert "Fix pressing unmapped buttons causing unexpected inputs"
Instead, we should not exclude certain buttons from mappings.

This reverts commit 21b6af349358a0da2552a45e5448dc551f490253.
2021-08-08 11:16:57 -05:00
Cameron Gutman
21b6af3493 Fix pressing unmapped buttons causing unexpected inputs 2021-08-08 11:07:13 -05:00
Cameron Gutman
6f15b257d7 Add DRM_FORMAT_NV12_10 definition for RK backend
Fixes #742
2021-08-08 10:05:43 -05:00
Cameron Gutman
3b51b40178 Update SDL_GameControllerDB 2021-08-08 09:34:56 -05:00
Cameron Gutman
7ff0bcd8fd Fix initialization of mapping structs 2021-08-07 18:08:55 -05:00
TheChoconut
469b2ed866 Mouse emulation support for evdev 2021-08-03 17:36:07 -05:00
Cameron Gutman
3a63466fe2 Version 2.5.0 2021-07-25 19:15:53 -05:00
Cameron Gutman
cd2f1bb012 Add a verbose print when a device is ignored as an accelerometer 2021-07-25 19:13:40 -05:00
Cameron Gutman
663e4b4b7a Avoid mapping PS4 trackpad as a gamepad 2021-07-25 19:10:01 -05:00
Cameron Gutman
836e61b7c3 Remove gamepad from host when removed from client 2021-07-25 18:54:39 -05:00
Cameron Gutman
12efeda8e1 Ignore accelerometers and improve debugging of player numbers 2021-07-25 18:49:11 -05:00
Cameron Gutman
4dd6ab97df Fix over-counting of evdev gamepads on stream start 2021-07-25 18:33:52 -05:00
Cameron Gutman
45c1a504e9 Avoid stuck keys on disconnect and remove useless sleep 2021-07-25 18:03:07 -05:00
Cameron Gutman
781afc7c66 Fix surround channel mappings on PulseAudio 2021-07-25 17:51:22 -05:00
Cameron Gutman
0698e8b43f Fix PulseAudio when manually specifying the audio device 2021-07-25 17:30:46 -05:00
Cameron Gutman
86140eb192 Fix Pi fallback to ALSA/PA after 7109301a 2021-07-25 17:15:04 -05:00
Cameron Gutman
e8fbb2ec9c Add half-axis support for dpad 2021-07-25 17:04:43 -05:00
Cameron Gutman
35af4c4ede Add half-axis support for triggers 2021-07-25 16:47:06 -05:00
Cameron Gutman
e2c2575efb Add friendly messages for certain error cases 2021-07-25 15:25:13 -05:00
Cameron Gutman
5215271b60 Add meta modifiers 2021-07-25 15:15:14 -05:00
Cameron Gutman
b71862d622 Fix left vs right modifiers, insert key, and super keys 2021-07-25 15:03:26 -05:00
Cameron Gutman
30da7e2bc2 Don't prefix GPU type with 'NVIDIA' 2021-07-25 15:02:30 -05:00
Cameron Gutman
0cfc557c9c Don't fail if SOPS isn't supported at the current resolution 2021-07-25 15:01:46 -05:00
Cameron Gutman
1478d69123 Pass RTSP session URL to moonlight-common-c 2021-07-25 14:22:39 -05:00
Cameron Gutman
fb57f3cb4d Add arbitrary audio duration support to all audio backends 2021-07-25 14:01:19 -05:00
Cameron Gutman
4e09dccfc0 Use moonlight-common-c definition for max channel count 2021-07-25 13:33:35 -05:00
Cameron Gutman
d1937cb8e6 Fix usage help after remote and FPS default changes 2021-07-25 13:28:58 -05:00
Cameron Gutman
ef719dc938 Cleanup and optimize FFmpeg code 2021-07-25 13:26:45 -05:00
Cameron Gutman
66ec344a31 Remove old reference to ENET_LIBRARIES 2021-07-25 13:25:27 -05:00
Cameron Gutman
8602c85714 Avoid deprecated av_init_packet() function 2021-07-25 13:17:21 -05:00
Cameron Gutman
48de7760dc Add Raspbian package build to CI 2021-07-24 18:38:34 -05:00
Cameron Gutman
572644800d Add AppVeyor CI 2021-07-24 17:48:40 -05:00
Cameron Gutman
30464979dc Enable audio encryption unless on a slow CPU 2021-07-24 16:25:03 -05:00
Cameron Gutman
b907c4b608 Use the same remote streaming and packet size defaults as other clients 2021-07-24 15:57:55 -05:00
Cameron Gutman
56d1fee17b Default to 60 FPS at all resolutions
The time of most hardware maxing at 1080p30 has long since passed.
2021-07-24 15:40:59 -05:00
Cameron Gutman
2fa276182f Fix ALSA and PA mappings for 7.1 surround sound 2021-07-24 15:35:27 -05:00
Cameron Gutman
0ebd86184b Update SDL_GameControllerDB and convert it to a submodule 2021-07-24 15:17:51 -05:00
Cameron Gutman
1cb4699057 Start plumbing 7.1 surround sound support 2021-07-24 08:14:03 -05:00
Cameron Gutman
01d43ff596 FFmpeg VAAPI doesn't need slices and is incompatible with RFI 2021-07-24 07:56:41 -05:00
Cameron Gutman
98092f8baf FFmpeg software decoding doesn't support RFI for HEVC 2021-07-24 07:55:52 -05:00
Cameron Gutman
83982d3981 Change default value of 'unsupported' to true to match other clients 2021-07-24 07:50:55 -05:00
Cameron Gutman
fc44f010f8 Update SPS fixup code to match current Qt/Android version 2021-07-24 07:39:03 -05:00
Cameron Gutman
615ed824ac Use 48KHz sample rate for PA test to match the actual stream 2021-07-24 07:33:07 -05:00
Cameron Gutman
7109301a46 Never use audio callbacks for fake platform 2021-07-24 07:31:01 -05:00
Cameron Gutman
e84ae26ccc Avoid needless EVP_get_digestbyname() and EVP_DigestInit_ex() calls 2021-07-24 07:27:03 -05:00
Cameron Gutman
faa7eef9a4 Don't add X509v3 extensions
There is something wrong with the combination of extension we add
that causes OpenSSL to believe these certificates were issued by
a CA and fail to validate them because it can't find the issuing
CA cert.

The certificates work fine without the extensions, so just don't
add them (which is what other Moonlight clients do).
2021-07-24 07:21:52 -05:00
Cameron Gutman
296c8de759 Fix copying rikeyid value 2021-07-24 07:13:55 -05:00
Iwan Timmer
b4ef1dde42 Update urls in docs 2021-07-22 21:10:24 +02:00
Iwan Timmer
d7f9bc0b25 Switch back to upstream moonlight-common-c 2021-07-22 21:04:08 +02:00
irtimmer
8f4d9aa5ee
Merge pull request #816 from mariotaku/master
Randomized input IV
2021-07-20 18:34:33 +02:00
Mariotaku Lee
660cc7de41 Added missing import 2021-04-29 03:56:45 +09:00
Mariotaku Lee
a97b2cc3af Randomized input IV 2021-04-29 03:43:40 +09:00
irtimmer
7c8795febc
Merge pull request #814 from mariotaku/master
Fixed trigger left/right range
2021-04-21 19:41:51 +02:00
Iwan Timmer
5baa2c9023 Merge branch 'clientid' 2021-04-21 19:40:44 +02:00
KiralyCraft
338063fc2c Changed random Client ID to the same as official Moonlight 2021-04-21 19:39:37 +02:00
Mariotaku Lee
9cec4208a4 Fixed trigger left/right range
Added .ignore for common build dir
2021-04-19 15:41:53 +09:00
Iwan Timmer
f9a20348ab moonlight-common-c: merge new upstream changes 2021-04-14 11:21:32 +02:00
Romain Tisserand
5b6639c8a7 Fix gcc-10 compilation (fno-common flag enabled by default) 2020-07-11 21:29:09 +02:00
Cameron Gutman
957bc49da9 Fix frame rate locked to 60 FPS with GFE 3.20.3 2020-05-25 21:38:14 -07:00
Cameron Gutman
0c6f98e544 Fix surround sound on a resumed session 2020-04-23 19:29:07 -07:00
Iwan Timmer
14644f1269 Update moonlight-common-c 2020-04-19 11:56:55 +02:00
Iwan Timmer
95104759ab Fix missed merging conflicts
Fixes commit 4711129
2020-04-19 11:37:02 +02:00
Iwan Timmer
4711129d14 Merge remote-tracking branch 'tomblind/touchscreen' 2020-04-18 21:43:09 +02:00
Tom
5beb32d066 Emulating right click on touch screens with a long press 2020-04-18 07:34:52 -06:00
Tom
6b8260c1e5 refactored touchscreen code for clarity and safety, and removed hacked in rotation 2020-04-18 07:03:59 -06:00
Tom
b22b07e5a0 Applied display rotation to mouse movement in evdev 2020-04-17 16:50:04 -06:00
Tom
aae24557f9 Added transform flag to mmal display settings so rotation takes effect 2020-04-17 12:57:51 -06:00
Tom
4cd2d4b8dc Included video.h in pi and mmal to give access to rotation flags 2020-04-17 11:19:01 -06:00
Tom
ceacef6329 Added display rotation support to mmal decoder 2020-04-17 10:59:15 -06:00
Tom
4bfeee690a Added command line option to control omx rotation 2020-04-17 10:44:16 -06:00
Tom
c9a624595b initial touchscreen support (rotated 90 degrees for the moment) 2020-04-13 13:16:25 -06:00
Tom
a205edc1c7 forced omx rotation to 90 degrees 2020-04-13 13:03:32 -06:00
Iwan Timmer
7f856d3284 Warn about unsupported resolutions in combination with SOPS
When using unsupported resolutions SOPS will default to 720p60
2020-03-17 22:37:18 +01:00
Iwan Timmer
a66d75b145 Sent max FPS value of 60 when using SOPS
SOPS defaults to 720p60 when using FPS values higher than 60.
This FPS value isn't used for the actual stream so higher FPS is still working.
2020-03-17 22:29:32 +01:00
Iwan Timmer
922cd29adc Add MMAL video decoder 2020-03-17 22:17:34 +01:00
Iwan Timmer
70e54ef300 Update moonlight-common-c 2020-03-09 20:33:27 +01:00
Iwan Timmer
8f9e2704a6 Remove references to fora
Most referenced fora are currently rather quiet and therefore not very helpfull for new users
2020-03-09 20:13:01 +01:00
Iwan Timmer
3a86fecd9e Add reference to source of gamecontrollerdb.txt 2020-01-05 22:05:04 +01:00
Albert Andaluz
4528b78d81 Added git branch name and short hash to moonlight binary 2020-01-05 22:00:30 +01:00
Albert Andaluz
fe1c203b10 when sending CTRL+ALT+SHIFT+Q key sequence for disconnecting from the remote host, always send a keydown event to avoid leaving the remote host keyboard in key_UP state. (seen with Geforce Experience 3.17) 2020-01-05 22:00:30 +01:00
Hugo Hromic
ecf09e8907 Add option for disabling all input (view-only mode) in streaming session
* By default this option is set to `false` and can be enabled using the
  new `-viewonly`/`viewonly` command-line/config options.
* When enabled, none of the input modules will be initialised, making
  the streaming session view-only, i.e. no input is sent to host.

To facilitate implementation, SDL initialisation with `sdl_init()` was moved
a bit earlier in the code to decouple from input initialisation.
2019-10-05 21:23:36 +01:00
Hugo Hromic
4ee5015557 Clarify -localaudio option in help text
* It makes audio play on host computer instead of remote client
2019-10-05 19:13:37 +01:00
Hugo Hromic
6c8b3bb29d Remove redundant config parsing conditionals
* They are already covered in the last `else` block
2019-10-05 19:13:37 +01:00
Hugo Hromic
434dba31de General codebase clean-ups (no code changes)
* Remove trailing spaces
* Replace TABs with spaces
* Add missing indentation
2019-10-05 19:13:24 +01:00
Hugo Hromic
c8e090a5e1 Minor config codebase clean-ups for consistency
* Improve readability of conditional operators
* Remove unnecessary trailing semi-colon in #define
* Use NULL-constant instead of zero-value
2019-10-05 19:12:48 +01:00
Hugo Hromic
243ef8ae0b
Also grab mouse input devices (fixes #756) 2019-09-12 17:41:00 +01:00
Iwan Timmer
eb94742266 Merge remote-tracking branch 'cgutman/sha256_buster' 2019-07-06 16:55:41 +02:00
Cameron Gutman
519a14a427 Use a SHA256 signature for the client certificate 2019-07-05 13:36:49 -07:00
5schatten
52efef7a66 video/rockchip: define READ_BUF_SIZE as a size of 0x00100000
If you use SZ_1M to define READ_BUF_SIZE you also need to include the sizes.h header file which is apparently missing.
2019-07-03 18:00:12 +02:00
74 changed files with 3559 additions and 2418 deletions

View File

@ -1,7 +1,7 @@
# Contribution Guide
## Got a Question or Problem?
Please take a look at the [wiki](https://github.com/irtimmer/moonlight-embedded/wiki) for answers to your questions about using Moonlight Embedded.
Please take a look at the [wiki](https://github.com/moonlight-stream/moonlight-embedded/wiki) for answers to your questions about using Moonlight Embedded.
If you still have questions about Moonlight Embedded, please use one of the different forums discussing Moonlight Embedded.

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
build/

5
.gitmodules vendored
View File

@ -1,3 +1,6 @@
[submodule "common"]
path = third_party/moonlight-common-c
url = https://github.com/irtimmer/moonlight-common-c.git
url = https://github.com/moonlight-stream/moonlight-common-c.git
[submodule "third_party/SDL_GameControllerDB"]
path = third_party/SDL_GameControllerDB
url = https://github.com/gabomdq/SDL_GameControllerDB.git

View File

@ -1,8 +1,13 @@
cmake_minimum_required(VERSION 3.1)
project(moonlight-embedded VERSION 2.4.10 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)
@ -11,25 +16,48 @@ set(MOONLIGHT_DEFINITIONS)
find_package(ALSA)
find_package(Opus REQUIRED)
find_package(Broadcom)
find_package(Broadcom-OMX)
find_package(Freescale)
find_package(Amlogic)
find_package(Rockchip)
find_package(PkgConfig REQUIRED)
option(ENABLE_SDL "Compile SDL support" ON)
option(ENABLE_FFMPEG "Compile FFMPEG support" ON)
option(ENABLE_X11 "Compile X11 support (requires ENABLE_FFMPEG)" ON)
option(ENABLE_CEC "Compile CEC support" ON)
option(ENABLE_PULSE "Compile PulseAudio support" ON)
pkg_check_modules(EVDEV REQUIRED libevdev)
pkg_check_modules(UDEV REQUIRED libudev)
pkg_check_modules(SDL sdl2>=2.0.4)
pkg_check_modules(AVCODEC libavcodec)
pkg_check_modules(AVUTIL libavutil)
pkg_check_modules(XLIB x11)
pkg_check_modules(VDPAU vdpau)
pkg_check_modules(LIBVA libva)
pkg_check_modules(LIBVA_X11 libva-x11)
pkg_check_modules(PULSE libpulse-simple)
pkg_check_modules(CEC libcec>=4)
pkg_check_modules(EGL egl)
pkg_check_modules(GLES glesv2)
if (ENABLE_SDL)
pkg_check_modules(SDL sdl2>=2.0.4)
endif()
if (ENABLE_FFMPEG)
pkg_check_modules(AVCODEC libavcodec)
pkg_check_modules(AVUTIL libavutil)
pkg_check_modules(VDPAU vdpau)
pkg_check_modules(LIBVA libva)
pkg_check_modules(EGL egl)
pkg_check_modules(GLES glesv2)
if (ENABLE_X11)
pkg_check_modules(XLIB x11)
pkg_check_modules(LIBVA_X11 libva-x11)
endif()
endif()
if (ENABLE_PULSE)
pkg_check_modules(PULSE libpulse-simple)
endif()
if (ENABLE_CEC)
pkg_check_modules(CEC libcec>=4)
endif()
pkg_check_modules(MMAL mmal)
if (NOT MMAL_FOUND)
find_package(MMAL)
endif()
set(VDPAU_ACCEL_FOUND FALSE)
set(VA_ACCEL_FOUND FALSE)
@ -58,8 +86,25 @@ include_directories("${PROJECT_BINARY_DIR}")
add_subdirectory(libgamestream)
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)
@ -71,28 +116,37 @@ endif()
if(AMLOGIC_FOUND)
list(APPEND MOONLIGHT_DEFINITIONS HAVE_AML)
list(APPEND MOONLIGHT_OPTIONS AML)
add_library(moonlight-aml SHARED ./src/video/aml.c ${ILCLIENT_SRC_LIST})
add_library(moonlight-aml SHARED ./src/video/aml.c ./src/util.c ${ILCLIENT_SRC_LIST})
target_include_directories(moonlight-aml PRIVATE ${AMLOGIC_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR})
target_link_libraries(moonlight-aml gamestream ${AMLOGIC_LIBRARIES})
set_property(TARGET moonlight-aml PROPERTY COMPILE_DEFINITIONS ${AMLOGIC_DEFINITIONS})
install(TARGETS moonlight-aml DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif()
if(BROADCOM_FOUND)
if(BROADCOM-OMX_FOUND)
list(APPEND MOONLIGHT_DEFINITIONS HAVE_PI)
list(APPEND MOONLIGHT_OPTIONS PI)
aux_source_directory(./third_party/ilclient ILCLIENT_SRC_LIST)
add_library(moonlight-pi SHARED ./src/video/pi.c ./src/audio/omx.c ${ILCLIENT_SRC_LIST})
add_library(moonlight-pi SHARED ./src/video/pi.c ./src/audio/omx.c ./src/util.c ${ILCLIENT_SRC_LIST})
target_include_directories(moonlight-pi PRIVATE ./third_party/ilclient ${BROADCOM_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR} ${OPUS_INCLUDE_DIRS})
target_link_libraries(moonlight-pi gamestream ${BROADCOM_LIBRARIES} ${OPUS_LIBRARY})
set_property(TARGET moonlight-pi PROPERTY COMPILE_DEFINITIONS ${BROADCOM_DEFINITIONS})
target_link_libraries(moonlight-pi gamestream ${BROADCOM_OMX_LIBRARIES} ${OPUS_LIBRARY})
set_property(TARGET moonlight-pi PROPERTY COMPILE_DEFINITIONS ${BROADCOM_OMX_DEFINITIONS})
install(TARGETS moonlight-pi DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif()
if(MMAL_FOUND)
list(APPEND MOONLIGHT_DEFINITIONS HAVE_MMAL)
list(APPEND MOONLIGHT_OPTIONS MMAL)
add_library(moonlight-mmal SHARED ./src/video/mmal.c ./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})
@ -101,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})
@ -157,12 +211,12 @@ if (PULSE_FOUND)
target_link_libraries(moonlight ${PULSE_LIBRARIES})
endif()
if (AMLOGIC_FOUND OR BROADCOM_FOUND OR FREESCALE_FOUND OR ROCKCHIP_FOUND OR X11_FOUND)
if (AMLOGIC_FOUND OR BROADCOM-OMX_FOUND OR MMAL_FOUND OR FREESCALE_FOUND OR ROCKCHIP_FOUND OR X11_FOUND)
list(APPEND MOONLIGHT_DEFINITIONS HAVE_EMBEDDED)
list(APPEND MOONLIGHT_OPTIONS EMBEDDED)
endif()
if(NOT AMLOGIC_FOUND AND NOT BROADCOM_FOUND AND NOT FREESCALE_FOUND AND NOT ROCKCHIP_FOUND AND NOT SOFTWARE_FOUND)
if(NOT AMLOGIC_FOUND AND NOT BROADCOM-OMX_FOUND AND NOT MMAL_FOUND AND NOT FREESCALE_FOUND AND NOT ROCKCHIP_FOUND AND NOT SOFTWARE_FOUND)
message(FATAL_ERROR "No video output available")
endif()
@ -175,5 +229,5 @@ target_link_libraries(moonlight ${EVDEV_LIBRARIES} ${OPUS_LIBRARY} ${UDEV_LIBRAR
add_subdirectory(docs)
install(TARGETS moonlight DESTINATION ${CMAKE_INSTALL_BINDIR})
install(FILES gamecontrollerdb.txt DESTINATION ${CMAKE_INSTALL_DATADIR}/moonlight)
install(FILES ./third_party/SDL_GameControllerDB/gamecontrollerdb.txt DESTINATION ${CMAKE_INSTALL_DATADIR}/moonlight)
install(FILES moonlight.conf DESTINATION ${CMAKE_INSTALL_SYSCONFDIR})

View File

@ -1,49 +1,24 @@
# Moonlight Embedded
Moonlight Embedded is an open source implementation of NVIDIA's GameStream, as used by the NVIDIA Shield, but built for Linux.
[![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 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 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 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/irtimmer/moonlight-embedded/wiki).
## Requirements
* [GFE compatible](http://shield.nvidia.com/play-pc-games/) computer with GTX 600/700/900/1000 series GPU (for the PC you're streaming from)
* High-end wireless router (802.11n dual-band recommended) or wired network
* Geforce Experience 2.1.1 or higher
## Quick Start
* Ensure your GFE server and client are on the same network
* Turn on Shield Streaming in the GFE settings
* Pair Moonlight Embedded with the GFE server
* Accept the pairing confirmation on your PC
* Connect to the GFE Server with Moonlight Embedded
* Play games!
More information about installing and runnning Moonlight Embedded is available on the [wiki](https://github.com/moonlight-stream/moonlight-embedded/wiki).
## Bugs
Please check the fora, wiki and old bug reports before submitting a new bug report.
Please check the wiki and old bug reports before submitting a new bug report.
Bugs can be reported to the [issue tracker](https://github.com/irtimmer/moonlight-embedded/issues).
Bugs can be reported to the [issue tracker](https://github.com/moonlight-stream/moonlight-embedded/issues).
## See also
[Moonlight-common-c](https://github.com/moonlight-stream/moonlight-common-c) is the shared codebase between
different C implementations of Moonlight
[Moonlight-common-c](https://github.com/irtimmer/moonlight-common-c) is the fork used by Moonlight Embedded
## Discussion
[Discord](https://discord.gg/6ERtzFY) Moonlight in General with Moonlight Embedded channel
[XDA](http://forum.xda-developers.com/showthread.php?t=2505510) Moonlight in General
[Raspberry Pi Forum](http://www.raspberrypi.org/forums/viewtopic.php?f=78&t=65878) Moonlight Embedded for Raspberry Pi
[SolidRun Community](http://www.solid-run.com/community/viewtopic.php?f=13&t=1489&p=11173) Moonlight Embedded for Cubox-i and Hummingboard
[ODROID Forum](http://forum.odroid.com/viewtopic.php?f=91&t=15456) Moonlight Embedded on ODROID
[Moonlight-common-c](https://github.com/moonlight-stream/moonlight-common-c) is the shared codebase between different Moonlight implementations
## Contribute

30
appveyor.yml Normal file
View File

@ -0,0 +1,30 @@
version: 0.0.0.{build}
clone_depth: 1
environment:
matrix:
- APPVEYOR_BUILD_WORKER_IMAGE: Ubuntu2004
PACKAGES: libssl-dev libopus-dev libasound2-dev libudev-dev libavahi-client-dev libcurl4-openssl-dev libevdev-dev libexpat1-dev libpulse-dev uuid-dev cmake gcc g++ libavcodec-dev libavutil-dev libsdl2-dev libva-dev libvdpau-dev libcec-dev libp8-platform-dev
BUILD_TARGET: ubuntu
- APPVEYOR_BUILD_WORKER_IMAGE: Ubuntu2004
PACKAGES: qemu binfmt-support qemu-user-static
BUILD_TARGET: raspbian
install:
- 'sudo apt update || true'
- 'sudo apt install -y $PACKAGES'
- '[ "$BUILD_TARGET" != raspbian ] || docker run --rm --privileged multiarch/qemu-user-static --reset -p yes'
before_build:
- 'git submodule update --init --recursive'
build_script:
- 'if [[ "$BUILD_TARGET" = ubuntu ]]; then mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX=/tmp .. && make -j$(nproc) && make install; fi'
- 'if [[ "$BUILD_TARGET" = raspbian ]]; then git clone --recursive https://github.com/cgutman/moonlight-embedded-packaging.git && cd moonlight-embedded-packaging && sh -c "./build-rpi-buster.sh $APPVEYOR_REPO_COMMIT"; fi'
after_build:
- sh: '[ "$BUILD_TARGET" != raspbian ] || appveyor PushArtifact out_*/moonlight-embedded_*.deb'
- sh: '[ "$BUILD_TARGET" != raspbian ] || appveyor PushArtifact out_*/moonlight-embedded-dbgsym_*.deb'
deploy: off

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

@ -29,8 +29,8 @@ find_library(BCM_HOST_LIBRARY
mark_as_advanced(BCM_HOST_LIBRARY)
include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Broadcom DEFAULT_MSG BROADCOM_INCLUDE_DIR VCOS_LIBRARY VCHIQ_LIBRARY OPENMAXIL_LIBRARY BCM_HOST_LIBRARY)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Broadcom-OMX DEFAULT_MSG BROADCOM_INCLUDE_DIR VCOS_LIBRARY VCHIQ_LIBRARY OPENMAXIL_LIBRARY BCM_HOST_LIBRARY)
set(BROADCOM_LIBRARIES ${BCM_HOST_LIBRARY} ${OPENMAXIL_LIBRARY} ${VCHIQ_LIBRARY} ${VCOS_LIBRARY})
set(BROADCOM_OMX_LIBRARIES ${BCM_HOST_LIBRARY} ${OPENMAXIL_LIBRARY} ${VCHIQ_LIBRARY} ${VCOS_LIBRARY})
set(BROADCOM_INCLUDE_DIRS ${BROADCOM_INCLUDE_DIR} ${BROADCOM_INCLUDE_DIR}/interface/vmcs_host/linux ${BROADCOM_INCLUDE_DIR}/interface/vcos/pthreads)
set(BROADCOM_DEFINITIONS USE_VCHIQ_ARM HAVE_LIBOPENMAX=2 OMX OMX_SKIP64BIT USE_EXTERNAL_OMX HAVE_LIBBCM_HOST USE_EXTERNAL_LIBBCM_HOST)
set(BROADCOM_OMX_DEFINITIONS USE_VCHIQ_ARM HAVE_LIBOPENMAX=2 OMX OMX_SKIP64BIT USE_EXTERNAL_OMX HAVE_LIBBCM_HOST USE_EXTERNAL_LIBBCM_HOST)

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

41
cmake/FindMMAL.cmake Normal file
View File

@ -0,0 +1,41 @@
find_path(BROADCOM_INCLUDE_DIR
NAMES bcm_host.h
DOC "Broadcom include directory"
PATHS /opt/vc/include)
mark_as_advanced(BROADCOM_INCLUDE_DIR)
find_library(VCOS_LIBRARY
NAMES libvcos.so
DOC "Path to VCOS Library"
PATHS /opt/vc/lib)
mark_as_advanced(VCOS_LIBRARY)
find_library(BCM_HOST_LIBRARY
NAMES libbcm_host.so
DOC "Path to Broadcom Host Library"
PATHS /opt/vc/lib)
mark_as_advanced(BCM_HOST_LIBRARY)
find_library(MMAL_CORE_LIBRARY
NAMES libmmal_core.so
DOC "Path to MMAL Core Library"
PATHS /opt/vc/lib)
mark_as_advanced(MMAL_CORE_LIBRARY)
find_library(MMAL_UTIL_LIBRARY
NAMES libmmal_util.so
DOC "Path to MMAL Util Library"
PATHS /opt/vc/lib)
mark_as_advanced(MMAL_UTIL_LIBRARY)
find_library(MMAL_VC_CLIENT_LIBRARY
NAMES libmmal_vc_client.so
DOC "Path to MMAL Client Library"
PATHS /opt/vc/lib)
mark_as_advanced(MMAL_VC_CLIENT_LIBRARY)
include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(MMAL DEFAULT_MSG BROADCOM_INCLUDE_DIR VCOS_LIBRARY MMAL_CORE_LIBRARY MMAL_UTIL_LIBRARY MMAL_VC_CLIENT_LIBRARY BCM_HOST_LIBRARY)
set(MMAL_LINK_LIBRARIES ${BCM_HOST_LIBRARY} ${VCOS_LIBRARY} ${MMAL_CORE_LIBRARY} ${MMAL_UTIL_LIBRARY} ${MMAL_VC_CLIENT_LIBRARY})
set(MMAL_INCLUDE_DIRS ${BROADCOM_INCLUDE_DIR})

View File

@ -0,0 +1,29 @@
include(FindGit)
if(GIT_FOUND AND IS_DIRECTORY "${PROJECT_SOURCE_DIR}/.git")
set(GIT_BRANCH "")
set(GIT_COMMIT_HASH "")
# Get the current working branch
execute_process(
COMMAND ${GIT_EXECUTABLE} name-rev --name-only HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_BRANCH0
OUTPUT_STRIP_TRAILING_WHITESPACE
)
# Clean Branch name to have only the branch
STRING(REGEX REPLACE "((origin))+" "" GIT_BRANCH1 ${GIT_BRANCH0})
STRING(REGEX REPLACE "((remotes))+" "" GIT_BRANCH2 ${GIT_BRANCH1})
STRING(REGEX REPLACE "\\/+" "" GIT_BRANCH3 ${GIT_BRANCH2})
STRING(REGEX REPLACE "~[0-9]*" "" GIT_BRANCH4 ${GIT_BRANCH3})
STRING(REGEX REPLACE "\\^[0-9]*" "" GIT_BRANCH ${GIT_BRANCH4})
# Get the latest abbreviated commit hash of the working branch
execute_process(
COMMAND git log -1 --format=%h
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_COMMIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
endif()

View File

@ -1,4 +1,4 @@
add_custom_command(OUTPUT moonlight.1 COMMAND pod2man --section=1 --center="Moonlight Embedded Manual" --name="MOONLIGHT" --release="moonlight 2.1.0" ${CMAKE_CURRENT_SOURCE_DIR}/README.pod > moonlight.1)
add_custom_command(OUTPUT moonlight.1 COMMAND pod2man --section=1 --center="Moonlight Embedded Manual" --name="MOONLIGHT" --release="moonlight 2.5.0" ${CMAKE_CURRENT_SOURCE_DIR}/README.pod > moonlight.1)
add_custom_target(docs ALL DEPENDS moonlight.1)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/moonlight.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)

View File

@ -79,34 +79,33 @@ Change the vertical resolution to I<HEIGHT>
=item B<-fps> [I<FPS>]
Change the number of frame per second to I<FPS>.
Defaults to 60fps for 720p and 30fps for 1080p and higher.
Only 30 and 60 fps are currently supported by Gamestream.
Defaults to 60 FPS.
=item B<-bitrate> [I<BITRATE>]
Change bitrate to I<BITRATE> Kbps.
By default the bitrate depends on the selected resolution and fps.
For resolution 1080p and 60 fps and higher 20 Mbps is used.
For resolution 1080p or 60 fps and higher 10 Mbps is used
For other configurations 5 Mbps is used by default.
By default the bitrate depends on the selected resolution and FPS.
For resolution 1080p and 60 FPS and higher, 20 Mbps is used.
For resolution 1080p or 60 FPS and higher, 10 Mbps is used
For other configurations, 5 Mbps is used by default.
=item B<-packetsize> [I<PACKETSIZE>]
Change the network packetsize to I<PACKETSIZE> bytes.
The packetsize should the smaller than the MTU of the network.
This value must be a multiply of 16.
By default a safe value of 1024 is used.
This value must be a multiple of 16.
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>
=item B<-remote> [I<yes/no/auto>]
Enable the optimizations for remote connections in GFE.
Enable optimizations for LAN or WAN streaming.
=item B<-app> [I<APP>]
@ -121,9 +120,9 @@ Stop GFE from changing the graphical settings of the requested game or applicati
Play the audio on the host computer instead of this device.
=item B<-surround>
=item B<-surround> [I<5.1/7.1>]
Enable 5.1 surround sound instead of stereo.
Enable surround sound instead of stereo.
=item B<-keydir> [I<DIRECTORY>]
@ -141,14 +140,22 @@ By default the gamecontrollerdb.txt provided by Moonlight Embedded is used.
Select platform for audio and video output and input.
<PLATFORM> can be pi, imx, aml, x11, x11_vdpau, sdl or fake.
=item B<-unsupported>
=item B<-nounsupported>
Try streaming if GFE version or options are unsupported
Don't stream if resolution is not officially supported by the server
=item B<-quitappafter>
Send quit app request to remote after quitting session
=item B<-viewonly>
Disable all input processing (view-only mode)
=item B<-nomouseemulation>
Disable gamepad mouse emulation (activated by long pressing Start button)
=item B<-verbose>
Enable verbose output

View File

@ -1,226 +0,0 @@
default,Default Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
xwc,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
0000000058626f782033363020576900,Xbox 360 Wireless Controller,a:b0,b:b1,back:b14,dpdown:b11,dpleft:b12,dpright:b13,dpup:b10,guide:b7,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b6,x:b2,y:b3,
0000000058626f782047616d65706100,Xbox Gamepad (userspace driver),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,
0300000000f000000300000000010000,RetroPad,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,
0300000000f00000f100000000010000,Super RetroPort,a:b1,b:b5,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b0,y:b4,
03000000022000000090000011010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,
030000000d0500000308000010010000,Nostromo n45 Dual Analog Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b4,leftstick:b12,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b10,x:b2,y:b3,
030000000d0f00000900000010010000,Natec Genesis P44,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
030000000d0f00000d00000000010000,hori,a:b0,b:b6,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b3,leftx:b4,lefty:b5,rightshoulder:b7,start:b9,x:b1,y:b2,
030000000d0f00001000000011010000,HORI CO. LTD. FIGHTING STICK 3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,
030000000d0f00002200000011010000,HORI CO. LTD. REAL ARCADE Pro.V3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,
030000000d0f00005e00000011010000,Hori Fighting Commander 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
030000000d0f00005f00000011010000,Hori Fighting Commander 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
030000000d0f00006600000011010000,HORIPAD 4 (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
030000000d0f00006700000001010000,HORIPAD ONE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000000d0f00006a00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
030000000d0f00006b00000011010000,HORI CO. LTD. Real Arcade Pro.4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
030000000d0f00006e00000011010000,HORIPAD 4 (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
030000000d0f00009200000011010000,Hori Pokken Tournament DX Pro Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,
030000000d0f0000ee00000011010000,HORIPAD mini4,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
03000000100000008200000011010000,Akishop Customs PS360+ v1.66,a:b1,b:b2,back:b12,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:b6,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,
03000000100800000100000010010000,Twin USB PS2 Adapter,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,
03000000100800000300000010010000,USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b3,y:b0,
03000000120c00000500000000010000,Manta Dualshock 2,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,
03000000120c00000500000010010000,AxisPad,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,start:b11,x:b0,y:b1,
03000000120c0000100e000011010000,ZEROPLUS P4 Gamepad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
03000000250900000500000000010000,Sony PS2 pad with SmartJoy adapter,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,
03000000250900006688000000010000,MP-8866 Super Dual Box,a:b2,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,
0300000025090000e803000001010000,Mayflash Wii Classic Controller,a:b1,b:b0,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:a4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:a5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,
03000000260900008888000000010000,Cyber Gadget GameCube Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b6,righttrigger:a5,rightx:a2,righty:a3~,start:b7,x:b2,y:b3,
03000000280400000140000000010000,Gravis GamePad Pro USB ,a:b1,b:b2,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b0,y:b3,
03000000300f00000b01000010010000,Jess Tech GGE909 PC Recoil Pad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,
03000000300f00001001000010010000,Jess Tech Dual Analog Rumble Pad,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,
03000000300f00001211000011010000,QanBa Arcade JoyStick,a:b2,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b5,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b6,start:b9,x:b1,y:b3,
03000000321500000009000011010000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,
03000000321500000010000011010000,Razer RAIJU,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
03000000321500000104000011010000,Razer Panthera (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
03000000321500000204000011010000,Razer Panthera (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
0300000032150000030a000001010000,Razer Wildcat,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
03000000341a000005f7000010010000,GameCube {HuiJia USB box},a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,
03000000341a00000908000010010000,SL-6566,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,
03000000341a00003608000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
03000000380700001647000010040000,Mad Catz Wired Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
03000000380700001888000010010000,MadCatz PC USB Wired Stick 8818,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
03000000380700003847000090040000,Mad Catz Wired Xbox 360 Controller (SFIV),a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,
03000000380700003888000010010000,MadCatz PC USB Wired Stick 8838,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:a0,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
03000000380700005032000011010000,Mad Catz FightPad PRO (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
03000000380700005082000011010000,Mad Catz FightPad PRO (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
03000000380700008034000011010000,Mad Catz fightstick (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
03000000380700008084000011010000,Mad Catz fightstick (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
03000000380700008433000011010000,Mad Catz FightStick TE S+ (PS3),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
03000000380700008483000011010000,Mad Catz FightStick TE S+ (PS4),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
03000000450c00002043000010010000,XEOX Gamepad SL-6556-BK,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,
03000000451300000830000010010000,NYKO CORE,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
030000004c0500006802000010010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,
030000004c0500006802000010810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
030000004c0500006802000011010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,
030000004c0500006802000011810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
030000004c050000a00b000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
030000004c050000a00b000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
030000004c050000c405000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
030000004c050000c405000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
030000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
030000004c050000cc09000011010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
030000004c050000cc09000011810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
030000004f04000000b3000010010000,Thrustmaster Firestorm Dual Power,a:b0,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b11,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b10,x:b1,y:b3,
030000004f04000008d0000000010000,Thrustmaster Run N Drive Wireless,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
030000004f04000009d0000000010000,Thrustmaster Run N Drive Wireless PS3,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
030000004f04000012b3000010010000,Thrustmaster vibrating gamepad,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,
030000004f04000015b3000010010000,Thrustmaster Dual Analog 4,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,
030000004f04000020b3000010010000,Thrustmaster 2 in 1 DT,a:b0,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b1,y:b3,
030000004f04000023b3000000010000,Thrustmaster Dual Trigger 3-in-1,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
030000004f04000026b3000002040000,Thrustmaster Gamepad GP XID,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
03000000550900001072000011010000,NVIDIA Controller,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b13,leftshoulder:b4,leftstick:b8,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,
03000000550900001472000011010000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,
030000005e0400000202000000010000,Old Xbox pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,
030000005e0400000e00000000010000,Microsoft SideWinder,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,rightshoulder:b7,start:b8,x:b3,y:b4,
030000005e0400001907000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000005e0400008502000000010000,Microsoft X-Box pad (Japan),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,
030000005e0400008902000021010000,Microsoft X-Box pad v2 (US),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b5,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b2,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b3,y:b4,
030000005e0400008e02000004010000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000005e0400008e02000010010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000005e0400008e02000014010000,X360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000005e0400008e02000020200000,SpeedLink XEOX Pro Analog Gamepad pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000005e0400008e02000062230000,Microsoft X-Box 360 pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000005e0400008e02000073050000,Speedlink TORID Wireless Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000005e0400009102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000005e040000a102000000010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000005e040000a102000007010000,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000005e040000a102000014010000,Xbox 360 Wireless Receiver (XBOX),a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000005e040000d102000001010000,Microsoft X-Box One pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000005e040000d102000003020000,Microsoft X-Box One pad v2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000005e040000dd02000003020000,Microsoft X-Box One pad (Firmware 2015),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000005e040000e302000003020000,Microsoft X-Box One Elite pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000005e040000ea02000001030000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
03000000632500002305000010010000,ShanWan USB Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,
03000000632500007505000010010000,SHANWAN PS3/PC Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,
03000000666600000488000000010000,Super Joy Box 5 Pro,a:b2,b:b1,back:b9,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a2,righty:a3,start:b8,x:b3,y:b0,
03000000666600006706000000010000,boom PSX to PC Converter,a:b2,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a2,righty:a3,start:b11,x:b3,y:b0,
030000006b140000010d000011010000,Revolution Pro Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
030000006d0400000ac2000010010000,Logitech Inc. WingMan RumblePad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b2,rightx:a3,righty:a4,x:b3,y:b4,
030000006d04000011c2000010010000,Logitech WingMan Cordless RumblePad,a:b0,b:b1,back:b2,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b5,leftshoulder:b6,lefttrigger:b9,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b10,rightx:a3,righty:a4,start:b8,x:b3,y:b4,
030000006d04000015c2000010010000,Logitech Logitech Extreme 3D,a:b0,b:b4,back:b6,guide:b8,leftshoulder:b9,leftstick:h0.8,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:h0.2,start:b7,x:b2,y:b5,
030000006d04000016c2000010010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
030000006d04000016c2000011010000,Logitech Dual Action,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
030000006d04000018c2000010010000,Logitech RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
030000006d04000019c2000010010000,Logitech Cordless RumblePad 2,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
030000006d04000019c2000011010000,Logitech F710 Gamepad (DInput),a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
030000006d0400001dc2000014400000,Logitech F310 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000006d0400001ec2000020200000,Logitech F510 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000006d0400001fc2000005030000,Logitech F710 Gamepad (XInput),a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000006e0500000320000010010000,JC-U3613M - DirectInput Mode,a:b2,b:b3,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a2,righty:a3,start:b11,x:b0,y:b1,
030000006f0e00000103000000020000,Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000006f0e00000104000000010000,Gamestop Logic3 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000006f0e00001302000000010000,Afterglow,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000006f0e00001304000000010000,Generic X-Box pad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:a0,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:a3,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000006f0e00001402000011010000,PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
030000006f0e00001e01000011010000,Rock Candy PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
030000006f0e00001f01000000010000,Rock Candy,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000006f0e00003001000001010000,EA Sports PS3 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
030000006f0e00003901000000430000,Afterglow Prismatic Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000006f0e00003901000020060000,Afterglow Controller for Xbox One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000006f0e00004601000001010000,Rock Candy Xbox One Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000006f0e00006401000001010000,PDP Battlefield One,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
03000000780000000600000010010000,Microntek USB Joystick,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,
03000000790000000600000007010000,USB gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,
03000000790000000600000010010000,DragonRise Inc. Generic USB Joystick,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b0,
03000000790000001100000000010000,USB Gamepad1,a:b2,b:b1,back:b8,dpdown:a0,dpleft:a1,dpright:a2,dpup:a4,start:b9,
03000000790000004318000010010000,Mayflash GameCube Controller Adapter,a:b1,b:b0,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b2,y:b3,
03000000790000004418000010010000,Mayflash GameCube Controller,a:b1,b:b2,dpdown:b14,dpleft:b15,dpright:b13,dpup:b12,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:a4,rightx:a5,righty:a2,start:b9,x:b0,y:b3,
0300000079000000d218000011010000,MAGIC-NS,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
0300000079000000d418000000010000,GPD Win 2 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,
030000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,
030000008916000000fd000024010000,Razer Onza Tournament Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000008916000000fe000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000008916000001fd000024010000,Razer Onza Classic Edition,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
030000008f0e00000300000010010000,PS3 Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,
030000008f0e00000610000000010000,GreenAsia Electronics 4Axes 12Keys GamePad ,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b9,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b10,righttrigger:b5,rightx:a3,righty:a2,start:b11,x:b3,y:b0,
030000008f0e00000800000010010000,Gasia Co. Ltd PS(R) Gamepad,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,
030000008f0e00001200000010010000,GreenAsia Inc. USB Joystick,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b2,y:b3,
030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,
03000000a30600000901000000010000,Saitek P880,a:b2,b:b3,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,
03000000a30600000b04000000010000,Saitek P990 Dual Analog Pad,a:b1,b:b2,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b8,x:b0,y:b3,
03000000a30600000c04000011010000,Saitek P2900 Wireless Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b9,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b12,x:b0,y:b3,
03000000a30600000cff000010010000,Saitek P2500 Force Rumble Pad,a:b2,b:b3,back:b11,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:b7,rightx:a3,righty:a2,x:b0,y:b1,
03000000a306000018f5000010010000,Saitek PLC Saitek P3200 Rumble Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b0,y:b3,
03000000a306000023f6000011010000,Saitek Cyborg V.1 Game Pad,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a4,start:b9,x:b0,y:b3,
03000000ad1b000001f5000033050000,Hori Pad EX Turbo 2,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
03000000ad1b000016f0000090040000,Mad Catz Xbox 360 Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
03000000ad1b00002ef0000090040000,Mad Catz Fightpad SFxT,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,rightshoulder:b5,righttrigger:a5,start:b7,x:b2,y:b3,
03000000ad1b000038f0000090040000,Street Fighter IV FightStick TE,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
03000000b40400000a01000000010000,CYPRESS USB Gamepad,a:b0,b:b1,back:b5,guide:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b8,x:b3,y:b4,
03000000b50700001503000010010000,impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,
03000000ba2200002010000001010000,Jess Technology USB Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b3,y:b0,
03000000bc2000000055000010010000,ShanWan PS3/PC Wired GamePad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,
03000000bc2000000055000011010000,GameSir G3w,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,
03000000c01600008704000011010000,Serial/Keyboard/Mouse/Joystick,a:b12,b:b10,back:b4,dpdown:b2,dpleft:b3,dpright:b1,dpup:b0,leftshoulder:b9,leftstick:b14,lefttrigger:b6,leftx:a1,lefty:a0,rightshoulder:b8,rightstick:b15,righttrigger:b7,rightx:a2,righty:a3,start:b5,x:b13,y:b11,
03000000c0160000e105000001010000,Xin-Mo Xin-Mo Dual Arcade,a:b4,b:b3,back:b6,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,guide:b9,leftshoulder:b2,leftx:a0,lefty:a1,rightshoulder:b5,start:b7,x:b1,y:b0,
03000000c62400000053000000010000,PowerA,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
03000000c6240000025b000002020000,Thrustmaster GPX Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
03000000c6240000045d000024010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
03000000c6240000045d000025010000,Razer Sabertooth,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
03000000c62400003a54000001010000,PowerA 1428124-01,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
03000000c82d00000190000011010000,8Bitdo NES30 Pro 8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:a4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a5,rightx:a2,righty:a3,start:b11,x:b4,y:b3,
03000000c9110000f055000011010000,HJC Game GAMEPAD,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,
03000000d62000006dca000011010000,PowerA Pro Ex,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
03000000d814000007cd000011010000,Toodles 2008 Chimp PC/PS3,a:b0,b:b1,back:b8,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b9,x:b3,y:b2,
03000000d81400000862000011010000,HitBox (PS3/PC) Analog Mode,a:b1,b:b2,back:b8,guide:b9,leftshoulder:b4,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:b7,start:b12,x:b0,y:b3,
03000000de2800000112000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,
03000000de2800000211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,
03000000de2800004211000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,
03000000de280000fc11000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
03000000de280000ff11000001000000,Steam Virtual Gamepad,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
03000000e82000006058000001010000,Cideko AK08b,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,
03000000f025000021c1000010010000,ShanWan Gioteck PS3 Wired Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,
03000000f0250000c183000010010000,Goodbetterbest Ltd USB Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
03000000fd0500000030000000010000,InterAct GoPad I-73000 (Fighting Game Layout),a:b3,b:b4,back:b6,leftx:a0,lefty:a1,rightshoulder:b2,righttrigger:b5,start:b7,x:b0,y:b1,
03000000ff1100003133000010010000,PC Game Controller,a:b2,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b0,
03000000ff1100004133000010010000,PS2 Controller,a:b2,b:b1,back:b8,leftshoulder:b6,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,righttrigger:b5,start:b9,x:b3,y:b0,
05000000010000000100000003000000,Nintendo Wiimote,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,
05000000050b00000045000031000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,
05000000050b00000045000040000000,ASUS Gamepad,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b6,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b10,x:b2,y:b3,
05000000102800000900000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,
05000000172700004431000029010000,XiaoMi Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b20,leftshoulder:b6,leftstick:b13,lefttrigger:a7,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a6,rightx:a2,righty:a5,start:b11,x:b3,y:b4,
05000000203800000900000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,
050000003215000000090000163a0000,Razer Serval,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a4,rightx:a2,righty:a3,start:b7,x:b2,y:b3,
05000000362800000100000002010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,
05000000362800000100000003010000,OUYA Game Controller,a:b0,b:b3,dpdown:b9,dpleft:b10,dpright:b11,dpup:b8,guide:b14,leftshoulder:b4,leftstick:b6,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b7,righttrigger:a5,rightx:a3,righty:a4,x:b1,y:b2,
05000000380700006652000025010000,Mad Catz C.T.R.L.R ,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
0500000047532047616d657061640000,GameStop Gamepad,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,
0500000047532067616d657061640000,GS gamepad,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,
0500000049190000020400001b010000,Ipega PG-9069 - Bluetooth Gamepad,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b161,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,
05000000491900000204000021000000,Amazon Fire Game Controller,a:b0,b:b1,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,
050000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:a12,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:a13,rightx:a2,righty:a3,start:b3,x:b15,y:b12,
050000004c0500006802000000800000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
050000004c0500006802000000810000,PS3 Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
050000004c050000c405000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
050000004c050000c405000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
050000004c050000cc09000000010000,PS4 Controller,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:a4,rightx:a2,righty:a5,start:b9,x:b0,y:b3,
050000004c050000cc09000000810000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
050000004c050000cc09000001800000,PS4 Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:a5,rightx:a3,righty:a4,start:b9,x:b3,y:b2,
05000000504c415953544154494f4e00,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,
05000000550900001472000001000000,NVIDIA Controller v01.04,a:b0,b:b1,back:b14,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b4,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,
050000005e040000e002000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b10,leftshoulder:b4,leftstick:b8,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b9,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,
050000005e040000fd02000003090000,Xbox One Wireless Controller,a:b0,b:b1,back:b15,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b16,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b3,y:b4,
050000006964726f69643a636f6e0000,idroid:con,a:b1,b:b2,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b0,y:b3,
050000007e0500000620000001000000,Joy-Con (L),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b13,leftshoulder:b4,leftstick:b10,rightshoulder:b5,start:b8,x:b2,y:b3,
050000007e0500000720000001000000,Joy-Con (R),+leftx:h0.2,+lefty:h0.4,-leftx:h0.8,-lefty:h0.1,a:b0,b:b1,back:b12,leftshoulder:b4,leftstick:b11,rightshoulder:b5,start:b9,x:b2,y:b3,
050000007e0500000920000001000000,Nintendo Switch Pro Controller,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b12,leftshoulder:b4,leftstick:b10,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b11,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b2,y:b3,
050000007e0500003003000001000000,Nintendo Wii Remote Pro Controller,a:b0,b:b1,back:b8,dpdown:b14,dpleft:b15,dpright:b16,dpup:b13,guide:b10,leftshoulder:b4,leftstick:b11,lefttrigger:b6,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b12,righttrigger:b7,rightx:a2,righty:a3,start:b9,x:b3,y:b2,
05000000a00500003232000001000000,8Bitdo Zero GamePad,a:b0,b:b1,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b3,y:b4,
05000000ac0500003232000001000000,VR-BOX,a:b0,b:b1,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b10,lefttrigger:b4,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b11,righttrigger:b5,rightx:a3,righty:a2,start:b9,x:b2,y:b3,
05000000c82d00000061000000010000,8Bitdo SF30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,
05000000c82d00001038000000010000,8Bitdo FC30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b6,leftstick:b13,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:b9,rightx:a2,righty:a3,start:b11,x:b4,y:b3,
05000000c82d00002038000000010000,8Bitdo NES30 Pro,a:b1,b:b0,back:b10,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b2,leftshoulder:b6,leftstick:b13,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b7,rightstick:b14,righttrigger:a4,rightx:a2,righty:a3,start:b11,x:b4,y:b3,
05000000c82d00003028000000010000,8Bitdo SFC30 GamePad,a:b1,b:b0,back:b10,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b11,x:b4,y:b3,
05000000d62000007162000001000000,Moga Pro 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,
05000000d6200000ad0d000001000000,Moga Pro,a:b0,b:b1,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,
05000000d6200000e589000001000000,Moga 2 HID,a:b0,b:b1,back:b9,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b7,lefttrigger:a5,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b8,righttrigger:a4,rightx:a2,righty:a3,start:b6,x:b2,y:b3,
05000000de2800000212000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,
05000000de2800000511000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,
05000000de2800000611000001000000,Steam Controller,a:b0,b:b1,back:b6,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,righttrigger:a3,start:b7,x:b2,y:b3,
060000004c0500006802000000010000,PS3 Controller,a:b14,b:b13,back:b0,dpdown:b6,dpleft:b7,dpright:b5,dpup:b4,guide:b16,leftshoulder:b10,leftstick:b1,lefttrigger:b8,leftx:a0,lefty:a1,rightshoulder:b11,rightstick:b2,righttrigger:b9,rightx:a2,righty:a3,start:b3,x:b15,y:b12,
06000000adde0000efbe000002010000,Hidromancer Game Controller,a:b0,b:b1,back:b6,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,

View File

@ -7,11 +7,11 @@ find_package(OpenSSL 1.0.2 REQUIRED)
find_package(EXPAT REQUIRED)
pkg_check_modules(AVAHI REQUIRED avahi-client)
pkg_check_modules(ENET REQUIRED libenet)
aux_source_directory(./ GAMESTREAM_SRC_LIST)
aux_source_directory(../third_party/h264bitstream GAMESTREAM_SRC_LIST)
aux_source_directory(../third_party/moonlight-common-c/enet MOONLIGHT_COMMON_SRC_LIST)
aux_source_directory(../third_party/moonlight-common-c/src MOONLIGHT_COMMON_SRC_LIST)
aux_source_directory(../third_party/moonlight-common-c/reedsolomon MOONLIGHT_COMMON_SRC_LIST)
@ -23,10 +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(moonlight-common PRIVATE ../third_party/moonlight-common-c/reedsolomon ${ENET_INCLUDE_DIRS})
target_link_libraries(gamestream ${CURL_LIBRARIES} ${OPENSSL_LIBRARIES} ${EXPAT_LIBRARIES} ${AVAHI_LIBRARIES} ${LIBUUID_LIBRARIES})
target_link_libraries(moonlight-common ${ENET_LIBRARIES})
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 ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS})

View File

@ -30,6 +30,7 @@
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <uuid/uuid.h>
#include <openssl/sha.h>
#include <openssl/aes.h>
@ -45,19 +46,18 @@
#define UNIQUEID_BYTES 8
#define UNIQUEID_CHARS (UNIQUEID_BYTES*2)
#define CHANNEL_COUNT_STEREO 2
#define CHANNEL_COUNT_51_SURROUND 6
#define CHANNEL_MASK_STEREO 0x3
#define CHANNEL_MASK_51_SURROUND 0xFC
static char unique_id[UNIQUEID_CHARS+1];
static 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;
@ -92,19 +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) {
unsigned char unique_data[UNIQUEID_BYTES];
RAND_bytes(unique_data, UNIQUEID_BYTES);
for (int i = 0; i < UNIQUEID_BYTES; i++) {
sprintf(unique_id + (i * 2), "%02x", unique_data[i]);
}
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;
@ -167,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) {
@ -291,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;
@ -301,15 +324,7 @@ static int sign_it(const char *msg, size_t mlen, unsigned char **sig, size_t *sl
if (ctx == NULL)
return GS_FAILED;
const EVP_MD *md = EVP_get_digestbyname("SHA256");
if (md == NULL)
goto cleanup;
int rc = EVP_DigestInit_ex(ctx, md, NULL);
if (rc != 1)
goto cleanup;
rc = EVP_DigestSignInit(ctx, NULL, md, NULL, pkey);
int rc = EVP_DigestSignInit(ctx, NULL, EVP_sha256(), NULL, pkey);
if (rc != 1)
goto cleanup;
@ -345,39 +360,63 @@ static bool verifySignature(const char *data, int dataLength, char *signature, i
BIO* bio = BIO_new(BIO_s_mem());
BIO_puts(bio, cert);
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
BIO_free(bio);
if (!x509) {
return false;
}
EVP_PKEY* pubKey = X509_get_pubkey(x509);
EVP_MD_CTX *mdctx = NULL;
mdctx = EVP_MD_CTX_create();
EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, pubKey);
EVP_DigestVerifyUpdate(mdctx, data, dataLength);
int result = EVP_DigestVerifyFinal(mdctx, signature, signatureLength);
X509_free(x509);
EVP_PKEY_free(pubKey);
EVP_MD_CTX_destroy(mdctx);
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);
@ -387,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)
@ -431,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;
@ -491,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;
@ -550,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;
@ -563,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;
@ -596,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;
@ -618,12 +653,24 @@ int gs_pair(PSERVER_DATA server, char* pin) {
cleanup:
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;
}
@ -631,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)
@ -654,13 +701,15 @@ 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;
while (mode != NULL) {
if (mode->width == config->width && mode->height == config->height && mode->refresh == config->fps)
correct_mode = true;
if (mode->width == config->width && mode->height == config->height) {
if (mode->refresh == config->fps)
correct_mode = true;
}
mode = mode->next;
}
@ -668,31 +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);
srand(time(NULL));
char url[4096];
u_int32_t rikeyid = 0;
char rikey_hex[33];
bytes_to_hex(config->remoteInputAesKey, rikey_hex, 16);
RAND_bytes(config->remoteInputAesIv, sizeof(rikeyid));
memcpy(&rikeyid, config->remoteInputAesIv, sizeof(rikeyid));
rikeyid = htonl(rikeyid);
char rikey_hex[SIZEOF_AS_HEX_STR(config->remoteInputAesKey)];
bytes_to_hex(config->remoteInputAesKey, rikey_hex, sizeof(config->remoteInputAesKey));
PHTTP_DATA data = http_create_data();
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);
if (server->currentGame == 0) {
int channelCounnt = config->audioConfiguration == AUDIO_CONFIGURATION_STEREO ? CHANNEL_COUNT_STEREO : CHANNEL_COUNT_51_SURROUND;
int mask = config->audioConfiguration == AUDIO_CONFIGURATION_STEREO ? CHANNEL_MASK_STEREO : CHANNEL_MASK_51_SURROUND;
snprintf(url, sizeof(url), "https://%s:47984/launch?uniqueid=%s&uuid=%s&appid=%d&mode=%dx%dx%d&additionalStates=1&sops=%d&rikey=%s&rikeyid=%d&localAudioPlayMode=%d&surroundAudioInfo=%d&remoteControllersBitmap=%d&gcmap=%d", server->serverInfo.address, unique_id, uuid_str, appId, config->width, config->height, config->fps, sops, rikey_hex, rikeyid, localaudio, (mask << 16) + channelCounnt, gamepad_mask, gamepad_mask);
} else
snprintf(url, sizeof(url), "https://%s:47984/resume?uniqueid=%s&uuid=%s&rikey=%s&rikeyid=%d", server->serverInfo.address, unique_id, uuid_str, rikey_hex, rikeyid);
int surround_info = SURROUNDAUDIOINFO_FROM_AUDIO_CONFIGURATION(config->audioConfiguration);
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
@ -700,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")) {
@ -708,6 +761,14 @@ int gs_start_app(PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, b
goto cleanup;
}
free(result);
result = NULL;
if (xml_search(data->memory, data->size, "sessionUrl0", &result) == GS_OK) {
server->serverInfo.rtspSessionUrl = result;
result = NULL;
}
cleanup:
if (result != NULL)
free(result);
@ -720,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)
@ -728,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;
@ -750,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;
@ -763,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

@ -29,19 +29,20 @@
#define MAX_SUPPORTED_GFE_VERSION 7
typedef struct _SERVER_DATA {
const char* address;
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

@ -29,5 +29,6 @@
#define GS_UNSUPPORTED_VERSION -7
#define GS_NOT_SUPPORTED_MODE -8
#define GS_ERROR -9
#define GS_NOT_SUPPORTED_SOPS_RESOLUTION -10
extern const char* gs_error;

View File

@ -26,24 +26,21 @@
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)
{
size_t realsize = size * nmemb;
PHTTP_DATA mem = (PHTTP_DATA)userp;
mem->memory = realloc(mem->memory, mem->size + realsize + 1);
if(mem->memory == NULL)
return 0;
memcpy(&(mem->memory[mem->size]), contents, realsize);
mem->size += realsize;
mem->memory[mem->size] = 0;
return realsize;
}
@ -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);
@ -89,7 +89,7 @@ int http_request(char* url, PHTTP_DATA data) {
data->size = 0;
}
CURLcode res = curl_easy_perform(curl);
if(res != CURLE_OK) {
gs_error = curl_easy_strerror(res);
return GS_FAILED;
@ -99,7 +99,7 @@ int http_request(char* url, PHTTP_DATA data) {
if (debug)
printf("Response:\n%s\n\n", data->memory);
return GS_OK;
}

View File

@ -32,20 +32,18 @@ static const int SERIAL = 0;
static const int NUM_YEARS = 10;
int mkcert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int years);
int add_ext(X509 *cert, int nid, char *value);
CERT_KEY_PAIR mkcert_generate() {
BIO *bio_err;
X509 *x509 = NULL;
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();
ERR_load_crypto_strings();
mkcert(&x509, &pkey, NUM_BITS, SERIAL, NUM_YEARS);
p12 = PKCS12_create("limelight", "GameStream", pkey, x509, NULL, 0, 0, 0, 0, 0);
@ -54,9 +52,9 @@ CERT_KEY_PAIR mkcert_generate() {
ENGINE_cleanup();
#endif
CRYPTO_cleanup_all_ex_data();
BIO_free(bio_err);
return (CERT_KEY_PAIR) {x509, pkey, p12};
}
@ -70,117 +68,62 @@ void mkcert_save(const char* certFile, const char* p12File, const char* keyPairF
FILE* certFilePtr = fopen(certFile, "w");
FILE* keyPairFilePtr = fopen(keyPairFile, "w");
FILE* p12FilePtr = fopen(p12File, "wb");
//TODO: error check
PEM_write_PrivateKey(keyPairFilePtr, certKeyPair.pkey, NULL, NULL, 0, NULL, NULL);
PEM_write_X509(certFilePtr, certKeyPair.x509);
i2d_PKCS12_fp(p12FilePtr, certKeyPair.p12);
fclose(p12FilePtr);
fclose(certFilePtr);
fclose(keyPairFilePtr);
}
int mkcert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int years) {
X509 *x;
EVP_PKEY *pk;
RSA *rsa;
X509_NAME *name = NULL;
if (*pkeyp == NULL) {
if ((pk=EVP_PKEY_new()) == NULL) {
abort();
return(0);
}
} else {
pk = *pkeyp;
}
if (*x509p == NULL) {
if ((x = X509_new()) == NULL) {
goto err;
}
} else {
x = *x509p;
}
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 ((rsa = RSA_new()) == NULL)
goto err;
BIGNUM* bne = BN_new();
if (bne == NULL) {
abort();
goto err;
}
// pk must be initialized on input
EVP_PKEY *pk = NULL;;
EVP_PKEY_keygen(ctx, &pk);
BN_set_word(bne, RSA_F4);
if (RSA_generate_key_ex(rsa, bits, bne, NULL) == 0) {
abort();
goto err;
}
EVP_PKEY_CTX_free(ctx);
if (!EVP_PKEY_assign_RSA(pk, rsa)) {
abort();
goto err;
}
X509_set_version(x, 2);
ASN1_INTEGER_set(X509_get_serialNumber(x), serial);
X509_gmtime_adj(X509_get_notBefore(x), 0);
X509_gmtime_adj(X509_get_notAfter(x), (long)60*60*24*365*years);
X509_set_pubkey(x, pk);
name = X509_get_subject_name(x);
/* This function creates and adds the entry, working out the
* correct string type and performing checks on its length.
*/
X509* cert = X509_new();
X509_set_version(cert, 2);
ASN1_INTEGER_set(X509_get_serialNumber(cert), serial);
#if OPENSSL_VERSION_NUMBER < 0x10100000L
X509_gmtime_adj(X509_get_notBefore(cert), 0);
X509_gmtime_adj(X509_get_notAfter(cert), 60 * 60 * 24 * 365 * years);
#else
ASN1_TIME* before = ASN1_STRING_dup(X509_get0_notBefore(cert));
ASN1_TIME* after = ASN1_STRING_dup(X509_get0_notAfter(cert));
X509_gmtime_adj(before, 0);
X509_gmtime_adj(after, 60 * 60 * 24 * 365 * years);
X509_set1_notBefore(cert, before);
X509_set1_notAfter(cert, after);
ASN1_STRING_free(before);
ASN1_STRING_free(after);
#endif
X509_set_pubkey(cert, pk);
X509_NAME* name = X509_get_subject_name(cert);
X509_NAME_add_entry_by_txt(name,"CN", MBSTRING_ASC, (unsigned char*)"NVIDIA GameStream Client", -1, -1, 0);
/* Its self signed so set the issuer name to be the same as the
* subject.
*/
X509_set_issuer_name(x, name);
/* Add various extensions: standard extensions */
add_ext(x, NID_key_usage, "critical,digitalSignature,keyEncipherment");
add_ext(x, NID_subject_key_identifier, "hash");
if (!X509_sign(x, pk, EVP_sha1())) {
X509_set_issuer_name(cert, name);
if (!X509_sign(cert, pk, EVP_sha256())) {
goto err;
}
*x509p = x;
*x509p = cert;
*pkeyp = pk;
return(1);
err:
return(0);
}
/* Add extension using V3 code: we can set the config file as NULL
* because we wont reference any other sections.
*/
int add_ext(X509 *cert, int nid, char *value)
{
X509_EXTENSION *ex;
X509V3_CTX ctx;
/* This sets the 'context' of the extensions. */
/* No configuration database */
X509V3_set_ctx_nodb(&ctx);
/* Issuer and subject certs: both the target since it is self signed,
* no request and no CRL
*/
X509V3_set_ctx(&ctx, cert, cert, NULL, NULL, 0);
ex = X509V3_EXT_conf_nid(NULL, &ctx, nid, value);
if (!ex) {
return 0;
}
X509_add_ext(cert, ex, -1);
X509_EXTENSION_free(ex);
return 1;
}

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
@ -48,14 +48,16 @@ void gs_sps_fix(PLENTRY sps, int flags, uint8_t* out_buf, uint32_t* out_offset)
// GFE 2.5.11 changed the SPS to add additional extensions
// Some devices don't like these so we remove them here.
h264_stream->sps->vui.video_signal_type_present_flag = 0;
h264_stream->sps->vui.chroma_loc_info_present_flag = 0;
if (flags & GS_SPS_REMOVE_VST_FIXUP)
h264_stream->sps->vui.video_signal_type_present_flag = 0;
if (flags & GS_SPS_REMOVE_CLI_FIXUP)
h264_stream->sps->vui.chroma_loc_info_present_flag = 0;
if ((flags & GS_SPS_BITSTREAM_FIXUP) == GS_SPS_BITSTREAM_FIXUP) {
// The SPS that comes in the current H264 bytestream doesn't set the bitstream_restriction_flag
// or the max_dec_frame_buffering which increases decoding latency on some devices
// or the max_dec_frame_buffering which increases decoding latency on some devices.
// log2_max_mv_length_horizontal and log2_max_mv_length_vertical are set to more
// conservite values by GFE 25.11. We'll let those values stand.
// conservative values by GFE 2.5.11. We'll let those values stand.
if (!h264_stream->sps->vui.bitstream_restriction_flag) {
h264_stream->sps->vui.bitstream_restriction_flag = 1;
h264_stream->sps->vui.motion_vectors_over_pic_boundaries_flag = 1;
@ -72,11 +74,10 @@ void gs_sps_fix(PLENTRY sps, int flags, uint8_t* out_buf, uint32_t* out_offset)
// than what GFE sends in 2.5.11, but it doesn't seem to cause picture problems.
h264_stream->sps->vui.max_bytes_per_pic_denom = 2;
h264_stream->sps->vui.max_bits_per_mb_denom = 1;
} else // Devices that didn't/couldn't get bitstream restrictions before GFE 2.5.11 will continue to not receive them now
h264_stream->sps->vui.bitstream_restriction_flag = 0;
}
memcpy(out_buf+*out_offset, 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

@ -19,7 +19,9 @@
#include <Limelight.h>
#define GS_SPS_BITSTREAM_FIXUP 0x01
#define GS_SPS_BITSTREAM_FIXUP 0x01
#define GS_SPS_REMOVE_VST_FIXUP 0x02
#define GS_SPS_REMOVE_CLI_FIXUP 0x04
void gs_sps_init(int width, int height);
void gs_sps_fix(PLENTRY sps, int flags, uint8_t* out_buf, uint32_t* out_offset);

View File

@ -25,8 +25,6 @@
#define STATUS_OK 200
static XML_Parser parser;
struct xml_query {
char *memory;
size_t size;
@ -132,7 +130,7 @@ static void XMLCALL _xml_write_data(void *userData, const XML_Char *s, int len)
search->memory = realloc(search->memory, search->size + len + 1);
if(search->memory == NULL)
return;
memcpy(&(search->memory[search->size]), s, len);
search->size += len;
search->memory[search->size] = 0;
@ -162,7 +160,7 @@ int xml_search(char* data, size_t len, char* node, char** result) {
XML_ParserFree(parser);
*result = search.memory;
return GS_OK;
}

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)
@ -15,7 +19,8 @@
#bitrate = -1
## Size of network packets should be lower than MTU
#packetsize = 1024
## If streaming with WAN optimizations, this will be capped at 1024.
#packetsize = 1392
## Select video codec (auto/h264/h265)
#codec = auto
@ -43,6 +48,9 @@
## Send quit app request to remote after quitting session
#quitappafter = false
## Disable all input processing (view-only mode)
#viewonly = false
## Select audio device to play sound on
#audio = sysdefault
@ -61,11 +69,14 @@
## By default keys are stored in $XDG_CACHE_DIR/moonlight or ~/.cache/moonlight
#keydir = /dir/to/keys
## Enable QOS settings to optimize for internet instead of local network
#remote = false
## Enable QOS settings to optimize for internet or local network
## yes - optimize for WAN streaming
## no - optimize for LAN streaming
## auto (default) - decide automatically based on target IP address
#remote = auto
## Enable 5.1 surround sound
#surround = false
## Enable 5.1/7.1 surround sound
#surround = 5.1
## Load additional configuration files
#config = /path/to/config

View File

@ -20,6 +20,8 @@
#include "audio.h"
#include <stdio.h>
#include <string.h>
#include <opus_multistream.h>
#include <alsa/asoundlib.h>
@ -27,31 +29,36 @@
static snd_pcm_t *handle;
static OpusMSDecoder* decoder;
static short pcmBuffer[FRAME_SIZE * MAX_CHANNEL_COUNT];
static short* pcmBuffer;
static int samplesPerFrame;
static int alsa_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int arFlags) {
int rc;
unsigned char alsaMapping[MAX_CHANNEL_COUNT];
unsigned char alsaMapping[AUDIO_CONFIGURATION_MAX_CHANNEL_COUNT];
/* The supplied mapping array has order: FL-FR-C-LFE-RL-RR
* ALSA expects the order: FL-FR-RL-RR-C-LFE
/* The supplied mapping array has order: FL-FR-C-LFE-RL-RR-SL-SR
* ALSA expects the order: FL-FR-RL-RR-C-LFE-SL-SR
* We need copy the mapping locally and swap the channels around.
*/
alsaMapping[0] = opusConfig->mapping[0];
alsaMapping[1] = opusConfig->mapping[1];
if (opusConfig->channelCount == 6) {
memcpy(alsaMapping, opusConfig->mapping, sizeof(alsaMapping));
if (opusConfig->channelCount >= 6) {
alsaMapping[2] = opusConfig->mapping[4];
alsaMapping[3] = opusConfig->mapping[5];
alsaMapping[4] = opusConfig->mapping[2];
alsaMapping[5] = opusConfig->mapping[3];
}
samplesPerFrame = opusConfig->samplesPerFrame;
pcmBuffer = malloc(sizeof(short) * opusConfig->channelCount * samplesPerFrame);
if (pcmBuffer == NULL)
return -1;
decoder = opus_multistream_decoder_create(opusConfig->sampleRate, opusConfig->channelCount, opusConfig->streams, opusConfig->coupledStreams, alsaMapping, &rc);
snd_pcm_hw_params_t *hw_params;
snd_pcm_sw_params_t *sw_params;
snd_pcm_uframes_t period_size = FRAME_SIZE * 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;
@ -77,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);
@ -87,27 +94,38 @@ static int alsa_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGUR
}
static void alsa_renderer_cleanup() {
if (decoder != NULL)
if (decoder != NULL) {
opus_multistream_decoder_destroy(decoder);
decoder = NULL;
}
if (handle != NULL) {
snd_pcm_drain(handle);
snd_pcm_close(handle);
handle = NULL;
}
if (pcmBuffer != NULL) {
free(pcmBuffer);
pcmBuffer = NULL;
}
}
static void alsa_renderer_decode_and_play_sample(char* data, int length) {
int decodeLen = opus_multistream_decode(decoder, data, length, pcmBuffer, FRAME_SIZE, 0);
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);
}
}
@ -116,5 +134,5 @@ AUDIO_RENDERER_CALLBACKS audio_callbacks_alsa = {
.init = alsa_renderer_init,
.cleanup = alsa_renderer_cleanup,
.decodeAndPlaySample = alsa_renderer_decode_and_play_sample,
.capabilities = CAPABILITY_DIRECT_SUBMIT,
.capabilities = CAPABILITY_DIRECT_SUBMIT | CAPABILITY_SUPPORTS_ARBITRARY_AUDIO_DURATION,
};

View File

@ -21,10 +21,6 @@
#include <Limelight.h>
#define MAX_CHANNEL_COUNT 6
#define FRAME_SIZE 240
#define FRAME_BUFFER 12
#ifdef HAVE_ALSA
extern AUDIO_RENDERER_CALLBACKS audio_callbacks_alsa;
#endif
@ -35,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

@ -29,18 +29,24 @@ static OpusMSDecoder* decoder;
ILCLIENT_T* handle;
COMPONENT_T* component;
static OMX_BUFFERHEADERTYPE *buf;
static short pcmBuffer[FRAME_SIZE * MAX_CHANNEL_COUNT];
static short* pcmBuffer;
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[MAX_CHANNEL_COUNT];
unsigned char omxMapping[AUDIO_CONFIGURATION_MAX_CHANNEL_COUNT];
char* componentName = "audio_render";
channelCount = opusConfig->channelCount;
/* The supplied mapping array has order: FL-FR-C-LFE-RL-RR
* OMX expects the order: FL-FR-LFE-C-RL-RR
samplesPerFrame = opusConfig->samplesPerFrame;
pcmBuffer = malloc(sizeof(short) * channelCount * samplesPerFrame);
if (pcmBuffer == NULL)
return -1;
/* The supplied mapping array has order: FL-FR-C-LFE-RL-RR-SL-SR
* OMX expects the order: FL-FR-LFE-C-RL-RR-SL-SR
* We need copy the mapping locally and swap the channels around.
*/
memcpy(omxMapping, opusConfig->mapping, sizeof(omxMapping));
@ -155,8 +161,10 @@ static int omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURA
}
static void omx_renderer_cleanup() {
if (decoder != NULL)
if (decoder != NULL) {
opus_multistream_decoder_destroy(decoder);
decoder = NULL;
}
if (handle != NULL) {
if((buf = ilclient_get_input_buffer(component, 100, 1)) == NULL){
fprintf(stderr, "Can't get audio buffer\n");
@ -174,23 +182,28 @@ static void omx_renderer_cleanup() {
ilclient_disable_port_buffers(component, 100, NULL, NULL, NULL);
ilclient_change_component_state(component, OMX_StateIdle);
ilclient_change_component_state(component, OMX_StateLoaded);
handle = NULL;
}
if (pcmBuffer != NULL) {
free(pcmBuffer);
pcmBuffer = NULL;
}
}
static void omx_renderer_decode_and_play_sample(char* data, int length) {
int decodeLen = opus_multistream_decode(decoder, data, length, pcmBuffer, FRAME_SIZE, 0);
int decodeLen = opus_multistream_decode(decoder, data, length, pcmBuffer, samplesPerFrame, 0);
if (decodeLen > 0) {
buf = ilclient_get_input_buffer(component, 100, 1);
buf = ilclient_get_input_buffer(component, 100, 1);
buf->nOffset = 0;
buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN;
int bufLength = decodeLen * sizeof(short) * channelCount;
int bufLength = decodeLen * sizeof(short) * channelCount;
memcpy(buf->pBuffer, pcmBuffer, bufLength);
buf->nFilledLen = bufLength;
int r = OMX_EmptyThisBuffer(ilclient_get_handle(component), buf);
if (r != OMX_ErrorNone) {
fprintf(stderr, "Empty buffer error\n");
}
} else {
fprintf(stderr, "Empty buffer error\n");
}
} else if (decodeLen < 0) {
printf("Opus error from decode: %d\n", decodeLen);
}
}
@ -199,5 +212,5 @@ AUDIO_RENDERER_CALLBACKS audio_callbacks_omx = {
.init = omx_renderer_init,
.cleanup = omx_renderer_cleanup,
.decodeAndPlaySample = omx_renderer_decode_and_play_sample,
.capabilities = CAPABILITY_DIRECT_SUBMIT,
.capabilities = CAPABILITY_DIRECT_SUBMIT | CAPABILITY_SUPPORTS_ARBITRARY_AUDIO_DURATION,
};

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

@ -21,6 +21,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <opus_multistream.h>
#include <pulse/simple.h>
@ -28,18 +29,19 @@
static OpusMSDecoder* decoder;
static pa_simple *dev = NULL;
static short pcmBuffer[FRAME_SIZE * MAX_CHANNEL_COUNT];
static short* pcmBuffer;
static int samplesPerFrame;
static int channelCount;
bool audio_pulse_init(char* audio_device) {
pa_sample_spec spec = {
.format = PA_SAMPLE_S16LE,
.rate = 44000,
.rate = 48000,
.channels = 2
};
int error;
dev = pa_simple_new(audio_device, "Moonlight Embedded", PA_STREAM_PLAYBACK, NULL, "Streaming", &spec, NULL, NULL, &error);
dev = pa_simple_new(NULL, "Moonlight Embedded", PA_STREAM_PLAYBACK, audio_device, "Streaming", &spec, NULL, NULL, &error);
if (dev)
pa_simple_free(dev);
@ -49,17 +51,20 @@ bool audio_pulse_init(char* audio_device) {
static int pulse_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int arFlags) {
int rc, error;
unsigned char alsaMapping[MAX_CHANNEL_COUNT];
unsigned char alsaMapping[AUDIO_CONFIGURATION_MAX_CHANNEL_COUNT];
channelCount = opusConfig->channelCount;
samplesPerFrame = opusConfig->samplesPerFrame;
pcmBuffer = malloc(sizeof(short) * channelCount * samplesPerFrame);
if (pcmBuffer == NULL)
return -1;
/* The supplied mapping array has order: FL-FR-C-LFE-RL-RR
* ALSA expects the order: FL-FR-RL-RR-C-LFE
/* The supplied mapping array has order: FL-FR-C-LFE-RL-RR-SL-SR
* ALSA expects the order: FL-FR-RL-RR-C-LFE-SL-SR
* We need copy the mapping locally and swap the channels around.
*/
alsaMapping[0] = opusConfig->mapping[0];
alsaMapping[1] = opusConfig->mapping[1];
if (opusConfig->channelCount == 6) {
memcpy(alsaMapping, opusConfig->mapping, sizeof(alsaMapping));
if (opusConfig->channelCount >= 6) {
alsaMapping[2] = opusConfig->mapping[4];
alsaMapping[3] = opusConfig->mapping[5];
alsaMapping[4] = opusConfig->mapping[2];
@ -74,8 +79,11 @@ static int pulse_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGU
.channels = opusConfig->channelCount
};
pa_channel_map map;
pa_channel_map_init_auto(&map, opusConfig->channelCount, PA_CHANNEL_MAP_ALSA);
char* audio_device = (char*) context;
dev = pa_simple_new(audio_device, "Moonlight Embedded", PA_STREAM_PLAYBACK, NULL, "Streaming", &spec, NULL, NULL, &error);
dev = pa_simple_new(NULL, "Moonlight Embedded", PA_STREAM_PLAYBACK, audio_device, "Streaming", &spec, &map, NULL, &error);
if (!dev) {
printf("Pulseaudio error: %s\n", pa_strerror(error));
@ -86,25 +94,36 @@ static int pulse_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGU
}
static void pulse_renderer_decode_and_play_sample(char* data, int length) {
int decodeLen = opus_multistream_decode(decoder, data, length, pcmBuffer, FRAME_SIZE, 0);
int decodeLen = opus_multistream_decode(decoder, data, length, pcmBuffer, samplesPerFrame, 0);
if (decodeLen > 0) {
int error;
int rc = pa_simple_write(dev, pcmBuffer, decodeLen * sizeof(short) * channelCount, &error);
if (rc<0)
printf("Pulseaudio error: %s\n", pa_strerror(error));
} else {
} else if (decodeLen < 0) {
printf("Opus error from decode: %d\n", decodeLen);
}
}
static void pulse_renderer_cleanup() {
pa_simple_free(dev);
if (decoder != NULL) {
opus_multistream_decoder_destroy(decoder);
decoder = NULL;
}
if (dev != NULL) {
pa_simple_free(dev);
dev = NULL;
}
if (pcmBuffer != NULL) {
free(pcmBuffer);
pcmBuffer = NULL;
}
}
AUDIO_RENDERER_CALLBACKS audio_callbacks_pulse = {
.init = pulse_renderer_init,
.cleanup = pulse_renderer_cleanup,
.decodeAndPlaySample = pulse_renderer_decode_and_play_sample,
.capabilities = CAPABILITY_DIRECT_SUBMIT,
.capabilities = CAPABILITY_DIRECT_SUBMIT | CAPABILITY_SUPPORTS_ARBITRARY_AUDIO_DURATION,
};

View File

@ -26,7 +26,8 @@
#include <opus_multistream.h>
static OpusMSDecoder* decoder;
static short pcmBuffer[FRAME_SIZE * MAX_CHANNEL_COUNT];
static short* pcmBuffer;
static int samplesPerFrame;
static SDL_AudioDeviceID dev;
static int channelCount;
@ -35,6 +36,10 @@ static int sdl_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURA
decoder = opus_multistream_decoder_create(opusConfig->sampleRate, opusConfig->channelCount, opusConfig->streams, opusConfig->coupledStreams, opusConfig->mapping, &rc);
channelCount = opusConfig->channelCount;
samplesPerFrame = opusConfig->samplesPerFrame;
pcmBuffer = malloc(sizeof(short) * channelCount * samplesPerFrame);
if (pcmBuffer == NULL)
return -1;
SDL_InitSubSystem(SDL_INIT_AUDIO);
@ -45,13 +50,11 @@ static int sdl_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURA
want.channels = opusConfig->channelCount;
want.samples = 4096;
dev = SDL_OpenAudioDevice(NULL, 0, &want, &have, SDL_AUDIO_ALLOW_FORMAT_CHANGE);
dev = SDL_OpenAudioDevice(NULL, 0, &want, &have, 0);
if (dev == 0) {
printf("Failed to open audio: %s\n", SDL_GetError());
return -1;
} else {
if (have.format != want.format) // we let this one thing change.
printf("We didn't get requested audio format.\n");
SDL_PauseAudioDevice(dev, 0); // start audio playing.
}
@ -59,17 +62,27 @@ static int sdl_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURA
}
static void sdl_renderer_cleanup() {
if (decoder != NULL)
if (decoder != NULL) {
opus_multistream_decoder_destroy(decoder);
decoder = NULL;
}
SDL_CloseAudioDevice(dev);
if (pcmBuffer != NULL) {
free(pcmBuffer);
pcmBuffer = NULL;
}
if (dev != 0) {
SDL_CloseAudioDevice(dev);
dev = 0;
}
}
static void sdl_renderer_decode_and_play_sample(char* data, int length) {
int decodeLen = opus_multistream_decode(decoder, data, length, pcmBuffer, FRAME_SIZE, 0);
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);
}
}
@ -78,5 +91,5 @@ AUDIO_RENDERER_CALLBACKS audio_callbacks_sdl = {
.init = sdl_renderer_init,
.cleanup = sdl_renderer_cleanup,
.decodeAndPlaySample = sdl_renderer_decode_and_play_sample,
.capabilities = CAPABILITY_DIRECT_SUBMIT,
.capabilities = CAPABILITY_DIRECT_SUBMIT | CAPABILITY_SUPPORTS_ARBITRARY_AUDIO_DURATION,
};

View File

@ -17,7 +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"
@ -37,7 +40,7 @@
#define write_config_string(fd, key, value) fprintf(fd, "%s = %s\n", key, value)
#define write_config_int(fd, key, value) fprintf(fd, "%s = %d\n", key, value)
#define write_config_bool(fd, key, value) fprintf(fd, "%s = %s\n", key, value?"true":"false");
#define write_config_bool(fd, key, value) fprintf(fd, "%s = %s\n", key, value ? "true":"false")
bool inputAdded = false;
@ -56,18 +59,24 @@ static struct option long_options[] = {
{"audio", required_argument, NULL, 'm'},
{"localaudio", no_argument, NULL, 'n'},
{"config", required_argument, NULL, 'o'},
{"platform", required_argument, 0, 'p'},
{"platform", required_argument, NULL, 'p'},
{"save", required_argument, NULL, 'q'},
{"keydir", required_argument, NULL, 'r'},
{"remote", no_argument, NULL, 's'},
{"remote", required_argument, NULL, 's'},
{"windowed", no_argument, NULL, 't'},
{"surround", no_argument, NULL, 'u'},
{"surround", required_argument, NULL, 'u'},
{"fps", required_argument, NULL, 'v'},
{"codec", required_argument, NULL, 'x'},
{"unsupported", no_argument, NULL, 'y'},
{"nounsupported", no_argument, NULL, 'y'},
{"quitappafter", no_argument, NULL, '1'},
{"viewonly", no_argument, NULL, '2'},
{"rotate", required_argument, NULL, '3'},
{"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},
};
@ -102,7 +111,7 @@ char* get_path(char* name, char* extra_data_dirs) {
char* end;
do {
end = strstr(data_dir, ":");
int length = end != NULL?end - data_dir:strlen(data_dir);
int length = end != NULL ? end - data_dir:strlen(data_dir);
memcpy(path, data_dir, length);
if (path[0] == '/')
sprintf(path+length, MOONLIGHT_PATH "/%s", name);
@ -191,13 +200,22 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) {
strcpy(config->key_dir, value);
break;
case 's':
config->stream.streamingRemotely = 1;
if (strcasecmp(value, "auto") == 0)
config->stream.streamingRemotely = STREAM_CFG_AUTO;
else if (strcasecmp(value, "true") == 0 || strcasecmp(value, "yes") == 0)
config->stream.streamingRemotely = STREAM_CFG_REMOTE;
else if (strcasecmp(value, "false") == 0 || strcasecmp(value, "no") == 0)
config->stream.streamingRemotely = STREAM_CFG_LOCAL;
break;
case 't':
config->fullscreen = false;
break;
case 'u':
config->stream.audioConfiguration = AUDIO_CONFIGURATION_51_SURROUND;
if (strcasecmp(value, "5.1") == 0)
config->stream.audioConfiguration = AUDIO_CONFIGURATION_51_SURROUND;
else if (strcasecmp(value, "7.1") == 0)
config->stream.audioConfiguration = AUDIO_CONFIGURATION_71_SURROUND;
break;
case 'v':
config->stream.fps = atoi(value);
@ -207,21 +225,41 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) {
config->codec = CODEC_UNSPECIFIED;
else if (strcasecmp(value, "h264") == 0)
config->codec = CODEC_H264;
if (strcasecmp(value, "h265") == 0 || strcasecmp(value, "hevc") == 0)
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 = true;
config->unsupported = false;
break;
case '1':
config->quitappafter = true;
break;
case '2':
config->viewonly = true;
break;
case '3':
config->rotate = atoi(value);
break;
case 'z':
config->debug_level = 1;
break;
case 'Z':
config->debug_level = 2;
break;
case '4':
config->mouse_emulation = false;
break;
case '5':
config->pin = atoi(value);
break;
case '6':
config->port = atoi(value);
break;
case '7':
config->hdr = true;
break;
case 1:
if (config->action == NULL)
config->action = value;
@ -251,10 +289,6 @@ bool config_file_parse(char* filename, PCONFIGURATION config) {
config->address = value;
} else if (strcmp(key, "sops") == 0) {
config->sops = strcmp("true", value) == 0;
} else if (strcmp(key, "localaudio") == 0) {
config->localaudio = strcmp("true", value) == 0;
} else if (strcmp(key, "quitappafter") == 0) {
config->quitappafter = strcmp("true", value) == 0;
} else {
for (int i=0;long_options[i].name != NULL;i++) {
if (strcmp(long_options[i].name, key) == 0) {
@ -293,6 +327,10 @@ void config_save(char* filename, PCONFIGURATION config) {
write_config_bool(fd, "localaudio", config->localaudio);
if (config->quitappafter)
write_config_bool(fd, "quitappafter", config->quitappafter);
if (config->viewonly)
write_config_bool(fd, "viewonly", config->viewonly);
if (config->rotate != 0)
write_config_int(fd, "rotate", config->rotate);
if (strcmp(config->app, "Steam") != 0)
write_config_string(fd, "app", config->app);
@ -305,12 +343,25 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) {
config->stream.width = 1280;
config->stream.height = 720;
config->stream.fps = -1;
config->stream.fps = 60;
config->stream.bitrate = -1;
config->stream.packetSize = 1024;
config->stream.streamingRemotely = 0;
config->stream.packetSize = 1392;
config->stream.streamingRemotely = STREAM_CFG_AUTO;
config->stream.audioConfiguration = AUDIO_CONFIGURATION_STEREO;
config->stream.supportsHevc = false;
config->stream.supportedVideoFormats = SCM_H264;
// Opt in for video encryption if the CPU has good AES performance
if (has_fast_aes()) {
config->stream.encryptionFlags = ENCFLG_ALL;
}
else if (has_slow_aes()) {
// For extremely slow CPUs, opt out of audio encryption
config->stream.encryptionFlags = ENCFLG_NONE;
printf("Disabling encryption on low performance CPU\n");
}
else {
config->stream.encryptionFlags = ENCFLG_AUDIO;
}
config->debug_level = 0;
config->platform = "auto";
@ -322,9 +373,15 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) {
config->sops = true;
config->localaudio = false;
config->fullscreen = true;
config->unsupported = false;
config->unsupported = true;
config->quitappafter = false;
config->viewonly = false;
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"));
@ -333,7 +390,7 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) {
char* config_file = get_path("moonlight.conf", "/etc");
if (config_file)
config_file_parse(config_file, config);
if (argc == 2 && access(argv[1], F_OK) == 0) {
config->action = "stream";
if (!config_file_parse(argv[1], config))
@ -342,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:stuv:w:xy", 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);
}
}
@ -354,22 +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.fps == -1)
config->stream.fps = config->stream.height >= 1080 ? 30 : 60;
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;
@ -39,14 +37,20 @@ typedef struct _CONFIGURATION {
bool sops;
bool localaudio;
bool fullscreen;
int rotate;
bool unsupported;
bool quitappafter;
bool viewonly;
bool mouse_emulation;
char* inputs[MAX_INPUTS];
int inputsCount;
enum codecs codec;
bool hdr;
int pin;
unsigned short port;
} CONFIGURATION, *PCONFIGURATION;
bool inputAdded;
extern bool inputAdded;
bool config_file_parse(char* filename, PCONFIGURATION config);
void config_parse(int argc, char* argv[], PCONFIGURATION config);

View File

@ -20,5 +20,7 @@
#define VERSION_MAJOR @PROJECT_VERSION_MAJOR@
#define VERSION_MINOR @PROJECT_VERSION_MINOR@
#define VERSION_PATCH @PROJECT_VERSION_PATCH@
#cmakedefine GIT_BRANCH "@GIT_BRANCH@"
#cmakedefine GIT_COMMIT_HASH "@GIT_COMMIT_HASH@"
#define COMPILE_OPTIONS "@MOONLIGHT_OPTIONS@"

View File

@ -23,11 +23,45 @@
#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");
break;
case ML_ERROR_NO_VIDEO_FRAME:
printf("Your network connection isn't performing well. Reduce your video bitrate setting or try a faster connection.\n");
break;
case ML_ERROR_UNEXPECTED_EARLY_TERMINATION:
printf("The connection was unexpectedly terminated by the host due to a video capture error. Make sure no DRM-protected content is playing on the host.\n");
break;
case ML_ERROR_PROTECTED_CONTENT:
printf("The connection was terminated by the host due to DRM-protected content. Close any DRM-protected content on the host and try again.\n");
break;
default:
printf("Connection terminated with error: %d\n", errorCode);
break;
}
#ifdef HAVE_SDL
SDL_Event event;
event.type = SDL_QUIT;
SDL_PushEvent(&event);
#endif
static void connection_terminated() {
if (main_thread_id != 0)
pthread_kill(main_thread_id, SIGTERM);
}
@ -44,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:
@ -63,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

@ -64,7 +64,7 @@ static void on_cec_keypress(void* userdata, const cec_keypress* key) {
value = 0;
break;
}
if (value != 0) {
short code = 0x80 << 8 | value;
LiSendKeyboardEvent(code, (key->duration > 0)?KEY_ACTION_UP:KEY_ACTION_DOWN, 0);
@ -80,25 +80,25 @@ void cec_init() {
snprintf(g_config.strDeviceName, sizeof(g_config.strDeviceName), "Moonlight");
g_config.callbacks = &g_callbacks;
g_config.deviceTypes.types[0] = CEC_DEVICE_TYPE_PLAYBACK_DEVICE;
if (libcecc_initialise(&g_config, &g_iface, NULL) != 1) {
fprintf(stderr, "Failed to initialize libcec interface\n");
fflush(stderr);
return;
}
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");
fflush(stderr);
libcecc_destroy(&g_iface);
return;
}
strcpy(g_strPort, devices[0].comm);
if (!g_iface.open(g_iface.connection, g_strPort, 5000)) {
fprintf(stderr, "Unable to open the device on port %s\n", g_strPort);
@ -106,6 +106,6 @@ void cec_init() {
libcecc_destroy(&g_iface);
return;
}
g_iface.set_active_source(g_iface.connection, g_config.deviceTypes.types[0]);
}

View File

@ -38,7 +38,12 @@
#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
#define int16_to_le(val) val
@ -56,21 +61,35 @@ struct input_abs_parms {
struct input_device {
struct libevdev *dev;
bool is_keyboard;
bool is_mouse;
bool is_touchscreen;
int rotate;
struct mapping* map;
int key_map[KEY_MAX];
int abs_map[ABS_MAX];
int key_map[KEY_CNT];
int abs_map[ABS_CNT];
int hats_state[3][2];
int fd;
char modifiers;
__s32 mouseDeltaX, mouseDeltaY, mouseScroll;
#ifdef __linux__
__s32 mouseDeltaX, mouseDeltaY, mouseVScroll, mouseHScroll;
__s32 touchDownX, touchDownY, touchX, touchY;
#else
int32_t mouseDeltaX, mouseDeltaY, mouseVScroll, mouseHScroll;
int32_t touchDownX, touchDownY, touchX, touchY;
#endif
struct timeval touchDownTime;
struct timeval btnDownTime;
short controllerId;
int haptic_effect_id;
int buttonFlags;
char leftTrigger, rightTrigger;
unsigned char leftTrigger, rightTrigger;
short leftStickX, leftStickY;
short rightStickX, rightStickY;
bool gamepadModified;
bool mouseEmulation;
pthread_t meThread;
struct input_abs_parms xParms, yParms, rxParms, ryParms, zParms, rzParms;
struct input_abs_parms leftParms, rightParms, upParms, downParms;
};
#define HAT_UP 1
@ -81,6 +100,23 @@ static const int hat_constants[3][3] = {{HAT_UP | HAT_LEFT, HAT_UP, HAT_UP | HAT
#define set_hat(flags, flag, hat, hat_flag) flags = (hat & hat_flag) == hat_flag ? flags | flag : flags & ~flag
#define TOUCH_UP -1
#define TOUCH_CLICK_RADIUS 10
#define TOUCH_CLICK_DELAY 100000 // microseconds
#define TOUCH_RCLICK_TIME 750 // milliseconds
// How long the Start button must be pressed to toggle mouse emulation
#define MOUSE_EMULATION_LONG_PRESS_TIME 750
// How long between polling the gamepad to send virtual mouse input
#define MOUSE_EMULATION_POLLING_INTERVAL 50000
// Determines how fast the mouse will move each interval
#define MOUSE_EMULATION_MOTION_MULTIPLIER 3
// Determines the maximum motion amount before allowing movement
#define MOUSE_EMULATION_DEADZONE 2
// Limited by number of bits in activeGamepadMask
#define MAX_GAMEPADS 16
static struct input_device* devices = NULL;
static int numDevices = 0;
static int assignedControllerIds = 0;
@ -92,6 +128,9 @@ static short* currentAbs;
static bool* currentReverse;
static bool grabbingDevices;
static bool mouseEmulationEnabled;
static bool waitingToExitOnModifiersUp = false;
int evdev_gamepads = 0;
@ -129,13 +168,23 @@ static bool evdev_init_parms(struct input_device *dev, struct input_abs_parms *p
static void evdev_remove(int devindex) {
numDevices--;
if (devices[devindex].controllerId >= 0)
printf("Input device removed: %s (player %d)\n", libevdev_get_name(devices[devindex].dev), devices[devindex].controllerId + 1);
if (devices[devindex].controllerId >= 0) {
assignedControllerIds &= ~(1 << devices[devindex].controllerId);
LiSendMultiControllerEvent(devices[devindex].controllerId, assignedControllerIds, 0, 0, 0, 0, 0, 0, 0);
}
if (devices[devindex].mouseEmulation) {
devices[devindex].mouseEmulation = false;
pthread_join(devices[devindex].meThread, NULL);
}
libevdev_free(devices[devindex].dev);
loop_remove_fd(devices[devindex].fd);
close(devices[devindex].fd);
if (devindex != numDevices && numDevices > 0)
memcpy(&devices[devindex], &devices[numDevices], sizeof(struct input_device));
fprintf(stderr, "Removed input device\n");
}
static short evdev_convert_value(struct input_event *ev, struct input_device *dev, struct input_abs_parms *parms, bool reverse) {
@ -156,18 +205,121 @@ static short evdev_convert_value(struct input_event *ev, struct input_device *de
return (long long)(ev->value - (ev->value>parms->avg?parms->flat*2:0) - parms->min) * (SHRT_MAX-SHRT_MIN) / (parms->max-parms->min-parms->flat*2) + SHRT_MIN;
}
static char evdev_convert_value_byte(struct input_event *ev, struct input_device *dev, struct input_abs_parms *parms) {
static unsigned char evdev_convert_value_byte(struct input_event *ev, struct input_device *dev, struct input_abs_parms *parms, char halfaxis) {
if (parms->max == 0 && parms->min == 0) {
fprintf(stderr, "Axis not found: %d\n", ev->code);
return 0;
}
if (abs(ev->value-parms->min)<parms->flat)
return 0;
else if (ev->value>parms->max)
return UCHAR_MAX;
else
return (ev->value - parms->flat - parms->min) * UCHAR_MAX / (parms->diff - parms->flat);
if (halfaxis == 0) {
if (abs(ev->value-parms->min)<parms->flat)
return 0;
else if (ev->value>parms->max)
return UCHAR_MAX;
else
return (ev->value - parms->flat - parms->min) * UCHAR_MAX / (parms->diff - parms->flat);
} else {
short val = evdev_convert_value(ev, dev, parms, false);
if (halfaxis == '-' && val < 0)
return -(int)val * UCHAR_MAX / (SHRT_MAX-SHRT_MIN);
else if (halfaxis == '+' && val > 0)
return (int)val * UCHAR_MAX / (SHRT_MAX-SHRT_MIN);
else
return 0;
}
}
void *HandleMouseEmulation(void* param)
{
struct input_device* dev = (struct input_device*) param;
while (dev->mouseEmulation) {
usleep(MOUSE_EMULATION_POLLING_INTERVAL);
short rawX;
short rawY;
// Determine which analog stick is currently receiving the strongest input
if ((uint32_t)abs(dev->leftStickX) + abs(dev->leftStickY) > (uint32_t)abs(dev->rightStickX) + abs(dev->rightStickY)) {
rawX = dev->leftStickX;
rawY = dev->leftStickY;
} else {
rawX = dev->rightStickX;
rawY = dev->rightStickY;
}
float deltaX;
float deltaY;
// Produce a base vector for mouse movement with increased speed as we deviate further from center
deltaX = pow((float)rawX / 32767.0f * MOUSE_EMULATION_MOTION_MULTIPLIER, 3);
deltaY = pow((float)rawY / 32767.0f * MOUSE_EMULATION_MOTION_MULTIPLIER, 3);
// Enforce deadzones
deltaX = fabs(deltaX) > MOUSE_EMULATION_DEADZONE ? deltaX - MOUSE_EMULATION_DEADZONE : 0;
deltaY = fabs(deltaY) > MOUSE_EMULATION_DEADZONE ? deltaY - MOUSE_EMULATION_DEADZONE : 0;
if (deltaX != 0 || deltaY != 0)
LiSendMouseMoveEvent(deltaX, -deltaY);
}
return NULL;
}
#define SET_BTN_FLAG(x, y) supportedButtonFlags |= (x >= 0) ? y : 0
static void send_controller_arrival(struct input_device *dev) {
unsigned char type = LI_CTYPE_UNKNOWN;
unsigned int supportedButtonFlags = 0;
unsigned short capabilities = 0;
switch (libevdev_get_id_vendor(dev->dev)) {
case 0x045e: // Microsoft
type = LI_CTYPE_XBOX;
break;
case 0x054c: // Sony
type = LI_CTYPE_PS;
break;
case 0x057e: // Nintendo
type = LI_CTYPE_NINTENDO;
break;
}
const char* name = libevdev_get_name(dev->dev);
if (name && type == LI_CTYPE_UNKNOWN) {
// Try to guess based on the name
if (strstr(name, "Xbox") || strstr(name, "X-Box") || strstr(name, "XBox") || strstr(name, "XBOX")) {
type = LI_CTYPE_XBOX;
}
}
SET_BTN_FLAG(dev->map->btn_a, A_FLAG);
SET_BTN_FLAG(dev->map->btn_b, B_FLAG);
SET_BTN_FLAG(dev->map->btn_x, X_FLAG);
SET_BTN_FLAG(dev->map->btn_y, Y_FLAG);
SET_BTN_FLAG(dev->map->btn_back, BACK_FLAG);
SET_BTN_FLAG(dev->map->btn_start, PLAY_FLAG);
SET_BTN_FLAG(dev->map->btn_guide, SPECIAL_FLAG);
SET_BTN_FLAG(dev->map->btn_leftstick, LS_CLK_FLAG);
SET_BTN_FLAG(dev->map->btn_rightstick, RS_CLK_FLAG);
SET_BTN_FLAG(dev->map->btn_leftshoulder, LB_FLAG);
SET_BTN_FLAG(dev->map->btn_rightshoulder, RB_FLAG);
SET_BTN_FLAG(dev->map->btn_misc1, MISC_FLAG);
SET_BTN_FLAG(dev->map->btn_paddle1, PADDLE1_FLAG);
SET_BTN_FLAG(dev->map->btn_paddle2, PADDLE2_FLAG);
SET_BTN_FLAG(dev->map->btn_paddle3, PADDLE3_FLAG);
SET_BTN_FLAG(dev->map->btn_paddle4, PADDLE4_FLAG);
SET_BTN_FLAG(dev->map->btn_touchpad, TOUCHPAD_FLAG);
if (dev->map->abs_lefttrigger >= 0 && dev->map->abs_righttrigger >= 0)
capabilities |= LI_CCAP_ANALOG_TRIGGERS;
// TODO: Probe for this properly
capabilities |= LI_CCAP_RUMBLE;
LiSendControllerArrivalEvent(dev->controllerId, assignedControllerIds, type,
supportedButtonFlags, capabilities);
}
static bool evdev_handle_event(struct input_event *ev, struct input_device *dev) {
@ -176,32 +328,57 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev)
switch (ev->type) {
case EV_SYN:
if (dev->mouseDeltaX != 0 || dev->mouseDeltaY != 0) {
LiSendMouseMoveEvent(dev->mouseDeltaX, dev->mouseDeltaY);
switch (dev->rotate) {
case 90:
LiSendMouseMoveEvent(dev->mouseDeltaY, -dev->mouseDeltaX);
break;
case 180:
LiSendMouseMoveEvent(-dev->mouseDeltaX, -dev->mouseDeltaY);
break;
case 270:
LiSendMouseMoveEvent(-dev->mouseDeltaY, dev->mouseDeltaX);
break;
default:
LiSendMouseMoveEvent(dev->mouseDeltaX, dev->mouseDeltaY);
break;
}
dev->mouseDeltaX = 0;
dev->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;
printf("Assigned %s as player %d\n", libevdev_get_name(dev->dev), i+1);
break;
}
}
//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);
}
LiSendMultiControllerEvent(dev->controllerId, assignedControllerIds, dev->buttonFlags, dev->leftTrigger, dev->rightTrigger, dev->leftStickX, dev->leftStickY, dev->rightStickX, dev->rightStickY);
// Send event only if mouse emulation is disabled.
if (dev->mouseEmulation == false)
LiSendMultiControllerEvent(dev->controllerId, assignedControllerIds, dev->buttonFlags, dev->leftTrigger, dev->rightTrigger, dev->leftStickX, dev->leftStickY, dev->rightStickX, dev->rightStickY);
dev->gamepadModified = false;
}
break;
case EV_KEY:
if (ev->code > KEY_MAX)
return true;
if (ev->code < sizeof(keyCodes)/sizeof(keyCodes[0])) {
char modifier = 0;
switch (ev->code) {
@ -217,6 +394,10 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev)
case KEY_RIGHTCTRL:
modifier = MODIFIER_CTRL;
break;
case KEY_LEFTMETA:
case KEY_RIGHTMETA:
modifier = MODIFIER_META;
break;
}
if (modifier != 0) {
if (ev->value)
@ -225,18 +406,20 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev)
dev->modifiers &= ~modifier;
}
// Quit the stream if all the required quit keys are down
// After the quit key combo is pressed, quit once all keys are raised
if ((dev->modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS &&
ev->code == QUIT_KEY && ev->value != 0) {
waitingToExitOnModifiersUp = true;
return true;
} else if (waitingToExitOnModifiersUp && dev->modifiers == 0)
return false;
}
short code = 0x80 << 8 | keyCodes[ev->code];
LiSendKeyboardEvent(code, ev->value?KEY_ACTION_DOWN:KEY_ACTION_UP, dev->modifiers);
} else {
int mouseCode = 0;
short gamepadCode = 0;
int index = ev->code > BTN_MISC && ev->code < (BTN_MISC + KEY_MAX) ? dev->key_map[ev->code - BTN_MISC] : -1;
int gamepadCode = 0;
int index = dev->key_map[ev->code];
switch (ev->code) {
case BTN_LEFT:
@ -254,6 +437,27 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev)
case BTN_EXTRA:
mouseCode = BUTTON_X2;
break;
case BTN_TOUCH:
if (ev->value == 1) {
dev->touchDownTime = ev->time;
} else {
if (dev->touchDownX != TOUCH_UP && dev->touchDownY != TOUCH_UP) {
int deltaX = dev->touchX - dev->touchDownX;
int deltaY = dev->touchY - dev->touchDownY;
if (deltaX * deltaX + deltaY * deltaY < TOUCH_CLICK_RADIUS * TOUCH_CLICK_RADIUS) {
struct timeval elapsedTime;
timersub(&ev->time, &dev->touchDownTime, &elapsedTime);
int holdTimeMs = elapsedTime.tv_sec * 1000 + elapsedTime.tv_usec / 1000;
int button = holdTimeMs >= TOUCH_RCLICK_TIME ? BUTTON_RIGHT : BUTTON_LEFT;
LiSendMouseButtonEvent(BUTTON_ACTION_PRESS, button);
usleep(TOUCH_CLICK_DELAY);
LiSendMouseButtonEvent(BUTTON_ACTION_RELEASE, button);
}
}
dev->touchDownX = TOUCH_UP;
dev->touchDownY = TOUCH_UP;
}
break;
default:
gamepadModified = true;
if (dev->map == NULL)
@ -288,16 +492,68 @@ 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) {
LiSendMouseButtonEvent(ev->value?BUTTON_ACTION_PRESS:BUTTON_ACTION_RELEASE, mouseCode);
gamepadModified = false;
} else if (gamepadCode != 0) {
if (ev->value)
if (ev->value) {
dev->buttonFlags |= gamepadCode;
else
dev->btnDownTime = ev->time;
} else
dev->buttonFlags &= ~gamepadCode;
if (mouseEmulationEnabled && gamepadCode == PLAY_FLAG && ev->value == 0) {
struct timeval elapsedTime;
timersub(&ev->time, &dev->btnDownTime, &elapsedTime);
int holdTimeMs = elapsedTime.tv_sec * 1000 + elapsedTime.tv_usec / 1000;
if (holdTimeMs >= MOUSE_EMULATION_LONG_PRESS_TIME) {
if (dev->mouseEmulation) {
dev->mouseEmulation = false;
pthread_join(dev->meThread, NULL);
dev->meThread = 0;
printf("Mouse emulation disabled for controller %d.\n", dev->controllerId);
} else {
dev->mouseEmulation = true;
pthread_create(&dev->meThread, NULL, HandleMouseEmulation, dev);
printf("Mouse emulation enabled for controller %d.\n", dev->controllerId);
}
// clear gamepad state.
LiSendMultiControllerEvent(dev->controllerId, assignedControllerIds, 0, 0, 0, 0, 0, 0, 0);
}
} else if (dev->mouseEmulation) {
char action = ev->value ? BUTTON_ACTION_PRESS : BUTTON_ACTION_RELEASE;
switch (gamepadCode) {
case A_FLAG:
LiSendMouseButtonEvent(action, BUTTON_LEFT);
break;
case B_FLAG:
LiSendMouseButtonEvent(action, BUTTON_RIGHT);
break;
case X_FLAG:
LiSendMouseButtonEvent(action, BUTTON_MIDDLE);
break;
case LB_FLAG:
LiSendMouseButtonEvent(action, BUTTON_X1);
break;
case RB_FLAG:
LiSendMouseButtonEvent(action, BUTTON_X2);
break;
}
}
} else if (dev->map != NULL && index == dev->map->btn_lefttrigger)
dev->leftTrigger = ev->value ? UCHAR_MAX : 0;
else if (dev->map != NULL && index == dev->map->btn_righttrigger)
@ -318,12 +574,41 @@ 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;
case EV_ABS:
if (ev->code > ABS_MAX)
return true;
if (dev->is_touchscreen) {
switch (ev->code) {
case ABS_X:
if (dev->touchDownX == TOUCH_UP) {
dev->touchDownX = ev->value;
dev->touchX = ev->value;
} else {
dev->mouseDeltaX += (ev->value - dev->touchX);
dev->touchX = ev->value;
}
break;
case ABS_Y:
if (dev->touchDownY == TOUCH_UP) {
dev->touchDownY = ev->value;
dev->touchY = ev->value;
} else {
dev->mouseDeltaY += (ev->value - dev->touchY);
dev->touchY = ev->value;
}
break;
}
break;
}
if (dev->map == NULL)
break;
@ -361,17 +646,57 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev)
dev->rightStickX = evdev_convert_value(ev, dev, &dev->rxParms, dev->map->reverse_rightx);
else if (index == dev->map->abs_righty)
dev->rightStickY = evdev_convert_value(ev, dev, &dev->ryParms, !dev->map->reverse_righty);
else if (index == dev->map->abs_lefttrigger)
dev->leftTrigger = evdev_convert_value_byte(ev, dev, &dev->zParms);
else if (index == dev->map->abs_righttrigger)
dev->rightTrigger = evdev_convert_value_byte(ev, dev, &dev->rzParms);
else
gamepadModified = false;
if (index == dev->map->abs_lefttrigger) {
dev->leftTrigger = evdev_convert_value_byte(ev, dev, &dev->zParms, dev->map->halfaxis_lefttrigger);
gamepadModified = true;
}
if (index == dev->map->abs_righttrigger) {
dev->rightTrigger = evdev_convert_value_byte(ev, dev, &dev->rzParms, dev->map->halfaxis_righttrigger);
gamepadModified = true;
}
if (index == dev->map->abs_dpright) {
if (evdev_convert_value_byte(ev, dev, &dev->rightParms, dev->map->halfaxis_dpright) > 127)
dev->buttonFlags |= RIGHT_FLAG;
else
dev->buttonFlags &= ~RIGHT_FLAG;
gamepadModified = true;
}
if (index == dev->map->abs_dpleft) {
if (evdev_convert_value_byte(ev, dev, &dev->leftParms, dev->map->halfaxis_dpleft) > 127)
dev->buttonFlags |= LEFT_FLAG;
else
dev->buttonFlags &= ~LEFT_FLAG;
gamepadModified = true;
}
if (index == dev->map->abs_dpup) {
if (evdev_convert_value_byte(ev, dev, &dev->upParms, dev->map->halfaxis_dpup) > 127)
dev->buttonFlags |= UP_FLAG;
else
dev->buttonFlags &= ~UP_FLAG;
gamepadModified = true;
}
if (index == dev->map->abs_dpdown) {
if (evdev_convert_value_byte(ev, dev, &dev->downParms, dev->map->halfaxis_dpdown) > 127)
dev->buttonFlags |= DOWN_FLAG;
else
dev->buttonFlags &= ~DOWN_FLAG;
gamepadModified = true;
}
}
}
if (gamepadModified && (dev->buttonFlags & QUIT_BUTTONS) == QUIT_BUTTONS)
if (gamepadModified && (dev->buttonFlags & QUIT_BUTTONS) == QUIT_BUTTONS) {
LiSendMultiControllerEvent(dev->controllerId, assignedControllerIds, 0, 0, 0, 0, 0, 0, 0);
return false;
}
dev->gamepadModified |= gamepadModified;
return true;
@ -381,7 +706,7 @@ static bool evdev_handle_mapping_event(struct input_event *ev, struct input_devi
int index, hat_index;
switch (ev->type) {
case EV_KEY:
index = ev->code > BTN_MISC && ev->code < (BTN_MISC + KEY_MAX) ? dev->key_map[ev->code - BTN_MISC] : -1;
index = dev->key_map[ev->code];
if (currentKey != NULL) {
if (ev->value)
*currentKey = index;
@ -450,7 +775,7 @@ static int evdev_handle(int fd) {
return LOOP_OK;
}
void evdev_create(const char* device, struct mapping* mappings, bool verbose) {
void evdev_create(const char* device, struct mapping* mappings, bool verbose, int rotate) {
int fd = open(device, O_RDWR|O_NONBLOCK);
if (fd <= 0) {
fprintf(stderr, "Failed to open device %s\n", device);
@ -499,15 +824,55 @@ void evdev_create(const char* device, struct mapping* mappings, bool verbose) {
bool is_keyboard = libevdev_has_event_code(evdev, EV_KEY, KEY_Q);
bool is_mouse = libevdev_has_event_type(evdev, EV_REL) || libevdev_has_event_code(evdev, EV_KEY, BTN_LEFT);
bool is_touchscreen = libevdev_has_event_code(evdev, EV_KEY, BTN_TOUCH);
if (mappings == NULL && !(is_keyboard || is_mouse)) {
fprintf(stderr, "No mapping available for %s (%s) on %s\n", name, str_guid, device);
mappings = default_mapping;
// This classification logic comes from SDL
bool is_accelerometer =
((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_Z)) ||
(libevdev_has_event_code(evdev, EV_ABS, ABS_RX) &&
libevdev_has_event_code(evdev, EV_ABS, ABS_RY) &&
libevdev_has_event_code(evdev, EV_ABS, ABS_RZ))) &&
!libevdev_has_event_type(evdev, EV_KEY);
bool is_gamepad =
((libevdev_has_event_code(evdev, EV_ABS, ABS_X) &&
libevdev_has_event_code(evdev, EV_ABS, ABS_Y)) ||
(libevdev_has_event_code(evdev, EV_ABS, ABS_HAT0X) &&
libevdev_has_event_code(evdev, EV_ABS, ABS_HAT0Y))) &&
(libevdev_has_event_code(evdev, EV_KEY, BTN_TRIGGER) ||
libevdev_has_event_code(evdev, EV_KEY, BTN_A) ||
libevdev_has_event_code(evdev, EV_KEY, BTN_1) ||
libevdev_has_event_code(evdev, EV_ABS, ABS_RX) ||
libevdev_has_event_code(evdev, EV_ABS, ABS_RY) ||
libevdev_has_event_code(evdev, EV_ABS, ABS_RZ) ||
libevdev_has_event_code(evdev, EV_ABS, ABS_THROTTLE) ||
libevdev_has_event_code(evdev, EV_ABS, ABS_RUDDER) ||
libevdev_has_event_code(evdev, EV_ABS, ABS_WHEEL) ||
libevdev_has_event_code(evdev, EV_ABS, ABS_GAS) ||
libevdev_has_event_code(evdev, EV_ABS, ABS_BRAKE));
if (is_accelerometer) {
if (verbose)
printf("Ignoring accelerometer: %s\n", name);
libevdev_free(evdev);
close(fd);
return;
}
if (!is_keyboard && !is_mouse)
if (is_gamepad) {
evdev_gamepads++;
if (mappings == NULL) {
fprintf(stderr, "No mapping available for %s (%s) on %s\n", name, str_guid, device);
mappings = default_mapping;
}
} else {
if (verbose)
printf("Not mapping %s as a gamepad\n", name);
mappings = NULL;
}
int dev = numDevices;
numDevices++;
@ -526,18 +891,25 @@ void evdev_create(const char* device, struct mapping* mappings, bool verbose) {
devices[dev].fd = fd;
devices[dev].dev = evdev;
devices[dev].map = mappings;
/* Set unused evdev indices to -2 to avoid aliasing with the default -1 in our mappings */
memset(&devices[dev].key_map, -2, sizeof(devices[dev].key_map));
memset(&devices[dev].abs_map, -2, sizeof(devices[dev].abs_map));
devices[dev].is_keyboard = is_keyboard;
devices[dev].is_mouse = is_mouse;
devices[dev].is_touchscreen = is_touchscreen;
devices[dev].rotate = rotate;
devices[dev].touchDownX = TOUCH_UP;
devices[dev].touchDownY = TOUCH_UP;
int nbuttons = 0;
/* Count joystick buttons first like SDL does */
for (int i = BTN_JOYSTICK; i < KEY_MAX; ++i) {
if (libevdev_has_event_code(devices[dev].dev, EV_KEY, i))
devices[dev].key_map[i - BTN_MISC] = nbuttons++;
devices[dev].key_map[i] = nbuttons++;
}
for (int i = BTN_MISC; i < BTN_JOYSTICK; ++i) {
for (int i = 0; i < BTN_JOYSTICK; ++i) {
if (libevdev_has_event_code(devices[dev].dev, EV_KEY, i))
devices[dev].key_map[i - BTN_MISC] = nbuttons++;
devices[dev].key_map[i] = nbuttons++;
}
int naxes = 0;
@ -559,11 +931,15 @@ void evdev_create(const char* device, struct mapping* mappings, bool verbose) {
valid &= evdev_init_parms(&devices[dev], &(devices[dev].rxParms), devices[dev].map->abs_rightx);
valid &= evdev_init_parms(&devices[dev], &(devices[dev].ryParms), devices[dev].map->abs_righty);
valid &= evdev_init_parms(&devices[dev], &(devices[dev].rzParms), devices[dev].map->abs_righttrigger);
valid &= evdev_init_parms(&devices[dev], &(devices[dev].leftParms), devices[dev].map->abs_dpleft);
valid &= evdev_init_parms(&devices[dev], &(devices[dev].rightParms), devices[dev].map->abs_dpright);
valid &= evdev_init_parms(&devices[dev], &(devices[dev].upParms), devices[dev].map->abs_dpup);
valid &= evdev_init_parms(&devices[dev], &(devices[dev].downParms), devices[dev].map->abs_dpdown);
if (!valid)
fprintf(stderr, "Mapping for %s (%s) on %s is incorrect\n", name, str_guid, device);
}
if (grabbingDevices && is_keyboard) {
if (grabbingDevices && (is_keyboard || is_mouse || is_touchscreen)) {
if (ioctl(fd, EVIOCGRAB, 1) < 0) {
fprintf(stderr, "EVIOCGRAB failed with error %d\n", errno);
}
@ -643,10 +1019,10 @@ void evdev_map(char* device) {
char* buf = str_guid;
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);
@ -690,7 +1066,7 @@ void evdev_start() {
// we're ready to take input events. Ctrl+C works up until
// this point.
for (int i = 0; i < numDevices; i++) {
if (devices[i].is_keyboard && ioctl(devices[i].fd, EVIOCGRAB, 1) < 0) {
if ((devices[i].is_keyboard || devices[i].is_mouse || devices[i].is_touchscreen) && ioctl(devices[i].fd, EVIOCGRAB, 1) < 0) {
fprintf(stderr, "EVIOCGRAB failed with error %d\n", errno);
}
}
@ -705,8 +1081,9 @@ void evdev_stop() {
evdev_drain();
}
void evdev_init() {
void evdev_init(bool mouse_emulation_enabled) {
handler = evdev_handle_event;
mouseEmulationEnabled = mouse_emulation_enabled;
}
static struct input_device* evdev_get_input_device(unsigned short controller_id) {

View File

@ -21,10 +21,10 @@
extern int evdev_gamepads;
void evdev_create(const char* device, struct mapping* mappings, bool verbose);
void evdev_create(const char* device, struct mapping* mappings, bool verbose, int rotate);
void evdev_loop();
void evdev_init();
void evdev_init(bool mouse_emulation_enabled);
void evdev_start();
void evdev_stop();
void evdev_map(char* device);

View File

@ -47,7 +47,7 @@ static const short keyCodes[] = {
0xDB, //VK_BRACELEFT
0xDD, //VK_BRACERIGHT
0x0D, //VK_ENTER
0x11, //VK_CONTROL Left control
0xA2, //VK_CONTROL Left control
0x41, //VK_A
0x53, //VK_S
0x44, //VK_D
@ -60,7 +60,7 @@ static const short keyCodes[] = {
0xBA, //VK_SEMICOLON
0xDE, //VK_APOSTROPHE
0xC0, //VK_GRAVE
0x10, //VK_SHIFT Left shift
0xA0, //VK_SHIFT Left shift
0xDC, //VK_BACK_SLASH
0x5A, //VK_Z
0x58, //VK_X
@ -72,9 +72,9 @@ static const short keyCodes[] = {
0xBC, //VK_COMMA
0xBE, //VK_DOT
0xBF, //VK_SLASH
0x10, //VK_SHIFT Right shift
0xA1, //VK_SHIFT Right shift
0x6A, //VK_KPASTERISK
0x12, //VK_ALT Left alt
0xA4, //VK_ALT Left alt
0x20, //VK_SPACE
0x14, //VK_CAPS_LOCK
0x70, //VK_F1
@ -115,10 +115,10 @@ static const short keyCodes[] = {
0, //VK_MUHENKAN
0, //VK_KPJPCOMMA
0x0D, //VK_KPENTER
0x11, //VK_CONTROL Right ctrl
0xA3, //VK_CONTROL Right ctrl
0x6F, //VK_KPSLASH
0x2C, //VK_SYSRQ
0x12, //VK_ALT Right alt
0xA5, //VK_ALT Right alt
0, //KeyEvent.VK_LINEFEED
0x24, //VK_HOME
0x26, //VK_UP
@ -128,7 +128,7 @@ static const short keyCodes[] = {
0x23, //VK_END
0x28, //VK_DOWN
0x22, //VK_PAGE_DOWN
0x9B, //VK_INSERT
0x2D, //VK_INSERT
0x2E, //VK_DELETE
0, //VK_MACRO
0, //VK_MUTE
@ -139,4 +139,11 @@ static const short keyCodes[] = {
0, //VK_KPPLUSMINUS
0x13, //VK_PAUSE
0, //VK_SCALE AL Compiz Scale (Expose)
0, //KEY_KPCOMMA
0, //KEY_HANGEUL
0, //KEY_HANJA
0, //KEY_YEN
0x5B, //KEY_LEFTMETA
0x5C, //KEY_RIGHTMETA
0, //KEY_COMPOSE
};

View File

@ -22,6 +22,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stddef.h>
#include <string.h>
struct mapping* mapping_parse(char* mapping) {
@ -31,25 +32,33 @@ struct mapping* mapping_parse(char* mapping) {
if (guid == NULL || name == NULL)
return NULL;
struct mapping* map = malloc(sizeof(struct mapping));
struct mapping* map = calloc(sizeof(struct mapping), 1);
if (map == NULL) {
fprintf(stderr, "Not enough memory");
exit(EXIT_FAILURE);
}
strncpy(map->guid, guid, sizeof(map->guid));
strncpy(map->name, name, sizeof(map->name));
memset(&map->abs_leftx, -1, sizeof(short) * 31);
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));
char* option;
while ((option = strtok_r(NULL, ",", &strpoint)) != NULL) {
char *key = NULL, *value = NULL;
char *key = NULL, *orig_value = NULL;
int ret;
if ((ret = sscanf(option, "%m[^:]:%ms", &key, &value)) == 2) {
if ((ret = sscanf(option, "%m[^:]:%ms", &key, &orig_value)) == 2) {
int int_value, direction_value;
char *value = orig_value;
char flag = 0;
char half_axis = 0;
if (value[0] == '-' || value[0] == '+') {
half_axis = value[0];
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;
@ -85,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;
@ -98,10 +119,25 @@ struct mapping* mapping_parse(char* mapping) {
} else if (strcmp("righty", key) == 0) {
map->abs_righty = int_value;
map->reverse_righty = flag == '~';
} else if (strcmp("lefttrigger", key) == 0)
} else if (strcmp("lefttrigger", key) == 0) {
map->abs_lefttrigger = int_value;
else if (strcmp("righttrigger", key) == 0)
map->halfaxis_lefttrigger = half_axis;
} else if (strcmp("righttrigger", key) == 0) {
map->abs_righttrigger = int_value;
map->halfaxis_righttrigger = half_axis;
} else if (strcmp("dpright", key) == 0) {
map->abs_dpright = int_value;
map->halfaxis_dpright = half_axis;
} else if (strcmp("dpleft", key) == 0) {
map->abs_dpleft = int_value;
map->halfaxis_dpleft = half_axis;
} else if (strcmp("dpup", key) == 0) {
map->abs_dpup = int_value;
map->halfaxis_dpup = half_axis;
} else if (strcmp("dpdown", key) == 0) {
map->abs_dpdown = int_value;
map->halfaxis_dpdown = half_axis;
}
} else if (sscanf(value, "h%d.%d", &int_value, &direction_value) == 2) {
if (strcmp("dpright", key) == 0) {
map->hat_dpright = int_value;
@ -116,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')
@ -124,8 +162,8 @@ struct mapping* mapping_parse(char* mapping) {
if (key != NULL)
free(key);
if (value != NULL)
free(value);
if (orig_value != NULL)
free(orig_value);
}
map->guid[32] = '\0';
map->name[256] = '\0';
@ -189,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

@ -28,22 +28,31 @@ struct mapping {
bool reverse_leftx, reverse_lefty;
bool reverse_rightx, reverse_righty;
char halfaxis_lefttrigger, halfaxis_righttrigger;
char halfaxis_dpright, halfaxis_dpleft;
char halfaxis_dpup, halfaxis_dpdown;
/* abs_leftx must be the first member of the list of mapping indices! */
short abs_leftx, abs_lefty;
short abs_rightx, abs_righty;
short hat_dpright, hat_dpleft, hat_dpup, hat_dpdown;
short hat_dir_dpright, hat_dir_dpleft, hat_dir_dpup, hat_dir_dpdown;
short btn_dpup, btn_dpdown, btn_dpleft, btn_dpright;
short abs_dpright, abs_dpleft, abs_dpup, abs_dpdown;
short btn_a, btn_x, btn_y, btn_b;
short btn_back, btn_start, btn_guide;
short btn_leftstick, btn_rightstick;
short btn_leftshoulder, btn_rightshoulder;
short abs_lefttrigger, abs_righttrigger;
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;
/* next must be the last member after the list of mapping indices! */
struct mapping* next;
};

View File

@ -26,60 +26,406 @@
#define QUIT_KEY SDLK_q
#define QUIT_BUTTONS (PLAY_FLAG|BACK_FLAG|LB_FLAG|RB_FLAG)
#define FULLSCREEN_KEY SDLK_f
#define UNGRAB_KEY SDLK_z
static const int SDL_TO_LI_BUTTON_MAP[] = {
A_FLAG, B_FLAG, X_FLAG, Y_FLAG,
BACK_FLAG, SPECIAL_FLAG, PLAY_FLAG,
LS_CLK_FLAG, RS_CLK_FLAG,
LB_FLAG, RB_FLAG,
UP_FLAG, DOWN_FLAG, LEFT_FLAG, RIGHT_FLAG,
MISC_FLAG,
PADDLE1_FLAG, PADDLE2_FLAG, PADDLE3_FLAG, PADDLE4_FLAG,
TOUCHPAD_FLAG,
};
typedef struct _GAMEPAD_STATE {
char leftTrigger, rightTrigger;
unsigned char leftTrigger, rightTrigger;
short leftStickX, leftStickY;
short rightStickX, rightStickY;
int buttons;
SDL_JoystickID sdl_id;
SDL_GameController* controller;
#if !SDL_VERSION_ATLEAST(2, 0, 9)
SDL_Haptic* haptic;
int haptic_effect_id;
#endif
short id;
bool initialized;
} GAMEPAD_STATE, *PGAMEPAD_STATE;
static GAMEPAD_STATE gamepads[4];
// Limited by number of bits in activeGamepadMask
#define MAX_GAMEPADS 16
static GAMEPAD_STATE gamepads[MAX_GAMEPADS];
static int keyboard_modifiers;
static int activeGamepadMask = 0;
int sdl_gamepads = 0;
static PGAMEPAD_STATE get_gamepad(SDL_JoystickID sdl_id) {
for (int i = 0;i<4;i++) {
#define VK_0 0x30
#define VK_A 0x41
// These are real Windows VK_* codes
#ifndef VK_F1
#define VK_F1 0x70
#define VK_F13 0x7C
#define VK_NUMPAD0 0x60
#endif
int vk_for_sdl_scancode(SDL_Scancode scancode) {
// Set keycode. We explicitly use scancode here because GFE will try to correct
// for AZERTY layouts on the host but it depends on receiving VK_ values matching
// a QWERTY layout to work.
if (scancode >= SDL_SCANCODE_1 && scancode <= SDL_SCANCODE_9) {
// SDL defines SDL_SCANCODE_0 > SDL_SCANCODE_9, so we need to handle that manually
return (scancode - SDL_SCANCODE_1) + VK_0 + 1;
}
else if (scancode >= SDL_SCANCODE_A && scancode <= SDL_SCANCODE_Z) {
return (scancode - SDL_SCANCODE_A) + VK_A;
}
else if (scancode >= SDL_SCANCODE_F1 && scancode <= SDL_SCANCODE_F12) {
return (scancode - SDL_SCANCODE_F1) + VK_F1;
}
else if (scancode >= SDL_SCANCODE_F13 && scancode <= SDL_SCANCODE_F24) {
return (scancode - SDL_SCANCODE_F13) + VK_F13;
}
else if (scancode >= SDL_SCANCODE_KP_1 && scancode <= SDL_SCANCODE_KP_9) {
// SDL defines SDL_SCANCODE_KP_0 > SDL_SCANCODE_KP_9, so we need to handle that manually
return (scancode - SDL_SCANCODE_KP_1) + VK_NUMPAD0 + 1;
}
else {
switch (scancode) {
case SDL_SCANCODE_BACKSPACE:
return 0x08;
case SDL_SCANCODE_TAB:
return 0x09;
case SDL_SCANCODE_CLEAR:
return 0x0C;
case SDL_SCANCODE_KP_ENTER:
case SDL_SCANCODE_RETURN:
return 0x0D;
case SDL_SCANCODE_PAUSE:
return 0x13;
case SDL_SCANCODE_CAPSLOCK:
return 0x14;
case SDL_SCANCODE_ESCAPE:
return 0x1B;
case SDL_SCANCODE_SPACE:
return 0x20;
case SDL_SCANCODE_PAGEUP:
return 0x21;
case SDL_SCANCODE_PAGEDOWN:
return 0x22;
case SDL_SCANCODE_END:
return 0x23;
case SDL_SCANCODE_HOME:
return 0x24;
case SDL_SCANCODE_LEFT:
return 0x25;
case SDL_SCANCODE_UP:
return 0x26;
case SDL_SCANCODE_RIGHT:
return 0x27;
case SDL_SCANCODE_DOWN:
return 0x28;
case SDL_SCANCODE_SELECT:
return 0x29;
case SDL_SCANCODE_EXECUTE:
return 0x2B;
case SDL_SCANCODE_PRINTSCREEN:
return 0x2C;
case SDL_SCANCODE_INSERT:
return 0x2D;
case SDL_SCANCODE_DELETE:
return 0x2E;
case SDL_SCANCODE_HELP:
return 0x2F;
case SDL_SCANCODE_KP_0:
// See comment above about why we only handle SDL_SCANCODE_KP_0 here
return VK_NUMPAD0;
case SDL_SCANCODE_0:
// See comment above about why we only handle SDL_SCANCODE_0 here
return VK_0;
case SDL_SCANCODE_KP_MULTIPLY:
return 0x6A;
case SDL_SCANCODE_KP_PLUS:
return 0x6B;
case SDL_SCANCODE_KP_COMMA:
return 0x6C;
case SDL_SCANCODE_KP_MINUS:
return 0x6D;
case SDL_SCANCODE_KP_PERIOD:
return 0x6E;
case SDL_SCANCODE_KP_DIVIDE:
return 0x6F;
case SDL_SCANCODE_NUMLOCKCLEAR:
return 0x90;
case SDL_SCANCODE_SCROLLLOCK:
return 0x91;
case SDL_SCANCODE_LSHIFT:
return 0xA0;
case SDL_SCANCODE_RSHIFT:
return 0xA1;
case SDL_SCANCODE_LCTRL:
return 0xA2;
case SDL_SCANCODE_RCTRL:
return 0xA3;
case SDL_SCANCODE_LALT:
return 0xA4;
case SDL_SCANCODE_RALT:
return 0xA5;
case SDL_SCANCODE_LGUI:
return 0x5B;
case SDL_SCANCODE_RGUI:
return 0x5C;
case SDL_SCANCODE_APPLICATION:
return 0x5D;
case SDL_SCANCODE_AC_BACK:
return 0xA6;
case SDL_SCANCODE_AC_FORWARD:
return 0xA7;
case SDL_SCANCODE_AC_REFRESH:
return 0xA8;
case SDL_SCANCODE_AC_STOP:
return 0xA9;
case SDL_SCANCODE_AC_SEARCH:
return 0xAA;
case SDL_SCANCODE_AC_BOOKMARKS:
return 0xAB;
case SDL_SCANCODE_AC_HOME:
return 0xAC;
case SDL_SCANCODE_SEMICOLON:
return 0xBA;
case SDL_SCANCODE_EQUALS:
return 0xBB;
case SDL_SCANCODE_COMMA:
return 0xBC;
case SDL_SCANCODE_MINUS:
return 0xBD;
case SDL_SCANCODE_PERIOD:
return 0xBE;
case SDL_SCANCODE_SLASH:
return 0xBF;
case SDL_SCANCODE_GRAVE:
return 0xC0;
case SDL_SCANCODE_LEFTBRACKET:
return 0xDB;
case SDL_SCANCODE_BACKSLASH:
return 0xDC;
case SDL_SCANCODE_RIGHTBRACKET:
return 0xDD;
case SDL_SCANCODE_APOSTROPHE:
return 0xDE;
case SDL_SCANCODE_NONUSBACKSLASH:
return 0xE2;
default:
return 0;
}
}
}
static void send_controller_arrival(PGAMEPAD_STATE state) {
#if SDL_VERSION_ATLEAST(2, 0, 18)
unsigned int supportedButtonFlags = 0;
unsigned short capabilities = 0;
unsigned char type = LI_CTYPE_UNKNOWN;
for (int i = 0; i < SDL_arraysize(SDL_TO_LI_BUTTON_MAP); i++) {
if (SDL_GameControllerHasButton(state->controller, (SDL_GameControllerButton)i)) {
supportedButtonFlags |= SDL_TO_LI_BUTTON_MAP[i];
}
}
if (SDL_GameControllerGetBindForAxis(state->controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT).bindType == SDL_CONTROLLER_BINDTYPE_AXIS ||
SDL_GameControllerGetBindForAxis(state->controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT).bindType == SDL_CONTROLLER_BINDTYPE_AXIS)
capabilities |= LI_CCAP_ANALOG_TRIGGERS;
if (SDL_GameControllerHasRumble(state->controller))
capabilities |= LI_CCAP_RUMBLE;
if (SDL_GameControllerHasRumbleTriggers(state->controller))
capabilities |= LI_CCAP_TRIGGER_RUMBLE;
if (SDL_GameControllerGetNumTouchpads(state->controller) > 0)
capabilities |= LI_CCAP_TOUCHPAD;
if (SDL_GameControllerHasSensor(state->controller, SDL_SENSOR_ACCEL))
capabilities |= LI_CCAP_ACCEL;
if (SDL_GameControllerHasSensor(state->controller, SDL_SENSOR_GYRO))
capabilities |= LI_CCAP_GYRO;
if (SDL_GameControllerHasLED(state->controller))
capabilities |= LI_CCAP_RGB_LED;
switch (SDL_GameControllerGetType(state->controller)) {
case SDL_CONTROLLER_TYPE_XBOX360:
case SDL_CONTROLLER_TYPE_XBOXONE:
type = LI_CTYPE_XBOX;
break;
case SDL_CONTROLLER_TYPE_PS3:
case SDL_CONTROLLER_TYPE_PS4:
case SDL_CONTROLLER_TYPE_PS5:
type = LI_CTYPE_PS;
break;
case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO:
#if SDL_VERSION_ATLEAST(2, 24, 0)
case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT:
case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT:
case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR:
#endif
type = LI_CTYPE_NINTENDO;
break;
}
LiSendControllerArrivalEvent(state->id, activeGamepadMask, type, supportedButtonFlags, capabilities);
#endif
}
static PGAMEPAD_STATE get_gamepad(SDL_JoystickID sdl_id, bool add) {
// See if a gamepad already exists
for (int i = 0;i<MAX_GAMEPADS;i++) {
if (gamepads[i].initialized && gamepads[i].sdl_id == sdl_id)
return &gamepads[i];
}
if (!add)
return NULL;
for (int i = 0;i<MAX_GAMEPADS;i++) {
if (!gamepads[i].initialized) {
gamepads[i].sdl_id = sdl_id;
gamepads[i].id = i;
gamepads[i].initialized = true;
activeGamepadMask |= (1 << i);
return &gamepads[i];
} else if (gamepads[i].sdl_id == sdl_id)
return &gamepads[i];
}
}
return &gamepads[0];
}
static void init_gamepad(int joystick_index) {
if (SDL_IsGameController(joystick_index)) {
sdl_gamepads++;
SDL_GameController* controller = SDL_GameControllerOpen(joystick_index);
if (!controller) {
fprintf(stderr, "Could not open gamecontroller %i: %s\n", joystick_index, SDL_GetError());
return;
}
static void add_gamepad(int joystick_index) {
SDL_GameController* controller = SDL_GameControllerOpen(joystick_index);
if (!controller) {
fprintf(stderr, "Could not open gamecontroller %i: %s\n", joystick_index, SDL_GetError());
return;
}
SDL_Joystick* joystick = SDL_GameControllerGetJoystick(controller);
SDL_Haptic* haptic = SDL_HapticOpenFromJoystick(joystick);
if (haptic && (SDL_HapticQuery(haptic) & SDL_HAPTIC_LEFTRIGHT) == 0) {
SDL_HapticClose(haptic);
haptic = NULL;
}
SDL_Joystick* joystick = SDL_GameControllerGetJoystick(controller);
SDL_JoystickID joystick_id = SDL_JoystickInstanceID(joystick);
SDL_JoystickID sdl_id = SDL_JoystickInstanceID(joystick);
PGAMEPAD_STATE state = get_gamepad(joystick_index);
state->haptic = haptic;
state->haptic_effect_id = -1;
// Check if we have already set up a state for this gamepad
PGAMEPAD_STATE state = get_gamepad(joystick_id, false);
if (state) {
// This was probably a gamepad added during initialization, so we've already
// got state set up. However, we still need to inform the host about it, since
// we couldn't do that during initialization (since we weren't connected yet).
send_controller_arrival(state);
SDL_GameControllerClose(controller);
return;
}
// Create a new gamepad state
state = get_gamepad(joystick_id, true);
state->controller = controller;
#if !SDL_VERSION_ATLEAST(2, 0, 9)
state->haptic = SDL_HapticOpenFromJoystick(joystick);
if (haptic && (SDL_HapticQuery(state->haptic) & SDL_HAPTIC_LEFTRIGHT) == 0) {
SDL_HapticClose(state->haptic);
state->haptic = NULL;
}
state->haptic_effect_id = -1;
#endif
// Send the controller arrival event to the host
send_controller_arrival(state);
sdl_gamepads++;
}
static void remove_gamepad(SDL_JoystickID sdl_id) {
for (int i = 0;i<MAX_GAMEPADS;i++) {
if (gamepads[i].initialized && gamepads[i].sdl_id == sdl_id) {
#if !SDL_VERSION_ATLEAST(2, 0, 9)
if (gamepads[i].haptic_effect_id >= 0) {
SDL_HapticDestroyEffect(gamepads[i].haptic, gamepads[i].haptic_effect_id);
}
if (gamepads[i].haptic) {
SDL_HapticClose(gamepads[i].haptic);
}
#endif
SDL_GameControllerClose(gamepads[i].controller);
// This will cause disconnection of the virtual controller on the host PC
activeGamepadMask &= ~(1 << i);
LiSendMultiControllerEvent(i, activeGamepadMask, 0, 0, 0, 0, 0, 0, 0);
memset(&gamepads[i], 0, sizeof(*gamepads));
sdl_gamepads--;
break;
}
}
}
@ -87,22 +433,41 @@ void sdlinput_init(char* mappings) {
memset(gamepads, 0, sizeof(gamepads));
SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
#if !SDL_VERSION_ATLEAST(2, 0, 9)
SDL_InitSubSystem(SDL_INIT_HAPTIC);
#endif
SDL_GameControllerAddMappingsFromFile(mappings);
for (int i = 0; i < SDL_NumJoysticks(); ++i)
init_gamepad(i);
// Add game controllers here to ensure an accurate count
// goes to the host when starting a new session.
for (int i = 0; i < SDL_NumJoysticks(); ++i) {
if (SDL_IsGameController(i))
add_gamepad(i);
}
}
int sdlinput_handle_event(SDL_Event* event) {
int sdlinput_handle_event(SDL_Window* window, SDL_Event* event) {
int button = 0;
unsigned char touchEventType;
PGAMEPAD_STATE gamepad;
switch (event->type) {
case SDL_MOUSEMOTION:
LiSendMouseMoveEvent(event->motion.xrel, event->motion.yrel);
if (SDL_GetRelativeMouseMode())
LiSendMouseMoveEvent(event->motion.xrel, event->motion.yrel);
else {
int w, h;
SDL_GetWindowSize(window, &w, &h);
LiSendMousePositionEvent(event->motion.x, event->motion.y, w, h);
}
break;
case SDL_MOUSEWHEEL:
#if SDL_VERSION_ATLEAST(2, 0, 18)
LiSendHighResHScrollEvent((short)(event->wheel.preciseX * 120)); // WHEEL_DELTA
LiSendHighResScrollEvent((short)(event->wheel.preciseY * 120)); // WHEEL_DELTA
#else
LiSendHScrollEvent(event->wheel.x);
LiSendScrollEvent(event->wheel.y);
#endif
break;
case SDL_MOUSEBUTTONUP:
case SDL_MOUSEBUTTONDOWN:
@ -127,78 +492,81 @@ 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;
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;
break;
case SDL_CONTROLLER_AXIS_LEFTY:
gamepad->leftStickY = -event->caxis.value - 1;
gamepad->leftStickY = -SDL_max(event->caxis.value, (short)-32767);
break;
case SDL_CONTROLLER_AXIS_RIGHTX:
gamepad->rightStickX = event->caxis.value;
break;
case SDL_CONTROLLER_AXIS_RIGHTY:
gamepad->rightStickY = -event->caxis.value - 1;
gamepad->rightStickY = -SDL_max(event->caxis.value, (short)-32767);
break;
case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
gamepad->leftTrigger = (event->caxis.value >> 8) + 127;
gamepad->leftTrigger = (unsigned char)(event->caxis.value * 255UL / 32767);
break;
case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
gamepad->rightTrigger = (event->caxis.value >> 8) + 127;
gamepad->rightTrigger = (unsigned char)(event->caxis.value * 255UL / 32767);
break;
default:
return SDL_NOTHING;
@ -207,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;
@ -299,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
0x9B, //SDLK_INSERT
0x24, //SDLK_HOME
0x21, //SDLK_PAGEUP
0, //Not used
0x23, //SDLK_END
0x22, //SDLK_PAGEDOWN
0x27, //SDLK_RIGHT
0x25, //SDLK_LEFT
0x28, //SDLK_DOWN
0x26, //SDLK_UP
};
static const short keyCodes5[] = {
0x11, //SDLK_LCTRL
0x10, //SDLK_LSHIFT
0x12, //SDLK_LALT
0x5B, //SDLK_LGUI
0x11, //SDLK_LRCTRL
0x10, //SDLK_RSHIFT
0x12, //SDLK_RALT
0x5C, //SDLK_RGUI
};
void sdlinput_init(char* mappings);
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,7 @@ 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) {
struct udev_device *dev = udev_monitor_receive_device(udev_mon);
@ -46,7 +46,7 @@ static int udev_handle(int fd) {
const char *devnode = udev_device_get_devnode(dev);
int id;
if (devnode != NULL && sscanf(devnode, "/dev/input/event%d", &id) == 1) {
evdev_create(devnode, defaultMappings, debug);
evdev_create(devnode, defaultMappings, debug, inputRotate);
}
}
udev_device_unref(dev);
@ -54,7 +54,7 @@ static int udev_handle(int fd) {
return LOOP_OK;
}
void udev_init(bool autoload, struct mapping* mappings, bool verbose) {
void udev_init(bool autoload, struct mapping* mappings, bool verbose, int rotate) {
udev = udev_new();
debug = verbose;
if (!udev) {
@ -76,7 +76,7 @@ void udev_init(bool autoload, struct mapping* mappings, bool verbose) {
const char *devnode = udev_device_get_devnode(dev);
int id;
if (devnode != NULL && sscanf(devnode, "/dev/input/event%d", &id) == 1) {
evdev_create(devnode, mappings, verbose);
evdev_create(devnode, mappings, verbose, rotate);
}
udev_device_unref(dev);
}
@ -89,11 +89,13 @@ void udev_init(bool autoload, struct mapping* mappings, bool verbose) {
udev_monitor_enable_receiving(udev_mon);
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

@ -19,5 +19,5 @@
#include "mapping.h"
void udev_init(bool autoload, struct mapping* mappings, bool verbose);
void evdev_destroy();
void udev_init(bool autoload, struct mapping* mappings, bool verbose, int rotate);
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,15 +74,15 @@ void loop_add_fd(int fd, FdHandler handler, int events) {
void loop_remove_fd(int fd) {
numFds--;
int fdindex;
for (int i=0;i<numFds;i++) {
int fdindex = numFds;
for (int i=0;i<=numFds;i++) {
if (fds[i].fd == fd) {
fdindex = i;
break;
}
}
if (fdindex != numFds && numFds > 0) {
memcpy(&fds[fdindex], &fds[numFds], sizeof(struct pollfd));
memcpy(&fdHandlers[fdindex], &fdHandlers[numFds], sizeof(FdHandler));

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);
@ -104,7 +104,9 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform sys
if (ret == GS_NOT_SUPPORTED_4K)
fprintf(stderr, "Server doesn't support 4K\n");
else if (ret == GS_NOT_SUPPORTED_MODE)
fprintf(stderr, "Server doesn't support %dx%d (%d fps) or try --unsupported option\n", config->stream.width, config->stream.height, config->stream.fps);
fprintf(stderr, "Server doesn't support %dx%d (%d fps) or remove --nounsupported option\n", config->stream.width, config->stream.height, config->stream.fps);
else if (ret == GS_NOT_SUPPORTED_SOPS_RESOLUTION)
fprintf(stderr, "Optimal Playable Settings isn't supported for the resolution %dx%d, use supported resolution or add --nosops option\n", config->stream.width, config->stream.height);
else if (ret == GS_ERROR)
fprintf(stderr, "Gamestream error: %s\n", gs_error);
else
@ -116,6 +118,22 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform sys
if (config->fullscreen)
drFlags |= DISPLAY_FULLSCREEN;
switch (config->rotate) {
case 0:
break;
case 90:
drFlags |= DISPLAY_ROTATE_90;
break;
case 180:
drFlags |= DISPLAY_ROTATE_180;
break;
case 270:
drFlags |= DISPLAY_ROTATE_270;
break;
default:
printf("Ignoring invalid rotation value: %d\n", config->rotate);
}
if (config->debug_level > 0) {
printf("Stream %d x %d, %d fps, %d kbps\n", config->stream.width, config->stream.height, config->stream.fps, config->stream.bitrate);
connection_debug = true;
@ -128,9 +146,11 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform sys
LiStartConnection(&server->serverInfo, &config->stream, &connection_callbacks, platform_get_video(system), platform_get_audio(system, config->audio_device), NULL, drFlags, config->audio_device, 0);
if (IS_EMBEDDED(system)) {
evdev_start();
if (!config->viewonly)
evdev_start();
loop_main();
evdev_stop();
if (!config->viewonly)
evdev_stop();
}
#ifdef HAVE_SDL
else if (system == SDL)
@ -149,7 +169,11 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform sys
}
static void help() {
#ifdef GIT_BRANCH
printf("Moonlight Embedded %d.%d.%d-%s-%s\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, GIT_BRANCH, GIT_COMMIT_HASH);
#else
printf("Moonlight Embedded %d.%d.%d\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH);
#endif
printf("Usage: moonlight [action] (options) [host]\n");
printf(" moonlight [configfile]\n");
printf("\n Actions\n\n");
@ -171,20 +195,26 @@ 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");
printf("\t-fps <fps>\t\tSpecify the fps to use (default -1)\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-remote\t\t\tEnable remote optimizations\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");
printf("\t-localaudio\t\tPlay audio locally\n");
printf("\t-surround\t\tStream 5.1 surround sound (requires GFE 2.7)\n");
printf("\t-localaudio\t\tPlay audio locally on the host computer\n");
printf("\t-surround <5.1/7.1>\t\tStream 5.1 or 7.1 surround sound\n");
printf("\t-keydir <directory>\tLoad encryption keys from directory\n");
printf("\t-mapping <file>\t\tUse <file> as gamepad mappings configuration file\n");
printf("\t-platform <system>\tSpecify system used for audio, video and input: pi/imx/aml/rk/x11/x11_vdpau/sdl/fake (default auto)\n");
printf("\t-unsupported\t\tTry streaming if GFE version or options are unsupported\n");
printf("\t-nounsupported\t\tDon't stream if resolution is not officially supported by the server\n");
printf("\t-quitappafter\t\tSend quit app request to remote after quitting session\n");
printf("\t-viewonly\t\tDisable all input processing (view-only mode)\n");
printf("\t-nomouseemulation\t\tDisable gamepad mouse emulation support (long pressing Start button)\n");
#if defined(HAVE_SDL) || defined(HAVE_X11)
printf("\n WM options (SDL and X11 only)\n\n");
printf("\t-windowed\t\tDisplay screen in a window\n");
@ -211,19 +241,19 @@ int main(int argc, char* argv[]) {
if (config.action == NULL || strcmp("help", config.action) == 0)
help();
if (config.debug_level > 0)
printf("Moonlight Embedded %d.%d.%d (%s)\n", VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH, COMPILE_OPTIONS);
if (strcmp("map", config.action) == 0) {
if (strcmp("map", config.action) == 0) {
if (config.inputsCount != 1) {
printf("You need to specify one input device using -input.\n");
exit(-1);
}
evdev_create(config.inputs[0], NULL, config.debug_level > 0);
evdev_map(config.inputs[0]);
exit(0);
evdev_create(config.inputs[0], NULL, config.debug_level > 0, config.rotate);
evdev_map(config.inputs[0]);
exit(0);
}
if (config.address == NULL) {
@ -234,23 +264,23 @@ 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);
}
}
char host_config_file[128];
sprintf(host_config_file, "hosts/%s.conf", config.address);
if (access(host_config_file, R_OK) != -1)
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) {
@ -267,8 +297,10 @@ int main(int argc, char* argv[]) {
exit(-1);
}
if (config.debug_level > 0)
printf("NVIDIA %s, GFE %s (%s, %s)\n", server.gpuType, server.serverInfo.serverInfoGfeVersion, server.gsVersion, server.serverInfo.serverInfoAppVersion);
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);
@ -286,57 +318,90 @@ 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));
if (IS_EMBEDDED(system)) {
char* mapping_env = getenv("SDL_GAMECONTROLLERCONFIG");
if (config.mapping == NULL && mapping_env == NULL) {
fprintf(stderr, "Please specify mapping file as default mapping could not be found.\n");
exit(-1);
}
struct mapping* mappings = NULL;
if (config.mapping != NULL)
mappings = mapping_load(config.mapping, config.debug_level > 0);
if (mapping_env != NULL) {
struct mapping* map = mapping_parse(mapping_env);
map->next = mappings;
mappings = map;
}
for (int i=0;i<config.inputsCount;i++) {
if (config.debug_level > 0)
printf("Add input %s...\n", config.inputs[i]);
evdev_create(config.inputs[i], mappings, config.debug_level > 0);
}
udev_init(!inputAdded, mappings, config.debug_level > 0);
evdev_init();
rumble_handler = evdev_rumble;
#ifdef HAVE_LIBCEC
cec_init();
#endif /* HAVE_LIBCEC */
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
else if (system == SDL) {
if (config.inputsCount > 0) {
fprintf(stderr, "You can't select input devices as SDL will automatically use all available controllers\n");
exit(-1);
}
if (system == SDL)
sdl_init(config.stream.width, config.stream.height, config.fullscreen);
sdlinput_init(config.mapping);
rumble_handler = sdlinput_rumble;
}
#endif
if (config.viewonly) {
if (config.debug_level > 0)
printf("View-only mode enabled, no input will be sent to the host computer\n");
} else {
if (IS_EMBEDDED(system)) {
char* mapping_env = getenv("SDL_GAMECONTROLLERCONFIG");
if (config.mapping == NULL && mapping_env == NULL) {
fprintf(stderr, "Please specify mapping file as default mapping could not be found.\n");
exit(-1);
}
struct mapping* mappings = NULL;
if (config.mapping != NULL)
mappings = mapping_load(config.mapping, config.debug_level > 0);
if (mapping_env != NULL) {
struct mapping* map = mapping_parse(mapping_env);
map->next = mappings;
mappings = map;
}
for (int i=0;i<config.inputsCount;i++) {
if (config.debug_level > 0)
printf("Adding input device %s...\n", config.inputs[i]);
evdev_create(config.inputs[i], mappings, config.debug_level > 0, config.rotate);
}
udev_init(!inputAdded, mappings, config.debug_level > 0, config.rotate);
evdev_init(config.mouse_emulation);
rumble_handler = evdev_rumble;
#ifdef HAVE_LIBCEC
cec_init();
#endif /* HAVE_LIBCEC */
}
#ifdef HAVE_SDL
else if (system == SDL) {
if (config.inputsCount > 0) {
fprintf(stderr, "You can't select input devices as SDL will automatically use all available controllers\n");
exit(-1);
}
sdlinput_init(config.mapping);
rumble_handler = sdlinput_rumble;
rumble_triggers_handler = sdlinput_rumble_triggers;
set_motion_event_state_handler = sdlinput_set_motion_event_state;
set_controller_led_handler = sdlinput_set_controller_led;
}
#endif
}
stream(&server, &config, system);
} else if (strcmp("pair", config.action) == 0) {
char pin[5];
sprintf(pin, "%d%d%d%d", (int)random() % 10, (int)random() % 10, (int)random() % 10, (int)random() % 10);
if (config.pin > 0 && config.pin <= 9999) {
sprintf(pin, "%04d", config.pin);
} else {
sprintf(pin, "%d%d%d%d", (unsigned)random() % 10, (unsigned)random() % 10, (unsigned)random() % 10, (unsigned)random() % 10);
}
printf("Please enter the following PIN on the target PC: %s\n", pin);
fflush(stdout);
if (gs_pair(&server, &pin[0]) != GS_OK) {
fprintf(stderr, "Failed to pair to server: %s\n", gs_error);
} else {

View File

@ -52,9 +52,16 @@ enum platform platform_check(char* name) {
return PI;
}
#endif
#ifdef HAVE_MMAL
if (std || strcmp(name, "mmal") == 0) {
void *handle = dlopen("libmoonlight-mmal.so", RTLD_NOW | RTLD_GLOBAL);
if (handle != NULL && dlsym(RTLD_DEFAULT, "bcm_host_init") != NULL)
return MMAL;
}
#endif
#ifdef HAVE_AML
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;
}
@ -80,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;
@ -97,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
#ifdef HAVE_PI
#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
}
@ -113,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
#ifdef HAVE_PI
#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
}
@ -151,6 +164,10 @@ DECODER_RENDERER_CALLBACKS* platform_get_video(enum platform system) {
case PI:
return (PDECODER_RENDERER_CALLBACKS) dlsym(RTLD_DEFAULT, "decoder_callbacks_pi");
#endif
#ifdef HAVE_MMAL
case MMAL:
return (PDECODER_RENDERER_CALLBACKS) dlsym(RTLD_DEFAULT, "decoder_callbacks_mmal");
#endif
#ifdef HAVE_AML
case AML:
return (PDECODER_RENDERER_CALLBACKS) dlsym(RTLD_DEFAULT, "decoder_callbacks_aml");
@ -165,6 +182,8 @@ DECODER_RENDERER_CALLBACKS* platform_get_video(enum platform system) {
AUDIO_RENDERER_CALLBACKS* platform_get_audio(enum platform system, char* audio_device) {
switch (system) {
case FAKE:
return NULL;
#ifdef HAVE_SDL
case SDL:
return &audio_callbacks_sdl;
@ -173,6 +192,7 @@ AUDIO_RENDERER_CALLBACKS* platform_get_audio(enum platform system, char* audio_d
case PI:
if (audio_device == NULL || strcmp(audio_device, "local") == 0 || strcmp(audio_device, "hdmi") == 0)
return (PAUDIO_RENDERER_CALLBACKS) dlsym(RTLD_DEFAULT, "audio_callbacks_omx");
// fall-through
#endif
default:
#ifdef HAVE_PULSE
@ -182,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;
}
@ -199,6 +233,8 @@ char* platform_name(enum platform system) {
switch(system) {
case PI:
return "Raspberry Pi (Broadcom)";
case MMAL:
return "Raspberry Pi (Broadcom) MMAL";
case IMX:
return "i.MX6 (MXC Vivante)";
case AML:

View File

@ -26,12 +26,13 @@
#define IS_EMBEDDED(SYSTEM) SYSTEM != SDL
enum platform { NONE, SDL, X11, X11_VDPAU, X11_VAAPI, PI, IMX, AML, RK, FAKE };
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

@ -36,7 +36,7 @@
void sdl_init(int width, int height, bool fullscreen);
void sdl_loop();
SDL_mutex *mutex;
int sdlCurrentFrame, sdlNextFrame;
extern SDL_mutex *mutex;
extern int sdlCurrentFrame, sdlNextFrame;
#endif /* HAVE_SDL */

View File

@ -24,17 +24,43 @@
#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;
} else
return -1;
}
int read_file(char *path, char* output, int output_len) {
int fd = open(path, O_RDONLY);
if(fd >= 0) {
output_len = read(fd, output, output_len);
close(fd);
return output_len;
} else
return -1;
}
bool ensure_buf_size(void **buf, size_t *buf_size, size_t required_size) {
if (*buf_size >= required_size)
return false;
*buf_size = required_size;
*buf = realloc(*buf, *buf_size);
if (!*buf) {
fprintf(stderr, "Failed to allocate %zu bytes\n", *buf_size);
abort();
}
return true;
}

View File

@ -18,5 +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

@ -139,11 +139,10 @@ void egl_init(EGLNativeDisplayType native_display, NativeWindowType native_windo
glGenBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);
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

@ -32,8 +32,8 @@
#include <stdbool.h>
// General decoder and renderer state
static AVPacket pkt;
static AVCodec* decoder;
static AVPacket* pkt;
static const AVCodec* decoder;
static AVCodecContext* decoder_ctx;
static AVFrame** dec_frames;
@ -53,16 +53,79 @@ int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer
avcodec_register_all();
#endif
av_init_packet(&pkt);
pkt = av_packet_alloc();
if (pkt == NULL) {
printf("Couldn't allocate packet\n");
return -1;
}
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) {
@ -70,36 +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;
}
if (perf_lvl & DISABLE_LOOP_FILTER)
// Skip the loop filter for performance reasons
decoder_ctx->skip_loop_filter = AVDISCARD_ALL;
if (perf_lvl & LOW_LATENCY_DECODE)
// Use low delay single threaded encoding
decoder_ctx->flags |= AV_CODEC_FLAG_LOW_DELAY;
if (perf_lvl & SLICE_THREADING)
decoder_ctx->thread_type = FF_THREAD_SLICE;
else
decoder_ctx->thread_type = FF_THREAD_FRAME;
decoder_ctx->thread_count = thread_count;
decoder_ctx->width = width;
decoder_ctx->height = height;
decoder_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
int err = avcodec_open2(decoder_ctx, decoder, NULL);
if (err < 0) {
printf("Couldn't open codec");
return err;
}
printf("Using FFmpeg decoder: %s\n", decoder->name);
dec_frames_cnt = buffer_count;
dec_frames = malloc(buffer_count * sizeof(AVFrame*));
@ -127,10 +161,9 @@ int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer
// This function must be called after
// decoding is finished
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++) {
@ -161,15 +194,15 @@ AVFrame* ffmpeg_get_frame(bool native_frame) {
int ffmpeg_decode(unsigned char* indata, int inlen) {
int err;
pkt.data = indata;
pkt.size = inlen;
pkt->data = indata;
pkt->size = inlen;
err = avcodec_send_packet(decoder_ctx, &pkt);
err = avcodec_send_packet(decoder_ctx, pkt);
if (err < 0) {
char errorstring[512];
av_strerror(err, errorstring, sizeof(errorstring));
fprintf(stderr, "Decode failed - %s\n", errorstring);
}
return err < 0 ? err : 0;
}

View File

@ -1,6 +1,6 @@
/*
* This file is part of Moonlight Embedded.
*
*
* Based on Moonlight Pc implementation
*
* Moonlight is free software; you can redistribute it and/or modify
@ -21,18 +21,8 @@
#include <libavcodec/avcodec.h>
// Disables the deblocking filter at the cost of image quality
#define DISABLE_LOOP_FILTER 0x1
// Uses the low latency decode flag (disables multithreading)
#define LOW_LATENCY_DECODE 0x2
// Threads process each slice, rather than each frame
// Enable multi-threaded decoding
#define SLICE_THREADING 0x4
// Uses nonstandard speedup tricks
#define FAST_DECODE 0x8
// Uses bilinear filtering instead of bicubic
#define BILINEAR_FILTERING 0x10
// Uses a faster bilinear filtering with lower image quality
#define FAST_BILINEAR_FILTERING 0x20
// Uses hardware acceleration
#define VDPAU_ACCELERATION 0x40
#define VAAPI_ACCELERATION 0x80

View File

@ -46,7 +46,7 @@ static enum AVPixelFormat va_get_format(AVCodecContext* context, const enum AVPi
fprintf(stderr, "Failed to initialize VAAPI frame context");
return AV_PIX_FMT_NONE;
}
context->pix_fmt = AV_PIX_FMT_VAAPI;
context->hw_device_ctx = device_ref;
context->hw_frames_ctx = hw_ctx;
@ -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
@ -139,11 +139,11 @@ static int decoder_renderer_setup(int videoFormat, int width, int height, int re
}
close(fd_fb);
int regfbcount = MIN_FRAME_BUFFER_COUNT + 2;
int picWidth = ((width + 15) & ~15);
int picHeight = ((height + 15) & ~15);
char v4l_device[16];
sprintf(v4l_device, "/dev/video%d", 17);
fd = open(v4l_device, O_RDWR, 0);
@ -192,7 +192,7 @@ static int decoder_renderer_setup(int videoFormat, int width, int height, int re
fprintf(stderr, "Not enough video buffers\n");
return -2;
}
for (int i = 0; i < regfbcount; i++) {
struct v4l2_buffer buffer = {0};
struct vpu_buf *buf;
@ -233,7 +233,7 @@ static int decoder_renderer_setup(int videoFormat, int width, int height, int re
return -2;
}
}
vpu_setup(buffers, regfbcount, width, height);
if (pipe(pipefd) == -1 || pipe(clearpipefd) == -1) {

View File

@ -143,7 +143,7 @@ void vpu_setup(struct vpu_buf* buffers[], int bufferCount, int width, int height
}
fb[i].bufMvCol = mvcol_md[i].phy_addr;
}
bufinfo.avcSliceBufInfo.bufferBase = slice_mem_desc.phy_addr;
bufinfo.avcSliceBufInfo.bufferSize = phy_slicebuf_size;
@ -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);
@ -237,7 +237,7 @@ bool vpu_decode(PDECODE_UNIT decodeUnit) {
fprintf(stderr, "Failed to decode frame\n");
exit(EXIT_FAILURE);
}
currentFrame = outinfo.indexFrameDisplay;
return true;
}
@ -249,7 +249,7 @@ void vpu_clear(int disp_clr_index) {
void vpu_cleanup() {
IOFreePhyMem(&ps_mem_desc);
IOFreePhyMem(&slice_mem_desc);
IOFreeVirtMem(&mem_desc);
IOFreePhyMem(&mem_desc);
vpu_UnInit();

293
src/video/mmal.c Normal file
View File

@ -0,0 +1,293 @@
/*
Copyright (c) 2012, Broadcom Europe Ltd
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// Video decode on Raspberry Pi using MMAL
// Based upon example code from the Raspberry Pi
#include "video.h"
#include <Limelight.h>
#include <sps.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <bcm_host.h>
#include <interface/mmal/mmal.h>
#include <interface/mmal/util/mmal_default_components.h>
#include <interface/mmal/util/mmal_util_params.h>
#include <interface/mmal/util/mmal_util.h>
#include <interface/mmal/vc/mmal_vc_api.h>
#include <interface/vcos/vcos.h>
#define ALIGN(x, a) (((x)+(a)-1)&~((a)-1))
static VCOS_SEMAPHORE_T semaphore;
static MMAL_COMPONENT_T *decoder = NULL, *renderer = NULL;
static MMAL_POOL_T *pool_in = NULL, *pool_out = NULL;
static void input_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) {
mmal_buffer_header_release(buf);
if (port == decoder->input[0])
vcos_semaphore_post(&semaphore);
}
static void control_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) {
if (buf->cmd == MMAL_EVENT_ERROR) {
MMAL_STATUS_T status = *(uint32_t *) buf->data;
fprintf(stderr, "Video decode error MMAL_EVENT_ERROR:%d\n", status);
}
mmal_buffer_header_release(buf);
}
static void output_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) {
if (mmal_port_send_buffer(renderer->input[0], buf) != MMAL_SUCCESS) {
fprintf(stderr, "Can't display decoded frame\n");
mmal_buffer_header_release(buf);
}
}
static int decoder_renderer_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
if (videoFormat != VIDEO_FORMAT_H264) {
fprintf(stderr, "Video format not supported\n");
return -1;
}
bcm_host_init();
mmal_vc_init();
gs_sps_init(width, height);
vcos_semaphore_create(&semaphore, "video_decoder", 1);
if (mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_DECODER, &decoder) != MMAL_SUCCESS) {
fprintf(stderr, "Can't create decoder\n");
return -2;
}
MMAL_ES_FORMAT_T *format_in = decoder->input[0]->format;
format_in->type = MMAL_ES_TYPE_VIDEO;
format_in->encoding = MMAL_ENCODING_H264;
format_in->es->video.width = ALIGN(width, 32);
format_in->es->video.height = ALIGN(height, 16);
format_in->es->video.crop.width = width;
format_in->es->video.crop.height = height;
format_in->es->video.frame_rate.num = redrawRate;
format_in->es->video.frame_rate.den = 1;
format_in->es->video.par.num = 1;
format_in->es->video.par.den = 1;
format_in->flags = MMAL_ES_FORMAT_FLAG_FRAMED;
if (mmal_port_format_commit(decoder->input[0]) != MMAL_SUCCESS) {
fprintf(stderr, "Can't commit input format to decoder\n");
return -3;
}
decoder->input[0]->buffer_num = 5;
decoder->input[0]->buffer_size = INITIAL_DECODER_BUFFER_SIZE;
pool_in = mmal_port_pool_create(decoder->input[0], decoder->input[0]->buffer_num, decoder->output[0]->buffer_size);
MMAL_ES_FORMAT_T *format_out = decoder->output[0]->format;
format_out->encoding = MMAL_ENCODING_OPAQUE;
if (mmal_port_format_commit(decoder->output[0]) != MMAL_SUCCESS) {
fprintf(stderr, "Can't commit output format to decoder\n");
return -3;
}
decoder->output[0]->buffer_num = 3;
decoder->output[0]->buffer_size = decoder->output[0]->buffer_size_recommended;
pool_out = mmal_port_pool_create(decoder->output[0], decoder->output[0]->buffer_num, decoder->output[0]->buffer_size);
if (mmal_port_enable(decoder->control, control_callback) != MMAL_SUCCESS) {
fprintf(stderr, "Can't enable control port\n");
return -4;
}
if (mmal_component_create(MMAL_COMPONENT_DEFAULT_VIDEO_RENDERER, &renderer) != MMAL_SUCCESS) {
fprintf(stderr, "Can't create renderer\n");
return -5;
}
format_in = renderer->input[0]->format;
format_in->encoding = MMAL_ENCODING_OPAQUE;
format_in->es->video.width = width;
format_in->es->video.height = height;
format_in->es->video.crop.x = 0;
format_in->es->video.crop.y = 0;
format_in->es->video.crop.width = width;
format_in->es->video.crop.height = height;
if (mmal_port_format_commit(renderer->input[0]) != MMAL_SUCCESS) {
fprintf(stderr, "Can't set output format\n");
return -6;
}
MMAL_DISPLAYREGION_T param;
param.hdr.id = MMAL_PARAMETER_DISPLAYREGION;
param.hdr.size = sizeof(MMAL_DISPLAYREGION_T);
param.set = MMAL_DISPLAY_SET_LAYER | MMAL_DISPLAY_SET_NUM | MMAL_DISPLAY_SET_FULLSCREEN | MMAL_DISPLAY_SET_TRANSFORM;
param.layer = 128;
param.display_num = 0;
param.fullscreen = true;
int displayRotation = drFlags & DISPLAY_ROTATE_MASK;
switch (displayRotation) {
case DISPLAY_ROTATE_90:
param.transform = MMAL_DISPLAY_ROT90;
break;
case DISPLAY_ROTATE_180:
param.transform = MMAL_DISPLAY_ROT180;
break;
case DISPLAY_ROTATE_270:
param.transform = MMAL_DISPLAY_ROT270;
break;
default:
param.transform = MMAL_DISPLAY_ROT0;
break;
}
if (mmal_port_parameter_set(renderer->input[0], &param.hdr) != MMAL_SUCCESS) {
fprintf(stderr, "Can't set parameters\n");
return -7;
}
if (mmal_port_enable(renderer->control, control_callback) != MMAL_SUCCESS) {
fprintf(stderr, "Can't enable control port\n");
return -8;
}
if (mmal_component_enable(renderer) != MMAL_SUCCESS) {
fprintf(stderr, "Can't enable renderer\n");
return -9;
}
if (mmal_port_enable(renderer->input[0], input_callback) != MMAL_SUCCESS) {
fprintf(stderr, "Can't enable renderer input port\n");
return -10;
}
if (mmal_port_enable(decoder->input[0], input_callback) != MMAL_SUCCESS) {
fprintf(stderr, "Can't enable decoder input port\n");
return -11;
}
if (mmal_port_enable(decoder->output[0], output_callback) != MMAL_SUCCESS) {
fprintf(stderr, "Can't enable decoder output port\n");
return -12;
}
if (mmal_component_enable(decoder) != MMAL_SUCCESS) {
fprintf(stderr, "Can't enable decoder\n");
return -13;
}
return 0;
}
static void decoder_renderer_cleanup() {
if (decoder)
mmal_component_destroy(decoder);
if (renderer)
mmal_component_destroy(renderer);
if (pool_in)
mmal_pool_destroy(pool_in);
if (pool_out)
mmal_pool_destroy(pool_out);
vcos_semaphore_delete(&semaphore);
}
static int decoder_renderer_submit_decode_unit(PDECODE_UNIT decodeUnit) {
MMAL_STATUS_T status;
MMAL_BUFFER_HEADER_T *buf = NULL;
PLENTRY entry = decodeUnit->bufferList;
bool first_entry = false;
while (entry != NULL) {
if (buf == NULL) {
vcos_semaphore_wait(&semaphore);
if ((buf = mmal_queue_get(pool_in->queue)) != NULL) {
buf->flags = 0;
buf->offset = 0;
buf->pts = buf->dts = MMAL_TIME_UNKNOWN;
} else {
fprintf(stderr, "Video buffer full\n");
return DR_NEED_IDR;
}
}
if (entry->bufferType != BUFFER_TYPE_PICDATA)
buf->flags |= MMAL_BUFFER_HEADER_FLAG_CONFIG;
else if (!first_entry) {
buf->flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_START;
first_entry = true;
}
if (entry->bufferType == BUFFER_TYPE_SPS)
gs_sps_fix(entry, GS_SPS_BITSTREAM_FIXUP, buf->data, &buf->length);
else {
if (entry->length + buf->length > buf->alloc_size) {
fprintf(stderr, "Video decoder buffer too small\n");
mmal_buffer_header_release(buf);
return DR_NEED_IDR;
}
memcpy(buf->data + buf->length, entry->data, entry->length);
buf->length += entry->length;
}
if (entry->bufferType != BUFFER_TYPE_PICDATA || entry->next == NULL || entry->next->bufferType != BUFFER_TYPE_PICDATA) {
buf->flags |= MMAL_BUFFER_HEADER_FLAG_FRAME_END;
if ((status = mmal_port_send_buffer(decoder->input[0], buf)) != MMAL_SUCCESS) {
mmal_buffer_header_release(buf);
return DR_NEED_IDR;
}
buf = NULL;
}
entry = entry->next;
}
//Send available output buffers to decoder
while ((buf = mmal_queue_get(pool_out->queue))) {
if ((status = mmal_port_send_buffer(decoder->output[0], buf)) != MMAL_SUCCESS)
mmal_buffer_header_release(buf);
}
return DR_OK;
}
DECODER_RENDERER_CALLBACKS decoder_callbacks_mmal = {
.setup = decoder_renderer_setup,
.cleanup = decoder_renderer_cleanup,
.submitDecodeUnit = decoder_renderer_submit_decode_unit,
.capabilities = CAPABILITY_DIRECT_SUBMIT,
};

View File

@ -28,6 +28,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Video decode on Raspberry Pi using OpenMAX IL though the ilcient helper library
// Based upon video decode example from the Raspberry Pi firmware
#include "video.h"
#include <Limelight.h>
#include <sps.h>
@ -39,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;
@ -60,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));
@ -130,18 +128,34 @@ 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);
}
OMX_CONFIG_ROTATIONTYPE rotationType;
memset(&rotationType, 0, sizeof(OMX_CONFIG_ROTATIONTYPE));
rotationType.nSize = sizeof(OMX_CONFIG_ROTATIONTYPE);
rotationType.nVersion.nVersion = OMX_VERSION;
rotationType.nPortIndex = 90;
int displayRotation = drFlags & DISPLAY_ROTATE_MASK;
switch (displayRotation) {
case DISPLAY_ROTATE_90:
rotationType.nRotation = 90;
break;
case DISPLAY_ROTATE_180:
rotationType.nRotation = 180;
break;
case DISPLAY_ROTATE_270:
rotationType.nRotation = 270;
break;
}
if(OMX_SetParameter(ILC_GET_HANDLE(video_render), OMX_IndexConfigCommonRotate, &rotationType) != OMX_ErrorNone) {
fprintf(stderr, "Failed to set video rotation\n");
exit(EXIT_FAILURE);
}
OMX_PARAM_PORTDEFINITIONTYPE port;
memset(&port, 0, sizeof(OMX_PARAM_PORTDEFINITIONTYPE));
@ -154,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) {
@ -172,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,24 +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 (SZ_1M)
#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
// 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;
@ -65,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;
@ -76,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) {
@ -162,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;
@ -193,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
@ -218,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");
}
@ -250,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) {
@ -287,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) {
@ -302,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;
}
}
@ -313,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++) {
@ -324,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) {
@ -337,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;
@ -375,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;
}
@ -401,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);
@ -428,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)");
}
}
}
@ -442,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,24 +29,18 @@
#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) {
int avc_flags = SLICE_THREADING;
if (ffmpeg_init(videoFormat, width, height, avc_flags, SDL_BUFFER_FRAMES, sysconf(_SC_NPROCESSORS_ONLN)) < 0) {
if (ffmpeg_init(videoFormat, width, height, SLICE_THREADING, SDL_BUFFER_FRAMES, SLICES_PER_FRAME) < 0) {
fprintf(stderr, "Couldn't initialize video decoding\n");
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;
}
@ -55,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;
}
@ -93,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(4) | CAPABILITY_REFERENCE_FRAME_INVALIDATION_AVC | CAPABILITY_REFERENCE_FRAME_INVALIDATION_HEVC | CAPABILITY_DIRECT_SUBMIT,
.capabilities = CAPABILITY_SLICES_PER_FRAME(SLICES_PER_FRAME) | CAPABILITY_REFERENCE_FRAME_INVALIDATION_HEVC | CAPABILITY_DIRECT_SUBMIT,
};

View File

@ -24,11 +24,17 @@
#define DISPLAY_FULLSCREEN 1
#define ENABLE_HARDWARE_ACCELERATION_1 2
#define ENABLE_HARDWARE_ACCELERATION_2 4
#define DISPLAY_ROTATE_MASK 24
#define DISPLAY_ROTATE_90 8
#define DISPLAY_ROTATE_180 16
#define DISPLAY_ROTATE_270 24
#define INIT_EGL 1
#define INIT_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,11 +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;
@ -57,7 +59,7 @@ static int frame_handle(int pipefd) {
if (frame) {
if (ffmpeg_decoder == SOFTWARE)
egl_draw(frame->data);
#ifdef HAVE_VAAPI
#ifdef HAVE_VAAPI
else if (ffmpeg_decoder == VAAPI)
vaapi_queue(frame, window, display_width, display_height);
#endif
@ -81,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");
@ -124,13 +122,15 @@ int x11_setup(int videoFormat, int width, int height, int redrawRate, void* cont
}
XFlush(display);
int avc_flags = SLICE_THREADING;
int avc_flags;
if (drFlags & X11_VDPAU_ACCELERATION)
avc_flags |= VDPAU_ACCELERATION;
avc_flags = VDPAU_ACCELERATION;
else if (drFlags & X11_VAAPI_ACCELERATION)
avc_flags |= VAAPI_ACCELERATION;
avc_flags = VAAPI_ACCELERATION;
else
avc_flags = SLICE_THREADING;
if (ffmpeg_init(videoFormat, width, height, avc_flags, 2, 2) < 0) {
if (ffmpeg_init(videoFormat, width, height, avc_flags, 2, SLICES_PER_FRAME) < 0) {
fprintf(stderr, "Couldn't initialize video decoding\n");
return -1;
}
@ -164,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;
}
@ -185,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(4) | CAPABILITY_REFERENCE_FRAME_INVALIDATION_AVC | CAPABILITY_REFERENCE_FRAME_INVALIDATION_HEVC | 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 = {
@ -199,5 +202,5 @@ DECODER_RENDERER_CALLBACKS decoder_callbacks_x11_vaapi = {
.setup = x11_setup_vaapi,
.cleanup = x11_cleanup,
.submitDecodeUnit = x11_submit_decode_unit,
.capabilities = CAPABILITY_SLICES_PER_FRAME(4) | CAPABILITY_REFERENCE_FRAME_INVALIDATION_AVC | CAPABILITY_REFERENCE_FRAME_INVALIDATION_HEVC | CAPABILITY_DIRECT_SUBMIT,
.capabilities = CAPABILITY_DIRECT_SUBMIT,
};

1
third_party/SDL_GameControllerDB vendored Submodule

@ -0,0 +1 @@
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 b70f072090f734cc35fd163ce9d39e2d22ceba57
Subproject commit 8af4562af672dd6b9ed28553ead172984fd9a683