From 4898ea521d4204c0a0d0c789d4b17c13486671ce Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Wed, 2 Mar 2016 22:13:52 +0100 Subject: [PATCH 01/42] Provide contribution guidelines --- CONTRIBUTING.md | 32 ++++++++++++++++++++++++++++++++ ISSUE_TEMPLATE.md | 17 +++++++++++++++++ PULL_REQUEST_TEMPLATE.md | 3 +++ README.md | 3 +++ 4 files changed, 55 insertions(+) create mode 100644 CONTRIBUTING.md create mode 100644 ISSUE_TEMPLATE.md create mode 100644 PULL_REQUEST_TEMPLATE.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..1d610d3 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,32 @@ +# Contribution Guide + +## Got a Question or Problem? +Please take a look at the [wiki](https://github.com/irtimmer/moonlight-embedded/wiki) to see if answers your questions about the usage of Moonlight Embedded. + +If you still have questions about Moonlight Embedded, please use one of the different fora discussing Moonlight Embedded. + +[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 + +## Found an Issue? +If you think you found a bug in Moonlight Embedded you can submit a issue. But please ensure first you have checked the following or otherwise we will mark the issue as invalid: +- [ ] It's a bug in Moonlight Embedded and not in NVidia Geforce Experience or Steam as we otherwise can't fix them +- [ ] It's not a misconfiguration of your own setup. Like firewall misconfiguration. +- [ ] Their is no other bug report with the same issue. Check also the closed issues in case your bug is already solved in master. + +Also provide as much information as possible about your setup and how to produce the issue so their are higher chances we can reproduce the issue and fix it. Even better you can submit a Pull Request +with a fix. + +## Feature request +There are not much developers working on Moonlight Embedded. So it currently doesn't make much sense to use the issue tracker to submit feature request. Please try to implement it yourself and submit a pull request or discuss it on one of the fora to see if someone else is able to implement it. + +## Submitting a Pull Request +Have you created a cool new feature or fixed a few bugs you can submit a pull request. But before your request is merged you have to check the following. +- [ ] Your branch is based on a recent commit and can be merge cleanly +- [ ] Your code uses the same code style as the rest of the code +- [ ] Your history is cleanup and you provide one or multiple commits +- [ ] Your commits only changes the necessery lines and not accidently changes whitespace or add or remove empty lines. + +If these guide lines are not met we maybe won't merge your pull request or take some to cleanup your pull request before merging. Depending on how bad we wan't your code. \ No newline at end of file diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md new file mode 100644 index 0000000..49b047f --- /dev/null +++ b/ISSUE_TEMPLATE.md @@ -0,0 +1,17 @@ +Makes sure these boxes are checked before submitting your issue: + +- [ ] It's a bug in Moonlight Embedded and not in NVidia Geforce Experience or Steam as we otherwise can't fix them +- [ ] It's not a misconfiguration of your own setup. Like firewall misconfiguration. +- [ ] Their is no other bug report with the same issue. Check also the closed issues in case your bug is already solved in master. + +Please provide the following info. + +**_NVidia Geforce Experience version:_** +**Moonlight Embedded version:** +**Moonlight Embedded running on:** _Raspberry Pi/Cubox-i/Hummingboard/Other linux device/..._ + +**Output of Moonlight Embedded:** + +**What is the expected result?** + +**What happens instead of that?** diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..6a0bebe --- /dev/null +++ b/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,3 @@ +**Description** + +**Purpose** diff --git a/README.md b/README.md index 6ded03c..7a741fc 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,8 @@ More information about installing and runnning Moonlight Embedded is available o ## Bugs +Please check the fora, wiki and old bug reports before submitting a new bug report. + Bugs can be reported to the [issue tracker](https://github.com/irtimmer/moonlight-embedded/issues). ## See also @@ -47,6 +49,7 @@ different C implementations of Moonlight [XDA](http://forum.xda-developers.com/showthread.php?t=2505510) [Raspberry Pi Forum](http://www.raspberrypi.org/forums/viewtopic.php?f=78&t=65878) [SolidRun Community](http://www.solid-run.com/community/viewtopic.php?f=13&t=1489&p=11173) +[ODROID Forum](http://forum.odroid.com/viewtopic.php?f=91&t=15456) Moonlight Embedded on ODROID ## Contribute From a89bd5bf412284f0fb92bb6b322954ee257ce0f6 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sat, 5 Mar 2016 19:34:02 +0100 Subject: [PATCH 02/42] Remove FindCEC.cmake --- cmake/FindCEC.cmake | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 cmake/FindCEC.cmake diff --git a/cmake/FindCEC.cmake b/cmake/FindCEC.cmake deleted file mode 100644 index 0f94c8f..0000000 --- a/cmake/FindCEC.cmake +++ /dev/null @@ -1,19 +0,0 @@ -# - Try to find CEC -# Once done this will define -# -# CEC_FOUND - system has libcec -# CEC_INCLUDE_DIRS - the libcec include directory -# CEC_LIBRARIES - The libcec libraries - -if(PKG_CONFIG_FOUND) - pkg_check_modules (CEC libcec>=3.0.0) -else() - find_path(CEC_INCLUDE_DIRS libcec/cec.h) - find_library(CEC_LIBRARIES cec) -endif() - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(CEC DEFAULT_MSG CEC_INCLUDE_DIRS CEC_LIBRARIES) - -list(APPEND CEC_DEFINITIONS -DHAVE_LIBCEC=1) -mark_as_advanced(CEC_INCLUDE_DIRS CEC_LIBRARIES CEC_DEFINITIONS) From 3d12cc40062f9a5d8d37346050f162162ea078c3 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sat, 5 Mar 2016 19:34:55 +0100 Subject: [PATCH 03/42] Simplify issue template --- ISSUE_TEMPLATE.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 49b047f..4dab664 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -1,9 +1,3 @@ -Makes sure these boxes are checked before submitting your issue: - -- [ ] It's a bug in Moonlight Embedded and not in NVidia Geforce Experience or Steam as we otherwise can't fix them -- [ ] It's not a misconfiguration of your own setup. Like firewall misconfiguration. -- [ ] Their is no other bug report with the same issue. Check also the closed issues in case your bug is already solved in master. - Please provide the following info. **_NVidia Geforce Experience version:_** From 6b5eef857202c3e02144a62537067c21dcb8e584 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 6 Mar 2016 12:43:31 +0100 Subject: [PATCH 04/42] Can force video hardware acceleration with VDPAU --- src/video/sdl.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/video/sdl.c b/src/video/sdl.c index 89cb657..764cbb0 100644 --- a/src/video/sdl.c +++ b/src/video/sdl.c @@ -34,6 +34,9 @@ static char* ffmpeg_buffer; static void sdl_setup(int width, int height, int redrawRate, void* context, int drFlags) { int avc_flags = SLICE_THREADING; + if (drFlags && FORCE_HARDWARE_ACCELERATION) + avc_flags |= HARDWARE_ACCELERATION; + if (ffmpeg_init(width, height, avc_flags, 2) < 0) { fprintf(stderr, "Couldn't initialize video decoding\n"); exit(1); From 46c8c89baff4e1f937a6ed798f590b6c2b2b6cb0 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 6 Mar 2016 12:44:26 +0100 Subject: [PATCH 05/42] Update FFMpeg API to new version --- src/video/ffmpeg.c | 2 +- src/video/ffmpeg_vdpau.c | 22 ++++++++++------------ 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/video/ffmpeg.c b/src/video/ffmpeg.c index 9380f6a..4dc56e3 100644 --- a/src/video/ffmpeg.c +++ b/src/video/ffmpeg.c @@ -89,7 +89,7 @@ int ffmpeg_init(int width, int height, int perf_lvl, int thread_count) { decoder_ctx->width = width; decoder_ctx->height = height; - decoder_ctx->pix_fmt = PIX_FMT_YUV420P; + decoder_ctx->pix_fmt = AV_PIX_FMT_YUV420P; int err = avcodec_open2(decoder_ctx, decoder, NULL); if (err < 0) { diff --git a/src/video/ffmpeg_vdpau.c b/src/video/ffmpeg_vdpau.c index c8c9cc2..ae2b119 100644 --- a/src/video/ffmpeg_vdpau.c +++ b/src/video/ffmpeg_vdpau.c @@ -64,23 +64,22 @@ struct vdpau_render_state* vdp_get_free_render_state() { return render_state; } -static int vdp_get_buffer(AVCodecContext* context, AVFrame* frame) { +static void vdp_release_buffer(void* opaque, uint8_t *data) { + struct vdpau_render_state *render_state = (struct vdpau_render_state *) data; + render_state->state = 0; +} + +static int vdp_get_buffer(AVCodecContext* context, AVFrame* frame, int flags) { struct vdpau_render_state* pRenderState = vdp_get_free_render_state(); frame->data[0] = (uint8_t*) pRenderState; - frame->type = FF_BUFFER_TYPE_USER; + frame->buf[0] = av_buffer_create(frame->data[0], 0, vdp_release_buffer, NULL, 0); pRenderState->state |= FF_VDPAU_STATE_USED_FOR_RENDER; return 0; } -static void vdp_release_buffer(AVCodecContext* context, AVFrame* frame) { - struct vdpau_render_state *render_state = (struct vdpau_render_state *)frame->data[0]; - render_state->state = 0; - frame->data[0] = 0; -} - static enum AVPixelFormat vdp_get_format(AVCodecContext* context, const enum AVPixelFormat* pixel_format) { - return PIX_FMT_VDPAU_H264; + return AV_PIX_FMT_VDPAU_H264; } static void vdp_draw_horiz_band(struct AVCodecContext* context, const AVFrame* frame, int offset[4], int y, int type, int height) { @@ -108,8 +107,7 @@ int vdpau_init(AVCodecContext* decoder_ctx, int width, int height) { vdp_get_proc_address(vdp_device, VDP_FUNC_ID_DECODER_CREATE, (void**)&vdp_decoder_create); vdp_get_proc_address(vdp_device, VDP_FUNC_ID_VIDEO_MIXER_CREATE, (void**)&vdp_video_mixer_create); - decoder_ctx->get_buffer = vdp_get_buffer; - decoder_ctx->release_buffer = vdp_release_buffer; + decoder_ctx->get_buffer2 = vdp_get_buffer; decoder_ctx->draw_horiz_band = vdp_draw_horiz_band; decoder_ctx->get_format = vdp_get_format; decoder_ctx->slice_flags = SLICE_FLAG_CODED_ORDER | SLICE_FLAG_ALLOW_FIELD; @@ -119,7 +117,7 @@ int vdpau_init(AVCodecContext* decoder_ctx, int width, int height) { printf("Couldn't allocate frame\n"); return -1; } - cpu_frame->format = PIX_FMT_YUV420P; + cpu_frame->format = AV_PIX_FMT_YUV420P; cpu_frame->width = width; cpu_frame->height = height; av_frame_get_buffer(cpu_frame, 32); From 3943ba8cd87e734ef46bfd20c35d899e931ee896 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Tue, 8 Mar 2016 14:51:08 +0100 Subject: [PATCH 06/42] Update moonlight-common-c for GFE 2.11 --- libgamestream/CMakeLists.txt | 5 +++-- src/video/fake.c | 2 +- src/video/imx.c | 2 +- src/video/pi.c | 2 +- src/video/sdl.c | 2 +- third_party/moonlight-common-c | 2 +- 6 files changed, 8 insertions(+), 7 deletions(-) diff --git a/libgamestream/CMakeLists.txt b/libgamestream/CMakeLists.txt index a7896a5..88b2802 100644 --- a/libgamestream/CMakeLists.txt +++ b/libgamestream/CMakeLists.txt @@ -5,6 +5,7 @@ find_package(OpenSSL 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) @@ -21,8 +22,8 @@ target_link_libraries(gamestream moonlight-common) set_target_properties(gamestream PROPERTIES SOVERSION 0 VERSION ${MOONLIGHT_VERSION}) set_target_properties(moonlight-common PROPERTIES SOVERSION 0 VERSION ${MOONLIGHT_VERSION}) -target_include_directories(gamestream PRIVATE ../third_party/moonlight-common-c ../third_party/h264bitstream ${AVAHI_INCLUDE_DIRS} ${LIBUUID_INCLUDE_DIRS}) -target_link_libraries(gamestream ${CURL_LIBRARIES} ${OPENSSL_LIBRARIES} ${EXPAT_LIBRARIES} ${AVAHI_LIBRARIES} ${LIBUUID_LIBRARIES}) +target_include_directories(gamestream PRIVATE ../third_party/moonlight-common-c ../third_party/h264bitstream ${AVAHI_INCLUDE_DIRS} ${LIBUUID_INCLUDE_DIRS} ${ENET_INCLUDE_DIRS}) +target_link_libraries(gamestream ${CURL_LIBRARIES} ${OPENSSL_LIBRARIES} ${EXPAT_LIBRARIES} ${AVAHI_LIBRARIES} ${LIBUUID_LIBRARIES} ${ENET_LIBRARIES}) target_link_libraries(gamestream ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS}) diff --git a/src/video/fake.c b/src/video/fake.c index 6468ac6..163fa37 100644 --- a/src/video/fake.c +++ b/src/video/fake.c @@ -24,7 +24,7 @@ static FILE* fd; static const char* fileName = "fake.h264"; -void decoder_renderer_setup(int width, int height, int redrawRate, void* context, int drFlags) { +void decoder_renderer_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { fd = fopen(fileName, "w"); } diff --git a/src/video/imx.c b/src/video/imx.c index 740b566..5faf279 100644 --- a/src/video/imx.c +++ b/src/video/imx.c @@ -83,7 +83,7 @@ bool video_imx_init() { return vpu_Init(NULL) == RETCODE_SUCCESS; } -static void decoder_renderer_setup(int width, int height, int redrawRate, void* context, int drFlags) { +static void decoder_renderer_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { struct mxcfb_gbl_alpha alpha; dbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; diff --git a/src/video/pi.c b/src/video/pi.c index 9ff5eb7..8d8f6b7 100644 --- a/src/video/pi.c +++ b/src/video/pi.c @@ -53,7 +53,7 @@ static unsigned char *dest; static int port_settings_changed; static int first_packet; -static void decoder_renderer_setup(int width, int height, int redrawRate, void* context, int drFlags) { +static void decoder_renderer_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { bcm_host_init(); gs_sps_init(width, height); diff --git a/src/video/sdl.c b/src/video/sdl.c index 764cbb0..70a0e62 100644 --- a/src/video/sdl.c +++ b/src/video/sdl.c @@ -32,7 +32,7 @@ static char* ffmpeg_buffer; -static void sdl_setup(int width, int height, int redrawRate, void* context, int drFlags) { +static void sdl_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { int avc_flags = SLICE_THREADING; if (drFlags && FORCE_HARDWARE_ACCELERATION) avc_flags |= HARDWARE_ACCELERATION; diff --git a/third_party/moonlight-common-c b/third_party/moonlight-common-c index fbd58c6..4a6b489 160000 --- a/third_party/moonlight-common-c +++ b/third_party/moonlight-common-c @@ -1 +1 @@ -Subproject commit fbd58c60ea12d8760bae86b7d98b2d0208418baf +Subproject commit 4a6b489f51a76c5106960e4069f54d2183b123cb From c1f5a2a30ec89a8d3a1d5843b3df53f82dc8df41 Mon Sep 17 00:00:00 2001 From: Daniel Mehrwald Date: Sun, 13 Mar 2016 11:09:59 -0400 Subject: [PATCH 07/42] Add: Amlogic hardware video decoder backend --- CMakeLists.txt | 21 +++++- README.md | 5 +- cmake/FindAmlogic.cmake | 29 +++++++++ moonlight.conf | 1 + src/config.c | 12 +++- src/config.h | 3 + src/main.c | 9 +++ src/platform.c | 8 +++ src/platform.h | 5 +- src/video/aml.c | 138 ++++++++++++++++++++++++++++++++++++++++ 10 files changed, 225 insertions(+), 6 deletions(-) create mode 100644 cmake/FindAmlogic.cmake create mode 100644 src/video/aml.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 0beb0b7..4901a42 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,6 +17,7 @@ find_package(ALSA REQUIRED) find_package(Opus REQUIRED) find_package(Broadcom) find_package(Freescale) +find_package(Amlogic) find_package(PkgConfig REQUIRED) pkg_check_modules(EVDEV REQUIRED libevdev) @@ -47,10 +48,17 @@ if(CMAKE_BUILD_TYPE MATCHES Debug) list(APPEND SRC_LIST ./src/video/fake.c) list(APPEND MOONLIGHT_DEFINITIONS HAVE_FAKE LC_DEBUG) list(APPEND MOONLIGHT_OPTIONS FAKE DEBUG) -elseif(NOT BROADCOM_FOUND AND NOT FREESCALE_FOUND AND NOT SOFTWARE_FOUND) +elseif(NOT AMLOGIC_FOUND AND NOT BROADCOM_FOUND AND NOT FREESCALE_FOUND AND NOT SOFTWARE_FOUND) message(FATAL_ERROR "No video output available") endif() +if (AMLOGIC_FOUND) + set(SOFTWARE_FOUND FALSE) + list(APPEND SRC_LIST ./src/video/aml.c) + list(APPEND MOONLIGHT_DEFINITIONS HAVE_AML) + list(APPEND MOONLIGHT_OPTIONS AML) +endif() + if (SOFTWARE_FOUND) list(APPEND SRC_LIST ./src/video/ffmpeg.c ./src/video/sdl.c ./src/audio/sdl.c) list(APPEND MOONLIGHT_DEFINITIONS HAVE_SDL) @@ -62,7 +70,7 @@ if (SOFTWARE_FOUND) endif() endif() -if (BROADCOM_FOUND OR FREESCALE_FOUND OR CMAKE_BUILD_TYPE MATCHES Debug) +if (AMLOGIC_FOUND OR BROADCOM_FOUND OR FREESCALE_FOUND OR CMAKE_BUILD_TYPE MATCHES Debug) list(APPEND MOONLIGHT_DEFINITIONS HAVE_EMBEDDED) list(APPEND MOONLIGHT_OPTIONS EMBEDDED) endif() @@ -90,6 +98,15 @@ if (CEC_FOUND) target_link_libraries(moonlight ${CEC_LIBRARIES}) endif() +if(AMLOGIC_FOUND) + list(APPEND MOONLIGHT_DEFINITIONS HAVE_AML) + list(APPEND MOONLIGHT_OPTIONS AML) + target_include_directories(moonlight PRIVATE ${AMLOGIC_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR}) + target_link_libraries(moonlight gamestream ${AMLOGIC_LIBRARIES}) + set_property(TARGET moonlight PROPERTY COMPILE_DEFINITIONS ${AMLOGIC_DEFINITIONS}) + install(TARGETS moonlight DESTINATION ${CMAKE_INSTALL_LIBDIR}) +endif() + if(BROADCOM_FOUND) list(APPEND MOONLIGHT_DEFINITIONS HAVE_PI) list(APPEND MOONLIGHT_OPTIONS PI) diff --git a/README.md b/README.md index 7a741fc..313f1bc 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ Moonlight Embedded is an open source implementation of NVIDIA's GameStream, as used by the NVIDIA Shield, but built for Linux. Moonlight Embedded allows you to stream your full collection of Steam games from -your powerful Windows desktop to your (embedded) Linux system, like Raspberry Pi, CuBox-i and Hummingboard. +your powerful Windows desktop to your (embedded) Linux system, like ODROID, Raspberry Pi, CuBox-i and Hummingboard. ## Documentation @@ -14,7 +14,8 @@ More information about installing and runnning Moonlight Embedded is available o * Streams Steam and all of your games from your PC to your embedded system. * Use mDNS to scan for compatible GeForce Experience (GFE) machines on the network. * Qwerty Keyboard, Mouse and Gamepad support -* Support hardware video decoding on Raspberry Pi and i.MX 6 devices +* Support H264 hardware video decoding on ODROID, Raspberry Pi and i.MX 6 devices +* Support HEVC hardware video decoding on ODROID C1/C2 ## Requirements diff --git a/cmake/FindAmlogic.cmake b/cmake/FindAmlogic.cmake new file mode 100644 index 0000000..eb06031 --- /dev/null +++ b/cmake/FindAmlogic.cmake @@ -0,0 +1,29 @@ +find_path(AMLOGIC_INCLUDE_DIR + NAMES codec.h + DOC "Amlogic include directory" + PATHS /usr/local/include/amcodec /usr/include/amcodec) +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) +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) + +set(AMLOGIC_LIBRARIES ${AMCODEC_LIBRARY} ${AMADEC_LIBRARY} ${AMAVUTILS_LIBRARY}) +set(AMLOGIC_INCLUDE_DIRS ${AMLOGIC_INCLUDE_DIR}) diff --git a/moonlight.conf b/moonlight.conf index 79ccdbb..0909648 100644 --- a/moonlight.conf +++ b/moonlight.conf @@ -42,6 +42,7 @@ ## Select the audio and video decoder to use ## default - autodetect +## aml - hardware video decoder for ODROID-C1/C2 ## omx - hardware video decoder for Raspberry Pi ## imx - hardware video decoder for i.MX6 devices ## sdl - software decoder diff --git a/src/config.c b/src/config.c index 52dae3b..615889f 100644 --- a/src/config.c +++ b/src/config.c @@ -63,6 +63,9 @@ static struct option long_options[] = { {"surround", no_argument, NULL, 'u'}, {"fps", required_argument, NULL, 'v'}, {"forcehw", no_argument, NULL, 'w'}, +#ifdef HAVE_AML + {"hevc", no_argument, NULL, 'x'}, +#endif {0, 0, 0, 0}, }; @@ -199,6 +202,12 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) { break; case 'w': config->forcehw = true; + break; +#ifdef HAVE_AML + case 'x': + config->hevc = true; + break; +#endif case 1: if (config->action == NULL) config->action = value; @@ -289,6 +298,7 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) { config->sops = true; config->localaudio = false; config->fullscreen = true; + config->hevc = false; config->inputsCount = 0; config->mapping = get_path("mappings/default.conf", getenv("XDG_DATA_DIRS")); @@ -306,7 +316,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", long_options, &option_index)) != -1) { + while ((c = getopt_long_only(argc, argv, "-abc:d:efg:h:i:j:k:lm:no:p:q:r:stuv:w:x", long_options, &option_index)) != -1) { parse_argument(c, optarg, config); } } diff --git a/src/config.h b/src/config.h index 9b88b5c..77872b1 100644 --- a/src/config.h +++ b/src/config.h @@ -43,6 +43,9 @@ typedef struct _CONFIGURATION { bool forcehw; struct input_config inputs[MAX_INPUTS]; int inputsCount; +#ifdef HAVE_AML + bool hevc; +#endif } CONFIGURATION, *PCONFIGURATION; bool inputAdded; diff --git a/src/main.c b/src/main.c index 7459cf0..64c9421 100644 --- a/src/main.c +++ b/src/main.c @@ -83,6 +83,11 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform sys exit(-1); } + // h265 + if (config->hevc) { + config->stream.supportsHevc = 1; + } + int ret = gs_start_app(server, &config->stream, appId, config->sops, config->localaudio); if (ret < 0) { if (ret == GS_NOT_SUPPORTED_4K) @@ -154,6 +159,10 @@ static void help() { printf("\t-audio \t\tUse as ALSA audio output device (default sysdefault)\n"); printf("\t-forcehw \t\tTry to use video hardware acceleration\n"); #endif + #ifdef HAVE_AML + printf("\n Amlogic Codec options\n\n"); + printf("\t-hevc \t\tUse high efficiency video decoding (HEVC)\n"); + #endif printf("\nUse Ctrl+Alt+Shift+Q to exit streaming session\n\n"); exit(0); } diff --git a/src/platform.c b/src/platform.c index 3d4a81f..ba2d436 100644 --- a/src/platform.c +++ b/src/platform.c @@ -52,6 +52,10 @@ enum platform platform_check(char* name) { if (std || strcmp(name, "sdl") == 0) return SDL; #endif + #ifdef HAVE_AML + if (std || strcmp(name, "aml") == 0) + return AML; + #endif #ifdef HAVE_FAKE if (std || strcmp(name, "fake") == 0) return FAKE; @@ -73,6 +77,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_AML + case AML: + return &decoder_callbacks_aml; + #endif #ifdef HAVE_FAKE case FAKE: return &decoder_callbacks_fake; diff --git a/src/platform.h b/src/platform.h index ac08228..e1cb86d 100644 --- a/src/platform.h +++ b/src/platform.h @@ -25,12 +25,15 @@ #define IS_EMBEDDED(SYSTEM) SYSTEM != SDL -enum platform { NONE, SDL, PI, IMX, FAKE }; +enum platform { NONE, SDL, PI, IMX, AML, FAKE }; enum platform platform_check(char*); PDECODER_RENDERER_CALLBACKS platform_get_video(enum platform system); PAUDIO_RENDERER_CALLBACKS platform_get_audio(enum platform system); +#ifdef HAVE_AML +extern DECODER_RENDERER_CALLBACKS decoder_callbacks_aml; +#endif #ifdef HAVE_FAKE extern DECODER_RENDERER_CALLBACKS decoder_callbacks_fake; #endif diff --git a/src/video/aml.c b/src/video/aml.c new file mode 100644 index 0000000..673747d --- /dev/null +++ b/src/video/aml.c @@ -0,0 +1,138 @@ +/* + * This file is part of Moonlight Embedded. + * + * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2016 OtherCrashOverride, Daniel Mehrwald + * + * 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 . + */ + +#include "limelight-common/Limelight.h" + +#include +#include +#include +#include +#include +#include +#include + +static codec_para_t codecParam = { 0 }; +const size_t EXTERNAL_PTS = (1); +const size_t SYNC_OUTSIDE = (2); + +int osd_blank(char *path,int cmd) { + int fd; + char bcmd[16]; + + fd = open(path, O_CREAT|O_RDWR | O_TRUNC, 0644); + + if(fd>=0) { + sprintf(bcmd,"%d",cmd); + if (write(fd,bcmd,strlen(bcmd)) < 0) { + printf("osd_blank error during write.\n"); + } + close(fd); + return 0; + } + + return -1; +} + +void init_display() { + osd_blank("/sys/class/graphics/fb0/blank",1); + osd_blank("/sys/class/graphics/fb1/blank",0); +} + +void restore_display() { + osd_blank("/sys/class/graphics/fb0/blank",0); + osd_blank("/sys/class/graphics/fb1/blank",0); +} + +void aml_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { + fprintf(stderr, "\nvideoFormat=%d nwidth=%d, height=%d, redrawRate=%d, context=%p, drFlags=%x\n", + videoFormat, width, height, redrawRate, context, drFlags); + + init_display(); + + codecParam.stream_type = STREAM_TYPE_ES_VIDEO; + codecParam.has_video = 1; + codecParam.noblock = 0; + + switch (videoFormat) { + case VIDEO_FORMAT_H264: // 1 + if (width > 1920 || height > 1080) { + codecParam.video_type = VFORMAT_H264_4K2K; + codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_H264_4K2K; ///< video format, such as H264, MPEG2... + } else { + codecParam.video_type = VFORMAT_H264; + codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_H264; ///< video format, such as H264, MPEG2... + } + + fprintf(stdout, "Decoding H264 video.\n"); + break; + case VIDEO_FORMAT_H265: // 2 + + codecParam.video_type = VFORMAT_HEVC; + codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_HEVC; ///< video format, such as H264, MPEG2... + + fprintf(stdout, "Decoding HEVC video.\n"); + break; + default: + printf("Unsupported video format.\n"); + exit(1); + } + + codecParam.am_sysinfo.width = width; //< video source width + codecParam.am_sysinfo.height = height; //< video source height + codecParam.am_sysinfo.rate = (96000 / (redrawRate)); //< video source frame duration + codecParam.am_sysinfo.param = (void *)(EXTERNAL_PTS | SYNC_OUTSIDE); //< other parameters for video decoder + + int api = codec_init(&codecParam); + fprintf(stdout, "codec_init=%x\n", api); + + if (api != 0) { + fprintf(stderr, "codec_init failed.\n"); + exit(1); + } +} + +void aml_cleanup() { + int api = codec_close(&codecParam); + restore_display(); +} + +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; + } + + entry = entry->next; + } + return result; +} + +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), +}; From 0cab1f890a41f0a6fc7e790904708e0404a391a7 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Mon, 14 Mar 2016 16:39:44 +0100 Subject: [PATCH 08/42] Make hevc generic for all platforms --- src/config.c | 6 +----- src/config.h | 2 -- src/main.c | 5 +---- 3 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/config.c b/src/config.c index 615889f..eecda4c 100644 --- a/src/config.c +++ b/src/config.c @@ -63,9 +63,7 @@ static struct option long_options[] = { {"surround", no_argument, NULL, 'u'}, {"fps", required_argument, NULL, 'v'}, {"forcehw", no_argument, NULL, 'w'}, -#ifdef HAVE_AML - {"hevc", no_argument, NULL, 'x'}, -#endif + {"forcehevc", no_argument, NULL, 'x'}, {0, 0, 0, 0}, }; @@ -203,11 +201,9 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) { case 'w': config->forcehw = true; break; -#ifdef HAVE_AML case 'x': config->hevc = true; break; -#endif case 1: if (config->action == NULL) config->action = value; diff --git a/src/config.h b/src/config.h index 77872b1..8479b3c 100644 --- a/src/config.h +++ b/src/config.h @@ -43,9 +43,7 @@ typedef struct _CONFIGURATION { bool forcehw; struct input_config inputs[MAX_INPUTS]; int inputsCount; -#ifdef HAVE_AML bool hevc; -#endif } CONFIGURATION, *PCONFIGURATION; bool inputAdded; diff --git a/src/main.c b/src/main.c index 64c9421..3f12e49 100644 --- a/src/main.c +++ b/src/main.c @@ -142,6 +142,7 @@ static void help() { printf("\t-60fps\t\t\tUse 60fps [default]\n"); printf("\t-bitrate \tSpecify the bitrate in Kbps\n"); printf("\t-packetsize \tSpecify the maximum packetsize in bytes\n"); + printf("\t-hevc \t\tUse high efficiency video decoding (HEVC)\n"); printf("\t-remote\t\t\tEnable remote optimizations\n"); printf("\t-app \t\tName of app to stream\n"); printf("\t-nosops\t\t\tDon't allow GFE to modify game settings\n"); @@ -159,10 +160,6 @@ static void help() { printf("\t-audio \t\tUse as ALSA audio output device (default sysdefault)\n"); printf("\t-forcehw \t\tTry to use video hardware acceleration\n"); #endif - #ifdef HAVE_AML - printf("\n Amlogic Codec options\n\n"); - printf("\t-hevc \t\tUse high efficiency video decoding (HEVC)\n"); - #endif printf("\nUse Ctrl+Alt+Shift+Q to exit streaming session\n\n"); exit(0); } From ecf1c544eca0c103e207ccbc5713bf90d3dbbd52 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Mon, 14 Mar 2016 17:43:29 +0100 Subject: [PATCH 09/42] Make Amlogic decoder a plugin --- CMakeLists.txt | 16 +++++----------- src/platform.c | 14 +++++++++----- src/platform.h | 3 --- 3 files changed, 14 insertions(+), 19 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4901a42..8bcf54f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,13 +52,6 @@ elseif(NOT AMLOGIC_FOUND AND NOT BROADCOM_FOUND AND NOT FREESCALE_FOUND AND NOT message(FATAL_ERROR "No video output available") endif() -if (AMLOGIC_FOUND) - set(SOFTWARE_FOUND FALSE) - list(APPEND SRC_LIST ./src/video/aml.c) - list(APPEND MOONLIGHT_DEFINITIONS HAVE_AML) - list(APPEND MOONLIGHT_OPTIONS AML) -endif() - if (SOFTWARE_FOUND) list(APPEND SRC_LIST ./src/video/ffmpeg.c ./src/video/sdl.c ./src/audio/sdl.c) list(APPEND MOONLIGHT_DEFINITIONS HAVE_SDL) @@ -101,10 +94,11 @@ endif() if(AMLOGIC_FOUND) list(APPEND MOONLIGHT_DEFINITIONS HAVE_AML) list(APPEND MOONLIGHT_OPTIONS AML) - target_include_directories(moonlight PRIVATE ${AMLOGIC_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR}) - target_link_libraries(moonlight gamestream ${AMLOGIC_LIBRARIES}) - set_property(TARGET moonlight PROPERTY COMPILE_DEFINITIONS ${AMLOGIC_DEFINITIONS}) - install(TARGETS moonlight DESTINATION ${CMAKE_INSTALL_LIBDIR}) + add_library(moonlight-aml SHARED ./src/video/aml.c ${ILCLIENT_SRC_LIST}) + target_include_directories(moonlight-aml PRIVATE ${AMLOGIC_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR}) + target_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) diff --git a/src/platform.c b/src/platform.c index ba2d436..d9f01e2 100644 --- a/src/platform.c +++ b/src/platform.c @@ -25,6 +25,7 @@ #include #include #include +#include #include typedef bool(*ImxInit)(); @@ -48,14 +49,17 @@ enum platform platform_check(char* name) { return PI; } #endif + #ifdef HAVE_AML + if (std || strcmp(name, "aml") == 0) { + void *handle = dlopen("libmoonlight-aml.so", RTLD_NOW | RTLD_GLOBAL); + if (handle != NULL && access("/dev/amvideo", F_OK) != -1) + return AML; + } + #endif #ifdef HAVE_SDL if (std || strcmp(name, "sdl") == 0) return SDL; #endif - #ifdef HAVE_AML - if (std || strcmp(name, "aml") == 0) - return AML; - #endif #ifdef HAVE_FAKE if (std || strcmp(name, "fake") == 0) return FAKE; @@ -79,7 +83,7 @@ DECODER_RENDERER_CALLBACKS* platform_get_video(enum platform system) { #endif #ifdef HAVE_AML case AML: - return &decoder_callbacks_aml; + return (PDECODER_RENDERER_CALLBACKS) dlsym(RTLD_DEFAULT, "decoder_callbacks_aml"); #endif #ifdef HAVE_FAKE case FAKE: diff --git a/src/platform.h b/src/platform.h index e1cb86d..9a0cf5d 100644 --- a/src/platform.h +++ b/src/platform.h @@ -31,9 +31,6 @@ enum platform platform_check(char*); PDECODER_RENDERER_CALLBACKS platform_get_video(enum platform system); PAUDIO_RENDERER_CALLBACKS platform_get_audio(enum platform system); -#ifdef HAVE_AML -extern DECODER_RENDERER_CALLBACKS decoder_callbacks_aml; -#endif #ifdef HAVE_FAKE extern DECODER_RENDERER_CALLBACKS decoder_callbacks_fake; #endif From 56094f303304a8c2fb03e00408fc1034c8b66d01 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Mon, 14 Mar 2016 19:13:06 +0100 Subject: [PATCH 10/42] Update moonlight-common-c --- third_party/moonlight-common-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/moonlight-common-c b/third_party/moonlight-common-c index 4a6b489..3382238 160000 --- a/third_party/moonlight-common-c +++ b/third_party/moonlight-common-c @@ -1 +1 @@ -Subproject commit 4a6b489f51a76c5106960e4069f54d2183b123cb +Subproject commit 33822382beda78e1a0a4839251eda444bbdc60b3 From 58da4da6c30b46554a325d865eb85c043c9e284a Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Tue, 15 Mar 2016 18:15:42 +0100 Subject: [PATCH 11/42] Give error when enabling HEVC on unsupported platforms --- src/video/imx.c | 5 +++++ src/video/pi.c | 5 +++++ src/video/sdl.c | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/src/video/imx.c b/src/video/imx.c index 5faf279..3d83f32 100644 --- a/src/video/imx.c +++ b/src/video/imx.c @@ -84,6 +84,11 @@ bool video_imx_init() { } static void 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"); + exit(1); + } + struct mxcfb_gbl_alpha alpha; dbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; diff --git a/src/video/pi.c b/src/video/pi.c index 8d8f6b7..d5b87e9 100644 --- a/src/video/pi.c +++ b/src/video/pi.c @@ -54,6 +54,11 @@ static int port_settings_changed; static int first_packet; static void 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"); + exit(1); + } + bcm_host_init(); gs_sps_init(width, height); diff --git a/src/video/sdl.c b/src/video/sdl.c index 70a0e62..519553e 100644 --- a/src/video/sdl.c +++ b/src/video/sdl.c @@ -33,6 +33,11 @@ static char* ffmpeg_buffer; static void sdl_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"); + exit(1); + } + int avc_flags = SLICE_THREADING; if (drFlags && FORCE_HARDWARE_ACCELERATION) avc_flags |= HARDWARE_ACCELERATION; From feb174478cd7e8041c1ef672734e2d7e6c5504d1 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Tue, 15 Mar 2016 18:21:01 +0100 Subject: [PATCH 12/42] Attempt to support HEVC with VDPAU and software decoding --- src/video/ffmpeg.c | 23 ++++++++++++++++++++--- src/video/sdl.c | 5 ----- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/video/ffmpeg.c b/src/video/ffmpeg.c index 4dc56e3..3027617 100644 --- a/src/video/ffmpeg.c +++ b/src/video/ffmpeg.c @@ -23,6 +23,8 @@ #include "ffmpeg_vdpau.h" #endif +#include "limelight-common/Limelight.h" + #include #include #include @@ -42,7 +44,7 @@ enum decoders decoder_system; // This function must be called before // any other decoding functions -int ffmpeg_init(int width, int height, int perf_lvl, int thread_count) { +int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int thread_count) { // Initialize the avcodec library and register codecs av_log_set_level(AV_LOG_QUIET); avcodec_register_all(); @@ -51,7 +53,15 @@ int ffmpeg_init(int width, int height, int perf_lvl, int thread_count) { #ifdef HAVE_VDPAU if (perf_lvl & HARDWARE_ACCELERATION) { - decoder = avcodec_find_decoder_by_name("h264_vdpau"); + switch (videoFormat) { + case VIDEO_FORMAT_H264: + decoder = avcodec_find_decoder_by_name("h264_vdpau"); + break; + case VIDEO_FORMAT_H265: + decoder = avcodec_find_decoder_by_name("hevc_vdpau"); + break; + } + if (decoder != NULL) decoder_system = VDPAU; } @@ -59,7 +69,14 @@ int ffmpeg_init(int width, int height, int perf_lvl, int thread_count) { if (decoder == NULL) { decoder_system = SOFTWARE; - decoder = avcodec_find_decoder_by_name("h264"); + 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; + } if (decoder == NULL) { printf("Couldn't find decoder\n"); return -1; diff --git a/src/video/sdl.c b/src/video/sdl.c index 519553e..70a0e62 100644 --- a/src/video/sdl.c +++ b/src/video/sdl.c @@ -33,11 +33,6 @@ static char* ffmpeg_buffer; static void sdl_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"); - exit(1); - } - int avc_flags = SLICE_THREADING; if (drFlags && FORCE_HARDWARE_ACCELERATION) avc_flags |= HARDWARE_ACCELERATION; From 510d5e86e02a949de8d682141be1dcb1ce02e025 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Tue, 15 Mar 2016 18:33:44 +0100 Subject: [PATCH 13/42] Update README --- README.md | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 313f1bc..cdc922b 100644 --- a/README.md +++ b/README.md @@ -3,20 +3,12 @@ Moonlight Embedded is an open source implementation of NVIDIA's GameStream, as used by the NVIDIA Shield, but built for Linux. Moonlight Embedded allows you to stream your full collection of Steam games from -your powerful Windows desktop to your (embedded) Linux system, like ODROID, Raspberry Pi, CuBox-i and Hummingboard. +your powerful Windows desktop to your (embedded) Linux system, like Raspberry Pi, CuBox-i and ODROID. ## Documentation More information about installing and runnning Moonlight Embedded is available on the [wiki](https://github.com/irtimmer/moonlight-embedded/wiki). -## Features - -* Streams Steam and all of your games from your PC to your embedded system. -* Use mDNS to scan for compatible GeForce Experience (GFE) machines on the network. -* Qwerty Keyboard, Mouse and Gamepad support -* Support H264 hardware video decoding on ODROID, Raspberry Pi and i.MX 6 devices -* Support HEVC hardware video decoding on ODROID C1/C2 - ## Requirements * [GFE compatible](http://shield.nvidia.com/play-pc-games/) computer with GTX 600/700/900 series GPU (for the PC you're streaming from) @@ -47,9 +39,9 @@ different C implementations of Moonlight ## Discussion -[XDA](http://forum.xda-developers.com/showthread.php?t=2505510) -[Raspberry Pi Forum](http://www.raspberrypi.org/forums/viewtopic.php?f=78&t=65878) -[SolidRun Community](http://www.solid-run.com/community/viewtopic.php?f=13&t=1489&p=11173) +[XDA](http://forum.xda-developers.com/showthread.php?t=2505510) Moonlight in General +[Raspberry Pi Forum](http://www.raspberrypi.org/forums/viewtopic.php?f=78&t=65878) Moonlight Embedded for Raspberry Pi +[SolidRun Community](http://www.solid-run.com/community/viewtopic.php?f=13&t=1489&p=11173) Moonlight Embedded for Cubox-i and Hummingboard [ODROID Forum](http://forum.odroid.com/viewtopic.php?f=91&t=15456) Moonlight Embedded on ODROID ## Contribute From e53be38bdb5d2b7d9b67c8cd817a86dd35f0fd4d Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Wed, 16 Mar 2016 19:29:10 +0100 Subject: [PATCH 14/42] Fix missing functino arguments for FFMpeg --- src/video/ffmpeg.h | 2 +- src/video/sdl.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video/ffmpeg.h b/src/video/ffmpeg.h index 1421e2d..2714222 100644 --- a/src/video/ffmpeg.h +++ b/src/video/ffmpeg.h @@ -34,7 +34,7 @@ // Uses hardware acceleration #define HARDWARE_ACCELERATION 0x40 -int ffmpeg_init(int width, int height, int perf_lvl, int thread_count); +int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int thread_count); void ffmpeg_destroy(void); int ffmpeg_draw_frame(AVFrame *pict); diff --git a/src/video/sdl.c b/src/video/sdl.c index 70a0e62..a988870 100644 --- a/src/video/sdl.c +++ b/src/video/sdl.c @@ -37,7 +37,7 @@ static void sdl_setup(int videoFormat, int width, int height, int redrawRate, vo if (drFlags && FORCE_HARDWARE_ACCELERATION) avc_flags |= HARDWARE_ACCELERATION; - if (ffmpeg_init(width, height, avc_flags, 2) < 0) { + if (ffmpeg_init(videoFormat, width, height, avc_flags, 2) < 0) { fprintf(stderr, "Couldn't initialize video decoding\n"); exit(1); } From 48bbcbf954b7f51f8c68846ac2f7d93bd9ff04d9 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Mon, 1 Feb 2016 13:53:48 +0100 Subject: [PATCH 15/42] Update libgamestream for h265 --- docs/README.pod | 5 +++++ libgamestream/client.c | 13 +++++++++++++ libgamestream/client.h | 2 ++ moonlight.conf | 3 +++ src/config.c | 4 ++-- src/config.h | 1 - src/main.c | 7 +------ 7 files changed, 26 insertions(+), 9 deletions(-) diff --git a/docs/README.pod b/docs/README.pod index 79712a1..4f94385 100644 --- a/docs/README.pod +++ b/docs/README.pod @@ -95,6 +95,11 @@ Change the network packetsize to I. The packetsize should the smaller than the MTU of the network. By default a safe value of 1024 is used. +=item B<-forcehevc> + +Request a h265/HEVC from the server. +Will still use h264 if server doesn't support HEVC. + =item B<-remote> Enable the optimizations for remote connections in GFE. diff --git a/libgamestream/client.c b/libgamestream/client.c index 0f0ecca..c80f88e 100644 --- a/libgamestream/client.c +++ b/libgamestream/client.c @@ -172,6 +172,7 @@ static int load_server_status(PSERVER_DATA server) { char *stateText = NULL; char *heightText = NULL; char *serverCodecModeSupportText = NULL; + char *maxLumaPixelsHEVC = NULL; uuid_t uuid; char uuid_str[37]; @@ -211,9 +212,13 @@ static int load_server_status(PSERVER_DATA server) { 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; + server->paired = pairedText != NULL && strcmp(pairedText, "1") == 0; server->currentGame = currentGameText == NULL ? 0 : atoi(currentGameText); server->supports4K = heightText != NULL && serverCodecModeSupportText != NULL && atoi(heightText) >= 2160; + server->maxLumaPixelsHEVC = maxLumaPixelsHEVC == NULL ? 0 : atol(maxLumaPixelsHEVC); char *versionSep = strstr(versionText, "."); if (versionSep != NULL) { *versionSep = 0; @@ -246,6 +251,9 @@ static int load_server_status(PSERVER_DATA server) { if (serverCodecModeSupportText != NULL) free(serverCodecModeSupportText); + if (maxLumaPixelsHEVC != NULL) + free(maxLumaPixelsHEVC); + return ret; } @@ -477,6 +485,11 @@ int gs_start_app(PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, b if (data == NULL) return GS_OUT_OF_MEMORY; + //Check support for H.265 video + //TODO: Find a better way to detect this + if (!(config->supportsHevc && server->maxLumaPixelsHEVC > 0 && server->gpuType != NULL && strstr(server->gpuType, "GTX 9"))) + config->supportsHevc = VIDEO_FORMAT_H264; + uuid_generate_random(uuid); uuid_unparse(uuid, uuid_str); if (server->currentGame == 0) { diff --git a/libgamestream/client.h b/libgamestream/client.h index 826a6c9..92051ce 100644 --- a/libgamestream/client.h +++ b/libgamestream/client.h @@ -27,10 +27,12 @@ typedef struct _SERVER_DATA { const char* address; + char* gpuType; bool paired; bool supports4K; int currentGame; int serverMajorVersion; + long maxLumaPixelsHEVC; } SERVER_DATA, *PSERVER_DATA; int gs_init(PSERVER_DATA server, const char *keyDirectory); diff --git a/moonlight.conf b/moonlight.conf index 0909648..a800d6c 100644 --- a/moonlight.conf +++ b/moonlight.conf @@ -17,6 +17,9 @@ ## Size of network packets should be lower than MTU #packetsize = 1024 +## Use of h265/HEVC video codec +#h265 = false + ## Default started application on host #app = Steam diff --git a/src/config.c b/src/config.c index eecda4c..6bfef0e 100644 --- a/src/config.c +++ b/src/config.c @@ -202,7 +202,7 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) { config->forcehw = true; break; case 'x': - config->hevc = true; + config->stream.supportsHevc = true; break; case 1: if (config->action == NULL) @@ -285,6 +285,7 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) { config->stream.packetSize = 1024; config->stream.streamingRemotely = 0; config->stream.audioConfiguration = AUDIO_CONFIGURATION_STEREO; + config->stream.supportsHevc = false; config->platform = "default"; config->app = "Steam"; @@ -294,7 +295,6 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) { config->sops = true; config->localaudio = false; config->fullscreen = true; - config->hevc = false; config->inputsCount = 0; config->mapping = get_path("mappings/default.conf", getenv("XDG_DATA_DIRS")); diff --git a/src/config.h b/src/config.h index 8479b3c..9b88b5c 100644 --- a/src/config.h +++ b/src/config.h @@ -43,7 +43,6 @@ typedef struct _CONFIGURATION { bool forcehw; struct input_config inputs[MAX_INPUTS]; int inputsCount; - bool hevc; } CONFIGURATION, *PCONFIGURATION; bool inputAdded; diff --git a/src/main.c b/src/main.c index 3f12e49..238768e 100644 --- a/src/main.c +++ b/src/main.c @@ -83,11 +83,6 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform sys exit(-1); } - // h265 - if (config->hevc) { - config->stream.supportsHevc = 1; - } - int ret = gs_start_app(server, &config->stream, appId, config->sops, config->localaudio); if (ret < 0) { if (ret == GS_NOT_SUPPORTED_4K) @@ -142,7 +137,7 @@ static void help() { printf("\t-60fps\t\t\tUse 60fps [default]\n"); printf("\t-bitrate \tSpecify the bitrate in Kbps\n"); printf("\t-packetsize \tSpecify the maximum packetsize in bytes\n"); - printf("\t-hevc \t\tUse high efficiency video decoding (HEVC)\n"); + printf("\t-forcehevc\t\tUse high efficiency video decoding (HEVC)\n"); printf("\t-remote\t\t\tEnable remote optimizations\n"); printf("\t-app \t\tName of app to stream\n"); printf("\t-nosops\t\t\tDon't allow GFE to modify game settings\n"); From 129962d1263ac4f269e7a6e3df892ec3f1b3e0e8 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 20 Mar 2016 21:07:56 +0100 Subject: [PATCH 16/42] Remove unneeded check --- libgamestream/client.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/libgamestream/client.c b/libgamestream/client.c index c80f88e..37f3445 100644 --- a/libgamestream/client.c +++ b/libgamestream/client.c @@ -485,11 +485,6 @@ int gs_start_app(PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, b if (data == NULL) return GS_OUT_OF_MEMORY; - //Check support for H.265 video - //TODO: Find a better way to detect this - if (!(config->supportsHevc && server->maxLumaPixelsHEVC > 0 && server->gpuType != NULL && strstr(server->gpuType, "GTX 9"))) - config->supportsHevc = VIDEO_FORMAT_H264; - uuid_generate_random(uuid); uuid_unparse(uuid, uuid_str); if (server->currentGame == 0) { From 43c06a2338d10ecad979707667bffee321f18200 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 20 Mar 2016 22:11:17 +0100 Subject: [PATCH 17/42] Show information about host system --- libgamestream/client.c | 12 +++--------- libgamestream/client.h | 2 +- src/main.c | 3 +++ 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/libgamestream/client.c b/libgamestream/client.c index 37f3445..6f0beaa 100644 --- a/libgamestream/client.c +++ b/libgamestream/client.c @@ -172,7 +172,6 @@ static int load_server_status(PSERVER_DATA server) { char *stateText = NULL; char *heightText = NULL; char *serverCodecModeSupportText = NULL; - char *maxLumaPixelsHEVC = NULL; uuid_t uuid; char uuid_str[37]; @@ -215,14 +214,12 @@ static int load_server_status(PSERVER_DATA server) { if (xml_search(data->memory, data->size, "gputype", &server->gpuType) != GS_OK) goto cleanup; + if (xml_search(data->memory, data->size, "GfeVersion", &server->gfeVersion) != GS_OK) + goto cleanup; + server->paired = pairedText != NULL && strcmp(pairedText, "1") == 0; server->currentGame = currentGameText == NULL ? 0 : atoi(currentGameText); server->supports4K = heightText != NULL && serverCodecModeSupportText != NULL && atoi(heightText) >= 2160; - server->maxLumaPixelsHEVC = maxLumaPixelsHEVC == NULL ? 0 : atol(maxLumaPixelsHEVC); - char *versionSep = strstr(versionText, "."); - if (versionSep != NULL) { - *versionSep = 0; - } server->serverMajorVersion = atoi(versionText); if (strstr(stateText, "_SERVER_AVAILABLE")) { // After GFE 2.8, current game remains set even after streaming @@ -251,9 +248,6 @@ static int load_server_status(PSERVER_DATA server) { if (serverCodecModeSupportText != NULL) free(serverCodecModeSupportText); - if (maxLumaPixelsHEVC != NULL) - free(maxLumaPixelsHEVC); - return ret; } diff --git a/libgamestream/client.h b/libgamestream/client.h index 92051ce..548690a 100644 --- a/libgamestream/client.h +++ b/libgamestream/client.h @@ -28,11 +28,11 @@ typedef struct _SERVER_DATA { const char* address; char* gpuType; + char* gfeVersion; bool paired; bool supports4K; int currentGame; int serverMajorVersion; - long maxLumaPixelsHEVC; } SERVER_DATA, *PSERVER_DATA; int gs_init(PSERVER_DATA server, const char *keyDirectory); diff --git a/src/main.c b/src/main.c index 238768e..6f67ca9 100644 --- a/src/main.c +++ b/src/main.c @@ -201,6 +201,7 @@ int main(int argc, char* argv[]) { exit(-1); } config.address[0] = 0; + printf("Searching for server...\n"); gs_discover_server(config.address); if (config.address[0] == 0) { fprintf(stderr, "Autodiscovery failed. Specify an IP address next time.\n"); @@ -229,6 +230,8 @@ int main(int argc, char* argv[]) { exit(-1); } + printf("NVIDIA %s, GFE %s (protocol version %d)\n", server.gpuType, server.gfeVersion, server.serverMajorVersion); + if (strcmp("list", config.action) == 0) { pair_check(&server); applist(&server); From 016ac91c1c19f868cc46cac5b4d794b414b8d74e Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Mon, 21 Mar 2016 17:10:29 +0100 Subject: [PATCH 18/42] Extra validation of returned XML --- libgamestream/client.c | 116 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 111 insertions(+), 5 deletions(-) diff --git a/libgamestream/client.c b/libgamestream/client.c index 6f0beaa..d9c1d17 100644 --- a/libgamestream/client.c +++ b/libgamestream/client.c @@ -307,8 +307,26 @@ static int sign_it(const char *msg, size_t mlen, unsigned char **sig, size_t *sl return result; } +int gs_unpair(PSERVER_DATA server) { + char url[4096]; + uuid_t uuid; + char uuid_str[37]; + PHTTP_DATA data = http_create_data(); + if (data == NULL) + return GS_OUT_OF_MEMORY; + + uuid_generate_random(uuid); + uuid_unparse(uuid, uuid_str); + sprintf(url, "http://%s:47989/unpair?uniqueid=%s&uuid=%s", server->address, unique_id, uuid_str); + ret = http_request(url, data); + + http_free_data(data); + return ret; +} + int gs_pair(PSERVER_DATA server, char* pin) { int ret = GS_OK; + char* result = NULL; char url[4096]; uuid_t uuid; char uuid_str[37]; @@ -337,6 +355,15 @@ int gs_pair(PSERVER_DATA server, char* pin) { else if ((ret = http_request(url, data)) != GS_OK) goto cleanup; + if ((ret = xml_search(data->memory, data->size, "paired", &result)) != GS_OK) + goto cleanup; + + if (strcmp(result, "1") != 0) { + gs_error = "Pairing failed"; + ret = GS_FAILED; + goto cleanup; + } + unsigned char salt_pin[20]; unsigned char aes_key_hash[20]; AES_KEY enc_key, dec_key; @@ -359,7 +386,19 @@ int gs_pair(PSERVER_DATA server, char* pin) { if ((ret = http_request(url, data)) != GS_OK) goto cleanup; - char *result; + free(result); + result = NULL; + if ((ret = xml_search(data->memory, data->size, "paired", &result)) != GS_OK) + goto cleanup; + + if (strcmp(result, "1") != 0) { + gs_error = "Pairing failed"; + ret = GS_FAILED; + goto cleanup; + } + + free(result); + result = NULL; if (xml_search(data->memory, data->size, "challengeresponse", &result) != GS_OK) { ret = GS_INVALID; goto cleanup; @@ -370,7 +409,6 @@ int gs_pair(PSERVER_DATA server, char* pin) { for (int count = 0; count < strlen(result); count += 2) { sscanf(&result[count], "%2hhx", &challenge_response_data_enc[count / 2]); } - free(result); for (int i = 0; i < 48; i += 16) { AES_decrypt(&challenge_response_data_enc[i], &challenge_response_data[i], &dec_key); @@ -399,11 +437,26 @@ int gs_pair(PSERVER_DATA server, char* pin) { if ((ret = http_request(url, data)) != GS_OK) goto cleanup; + free(result); + result = NULL; + if ((ret = xml_search(data->memory, data->size, "paired", &result)) != GS_OK) + goto cleanup; + + if (strcmp(result, "1") != 0) { + gs_error = "Pairing failed"; + ret = GS_FAILED; + goto cleanup; + } + + free(result); + result = NULL; if (xml_search(data->memory, data->size, "pairingsecret", &result) != GS_OK) { ret = GS_INVALID; goto cleanup; } + //TODO: verify pairingsecret + unsigned char *signature = NULL; size_t s_len; if (sign_it(client_secret_data, 16, &signature, &s_len, privateKey) != GS_OK) { @@ -424,15 +477,40 @@ int gs_pair(PSERVER_DATA server, char* pin) { if ((ret = http_request(url, data)) != GS_OK) goto cleanup; + free(result); + result = NULL; + if ((ret = xml_search(data->memory, data->size, "paired", &result)) != GS_OK) + goto cleanup; + + if (strcmp(result, "1") != 0) { + gs_error = "Pairing failed"; + ret = GS_FAILED; + goto cleanup; + } + uuid_generate_random(uuid); uuid_unparse(uuid, uuid_str); sprintf(url, "https://%s:47984/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&phrase=pairchallenge", server->address, unique_id, uuid_str); if ((ret = http_request(url, data)) != GS_OK) goto cleanup; + free(result); + result = NULL; + if ((ret = xml_search(data->memory, data->size, "paired", &result)) != GS_OK) + goto cleanup; + + if (strcmp(result, "1") != 0) { + gs_error = "Pairing failed"; + ret = GS_FAILED; + goto cleanup; + } + server->paired = true; cleanup: + if (result != NULL) + free(result); + http_free_data(data); return ret; @@ -461,6 +539,7 @@ int gs_applist(PSERVER_DATA server, PAPP_LIST *list) { int gs_start_app(PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, bool sops, bool localaudio) { uuid_t uuid; + char* result = NULL; char uuid_str[37]; if (config->height >= 2160 && !server->supports4K) @@ -488,9 +567,22 @@ int gs_start_app(PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, b } else sprintf(url, "https://%s:47984/resume?uniqueid=%s&uuid=%s&rikey=%s&rikeyid=%d", server->address, unique_id, uuid_str, rikey_hex, rikeyid); - int ret = http_request(url, data); - if (ret == GS_OK) + if ((ret = http_request(url, data)) == GS_OK) server->currentGame = appId; + else + goto cleanup; + + if ((ret = xml_search(data->memory, data->size, "cancel", &result)) != GS_OK) + goto cleanup; + + if (atoi(result) == 0) { + ret = GS_FAILED; + goto cleanup; + } + + cleanup: + if (result != NULL) + free(result); http_free_data(data); return ret; @@ -500,6 +592,7 @@ int gs_quit_app(PSERVER_DATA server) { char url[4096]; uuid_t uuid; char uuid_str[37]; + char* result = NULL; PHTTP_DATA data = http_create_data(); if (data == NULL) return GS_OUT_OF_MEMORY; @@ -507,7 +600,20 @@ int gs_quit_app(PSERVER_DATA server) { uuid_generate_random(uuid); uuid_unparse(uuid, uuid_str); sprintf(url, "https://%s:47984/cancel?uniqueid=%s&uuid=%s", server->address, unique_id, uuid_str); - int ret = http_request(url, data); + if ((ret = http_request(url, data)) != GS_OK) + goto cleanup; + + if ((ret = xml_search(data->memory, data->size, "cancel", &result)) != GS_OK) + goto cleanup; + + if (strcmp(result, "0") == 0) { + ret = GS_FAILED; + goto cleanup; + } + + cleanup: + if (result != NULL) + free(result); http_free_data(data); return ret; From 8de4fe1cf31ef32ab7cc1ece00042e92b5c85d15 Mon Sep 17 00:00:00 2001 From: Unknownforce351 Date: Fri, 4 Mar 2016 13:10:31 -0600 Subject: [PATCH 19/42] Initial OMX Support --- CMakeLists.txt | 6 +- src/audio.h | 3 + src/audio/alsa.c | 2 - src/audio/omx.c | 205 +++++++++++++++++++++++++++++++++++++++++++++++ src/config.c | 1 + src/main.c | 2 +- src/platform.c | 4 + 7 files changed, 217 insertions(+), 6 deletions(-) create mode 100644 src/audio/omx.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 8bcf54f..e2b6251 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,9 +105,9 @@ if(BROADCOM_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 ${ILCLIENT_SRC_LIST}) - target_include_directories(moonlight-pi PRIVATE ./third_party/ilclient ${BROADCOM_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR}) - target_link_libraries(moonlight-pi gamestream ${BROADCOM_LIBRARIES}) + add_library(moonlight-pi SHARED ./src/video/pi.c ./src/audio/omx.c ${ILCLIENT_SRC_LIST}) + target_include_directories(moonlight-pi PRIVATE ./third_party/ilclient ${BROADCOM_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR} ${OPUS_INCLUDE_DIRS}) + target_link_libraries(moonlight-pi gamestream ${BROADCOM_LIBRARIES} ${OPUS_LIBRARY}) set_property(TARGET moonlight-pi PROPERTY COMPILE_DEFINITIONS ${BROADCOM_DEFINITIONS}) install(TARGETS moonlight-pi DESTINATION ${CMAKE_INSTALL_LIBDIR}) endif() diff --git a/src/audio.h b/src/audio.h index 8236e2a..de7aece 100644 --- a/src/audio.h +++ b/src/audio.h @@ -31,3 +31,6 @@ extern AUDIO_RENDERER_CALLBACKS audio_callbacks_sdl; extern AUDIO_RENDERER_CALLBACKS audio_callbacks_pulse; bool audio_pulse_init(); #endif +#ifdef HAVE_PI +extern AUDIO_RENDERER_CALLBACKS audio_callbacks_omx; +#endif diff --git a/src/audio/alsa.c b/src/audio/alsa.c index 3999b2c..2815375 100644 --- a/src/audio/alsa.c +++ b/src/audio/alsa.c @@ -28,8 +28,6 @@ #define MAX_CHANNEL_COUNT 6 #define FRAME_SIZE 240 -const char* audio_device = "sysdefault"; - static snd_pcm_t *handle; static OpusMSDecoder* decoder; static short pcmBuffer[FRAME_SIZE * MAX_CHANNEL_COUNT]; diff --git a/src/audio/omx.c b/src/audio/omx.c new file mode 100644 index 0000000..b5ae4bd --- /dev/null +++ b/src/audio/omx.c @@ -0,0 +1,205 @@ +/* + * This file is part of Moonlight Embedded. + * + * Copyright (C) 2015 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 . + */ + +#include "../audio.h" + +#include + +#include +#include "bcm_host.h" +#include "ilclient.h" + +#define MAX_CHANNEL_COUNT 6 +#define FRAME_SIZE 240 + +static OpusMSDecoder* decoder; +ILCLIENT_T* handle; +COMPONENT_T* component; +static OMX_BUFFERHEADERTYPE *buf; +static short pcmBuffer[FRAME_SIZE * MAX_CHANNEL_COUNT]; +static int channelCount; + +static void omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) { + int rc, error; + OMX_ERRORTYPE err; + unsigned char omxMapping[6]; + 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 + * We need copy the mapping locally and swap the channels around. + */ + memcpy(omxMapping, opusConfig->mapping, sizeof(omxMapping)); + if (opusConfig->channelCount > 2) { + omxMapping[2] = opusConfig->mapping[3]; + omxMapping[3] = opusConfig->mapping[2]; + } + + decoder = opus_multistream_decoder_create(opusConfig->sampleRate, + opusConfig->channelCount, + opusConfig->streams, + opusConfig->coupledStreams, + omxMapping, + &rc); + + handle = ilclient_init(); + if (handle == NULL) { + fprintf(stderr, "IL client init failed\n"); + exit(1); + } + + if (ilclient_create_component(handle, &component, componentName, ILCLIENT_DISABLE_ALL_PORTS | ILCLIENT_ENABLE_INPUT_BUFFERS) != 0) { + fprintf(stderr, "Component create failed\n"); + exit(1); + } + + if (ilclient_change_component_state(component, OMX_StateIdle)!= 0) { + fprintf(stderr, "Couldn't change state to Idle\n"); + exit(1); + } + + // must be before we enable buffers + OMX_AUDIO_PARAM_PORTFORMATTYPE audioPortFormat; + memset(&audioPortFormat, 0, sizeof(OMX_AUDIO_PARAM_PORTFORMATTYPE)); + audioPortFormat.nSize = sizeof(OMX_AUDIO_PARAM_PORTFORMATTYPE); + audioPortFormat.nVersion.nVersion = OMX_VERSION; + + audioPortFormat.nPortIndex = 100; + + OMX_GetParameter(ilclient_get_handle(component), OMX_IndexParamAudioPortFormat, &audioPortFormat); + + audioPortFormat.eEncoding = OMX_AUDIO_CodingPCM; + OMX_SetParameter(ilclient_get_handle(component), OMX_IndexParamAudioPortFormat, &audioPortFormat); + + OMX_AUDIO_PARAM_PCMMODETYPE sPCMMode; + + memset(&sPCMMode, 0, sizeof(OMX_AUDIO_PARAM_PCMMODETYPE)); + sPCMMode.nSize = sizeof(OMX_AUDIO_PARAM_PCMMODETYPE); + sPCMMode.nVersion.nVersion = OMX_VERSION; + sPCMMode.nPortIndex = 100; + sPCMMode.nChannels = channelCount; + sPCMMode.eNumData = OMX_NumericalDataSigned; + sPCMMode.eEndian = OMX_EndianLittle; + sPCMMode.nSamplingRate = opusConfig->sampleRate; + sPCMMode.bInterleaved = OMX_TRUE; + sPCMMode.nBitPerSample = 16; + sPCMMode.ePCMMode = OMX_AUDIO_PCMModeLinear; + + switch(channelCount) { + case 1: + sPCMMode.eChannelMapping[0] = OMX_AUDIO_ChannelCF; + break; + case 8: + sPCMMode.eChannelMapping[7] = OMX_AUDIO_ChannelRS; + case 7: + sPCMMode.eChannelMapping[6] = OMX_AUDIO_ChannelLS; + case 6: + sPCMMode.eChannelMapping[5] = OMX_AUDIO_ChannelRR; + case 5: + sPCMMode.eChannelMapping[4] = OMX_AUDIO_ChannelLR; + case 4: + sPCMMode.eChannelMapping[3] = OMX_AUDIO_ChannelLFE; + case 3: + sPCMMode.eChannelMapping[2] = OMX_AUDIO_ChannelCF; + case 2: + sPCMMode.eChannelMapping[1] = OMX_AUDIO_ChannelRF; + sPCMMode.eChannelMapping[0] = OMX_AUDIO_ChannelLF; + break; + } + + err = OMX_SetParameter(ilclient_get_handle(component), OMX_IndexParamAudioPcm, &sPCMMode); + if(err != OMX_ErrorNone){ + fprintf(stderr, "PCM mode unsupported\n"); + return; + } + OMX_CONFIG_BRCMAUDIODESTINATIONTYPE arDest; + + if (audio_device && strlen(audio_device) < sizeof(arDest.sName)) { + memset(&arDest, 0, sizeof(OMX_CONFIG_BRCMAUDIODESTINATIONTYPE)); + arDest.nSize = sizeof(OMX_CONFIG_BRCMAUDIODESTINATIONTYPE); + arDest.nVersion.nVersion = OMX_VERSION; + + strcpy((char *)arDest.sName, audio_device); + + err = OMX_SetParameter(ilclient_get_handle(component), OMX_IndexConfigBrcmAudioDestination, &arDest); + if (err != OMX_ErrorNone) { + fprintf(stderr, "Error on setting audio destination\nomx option must be set to hdmi or local\n"); + exit(1); + } + } + + // input port + ilclient_enable_port_buffers(component, 100, NULL, NULL, NULL); + ilclient_enable_port(component, 100); + + err = ilclient_change_component_state(component, OMX_StateExecuting); + if (err < 0) { + fprintf(stderr, "Couldn't change state to Executing\n"); + exit(1); + } +} + +static void omx_renderer_cleanup() { + if (decoder != NULL) + opus_multistream_decoder_destroy(decoder); + if (handle != NULL) { + if((buf = ilclient_get_input_buffer(component, 100, 1)) == NULL){ + fprintf(stderr, "Can't get audio buffer\n"); + exit(EXIT_FAILURE); + } + + buf->nFilledLen = 0; + buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN | OMX_BUFFERFLAG_EOS; + + if(OMX_EmptyThisBuffer(ILC_GET_HANDLE(component), buf) != OMX_ErrorNone){ + fprintf(stderr, "Can't empty audio buffer\n"); + return; + } + + ilclient_disable_port_buffers(component, 100, NULL, NULL, NULL); + ilclient_change_component_state(component, OMX_StateIdle); + ilclient_change_component_state(component, OMX_StateLoaded); + } +} + +static void omx_renderer_decode_and_play_sample(char* data, int length) { + int decodeLen = opus_multistream_decode(decoder, data, length, pcmBuffer, FRAME_SIZE, 0); + if (decodeLen > 0) { + buf = ilclient_get_input_buffer(component, 100, 1); + buf->nOffset = 0; + buf->nFlags = OMX_BUFFERFLAG_TIME_UNKNOWN; + 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 { + printf("Opus error from decode: %d\n", decodeLen); + } +} + +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, +}; diff --git a/src/config.c b/src/config.c index 6bfef0e..fa65fd8 100644 --- a/src/config.c +++ b/src/config.c @@ -38,6 +38,7 @@ bool inputAdded = false; static bool mapped = true; +const char* audio_device = "sysdefault"; static struct option long_options[] = { {"720", no_argument, NULL, 'a'}, diff --git a/src/main.c b/src/main.c index 6f67ca9..0314496 100644 --- a/src/main.c +++ b/src/main.c @@ -152,7 +152,7 @@ static void help() { printf("\n I/O options\n\n"); printf("\t-mapping \t\tUse as gamepad mapping configuration file (use before -input)\n"); printf("\t-input \t\tUse as input. Can be used multiple times\n"); - printf("\t-audio \t\tUse as ALSA audio output device (default sysdefault)\n"); + printf("\t-audio \t\tUse as audio output device (default sysdefault)\n"); printf("\t-forcehw \t\tTry to use video hardware acceleration\n"); #endif printf("\nUse Ctrl+Alt+Shift+Q to exit streaming session\n\n"); diff --git a/src/platform.c b/src/platform.c index d9f01e2..7c2d44f 100644 --- a/src/platform.c +++ b/src/platform.c @@ -99,6 +99,10 @@ AUDIO_RENDERER_CALLBACKS* platform_get_audio(enum platform system) { case SDL: return &audio_callbacks_sdl; #endif + #ifdef HAVE_PI + case PI: + return (PAUDIO_RENDERER_CALLBACKS) dlsym(RTLD_DEFAULT, "audio_callbacks_omx"); + #endif default: #ifdef HAVE_PULSE if (audio_pulse_init()) From 68e18a20671da36fc3370ad087557f6f51b67604 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Fri, 25 Mar 2016 11:12:20 +0100 Subject: [PATCH 20/42] Default to corect audio device on Raspberry Pi --- docs/README.pod | 2 +- src/audio/alsa.c | 5 ++++- src/audio/omx.c | 5 ++++- src/config.c | 4 ++-- src/main.c | 4 ++-- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/docs/README.pod b/docs/README.pod index 4f94385..16e0a5a 100644 --- a/docs/README.pod +++ b/docs/README.pod @@ -137,7 +137,7 @@ To use a different gamepad mapping then the default the B<-mapping> should be sp =item B<-audio> [I] Use as audio output device. -The default value is 'sysdefault' +The default value is 'sysdefault' for ALSA and 'hdmi' for OMX on the Raspberry Pi. =back diff --git a/src/audio/alsa.c b/src/audio/alsa.c index 2815375..273ce82 100644 --- a/src/audio/alsa.c +++ b/src/audio/alsa.c @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015, 2016 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 @@ -62,6 +62,9 @@ static void alsa_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGU snd_pcm_uframes_t buffer_size = 12 * period_size; unsigned int sampleRate = opusConfig->sampleRate; + if (audio_device == NULL) + audio_device = "sysdefault"; + /* Open PCM device for playback. */ CHECK_RETURN(snd_pcm_open(&handle, audio_device, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK)) diff --git a/src/audio/omx.c b/src/audio/omx.c index b5ae4bd..bf8f7d3 100644 --- a/src/audio/omx.c +++ b/src/audio/omx.c @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015, 2016 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 @@ -131,6 +131,9 @@ static void omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGUR } OMX_CONFIG_BRCMAUDIODESTINATIONTYPE arDest; + if (audio_device == NULL) + audio_device = "hdmi"; + if (audio_device && strlen(audio_device) < sizeof(arDest.sName)) { memset(&arDest, 0, sizeof(OMX_CONFIG_BRCMAUDIODESTINATIONTYPE)); arDest.nSize = sizeof(OMX_CONFIG_BRCMAUDIODESTINATIONTYPE); diff --git a/src/config.c b/src/config.c index fa65fd8..2f660cb 100644 --- a/src/config.c +++ b/src/config.c @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015, 2016 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 @@ -38,7 +38,7 @@ bool inputAdded = false; static bool mapped = true; -const char* audio_device = "sysdefault"; +const char* audio_device = NULL; static struct option long_options[] = { {"720", no_argument, NULL, 'a'}, diff --git a/src/main.c b/src/main.c index 0314496..e1a5213 100644 --- a/src/main.c +++ b/src/main.c @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015, 2016 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 @@ -152,7 +152,7 @@ static void help() { printf("\n I/O options\n\n"); printf("\t-mapping \t\tUse as gamepad mapping configuration file (use before -input)\n"); printf("\t-input \t\tUse as input. Can be used multiple times\n"); - printf("\t-audio \t\tUse as audio output device (default sysdefault)\n"); + printf("\t-audio \t\tUse as audio output device\n"); printf("\t-forcehw \t\tTry to use video hardware acceleration\n"); #endif printf("\nUse Ctrl+Alt+Shift+Q to exit streaming session\n\n"); From e7ea222e4066a56e339908c181b34d8a63a09a7f Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Mon, 28 Mar 2016 13:10:53 +0200 Subject: [PATCH 21/42] Add missing declerations of ret in libgamestream --- libgamestream/client.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libgamestream/client.c b/libgamestream/client.c index d9c1d17..c14a269 100644 --- a/libgamestream/client.c +++ b/libgamestream/client.c @@ -308,6 +308,7 @@ static int sign_it(const char *msg, size_t mlen, unsigned char **sig, size_t *sl } int gs_unpair(PSERVER_DATA server) { + int ret = GS_OK; char url[4096]; uuid_t uuid; char uuid_str[37]; @@ -538,6 +539,7 @@ int gs_applist(PSERVER_DATA server, PAPP_LIST *list) { } int gs_start_app(PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, bool sops, bool localaudio) { + int ret = GS_OK; uuid_t uuid; char* result = NULL; char uuid_str[37]; @@ -589,6 +591,7 @@ int gs_start_app(PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, b } int gs_quit_app(PSERVER_DATA server) { + int ret = GS_OK; char url[4096]; uuid_t uuid; char uuid_str[37]; From d35cc8f64aae1171ff185827aa6fb8058a590943 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Mon, 28 Mar 2016 13:33:48 +0200 Subject: [PATCH 22/42] Update moonlight-common-c --- third_party/moonlight-common-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/moonlight-common-c b/third_party/moonlight-common-c index 3382238..55361df 160000 --- a/third_party/moonlight-common-c +++ b/third_party/moonlight-common-c @@ -1 +1 @@ -Subproject commit 33822382beda78e1a0a4839251eda444bbdc60b3 +Subproject commit 55361df6c84417e6b66ca1568f3629e9869bb73e From 751bfdc6376d1b4cc01b84bd18e3666842ed5a17 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Tue, 29 Mar 2016 11:17:29 +0200 Subject: [PATCH 23/42] Link libenet to moonlight-common instead of libgamestream --- libgamestream/CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libgamestream/CMakeLists.txt b/libgamestream/CMakeLists.txt index 88b2802..5a958cc 100644 --- a/libgamestream/CMakeLists.txt +++ b/libgamestream/CMakeLists.txt @@ -22,8 +22,10 @@ target_link_libraries(gamestream moonlight-common) set_target_properties(gamestream PROPERTIES SOVERSION 0 VERSION ${MOONLIGHT_VERSION}) set_target_properties(moonlight-common PROPERTIES SOVERSION 0 VERSION ${MOONLIGHT_VERSION}) -target_include_directories(gamestream PRIVATE ../third_party/moonlight-common-c ../third_party/h264bitstream ${AVAHI_INCLUDE_DIRS} ${LIBUUID_INCLUDE_DIRS} ${ENET_INCLUDE_DIRS}) -target_link_libraries(gamestream ${CURL_LIBRARIES} ${OPENSSL_LIBRARIES} ${EXPAT_LIBRARIES} ${AVAHI_LIBRARIES} ${LIBUUID_LIBRARIES} ${ENET_LIBRARIES}) +target_include_directories(gamestream PRIVATE ../third_party/moonlight-common-c ../third_party/h264bitstream ${AVAHI_INCLUDE_DIRS} ${LIBUUID_INCLUDE_DIRS}) +target_include_directories(moonlight-common PRIVATE ${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_link_libraries(gamestream ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS}) From 7c4768593c67461f13f871df7b0458c567e5be76 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Tue, 29 Mar 2016 11:39:00 +0200 Subject: [PATCH 24/42] Pairing support for Gen 7 servers --- libgamestream/client.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/libgamestream/client.c b/libgamestream/client.c index c14a269..e4eb2e7 100644 --- a/libgamestream/client.c +++ b/libgamestream/client.c @@ -366,11 +366,17 @@ int gs_pair(PSERVER_DATA server, char* pin) { } unsigned char salt_pin[20]; - unsigned char aes_key_hash[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); - SHA1(salt_pin, 20, aes_key_hash); + + int hash_length = server->serverMajorVersion >= 7 ? 32 : 20; + if (server->serverMajorVersion >= 7) + SHA256(salt_pin, 20, aes_key_hash); + 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); @@ -422,10 +428,13 @@ int gs_pair(PSERVER_DATA server, char* pin) { char challenge_response_hash[32]; char challenge_response_hash_enc[32]; char challenge_response_hex[65]; - memcpy(challenge_response, challenge_response_data + 20, 16); + memcpy(challenge_response, challenge_response_data + hash_length, 16); memcpy(challenge_response + 16, cert->signature->data, 256); memcpy(challenge_response + 16 + 256, client_secret_data, 16); - SHA1(challenge_response, 16 + 256 + 16, challenge_response_hash); + if (server->serverMajorVersion >= 7) + SHA256(challenge_response, 16 + 256 + 16, challenge_response_hash); + else + SHA1(challenge_response, 16 + 256 + 16, challenge_response_hash); for (int i = 0; i < 32; i += 16) { AES_encrypt(&challenge_response_hash[i], &challenge_response_hash_enc[i], &enc_key); From d55d47dbec685073e96ce4c1e4f972f9879d728c Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Wed, 30 Mar 2016 19:45:40 +0200 Subject: [PATCH 25/42] Update moonlight-common-c --- third_party/moonlight-common-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/moonlight-common-c b/third_party/moonlight-common-c index 55361df..73000b7 160000 --- a/third_party/moonlight-common-c +++ b/third_party/moonlight-common-c @@ -1 +1 @@ -Subproject commit 55361df6c84417e6b66ca1568f3629e9869bb73e +Subproject commit 73000b7fc200f96dd572b389265175947ab4e4ad From c6c4e5102c138810d7c7fc1c8ccfeb2fba8edef9 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Fri, 1 Apr 2016 00:31:32 -0400 Subject: [PATCH 26/42] Fix build with new moonlight-common-c source layout --- CMakeLists.txt | 2 +- libgamestream/CMakeLists.txt | 5 ++--- libgamestream/client.c | 2 +- libgamestream/client.h | 2 +- libgamestream/sps.h | 2 +- src/audio.h | 2 +- src/config.h | 2 +- src/connection.h | 2 +- src/input/cec.c | 2 +- src/input/evdev.c | 2 +- src/input/sdlinput.c | 2 +- src/main.c | 2 +- src/platform.h | 2 +- src/sdl.c | 2 +- src/video/aml.c | 2 +- src/video/fake.c | 2 +- src/video/ffmpeg.c | 2 +- src/video/imx.c | 2 +- src/video/pi.c | 2 +- src/video/sdl.c | 2 +- 20 files changed, 21 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e2b6251..9e8013e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,7 +41,7 @@ else() set(SOFTWARE_FOUND FALSE) endif() -SET(MOONLIGHT_COMMON_INCLUDE_DIR ./third_party/moonlight-common-c) +SET(MOONLIGHT_COMMON_INCLUDE_DIR ./third_party/moonlight-common-c/src) SET(GAMESTREAM_INCLUDE_DIR ./libgamestream) if(CMAKE_BUILD_TYPE MATCHES Debug) diff --git a/libgamestream/CMakeLists.txt b/libgamestream/CMakeLists.txt index 5a958cc..97ded6f 100644 --- a/libgamestream/CMakeLists.txt +++ b/libgamestream/CMakeLists.txt @@ -10,8 +10,7 @@ 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/limelight-common MOONLIGHT_COMMON_SRC_LIST) -aux_source_directory(../third_party/moonlight-common-c/limelight-common/OpenAES MOONLIGHT_COMMON_SRC_LIST) +aux_source_directory(../third_party/moonlight-common-c/src MOONLIGHT_COMMON_SRC_LIST) add_library(moonlight-common SHARED ${MOONLIGHT_COMMON_SRC_LIST}) @@ -22,7 +21,7 @@ target_link_libraries(gamestream moonlight-common) set_target_properties(gamestream PROPERTIES SOVERSION 0 VERSION ${MOONLIGHT_VERSION}) set_target_properties(moonlight-common PROPERTIES SOVERSION 0 VERSION ${MOONLIGHT_VERSION}) -target_include_directories(gamestream PRIVATE ../third_party/moonlight-common-c ../third_party/h264bitstream ${AVAHI_INCLUDE_DIRS} ${LIBUUID_INCLUDE_DIRS}) +target_include_directories(gamestream PRIVATE ../third_party/moonlight-common-c/src ../third_party/h264bitstream ${AVAHI_INCLUDE_DIRS} ${LIBUUID_INCLUDE_DIRS}) target_include_directories(moonlight-common PRIVATE ${ENET_INCLUDE_DIRS}) target_link_libraries(gamestream ${CURL_LIBRARIES} ${OPENSSL_LIBRARIES} ${EXPAT_LIBRARIES} ${AVAHI_LIBRARIES} ${LIBUUID_LIBRARIES}) target_link_libraries(moonlight-common ${ENET_LIBRARIES}) diff --git a/libgamestream/client.c b/libgamestream/client.c index e4eb2e7..820ce86 100644 --- a/libgamestream/client.c +++ b/libgamestream/client.c @@ -23,7 +23,7 @@ #include "client.h" #include "errors.h" -#include "limelight-common/Limelight.h" +#include #include #include diff --git a/libgamestream/client.h b/libgamestream/client.h index 548690a..10b78f6 100644 --- a/libgamestream/client.h +++ b/libgamestream/client.h @@ -21,7 +21,7 @@ #include "xml.h" -#include "limelight-common/Limelight.h" +#include #include diff --git a/libgamestream/sps.h b/libgamestream/sps.h index bd2cf46..9123b6f 100644 --- a/libgamestream/sps.h +++ b/libgamestream/sps.h @@ -17,7 +17,7 @@ * along with Moonlight; if not, see . */ -#include "limelight-common/Limelight.h" +#include #define GS_SPS_BITSTREAM_FIXUP 0x01 #define GS_SPS_BASELINE_HACK 0x02 diff --git a/src/audio.h b/src/audio.h index de7aece..3981212 100644 --- a/src/audio.h +++ b/src/audio.h @@ -19,7 +19,7 @@ #include -#include "limelight-common/Limelight.h" +#include extern const char* audio_device; diff --git a/src/config.h b/src/config.h index 9b88b5c..e2225ab 100644 --- a/src/config.h +++ b/src/config.h @@ -17,7 +17,7 @@ * along with Moonlight; if not, see . */ -#include "limelight-common/Limelight.h" +#include #include diff --git a/src/connection.h b/src/connection.h index e11537c..8b985b6 100644 --- a/src/connection.h +++ b/src/connection.h @@ -17,6 +17,6 @@ * along with Moonlight; if not, see . */ -#include "limelight-common/Limelight.h" +#include extern CONNECTION_LISTENER_CALLBACKS connection_callbacks; diff --git a/src/input/cec.c b/src/input/cec.c index 854a0e2..8c3892e 100644 --- a/src/input/cec.c +++ b/src/input/cec.c @@ -19,7 +19,7 @@ #ifdef HAVE_LIBCEC -#include "limelight-common/Limelight.h" +#include #include diff --git a/src/input/evdev.c b/src/input/evdev.c index 245105f..ea3ea7a 100644 --- a/src/input/evdev.c +++ b/src/input/evdev.c @@ -24,7 +24,7 @@ #include "mapping.h" #include "libevdev/libevdev.h" -#include "limelight-common/Limelight.h" +#include #include #include diff --git a/src/input/sdlinput.c b/src/input/sdlinput.c index da6a6b7..2c95fb1 100644 --- a/src/input/sdlinput.c +++ b/src/input/sdlinput.c @@ -22,7 +22,7 @@ #include "sdlinput.h" #include "../sdl.h" -#include "limelight-common/Limelight.h" +#include #define ACTION_MODIFIERS (MODIFIER_SHIFT|MODIFIER_ALT|MODIFIER_CTRL) #define QUIT_KEY SDLK_q diff --git a/src/main.c b/src/main.c index e1a5213..2463912 100644 --- a/src/main.c +++ b/src/main.c @@ -33,7 +33,7 @@ #include "input/cec.h" #include "input/sdlinput.h" -#include "limelight-common/Limelight.h" +#include #include #include diff --git a/src/platform.h b/src/platform.h index 9a0cf5d..ed0e786 100644 --- a/src/platform.h +++ b/src/platform.h @@ -17,7 +17,7 @@ * along with Moonlight; if not, see . */ -#include "limelight-common/Limelight.h" +#include #include #include diff --git a/src/sdl.c b/src/sdl.c index b48999e..6552748 100644 --- a/src/sdl.c +++ b/src/sdl.c @@ -22,7 +22,7 @@ #include "sdl.h" #include "input/sdlinput.h" -#include "limelight-common/Limelight.h" +#include static bool done; static int fullscreen_flags; diff --git a/src/video/aml.c b/src/video/aml.c index 673747d..2057d2e 100644 --- a/src/video/aml.c +++ b/src/video/aml.c @@ -18,7 +18,7 @@ * along with Moonlight; if not, see . */ -#include "limelight-common/Limelight.h" +#include #include #include diff --git a/src/video/fake.c b/src/video/fake.c index 163fa37..a9e3e3d 100644 --- a/src/video/fake.c +++ b/src/video/fake.c @@ -17,7 +17,7 @@ * along with Moonlight; if not, see . */ -#include "limelight-common/Limelight.h" +#include #include diff --git a/src/video/ffmpeg.c b/src/video/ffmpeg.c index 3027617..7de31e5 100644 --- a/src/video/ffmpeg.c +++ b/src/video/ffmpeg.c @@ -23,7 +23,7 @@ #include "ffmpeg_vdpau.h" #endif -#include "limelight-common/Limelight.h" +#include #include #include diff --git a/src/video/imx.c b/src/video/imx.c index 3d83f32..2cf4daa 100644 --- a/src/video/imx.c +++ b/src/video/imx.c @@ -17,7 +17,7 @@ * along with Moonlight; if not, see . */ -#include "limelight-common/Limelight.h" +#include #include #include diff --git a/src/video/pi.c b/src/video/pi.c index d5b87e9..c433e08 100644 --- a/src/video/pi.c +++ b/src/video/pi.c @@ -30,7 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "sps.h" -#include "limelight-common/Limelight.h" +#include #include #include diff --git a/src/video/sdl.c b/src/video/sdl.c index a988870..583c5ce 100644 --- a/src/video/sdl.c +++ b/src/video/sdl.c @@ -21,7 +21,7 @@ #include "../sdl.h" #include "ffmpeg.h" -#include "limelight-common/Limelight.h" +#include #include #include From 86532497ef510b75592f27d0e9ce1e6bac4538d1 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Fri, 1 Apr 2016 01:30:37 -0400 Subject: [PATCH 27/42] Fix pairing and streaming from Gen 7 servers --- libgamestream/client.c | 148 +++++++++++++++++++++++------------------ 1 file changed, 84 insertions(+), 64 deletions(-) diff --git a/libgamestream/client.c b/libgamestream/client.c index 820ce86..844a450 100644 --- a/libgamestream/client.c +++ b/libgamestream/client.c @@ -166,87 +166,107 @@ static int load_cert(const char* keyDirectory) { } static int load_server_status(PSERVER_DATA server) { - char *pairedText = NULL; - char *currentGameText = NULL; - char *versionText = NULL; - char *stateText = NULL; - char *heightText = NULL; - char *serverCodecModeSupportText = NULL; uuid_t uuid; char uuid_str[37]; - - int ret = GS_INVALID; + + int ret; char url[4096]; - uuid_generate_random(uuid); - uuid_unparse(uuid, uuid_str); - sprintf(url, "https://%s:47984/serverinfo?uniqueid=%s&uuid=%s", server->address, unique_id, uuid_str); + int i; - 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; - } + i = 0; + do { + char *pairedText = NULL; + char *currentGameText = NULL; + char *versionText = NULL; + char *stateText = NULL; + char *heightText = NULL; + char *serverCodecModeSupportText = NULL; - if (xml_search(data->memory, data->size, "currentgame", ¤tGameText) != GS_OK) { - goto cleanup; - } + ret = GS_INVALID; - if (xml_search(data->memory, data->size, "PairStatus", &pairedText) != GS_OK) - goto cleanup; + uuid_generate_random(uuid); + uuid_unparse(uuid, uuid_str); - if (xml_search(data->memory, data->size, "appversion", &versionText) != GS_OK) - goto cleanup; + // 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. + sprintf(url, "%s://%s:%d/serverinfo?uniqueid=%s&uuid=%s", + i == 0 ? "https" : "http", server->address, i == 0 ? 47984 : 47989, unique_id, uuid_str); - if (xml_search(data->memory, data->size, "state", &stateText) != GS_OK) - goto cleanup; + 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_search(data->memory, data->size, "Height", &heightText) != GS_OK) - goto cleanup; + if (xml_search(data->memory, data->size, "currentgame", ¤tGameText) != GS_OK) { + goto cleanup; + } - if (xml_search(data->memory, data->size, "ServerCodecModeSupport", &serverCodecModeSupportText) != GS_OK) - goto cleanup; + if (xml_search(data->memory, data->size, "PairStatus", &pairedText) != 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, "appversion", &versionText) != GS_OK) + goto cleanup; - if (xml_search(data->memory, data->size, "GfeVersion", &server->gfeVersion) != GS_OK) - goto cleanup; + if (xml_search(data->memory, data->size, "state", &stateText) != GS_OK) + goto cleanup; - server->paired = pairedText != NULL && strcmp(pairedText, "1") == 0; - server->currentGame = currentGameText == NULL ? 0 : atoi(currentGameText); - server->supports4K = heightText != NULL && serverCodecModeSupportText != NULL && atoi(heightText) >= 2160; - server->serverMajorVersion = atoi(versionText); - if (strstr(stateText, "_SERVER_AVAILABLE")) { - // 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; + if (xml_search(data->memory, data->size, "Height", &heightText) != GS_OK) + goto cleanup; - cleanup: - if (data != NULL) - http_free_data(data); + if (xml_search(data->memory, data->size, "ServerCodecModeSupport", &serverCodecModeSupportText) != GS_OK) + goto cleanup; - if (pairedText != NULL) - free(pairedText); + if (xml_search(data->memory, data->size, "gputype", &server->gpuType) != GS_OK) + goto cleanup; - if (currentGameText != NULL) - free(currentGameText); + if (xml_search(data->memory, data->size, "GfeVersion", &server->gfeVersion) != GS_OK) + goto cleanup; - if (versionText != NULL) - free(versionText); + // These fields are present on all version of GFE that this client supports + if (!strlen(currentGameText) || !strlen(pairedText) || !strlen(versionText) || !strlen(stateText)) + goto cleanup; - if (heightText != NULL) - free(heightText); + server->paired = pairedText != NULL && strcmp(pairedText, "1") == 0; + server->currentGame = currentGameText == NULL ? 0 : atoi(currentGameText); + server->supports4K = heightText != NULL && serverCodecModeSupportText != NULL && atoi(heightText) >= 2160; + server->serverMajorVersion = atoi(versionText); + if (strstr(stateText, "_SERVER_AVAILABLE")) { + // 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; - if (serverCodecModeSupportText != NULL) - free(serverCodecModeSupportText); + cleanup: + if (data != NULL) + http_free_data(data); + + if (pairedText != NULL) + free(pairedText); + + if (currentGameText != NULL) + free(currentGameText); + + if (versionText != NULL) + free(versionText); + + if (heightText != NULL) + free(heightText); + + if (serverCodecModeSupportText != NULL) + free(serverCodecModeSupportText); + + i++; + } while (ret != GS_OK && i < 2); return ret; } @@ -561,7 +581,7 @@ int gs_start_app(PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, b srand(time(NULL)); char url[4096]; - u_int32_t rikeyid = 1; + u_int32_t rikeyid = 0; char rikey_hex[33]; bytes_to_hex(config->remoteInputAesKey, rikey_hex, 16); @@ -583,10 +603,10 @@ int gs_start_app(PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, b else goto cleanup; - if ((ret = xml_search(data->memory, data->size, "cancel", &result)) != GS_OK) + if ((ret = xml_search(data->memory, data->size, "gamesession", &result)) != GS_OK) goto cleanup; - if (atoi(result) == 0) { + if (!strcmp(result, "0")) { ret = GS_FAILED; goto cleanup; } From 6524497d0c99ff51bff2d8f040bac6f942280a3c Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Fri, 1 Apr 2016 11:05:35 +0200 Subject: [PATCH 28/42] Update moonlight-common-c --- third_party/moonlight-common-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/moonlight-common-c b/third_party/moonlight-common-c index 73000b7..7262a97 160000 --- a/third_party/moonlight-common-c +++ b/third_party/moonlight-common-c @@ -1 +1 @@ -Subproject commit 73000b7fc200f96dd572b389265175947ab4e4ad +Subproject commit 7262a9763d7d63034a1ed86dbbc7575c26c9443a From 61812554c58f3798f976f3471d9a3f190d0ce319 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Fri, 1 Apr 2016 13:47:45 +0200 Subject: [PATCH 29/42] Unpair when pairing failed --- libgamestream/client.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/libgamestream/client.c b/libgamestream/client.c index 844a450..1e9f165 100644 --- a/libgamestream/client.c +++ b/libgamestream/client.c @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015-2016 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 @@ -538,6 +538,9 @@ int gs_pair(PSERVER_DATA server, char* pin) { server->paired = true; cleanup: + if (ret != GS_OK) + gs_unpair(server); + if (result != NULL) free(result); From f2ada1fa2066c64ef771f42d61b3f6ff46657cc1 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Fri, 1 Apr 2016 14:31:58 +0200 Subject: [PATCH 30/42] Verify server signature --- libgamestream/client.c | 57 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/libgamestream/client.c b/libgamestream/client.c index 1e9f165..9879a3c 100644 --- a/libgamestream/client.c +++ b/libgamestream/client.c @@ -327,6 +327,34 @@ static int sign_it(const char *msg, size_t mlen, unsigned char **sig, size_t *sl return result; } +static bool verifySignature(const char *data, int dataLength, const char *signature, int signatureLength, const char *cert) { + X509* x509; + 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) { + printf("Invalid certificate\n"); + 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); + printf("%d/%d = %d\n", dataLength, signatureLength, result); + + return result > 0; +} + int gs_unpair(PSERVER_DATA server) { int ret = GS_OK; char url[4096]; @@ -385,6 +413,24 @@ int gs_pair(PSERVER_DATA server, char* pin) { goto cleanup; } + free(result); + result = NULL; + 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'; + printf("%d / %d\n", strlen(result)/2, strlen(plaincert)); + unsigned char salt_pin[20]; unsigned char aes_key_hash[32]; AES_KEY enc_key, dec_key; @@ -485,7 +531,16 @@ int gs_pair(PSERVER_DATA server, char* pin) { goto cleanup; } - //TODO: verify pairingsecret + char pairing_secret[16 + 256]; + for (int count = 0; count < strlen(result); count += 2) { + sscanf(&result[count], "%2hhx", &pairing_secret[count / 2]); + } + + if (!verifySignature(pairing_secret, 16, pairing_secret+16, 256, plaincert)) { + gs_error = "MITM attack detected"; + ret = GS_FAILED; + goto cleanup; + } unsigned char *signature = NULL; size_t s_len; From 3c4cc4af69848cc11851ab27a8f1684a22897e95 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Fri, 1 Apr 2016 18:41:21 +0200 Subject: [PATCH 31/42] Remove debug statements --- libgamestream/client.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/libgamestream/client.c b/libgamestream/client.c index 9879a3c..b99e866 100644 --- a/libgamestream/client.c +++ b/libgamestream/client.c @@ -336,7 +336,6 @@ static bool verifySignature(const char *data, int dataLength, const char *signat BIO_free(bio); if (!x509) { - printf("Invalid certificate\n"); return false; } @@ -350,7 +349,6 @@ static bool verifySignature(const char *data, int dataLength, const char *signat X509_free(x509); EVP_PKEY_free(pubKey); EVP_MD_CTX_destroy(mdctx); - printf("%d/%d = %d\n", dataLength, signatureLength, result); return result > 0; } From 9b98371afc02773dfeef8773a110861ce656d154 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Fri, 1 Apr 2016 18:57:35 +0200 Subject: [PATCH 32/42] Add unpair command --- docs/README.pod | 4 ++++ libgamestream/client.h | 3 ++- src/main.c | 7 +++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/README.pod b/docs/README.pod index 16e0a5a..51d2c4a 100644 --- a/docs/README.pod +++ b/docs/README.pod @@ -19,6 +19,10 @@ Create a mapping file for a gamepad. Pair this computer with the host. +=item B + +Unpair this computer with the host. + =item B Stream game from host to this computer. diff --git a/libgamestream/client.h b/libgamestream/client.h index 10b78f6..dafc461 100644 --- a/libgamestream/client.h +++ b/libgamestream/client.h @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015, 2016 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 @@ -38,5 +38,6 @@ typedef struct _SERVER_DATA { int gs_init(PSERVER_DATA server, const char *keyDirectory); int gs_start_app(PSERVER_DATA server, PSTREAM_CONFIGURATION config, int appId, bool sops, bool localaudio); int gs_applist(PSERVER_DATA server, PAPP_LIST *app_list); +int gs_unpair(PSERVER_DATA server); int gs_pair(PSERVER_DATA server, char* pin); int gs_quit_app(PSERVER_DATA server); diff --git a/src/main.c b/src/main.c index 2463912..ba14d31 100644 --- a/src/main.c +++ b/src/main.c @@ -121,6 +121,7 @@ static void help() { printf("\n Actions\n\n"); printf("\tmap\t\t\tCreate mapping file for gamepad\n"); printf("\tpair\t\t\tPair device with computer\n"); + printf("\tunpair\t\t\tUnpair device with computer\n"); printf("\tstream\t\t\tStream computer to device\n"); printf("\tlist\t\t\tList available games and applications\n"); printf("\tquit\t\t\tQuit the application or game being streamed\n"); @@ -264,6 +265,12 @@ int main(int argc, char* argv[]) { } else { printf("Succesfully paired\n"); } + } else if (strcmp("unpair", config.action) == 0) { + if (gs_unpair(&server) != GS_OK) { + fprintf(stderr, "Failed to unpair to server: %s\n", gs_error); + } else { + printf("Succesfully unpaired\n"); + } } else if (strcmp("quit", config.action) == 0) { pair_check(&server); gs_quit_app(&server); From b30cf20f8c70e7eda1c70e908ef06b4819bacf9a Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Fri, 1 Apr 2016 19:13:16 +0200 Subject: [PATCH 33/42] Check protocol version --- libgamestream/client.c | 10 ++++++++++ libgamestream/client.h | 3 +++ libgamestream/errors.h | 3 ++- src/main.c | 3 +++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/libgamestream/client.c b/libgamestream/client.c index b99e866..937cba3 100644 --- a/libgamestream/client.c +++ b/libgamestream/client.c @@ -268,6 +268,16 @@ static int load_server_status(PSERVER_DATA server) { i++; } while (ret != GS_OK && i < 2); + if (ret == GS_OK) { + if (server->serverMajorVersion > MAX_SUPPORTED_GFE_VERSION) { + gs_error = "Ensure you're running the latest version of Moonlight Embedded or downgrade GeForce Experience and try again"; + ret = GS_UNSUPPORTED_VERSION; + } else if (server->serverMajorVersion < MIN_SUPPORTED_GFE_VERSION) { + gs_error = "Moonlight Embedded requires a newer version of GeForce Experience. Please upgrade GFE on your PC and try again."; + ret = GS_UNSUPPORTED_VERSION; + } + } + return ret; } diff --git a/libgamestream/client.h b/libgamestream/client.h index dafc461..9a8fc91 100644 --- a/libgamestream/client.h +++ b/libgamestream/client.h @@ -25,6 +25,9 @@ #include +#define MIN_SUPPORTED_GFE_VERSION 3 +#define MAX_SUPPORTED_GFE_VERSION 7 + typedef struct _SERVER_DATA { const char* address; char* gpuType; diff --git a/libgamestream/errors.h b/libgamestream/errors.h index 441aea3..99e3187 100644 --- a/libgamestream/errors.h +++ b/libgamestream/errors.h @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015, 2016 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 @@ -26,5 +26,6 @@ #define GS_WRONG_STATE -4 #define GS_IO_ERROR -5 #define GS_NOT_SUPPORTED_4K -6 +#define GS_UNSUPPORTED_VERSION -7 const char* gs_error; diff --git a/src/main.c b/src/main.c index ba14d31..378625b 100644 --- a/src/main.c +++ b/src/main.c @@ -226,6 +226,9 @@ int main(int argc, char* argv[]) { } else if (ret == GS_INVALID) { fprintf(stderr, "Invalid data received from server: %s\n", config.address, gs_error); exit(-1); + } else if (ret == GS_UNSUPPORTED_VERSION) { + fprintf(stderr, "Unsupported version: %s\n", gs_error); + exit(-1); } else if (ret != GS_OK) { fprintf(stderr, "Can't connect to server %s\n", config.address); exit(-1); From 0f5b50dfa721a544fa33a6d2714a9c70a0fb2037 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sun, 3 Apr 2016 02:14:53 -0400 Subject: [PATCH 34/42] Fix integer overflow in deadzone calculation --- src/input/evdev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/input/evdev.c b/src/input/evdev.c index ea3ea7a..9e42a5c 100644 --- a/src/input/evdev.c +++ b/src/input/evdev.c @@ -106,9 +106,9 @@ static short evdev_convert_value(struct input_event *ev, struct input_device *de else if (ev->value < parms->min) return reverse?SHRT_MAX:SHRT_MIN; else if (reverse) - return (parms->max - (ev->valueavg?parms->flat*2:0) - ev->value) * (SHRT_MAX-SHRT_MIN) / (parms->max-parms->min-parms->flat*2) + SHRT_MIN; + return (long long)(parms->max - (ev->valueavg?parms->flat*2:0) - ev->value) * (SHRT_MAX-SHRT_MIN) / (parms->max-parms->min-parms->flat*2) + SHRT_MIN; else - return (ev->value - (ev->value>parms->avg?parms->flat*2:0) - parms->min) * (SHRT_MAX-SHRT_MIN) / (parms->max-parms->min-parms->flat*2) + SHRT_MIN; + return (long long)(ev->value - (ev->value>parms->avg?parms->flat*2:0) - parms->min) * (SHRT_MAX-SHRT_MIN) / (parms->max-parms->min-parms->flat*2) + SHRT_MIN; } static char evdev_convert_value_byte(struct input_event *ev, struct input_device *dev, struct input_abs_parms *parms) { From e12beeaef3618c767f4d50b752882b3f06bbde01 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Fri, 1 Apr 2016 19:17:54 +0200 Subject: [PATCH 35/42] Allow unsupported version to try to run --- src/config.c | 7 ++++++- src/config.h | 3 ++- src/main.c | 6 ++++-- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/config.c b/src/config.c index 2f660cb..d19cf32 100644 --- a/src/config.c +++ b/src/config.c @@ -65,6 +65,7 @@ static struct option long_options[] = { {"fps", required_argument, NULL, 'v'}, {"forcehw", no_argument, NULL, 'w'}, {"forcehevc", no_argument, NULL, 'x'}, + {"unsupported", no_argument, NULL, 'y'}, {0, 0, 0, 0}, }; @@ -205,6 +206,9 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) { case 'x': config->stream.supportsHevc = true; break; + case 'y': + config->unsupported_version = true; + break; case 1: if (config->action == NULL) config->action = value; @@ -296,6 +300,7 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) { config->sops = true; config->localaudio = false; config->fullscreen = true; + config->unsupported_version = false; config->inputsCount = 0; config->mapping = get_path("mappings/default.conf", getenv("XDG_DATA_DIRS")); @@ -313,7 +318,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:x", long_options, &option_index)) != -1) { + while ((c = getopt_long_only(argc, argv, "-abc:d:efg:h:i:j:k:lm:no:p:q:r:stuv:w:xy", long_options, &option_index)) != -1) { parse_argument(c, optarg, config); } } diff --git a/src/config.h b/src/config.h index e2225ab..cb448da 100644 --- a/src/config.h +++ b/src/config.h @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015, 2016 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 @@ -41,6 +41,7 @@ typedef struct _CONFIGURATION { bool localaudio; bool fullscreen; bool forcehw; + bool unsupported_version; struct input_config inputs[MAX_INPUTS]; int inputsCount; } CONFIGURATION, *PCONFIGURATION; diff --git a/src/main.c b/src/main.c index 378625b..166c654 100644 --- a/src/main.c +++ b/src/main.c @@ -227,8 +227,10 @@ int main(int argc, char* argv[]) { fprintf(stderr, "Invalid data received from server: %s\n", config.address, gs_error); exit(-1); } else if (ret == GS_UNSUPPORTED_VERSION) { - fprintf(stderr, "Unsupported version: %s\n", gs_error); - exit(-1); + if (!config.unsupported_version) { + fprintf(stderr, "Unsupported version: %s\n", gs_error); + exit(-1); + } } else if (ret != GS_OK) { fprintf(stderr, "Can't connect to server %s\n", config.address); exit(-1); From be4a13aead94a328791be4fbfdf788b38f20a318 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sat, 2 Apr 2016 16:34:49 +0200 Subject: [PATCH 36/42] Cleanup aml decoder --- src/video/aml.c | 70 ++++++++++++++++++------------------------------- 1 file changed, 26 insertions(+), 44 deletions(-) diff --git a/src/video/aml.c b/src/video/aml.c index 2057d2e..704ab45 100644 --- a/src/video/aml.c +++ b/src/video/aml.c @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015, 2016 Iwan Timmer * Copyright (C) 2016 OtherCrashOverride, Daniel Mehrwald * * Moonlight is free software; you can redistribute it and/or modify @@ -29,88 +29,70 @@ #include static codec_para_t codecParam = { 0 }; -const size_t EXTERNAL_PTS = (1); -const size_t SYNC_OUTSIDE = (2); +static const size_t SYNC_OUTSIDE = (2); -int osd_blank(char *path,int cmd) { +static int osd_blank(char *path,int cmd) { int fd; - char bcmd[16]; + char bcmd[16]; fd = open(path, O_CREAT|O_RDWR | O_TRUNC, 0644); if(fd>=0) { sprintf(bcmd,"%d",cmd); - if (write(fd,bcmd,strlen(bcmd)) < 0) { - printf("osd_blank error during write.\n"); + int ret = write(fd,bcmd,strlen(bcmd)); + if (ret < 0) { + printf("osd_blank error during write: %x\n", ret); } close(fd); return 0; } return -1; -} - -void init_display() { - osd_blank("/sys/class/graphics/fb0/blank",1); - osd_blank("/sys/class/graphics/fb1/blank",0); -} - -void restore_display() { - osd_blank("/sys/class/graphics/fb0/blank",0); - osd_blank("/sys/class/graphics/fb1/blank",0); } void aml_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { - fprintf(stderr, "\nvideoFormat=%d nwidth=%d, height=%d, redrawRate=%d, context=%p, drFlags=%x\n", - videoFormat, width, height, redrawRate, context, drFlags); - - init_display(); + osd_blank("/sys/class/graphics/fb0/blank",1); + osd_blank("/sys/class/graphics/fb1/blank",0); codecParam.stream_type = STREAM_TYPE_ES_VIDEO; codecParam.has_video = 1; codecParam.noblock = 0; - + switch (videoFormat) { - case VIDEO_FORMAT_H264: // 1 + case VIDEO_FORMAT_H264: if (width > 1920 || height > 1080) { codecParam.video_type = VFORMAT_H264_4K2K; - codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_H264_4K2K; ///< video format, such as H264, MPEG2... + codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_H264_4K2K; } else { codecParam.video_type = VFORMAT_H264; - codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_H264; ///< video format, such as H264, MPEG2... + codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_H264; } - - fprintf(stdout, "Decoding H264 video.\n"); - break; - case VIDEO_FORMAT_H265: // 2 - + break; + case VIDEO_FORMAT_H265: codecParam.video_type = VFORMAT_HEVC; - codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_HEVC; ///< video format, such as H264, MPEG2... - - fprintf(stdout, "Decoding HEVC video.\n"); - break; + codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_HEVC; + break; default: - printf("Unsupported video format.\n"); + printf("Video format not supported\n"); exit(1); } - codecParam.am_sysinfo.width = width; //< video source width - codecParam.am_sysinfo.height = height; //< video source height - codecParam.am_sysinfo.rate = (96000 / (redrawRate)); //< video source frame duration - codecParam.am_sysinfo.param = (void *)(EXTERNAL_PTS | SYNC_OUTSIDE); //< other parameters for video decoder - - int api = codec_init(&codecParam); - fprintf(stdout, "codec_init=%x\n", api); + codecParam.am_sysinfo.width = width; + codecParam.am_sysinfo.height = height; + codecParam.am_sysinfo.rate = 96000 / redrawRate; + codecParam.am_sysinfo.param = (void *)(SYNC_OUTSIDE); + int api = codec_init(&codecParam); if (api != 0) { - fprintf(stderr, "codec_init failed.\n"); + fprintf(stderr, "codec_init error: %x\n", api); exit(1); } } void aml_cleanup() { int api = codec_close(&codecParam); - restore_display(); + osd_blank("/sys/class/graphics/fb0/blank",0); + osd_blank("/sys/class/graphics/fb1/blank",0); } int aml_submit_decode_unit(PDECODE_UNIT decodeUnit) { From 52b4c8ad59982586eb931436bede22937ee50136 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sat, 2 Apr 2016 16:55:44 +0200 Subject: [PATCH 37/42] Autouse HEVC on selected platforms --- src/main.c | 1 + src/platform.c | 11 +++++++++-- src/platform.h | 4 +++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/main.c b/src/main.c index 166c654..99783c7 100644 --- a/src/main.c +++ b/src/main.c @@ -181,6 +181,7 @@ int main(int argc, char* argv[]) { fprintf(stderr, "Platform '%s' not found\n", config.platform); exit(-1); } + config.stream.supportsHevc = config.stream.supportsHevc || platform_supports_hevc(system); if (strcmp("map", config.action) == 0) { if (config.address == NULL) { diff --git a/src/platform.c b/src/platform.c index 7c2d44f..d338a1d 100644 --- a/src/platform.c +++ b/src/platform.c @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015, 2016 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 @@ -22,7 +22,6 @@ #include "platform.h" #include "audio.h" -#include #include #include #include @@ -112,3 +111,11 @@ AUDIO_RENDERER_CALLBACKS* platform_get_audio(enum platform system) { } return NULL; } + +bool platform_supports_hevc(enum platform system) { + switch (system) { + case AML: + return true; + } + return false; +} diff --git a/src/platform.h b/src/platform.h index ed0e786..93dab74 100644 --- a/src/platform.h +++ b/src/platform.h @@ -1,7 +1,7 @@ /* * This file is part of Moonlight Embedded. * - * Copyright (C) 2015 Iwan Timmer + * Copyright (C) 2015, 2016 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 @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -30,6 +31,7 @@ enum platform { NONE, SDL, PI, IMX, AML, FAKE }; enum platform platform_check(char*); PDECODER_RENDERER_CALLBACKS platform_get_video(enum platform system); PAUDIO_RENDERER_CALLBACKS platform_get_audio(enum platform system); +bool platform_supports_hevc(enum platform system); #ifdef HAVE_FAKE extern DECODER_RENDERER_CALLBACKS decoder_callbacks_fake; From e84fc50aa1d8bd05f18cf17c65622d88d7919cf0 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 3 Apr 2016 12:38:44 +0200 Subject: [PATCH 38/42] Allow usage of Pulseaudio and ALSA on Raspberry Pi --- src/platform.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/platform.c b/src/platform.c index d338a1d..b03b68c 100644 --- a/src/platform.c +++ b/src/platform.c @@ -100,7 +100,8 @@ AUDIO_RENDERER_CALLBACKS* platform_get_audio(enum platform system) { #endif #ifdef HAVE_PI case PI: - return (PAUDIO_RENDERER_CALLBACKS) dlsym(RTLD_DEFAULT, "audio_callbacks_omx"); + if (audio_device == NULL || strcmp(audio_device, "local") == 0 || strcmp(audio_device, "hdmi") == 0) + return (PAUDIO_RENDERER_CALLBACKS) dlsym(RTLD_DEFAULT, "audio_callbacks_omx"); #endif default: #ifdef HAVE_PULSE From 2484725f44a98968a43bb03e983c0ac2a065ddd6 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 3 Apr 2016 12:39:31 +0200 Subject: [PATCH 39/42] Update moonlight-common-c --- third_party/moonlight-common-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/moonlight-common-c b/third_party/moonlight-common-c index 7262a97..b0f3fd2 160000 --- a/third_party/moonlight-common-c +++ b/third_party/moonlight-common-c @@ -1 +1 @@ -Subproject commit 7262a9763d7d63034a1ed86dbbc7575c26c9443a +Subproject commit b0f3fd2a1169e7d20a4aac5876908f965e98b2ab From 2d278b9370bcc1b747684716795f3e0e2612bb28 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 3 Apr 2016 14:27:01 +0200 Subject: [PATCH 40/42] Update moonlight-common-c --- third_party/moonlight-common-c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/moonlight-common-c b/third_party/moonlight-common-c index b0f3fd2..a6d9ab0 160000 --- a/third_party/moonlight-common-c +++ b/third_party/moonlight-common-c @@ -1 +1 @@ -Subproject commit b0f3fd2a1169e7d20a4aac5876908f965e98b2ab +Subproject commit a6d9ab0664c6e5229674b84f2591be4d04c79239 From d44f06961f264fe821dae9c4edcaac295fcc5d74 Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 3 Apr 2016 14:32:58 +0200 Subject: [PATCH 41/42] Fix unasked usage of VDPAU in SDL --- src/video/sdl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/video/sdl.c b/src/video/sdl.c index 583c5ce..5a9734f 100644 --- a/src/video/sdl.c +++ b/src/video/sdl.c @@ -34,7 +34,7 @@ static char* ffmpeg_buffer; static void sdl_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { int avc_flags = SLICE_THREADING; - if (drFlags && FORCE_HARDWARE_ACCELERATION) + if (drFlags & FORCE_HARDWARE_ACCELERATION) avc_flags |= HARDWARE_ACCELERATION; if (ffmpeg_init(videoFormat, width, height, avc_flags, 2) < 0) { From 9f9b7fe8d1c84ad734b2078b955e35424d016dcc Mon Sep 17 00:00:00 2001 From: Iwan Timmer Date: Sun, 3 Apr 2016 14:33:25 +0200 Subject: [PATCH 42/42] Update to version 2.2.0 --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e8013e..10f9392 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,8 +4,8 @@ SET(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") include(${CMAKE_ROOT}/Modules/GNUInstallDirs.cmake) set(MOONLIGHT_MAJOR_VERSION 2) -set(MOONLIGHT_MINOR_VERSION 1) -set(MOONLIGHT_PATCH_VERSION 4) +set(MOONLIGHT_MINOR_VERSION 2) +set(MOONLIGHT_PATCH_VERSION 0) set(MOONLIGHT_VERSION ${MOONLIGHT_MAJOR_VERSION}.${MOONLIGHT_MINOR_VERSION}.${MOONLIGHT_PATCH_VERSION}) aux_source_directory(./src SRC_LIST)