Merge tag 'v2.2.0' into raspbian/jessie

This commit is contained in:
Iwan Timmer
2016-04-03 14:38:44 +02:00
36 changed files with 888 additions and 181 deletions

View File

@@ -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)
@@ -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)
@@ -40,14 +41,14 @@ 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)
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()
@@ -62,7 +63,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,13 +91,23 @@ if (CEC_FOUND)
target_link_libraries(moonlight ${CEC_LIBRARIES})
endif()
if(AMLOGIC_FOUND)
list(APPEND MOONLIGHT_DEFINITIONS HAVE_AML)
list(APPEND MOONLIGHT_OPTIONS AML)
add_library(moonlight-aml SHARED ./src/video/aml.c ${ILCLIENT_SRC_LIST})
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)
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()

32
CONTRIBUTING.md Normal file
View File

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

11
ISSUE_TEMPLATE.md Normal file
View File

@@ -0,0 +1,11 @@
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?**

3
PULL_REQUEST_TEMPLATE.md Normal file
View File

@@ -0,0 +1,3 @@
**Description**
**Purpose**

View File

@@ -3,19 +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 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 hardware video decoding on Raspberry Pi and i.MX 6 devices
## 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)
@@ -33,6 +26,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
@@ -44,9 +39,10 @@ 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

29
cmake/FindAmlogic.cmake Normal file
View File

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

View File

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

View File

@@ -19,6 +19,10 @@ Create a mapping file for a gamepad.
Pair this computer with the host.
=item B<unpair>
Unpair this computer with the host.
=item B<stream>
Stream game from host to this computer.
@@ -95,6 +99,11 @@ Change the network packetsize to I<PACKETSIZE>.
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.
@@ -132,7 +141,7 @@ To use a different gamepad mapping then the default the B<-mapping> should be sp
=item B<-audio> [I<DEVICE>]
Use <DEVICE> 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

View File

@@ -5,12 +5,12 @@ 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)
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})
@@ -21,8 +21,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})
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})
target_link_libraries(gamestream ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS})

View File

@@ -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
@@ -23,7 +23,7 @@
#include "client.h"
#include "errors.h"
#include "limelight-common/Limelight.h"
#include <Limelight.h>
#include <sys/stat.h>
#include <stdbool.h>
@@ -166,85 +166,117 @@ 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;
i = 0;
do {
char *pairedText = NULL;
char *currentGameText = NULL;
char *versionText = NULL;
char *stateText = NULL;
char *heightText = NULL;
char *serverCodecModeSupportText = NULL;
ret = GS_INVALID;
uuid_generate_random(uuid);
uuid_unparse(uuid, uuid_str);
// Modern GFE versions don't allow serverinfo to be fetched over HTTPS if the client
// is not already paired. Since we can't pair without knowing the server version, we
// make another request over HTTP if the HTTPS request fails. We can't just use HTTP
// for everything because it doesn't accurately tell us if we're paired.
sprintf(url, "%s://%s:%d/serverinfo?uniqueid=%s&uuid=%s",
i == 0 ? "https" : "http", server->address, i == 0 ? 47984 : 47989, unique_id, uuid_str);
PHTTP_DATA data = http_create_data();
if (data == NULL) {
ret = GS_OUT_OF_MEMORY;
goto cleanup;
}
if (http_request(url, data) != GS_OK) {
ret = GS_IO_ERROR;
goto cleanup;
}
if (xml_search(data->memory, data->size, "currentgame", &currentGameText) != GS_OK) {
goto cleanup;
}
if (xml_search(data->memory, data->size, "PairStatus", &pairedText) != GS_OK)
goto cleanup;
if (xml_search(data->memory, data->size, "appversion", &versionText) != GS_OK)
goto cleanup;
if (xml_search(data->memory, data->size, "state", &stateText) != GS_OK)
goto cleanup;
if (xml_search(data->memory, data->size, "Height", &heightText) != GS_OK)
goto cleanup;
if (xml_search(data->memory, data->size, "ServerCodecModeSupport", &serverCodecModeSupportText) != GS_OK)
goto cleanup;
if (xml_search(data->memory, data->size, "gputype", &server->gpuType) != GS_OK)
goto cleanup;
if (xml_search(data->memory, data->size, "GfeVersion", &server->gfeVersion) != GS_OK)
goto cleanup;
// These fields are present on all version of GFE that this client supports
if (!strlen(currentGameText) || !strlen(pairedText) || !strlen(versionText) || !strlen(stateText))
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;
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);
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;
}
}
if (http_request(url, data) != GS_OK) {
ret = GS_IO_ERROR;
goto cleanup;
}
if (xml_search(data->memory, data->size, "currentgame", &currentGameText) != GS_OK) {
goto cleanup;
}
if (xml_search(data->memory, data->size, "PairStatus", &pairedText) != GS_OK)
goto cleanup;
if (xml_search(data->memory, data->size, "appversion", &versionText) != GS_OK)
goto cleanup;
if (xml_search(data->memory, data->size, "state", &stateText) != GS_OK)
goto cleanup;
if (xml_search(data->memory, data->size, "Height", &heightText) != GS_OK)
goto cleanup;
if (xml_search(data->memory, data->size, "ServerCodecModeSupport", &serverCodecModeSupportText) != 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;
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
// has ended. We emulate the old behavior by forcing it to zero
// if streaming is not active.
server->currentGame = 0;
}
ret = GS_OK;
cleanup:
if (data != NULL)
http_free_data(data);
if (pairedText != NULL)
free(pairedText);
if (currentGameText != NULL)
free(currentGameText);
if (versionText != NULL)
free(versionText);
if (heightText != NULL)
free(heightText);
if (serverCodecModeSupportText != NULL)
free(serverCodecModeSupportText);
return ret;
}
@@ -305,8 +337,53 @@ 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) {
return false;
}
EVP_PKEY* pubKey = X509_get_pubkey(x509);
EVP_MD_CTX *mdctx = NULL;
mdctx = EVP_MD_CTX_create();
EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, pubKey);
EVP_DigestVerifyUpdate(mdctx, data, dataLength);
int result = EVP_DigestVerifyFinal(mdctx, signature, signatureLength);
X509_free(x509);
EVP_PKEY_free(pubKey);
EVP_MD_CTX_destroy(mdctx);
return result > 0;
}
int gs_unpair(PSERVER_DATA server) {
int ret = GS_OK;
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];
@@ -335,12 +412,45 @@ 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;
}
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[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);
@@ -357,7 +467,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;
@@ -368,7 +490,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);
@@ -381,10 +502,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);
@@ -397,11 +521,35 @@ 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;
}
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;
if (sign_it(client_secret_data, 16, &signature, &s_len, privateKey) != GS_OK) {
@@ -422,15 +570,43 @@ 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 (ret != GS_OK)
gs_unpair(server);
if (result != NULL)
free(result);
http_free_data(data);
return ret;
@@ -458,7 +634,9 @@ 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];
if (config->height >= 2160 && !server->supports4K)
@@ -469,7 +647,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);
@@ -486,18 +664,33 @@ 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, "gamesession", &result)) != GS_OK)
goto cleanup;
if (!strcmp(result, "0")) {
ret = GS_FAILED;
goto cleanup;
}
cleanup:
if (result != NULL)
free(result);
http_free_data(data);
return ret;
}
int gs_quit_app(PSERVER_DATA server) {
int ret = GS_OK;
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;
@@ -505,7 +698,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;

View File

@@ -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
@@ -21,12 +21,17 @@
#include "xml.h"
#include "limelight-common/Limelight.h"
#include <Limelight.h>
#include <stdbool.h>
#define MIN_SUPPORTED_GFE_VERSION 3
#define MAX_SUPPORTED_GFE_VERSION 7
typedef struct _SERVER_DATA {
const char* address;
char* gpuType;
char* gfeVersion;
bool paired;
bool supports4K;
int currentGame;
@@ -36,5 +41,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);

View File

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

View File

@@ -17,7 +17,7 @@
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
*/
#include "limelight-common/Limelight.h"
#include <Limelight.h>
#define GS_SPS_BITSTREAM_FIXUP 0x01
#define GS_SPS_BASELINE_HACK 0x02

View File

@@ -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
@@ -42,6 +45,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

View File

@@ -19,7 +19,7 @@
#include <stdbool.h>
#include "limelight-common/Limelight.h"
#include <Limelight.h>
extern const char* audio_device;
@@ -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

View File

@@ -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
@@ -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];
@@ -64,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))

208
src/audio/omx.c Normal file
View File

@@ -0,0 +1,208 @@
/*
* This file is part of Moonlight Embedded.
*
* 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
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Moonlight is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
*/
#include "../audio.h"
#include <stdio.h>
#include <opus_multistream.h>
#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 == 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);
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,
};

View File

@@ -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,6 +38,7 @@
bool inputAdded = false;
static bool mapped = true;
const char* audio_device = NULL;
static struct option long_options[] = {
{"720", no_argument, NULL, 'a'},
@@ -63,6 +64,8 @@ static struct option long_options[] = {
{"surround", no_argument, NULL, 'u'},
{"fps", required_argument, NULL, 'v'},
{"forcehw", no_argument, NULL, 'w'},
{"forcehevc", no_argument, NULL, 'x'},
{"unsupported", no_argument, NULL, 'y'},
{0, 0, 0, 0},
};
@@ -199,6 +202,13 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) {
break;
case 'w':
config->forcehw = true;
break;
case 'x':
config->stream.supportsHevc = true;
break;
case 'y':
config->unsupported_version = true;
break;
case 1:
if (config->action == NULL)
config->action = value;
@@ -280,6 +290,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";
@@ -289,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"));
@@ -306,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", 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);
}
}

View File

@@ -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
@@ -17,7 +17,7 @@
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
*/
#include "limelight-common/Limelight.h"
#include <Limelight.h>
#include <stdbool.h>
@@ -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;

View File

@@ -17,6 +17,6 @@
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
*/
#include "limelight-common/Limelight.h"
#include <Limelight.h>
extern CONNECTION_LISTENER_CALLBACKS connection_callbacks;

View File

@@ -19,7 +19,7 @@
#ifdef HAVE_LIBCEC
#include "limelight-common/Limelight.h"
#include <Limelight.h>
#include <ceccloader.h>

View File

@@ -24,7 +24,7 @@
#include "mapping.h"
#include "libevdev/libevdev.h"
#include "limelight-common/Limelight.h"
#include <Limelight.h>
#include <libudev.h>
#include <stdio.h>
@@ -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->value<parms->avg?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->value<parms->avg?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) {

View File

@@ -22,7 +22,7 @@
#include "sdlinput.h"
#include "../sdl.h"
#include "limelight-common/Limelight.h"
#include <Limelight.h>
#define ACTION_MODIFIERS (MODIFIER_SHIFT|MODIFIER_ALT|MODIFIER_CTRL)
#define QUIT_KEY SDLK_q

View File

@@ -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
@@ -33,7 +33,7 @@
#include "input/cec.h"
#include "input/sdlinput.h"
#include "limelight-common/Limelight.h"
#include <Limelight.h>
#include <stdio.h>
#include <stdlib.h>
@@ -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");
@@ -137,6 +138,7 @@ static void help() {
printf("\t-60fps\t\t\tUse 60fps [default]\n");
printf("\t-bitrate <bitrate>\tSpecify the bitrate in Kbps\n");
printf("\t-packetsize <size>\tSpecify the maximum packetsize in bytes\n");
printf("\t-forcehevc\t\tUse high efficiency video decoding (HEVC)\n");
printf("\t-remote\t\t\tEnable remote optimizations\n");
printf("\t-app <app>\t\tName of app to stream\n");
printf("\t-nosops\t\t\tDon't allow GFE to modify game settings\n");
@@ -151,7 +153,7 @@ static void help() {
printf("\n I/O options\n\n");
printf("\t-mapping <file>\t\tUse <file> as gamepad mapping configuration file (use before -input)\n");
printf("\t-input <device>\t\tUse <device> as input. Can be used multiple times\n");
printf("\t-audio <device>\t\tUse <device> as ALSA audio output device (default sysdefault)\n");
printf("\t-audio <device>\t\tUse <device> 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");
@@ -179,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) {
@@ -200,6 +203,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");
@@ -223,11 +227,18 @@ 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) {
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);
}
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);
@@ -260,6 +271,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);

View File

@@ -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,9 +22,9 @@
#include "platform.h"
#include "audio.h"
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dlfcn.h>
typedef bool(*ImxInit)();
@@ -48,6 +48,13 @@ 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;
@@ -73,6 +80,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 (PDECODER_RENDERER_CALLBACKS) dlsym(RTLD_DEFAULT, "decoder_callbacks_aml");
#endif
#ifdef HAVE_FAKE
case FAKE:
return &decoder_callbacks_fake;
@@ -87,6 +98,11 @@ AUDIO_RENDERER_CALLBACKS* platform_get_audio(enum platform system) {
case SDL:
return &audio_callbacks_sdl;
#endif
#ifdef HAVE_PI
case PI:
if (audio_device == NULL || strcmp(audio_device, "local") == 0 || strcmp(audio_device, "hdmi") == 0)
return (PAUDIO_RENDERER_CALLBACKS) dlsym(RTLD_DEFAULT, "audio_callbacks_omx");
#endif
default:
#ifdef HAVE_PULSE
if (audio_pulse_init())
@@ -96,3 +112,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;
}

View File

@@ -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
@@ -17,19 +17,21 @@
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
*/
#include "limelight-common/Limelight.h"
#include <Limelight.h>
#include <dlfcn.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#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);
bool platform_supports_hevc(enum platform system);
#ifdef HAVE_FAKE
extern DECODER_RENDERER_CALLBACKS decoder_callbacks_fake;

View File

@@ -22,7 +22,7 @@
#include "sdl.h"
#include "input/sdlinput.h"
#include "limelight-common/Limelight.h"
#include <Limelight.h>
static bool done;
static int fullscreen_flags;

120
src/video/aml.c Normal file
View File

@@ -0,0 +1,120 @@
/*
* This file is part of Moonlight Embedded.
*
* Copyright (C) 2015, 2016 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 <http://www.gnu.org/licenses/>.
*/
#include <Limelight.h>
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <amcodec/codec.h>
static codec_para_t codecParam = { 0 };
static const size_t SYNC_OUTSIDE = (2);
static 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);
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 aml_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
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:
if (width > 1920 || height > 1080) {
codecParam.video_type = VFORMAT_H264_4K2K;
codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_H264_4K2K;
} else {
codecParam.video_type = VFORMAT_H264;
codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_H264;
}
break;
case VIDEO_FORMAT_H265:
codecParam.video_type = VFORMAT_HEVC;
codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_HEVC;
break;
default:
printf("Video format not supported\n");
exit(1);
}
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 error: %x\n", api);
exit(1);
}
}
void aml_cleanup() {
int api = codec_close(&codecParam);
osd_blank("/sys/class/graphics/fb0/blank",0);
osd_blank("/sys/class/graphics/fb1/blank",0);
}
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),
};

View File

@@ -17,14 +17,14 @@
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
*/
#include "limelight-common/Limelight.h"
#include <Limelight.h>
#include <stdio.h>
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");
}

View File

@@ -23,6 +23,8 @@
#include "ffmpeg_vdpau.h"
#endif
#include <Limelight.h>
#include <stdlib.h>
#include <libswscale/swscale.h>
#include <pthread.h>
@@ -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;
@@ -89,7 +106,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) {

View File

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

View File

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

View File

@@ -17,7 +17,7 @@
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
*/
#include "limelight-common/Limelight.h"
#include <Limelight.h>
#include <stdlib.h>
#include <stdio.h>
@@ -83,7 +83,12 @@ 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) {
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;

View File

@@ -30,7 +30,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "sps.h"
#include "limelight-common/Limelight.h"
#include <Limelight.h>
#include <stdio.h>
#include <stdlib.h>
@@ -53,7 +53,12 @@ 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) {
if (videoFormat != VIDEO_FORMAT_H264) {
fprintf(stderr, "Video format not supported\n");
exit(1);
}
bcm_host_init();
gs_sps_init(width, height);

View File

@@ -21,7 +21,7 @@
#include "../sdl.h"
#include "ffmpeg.h"
#include "limelight-common/Limelight.h"
#include <Limelight.h>
#include <SDL.h>
#include <SDL_thread.h>
@@ -32,9 +32,12 @@
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 (ffmpeg_init(width, height, avc_flags, 2) < 0) {
if (drFlags & FORCE_HARDWARE_ACCELERATION)
avc_flags |= HARDWARE_ACCELERATION;
if (ffmpeg_init(videoFormat, width, height, avc_flags, 2) < 0) {
fprintf(stderr, "Couldn't initialize video decoding\n");
exit(1);
}