mirror of
https://github.com/moonlight-stream/moonlight-embedded.git
synced 2025-07-01 15:25:35 +00:00
Compare commits
36 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a6bf7154a7 | ||
|
274d3db34d | ||
|
014af67397 | ||
|
eb4a202358 | ||
|
bac5360494 | ||
|
0e12282311 | ||
|
883498d642 | ||
|
55b49221d8 | ||
|
454f67b0ed | ||
|
2f03600bae | ||
|
fc904d2dac | ||
|
d6c9650a32 | ||
|
a9d6f17d5e | ||
|
ca4c517510 | ||
|
3b1b2ab51d | ||
|
b533c52edd | ||
|
d89bd8d20f | ||
|
8125d2194b | ||
|
8fb2c72ca1 | ||
|
9255f774f2 | ||
|
d3d79c3224 | ||
|
b08a04c378 | ||
|
5bd2799209 | ||
|
0712e05aab | ||
|
02cddf762b | ||
|
810ef140cb | ||
|
50a7454bc1 | ||
|
a610eddd97 | ||
|
b3fb22d427 | ||
|
3840b1409e | ||
|
3b72f5195b | ||
|
000d9da4a0 | ||
|
2aba7a10e7 | ||
|
0131274dce | ||
|
dc03235a62 | ||
|
c007eabf50 |
@ -1,10 +1,12 @@
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
project(moonlight-embedded VERSION 2.6.1 LANGUAGES C)
|
||||
cmake_minimum_required(VERSION 3.6)
|
||||
project(moonlight-embedded VERSION 2.7.0 LANGUAGES C)
|
||||
SET(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
|
||||
SET(CMAKE_C_STANDARD 99)
|
||||
include(${CMAKE_ROOT}/Modules/GNUInstallDirs.cmake)
|
||||
include(${CMAKE_SOURCE_DIR}/cmake/generate_version_header.cmake)
|
||||
|
||||
include(CheckCSourceCompiles)
|
||||
|
||||
add_compile_options(-Wall -Wextra -Wno-unused-parameter -Wno-pointer-sign -Wno-sign-compare -Wno-switch)
|
||||
|
||||
aux_source_directory(./src SRC_LIST)
|
||||
@ -87,6 +89,22 @@ add_executable(moonlight ${SRC_LIST})
|
||||
target_link_libraries(moonlight m)
|
||||
target_link_libraries(moonlight gamestream)
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
|
||||
set(ALSA_FOUND FALSE)
|
||||
target_sources(moonlight PRIVATE ./src/audio/oss.c)
|
||||
endif()
|
||||
|
||||
check_c_source_compiles("#include <sys/auxv.h>
|
||||
int main(void) { return getauxval(AT_HWCAP); }" HAVE_GETAUXVAL)
|
||||
if (HAVE_GETAUXVAL)
|
||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_GETAUXVAL)
|
||||
endif()
|
||||
|
||||
check_c_source_compiles("int main(void) { return __builtin_cpu_supports(\"aes\"); }" HAVE_BICS_AES)
|
||||
if (HAVE_BICS_AES)
|
||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_BICS_AES)
|
||||
endif()
|
||||
|
||||
if (CEC_FOUND)
|
||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_LIBCEC)
|
||||
list(APPEND MOONLIGHT_OPTIONS CEC)
|
||||
@ -98,7 +116,7 @@ endif()
|
||||
if(AMLOGIC_FOUND)
|
||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_AML)
|
||||
list(APPEND MOONLIGHT_OPTIONS AML)
|
||||
add_library(moonlight-aml SHARED ./src/video/aml.c ${ILCLIENT_SRC_LIST})
|
||||
add_library(moonlight-aml SHARED ./src/video/aml.c ./src/util.c ${ILCLIENT_SRC_LIST})
|
||||
target_include_directories(moonlight-aml PRIVATE ${AMLOGIC_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR})
|
||||
target_link_libraries(moonlight-aml gamestream ${AMLOGIC_LIBRARIES})
|
||||
set_property(TARGET moonlight-aml PROPERTY COMPILE_DEFINITIONS ${AMLOGIC_DEFINITIONS})
|
||||
@ -109,7 +127,7 @@ if(BROADCOM-OMX_FOUND)
|
||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_PI)
|
||||
list(APPEND MOONLIGHT_OPTIONS PI)
|
||||
aux_source_directory(./third_party/ilclient ILCLIENT_SRC_LIST)
|
||||
add_library(moonlight-pi SHARED ./src/video/pi.c ./src/audio/omx.c ${ILCLIENT_SRC_LIST})
|
||||
add_library(moonlight-pi SHARED ./src/video/pi.c ./src/audio/omx.c ./src/util.c ${ILCLIENT_SRC_LIST})
|
||||
target_include_directories(moonlight-pi PRIVATE ./third_party/ilclient ${BROADCOM_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR} ${OPUS_INCLUDE_DIRS})
|
||||
target_link_libraries(moonlight-pi gamestream ${BROADCOM_OMX_LIBRARIES} ${OPUS_LIBRARY})
|
||||
set_property(TARGET moonlight-pi PROPERTY COMPILE_DEFINITIONS ${BROADCOM_OMX_DEFINITIONS})
|
||||
@ -119,7 +137,7 @@ endif()
|
||||
if(MMAL_FOUND)
|
||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_MMAL)
|
||||
list(APPEND MOONLIGHT_OPTIONS MMAL)
|
||||
add_library(moonlight-mmal SHARED ./src/video/mmal.c)
|
||||
add_library(moonlight-mmal SHARED ./src/video/mmal.c ./src/util.c)
|
||||
target_include_directories(moonlight-mmal PRIVATE ${MMAL_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR})
|
||||
target_link_libraries(moonlight-mmal gamestream ${MMAL_LINK_LIBRARIES})
|
||||
install(TARGETS moonlight-mmal DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
@ -128,7 +146,7 @@ endif()
|
||||
if(FREESCALE_FOUND)
|
||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_IMX)
|
||||
list(APPEND MOONLIGHT_OPTIONS IMX)
|
||||
add_library(moonlight-imx SHARED ./src/video/imx.c ./src/video/imx_vpu.c)
|
||||
add_library(moonlight-imx SHARED ./src/video/imx.c ./src/video/imx_vpu.c ./src/util.c)
|
||||
target_include_directories(moonlight-imx PRIVATE ${FREESCALE_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR})
|
||||
target_link_libraries(moonlight-imx gamestream ${FREESCALE_LIBRARIES})
|
||||
install(TARGETS moonlight-imx DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
@ -137,7 +155,7 @@ endif()
|
||||
if(ROCKCHIP_FOUND)
|
||||
list(APPEND MOONLIGHT_DEFINITIONS HAVE_ROCKCHIP)
|
||||
list(APPEND MOONLIGHT_OPTIONS ROCKCHIP)
|
||||
add_library(moonlight-rk SHARED ./src/video/rk.c)
|
||||
add_library(moonlight-rk SHARED ./src/video/rk.c ./src/util.c)
|
||||
target_include_directories(moonlight-rk PRIVATE ${ROCKCHIP_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR})
|
||||
target_link_libraries(moonlight-rk gamestream ${ROCKCHIP_LIBRARIES})
|
||||
set_property(TARGET moonlight-rk PROPERTY COMPILE_DEFINITIONS ${ROCKCHIP_DEFINITIONS})
|
||||
|
@ -1,51 +1,77 @@
|
||||
# - Try to find LIBUUID
|
||||
# Find LIBUUID headers, libraries and the answer to all questions.
|
||||
# CMake - Cross Platform Makefile Generator
|
||||
# Copyright 2000-2024 Kitware, Inc. and Contributors
|
||||
# All rights reserved.
|
||||
#
|
||||
# LIBUUID_FOUND True if libuuid got found
|
||||
# LIBUUID_INCLUDE_DIRS Location of libuuid headers
|
||||
# LIBUUID_LIBRARIES List of libraries to use libuuid
|
||||
#
|
||||
# Copyright (c) 2008 Bjoern Ricks <bjoern.ricks@googlemail.com>
|
||||
#
|
||||
# Redistribution and use is allowed according to the terms of the New
|
||||
# BSD license.
|
||||
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
|
||||
# Distributed under the OSI-approved BSD 3-Clause License. See
|
||||
# https://cmake.org/licensing for details.
|
||||
#
|
||||
#[=======================================================================[.rst:
|
||||
FindLibUUID
|
||||
------------
|
||||
|
||||
INCLUDE( FindPkgConfig )
|
||||
Find LibUUID include directory and library.
|
||||
|
||||
IF ( LibUuid_FIND_REQUIRED )
|
||||
SET( _pkgconfig_REQUIRED "REQUIRED" )
|
||||
ELSE( LibUuid_FIND_REQUIRED )
|
||||
SET( _pkgconfig_REQUIRED "" )
|
||||
ENDIF ( LibUuid_FIND_REQUIRED )
|
||||
Imported Targets
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
IF ( LIBUUID_MIN_VERSION )
|
||||
PKG_SEARCH_MODULE( LIBUUID ${_pkgconfig_REQUIRED} uuid>=${LIBUUID_MIN_VERSION} )
|
||||
ELSE ( LIBUUID_MIN_VERSION )
|
||||
PKG_SEARCH_MODULE( LIBUUID ${_pkgconfig_REQUIRED} uuid )
|
||||
ENDIF ( LIBUUID_MIN_VERSION )
|
||||
An :ref:`imported target <Imported targets>` named
|
||||
``LibUUID::LibUUID`` is provided if LibUUID has been found.
|
||||
|
||||
Result Variables
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
IF( NOT LIBUUID_FOUND AND NOT PKG_CONFIG_FOUND )
|
||||
FIND_PATH( LIBUUID_INCLUDE_DIRS uuid/uuid.h )
|
||||
FIND_LIBRARY( LIBUUID_LIBRARIES uuid)
|
||||
This module defines the following variables:
|
||||
|
||||
# Report results
|
||||
IF ( LIBUUID_LIBRARIES AND LIBUUID_INCLUDE_DIRS )
|
||||
SET( LIBUUID_FOUND 1 )
|
||||
IF ( NOT LIBUUID_FIND_QUIETLY )
|
||||
MESSAGE( STATUS "Found libuuid: ${LIBUUID_LIBRARIES}" )
|
||||
ENDIF ( NOT LIBUUID_FIND_QUIETLY )
|
||||
ELSE ( LIBUUID_LIBRARIES AND LIBUUID_INCLUDE_DIRS )
|
||||
IF ( LIBUUID_FIND_REQUIRED )
|
||||
MESSAGE( SEND_ERROR "Could NOT find libuuid" )
|
||||
ELSE ( LIBUUID_FIND_REQUIRED )
|
||||
IF ( NOT LIBUUID_FIND_QUIETLY )
|
||||
MESSAGE( STATUS "Could NOT find libuuid" )
|
||||
ENDIF ( NOT LIBUUID_FIND_QUIETLY )
|
||||
ENDIF ( LIBUUID_FIND_REQUIRED )
|
||||
ENDIF ( LIBUUID_LIBRARIES AND LIBUUID_INCLUDE_DIRS )
|
||||
ENDIF( NOT LIBUUID_FOUND AND NOT PKG_CONFIG_FOUND )
|
||||
``LibUUID_FOUND``
|
||||
True if LibUUID was found, false otherwise.
|
||||
``LibUUID_INCLUDE_DIRS``
|
||||
Include directories needed to include LibUUID headers.
|
||||
``LibUUID_LIBRARIES``
|
||||
Libraries needed to link to LibUUID.
|
||||
|
||||
MARK_AS_ADVANCED( LIBUUID_LIBRARIES LIBUUID_INCLUDE_DIRS )
|
||||
Cache Variables
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
This module uses the following cache variables:
|
||||
|
||||
``LibUUID_LIBRARY``
|
||||
The location of the LibUUID library file.
|
||||
``LibUUID_INCLUDE_DIR``
|
||||
The location of the LibUUID include directory containing ``uuid/uuid.h``.
|
||||
|
||||
The cache variables should not be used by project code.
|
||||
They may be set by end users to point at LibUUID components.
|
||||
#]=======================================================================]
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
find_library(LibUUID_LIBRARY
|
||||
NAMES uuid
|
||||
)
|
||||
mark_as_advanced(LibUUID_LIBRARY)
|
||||
|
||||
find_path(LibUUID_INCLUDE_DIR
|
||||
NAMES uuid/uuid.h
|
||||
)
|
||||
mark_as_advanced(LibUUID_INCLUDE_DIR)
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
include(FindPackageHandleStandardArgs)
|
||||
FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibUUID
|
||||
FOUND_VAR LibUUID_FOUND
|
||||
REQUIRED_VARS LibUUID_LIBRARY LibUUID_INCLUDE_DIR
|
||||
)
|
||||
set(LIBUUID_FOUND ${LibUUID_FOUND})
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
# Provide documented result variables and targets.
|
||||
if(LibUUID_FOUND)
|
||||
set(LibUUID_INCLUDE_DIRS ${LibUUID_INCLUDE_DIR})
|
||||
set(LibUUID_LIBRARIES ${LibUUID_LIBRARY})
|
||||
if(NOT TARGET LibUUID::LibUUID)
|
||||
add_library(LibUUID::LibUUID UNKNOWN IMPORTED)
|
||||
set_target_properties(LibUUID::LibUUID PROPERTIES
|
||||
IMPORTED_LOCATION "${LibUUID_LIBRARY}"
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${LibUUID_INCLUDE_DIRS}"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
@ -23,9 +23,9 @@ target_link_libraries(gamestream moonlight-common)
|
||||
set_target_properties(gamestream PROPERTIES SOVERSION ${SO_VERSION} VERSION ${PROJECT_VERSION})
|
||||
set_target_properties(moonlight-common PROPERTIES SOVERSION ${SO_VERSION} VERSION ${PROJECT_VERSION})
|
||||
|
||||
target_include_directories(gamestream PRIVATE ../third_party/moonlight-common-c/src ../third_party/h264bitstream ${AVAHI_INCLUDE_DIRS} ${LIBUUID_INCLUDE_DIRS})
|
||||
target_include_directories(gamestream PRIVATE ../third_party/moonlight-common-c/src ../third_party/h264bitstream ${AVAHI_INCLUDE_DIRS} ${LibUUID_INCLUDE_DIRS})
|
||||
target_include_directories(moonlight-common PRIVATE ../third_party/moonlight-common-c/reedsolomon ../third_party/moonlight-common-c/enet/include)
|
||||
target_link_libraries(gamestream ${CURL_LIBRARIES} ${OPENSSL_LIBRARIES} ${EXPAT_LIBRARIES} ${AVAHI_LIBRARIES} ${LIBUUID_LIBRARIES})
|
||||
target_link_libraries(gamestream ${CURL_LIBRARIES} ${OPENSSL_LIBRARIES} ${EXPAT_LIBRARIES} ${AVAHI_LIBRARIES} ${LibUUID_LIBRARIES})
|
||||
|
||||
target_link_libraries(gamestream ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS})
|
||||
|
||||
|
@ -48,7 +48,7 @@
|
||||
|
||||
static char unique_id[UNIQUEID_CHARS+1];
|
||||
static X509 *cert;
|
||||
static char cert_hex[4096];
|
||||
static char cert_hex[8192];
|
||||
static EVP_PKEY *privateKey;
|
||||
|
||||
const char* gs_error;
|
||||
@ -56,8 +56,6 @@ const char* gs_error;
|
||||
#define LEN_AS_HEX_STR(x) ((x) * 2 + 1)
|
||||
#define SIZEOF_AS_HEX_STR(x) LEN_AS_HEX_STR(sizeof(x))
|
||||
|
||||
#define SIGNATURE_LEN 256
|
||||
|
||||
#define UUID_STRLEN 37
|
||||
|
||||
static int mkdirtree(const char* directory) {
|
||||
@ -310,6 +308,12 @@ static void bytes_to_hex(unsigned char *in, char *out, size_t len) {
|
||||
out[len * 2] = 0;
|
||||
}
|
||||
|
||||
static void hex_to_bytes(const char *in, unsigned char* out, size_t len) {
|
||||
for (int count = 0; count < len; count += 2) {
|
||||
sscanf(&in[count], "%2hhx", &out[count / 2]);
|
||||
}
|
||||
}
|
||||
|
||||
static int sign_it(const char *msg, size_t mlen, unsigned char **sig, size_t *slen, EVP_PKEY *pkey) {
|
||||
int result = GS_FAILED;
|
||||
|
||||
@ -422,13 +426,21 @@ int gs_unpair(PSERVER_DATA server) {
|
||||
int gs_pair(PSERVER_DATA server, char* pin) {
|
||||
int ret = GS_OK;
|
||||
char* result = NULL;
|
||||
char url[5120];
|
||||
size_t url_max_len = 16384;
|
||||
char* url = malloc(url_max_len);
|
||||
uuid_t uuid;
|
||||
char uuid_str[UUID_STRLEN];
|
||||
char* plaincert = NULL;
|
||||
char* challenge_response = NULL;
|
||||
char* pairing_secret = NULL;
|
||||
char* client_pairing_secret = NULL;
|
||||
char* client_pairing_secret_hex = NULL;
|
||||
PHTTP_DATA data = NULL;
|
||||
|
||||
if (server->paired) {
|
||||
gs_error = "Already paired";
|
||||
return GS_WRONG_STATE;
|
||||
ret = GS_WRONG_STATE;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
unsigned char salt_data[16];
|
||||
@ -438,8 +450,8 @@ int gs_pair(PSERVER_DATA server, char* pin) {
|
||||
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse(uuid, uuid_str);
|
||||
snprintf(url, sizeof(url), "http://%s:%u/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&phrase=getservercert&salt=%s&clientcert=%s", server->serverInfo.address, server->httpPort, unique_id, uuid_str, salt_hex, cert_hex);
|
||||
PHTTP_DATA data = http_create_data();
|
||||
snprintf(url, url_max_len, "http://%s:%u/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&phrase=getservercert&salt=%s&clientcert=%s", server->serverInfo.address, server->httpPort, unique_id, uuid_str, salt_hex, cert_hex);
|
||||
data = http_create_data();
|
||||
if (data == NULL)
|
||||
return GS_OUT_OF_MEMORY;
|
||||
else if ((ret = http_request(url, data)) != GS_OK)
|
||||
@ -461,18 +473,11 @@ int gs_pair(PSERVER_DATA server, char* pin) {
|
||||
if ((ret = xml_search(data->memory, data->size, "plaincert", &result)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
char plaincert[8192];
|
||||
|
||||
if (strlen(result)/2 > sizeof(plaincert) - 1) {
|
||||
gs_error = "Server certificate too big";
|
||||
ret = GS_FAILED;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (int count = 0; count < strlen(result); count += 2) {
|
||||
sscanf(&result[count], "%2hhx", &plaincert[count / 2]);
|
||||
}
|
||||
plaincert[strlen(result)/2] = '\0';
|
||||
size_t plaincertlen = strlen(result)/2;
|
||||
plaincert = malloc(plaincertlen + 1);
|
||||
hex_to_bytes(result, plaincert, plaincertlen*2);
|
||||
plaincert[plaincertlen] = 0;
|
||||
|
||||
unsigned char salt_pin[sizeof(salt_data) + 4];
|
||||
unsigned char aes_key[32]; // Must fit SHA256
|
||||
@ -494,7 +499,7 @@ int gs_pair(PSERVER_DATA server, char* pin) {
|
||||
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse(uuid, uuid_str);
|
||||
snprintf(url, sizeof(url), "http://%s:%u/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&clientchallenge=%s", server->serverInfo.address, server->httpPort, unique_id, uuid_str, challenge_hex);
|
||||
snprintf(url, url_max_len, "http://%s:%u/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&clientchallenge=%s", server->serverInfo.address, server->httpPort, unique_id, uuid_str, challenge_hex);
|
||||
if ((ret = http_request(url, data)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
@ -527,10 +532,7 @@ int gs_pair(PSERVER_DATA server, char* pin) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (int count = 0; count < strlen(result); count += 2) {
|
||||
sscanf(&result[count], "%2hhx", &challenge_response_data_enc[count / 2]);
|
||||
}
|
||||
|
||||
hex_to_bytes(result, challenge_response_data_enc, strlen(result));
|
||||
decrypt(challenge_response_data_enc, sizeof(challenge_response_data_enc), aes_key, challenge_response_data);
|
||||
|
||||
char client_secret_data[16];
|
||||
@ -539,7 +541,7 @@ int gs_pair(PSERVER_DATA server, char* pin) {
|
||||
const ASN1_BIT_STRING *asnSignature;
|
||||
X509_get0_signature(&asnSignature, NULL, cert);
|
||||
|
||||
char challenge_response[16 + SIGNATURE_LEN + sizeof(client_secret_data)];
|
||||
challenge_response = malloc(16 + asnSignature->length + sizeof(client_secret_data));
|
||||
char challenge_response_hash[32];
|
||||
char challenge_response_hash_enc[sizeof(challenge_response_hash)];
|
||||
char challenge_response_hex[SIZEOF_AS_HEX_STR(challenge_response_hash_enc)];
|
||||
@ -556,7 +558,7 @@ int gs_pair(PSERVER_DATA server, char* pin) {
|
||||
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse(uuid, uuid_str);
|
||||
snprintf(url, sizeof(url), "http://%s:%u/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&serverchallengeresp=%s", server->serverInfo.address, server->httpPort, unique_id, uuid_str, challenge_response_hex);
|
||||
snprintf(url, url_max_len, "http://%s:%u/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&serverchallengeresp=%s", server->serverInfo.address, server->httpPort, unique_id, uuid_str, challenge_response_hex);
|
||||
if ((ret = http_request(url, data)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
@ -580,19 +582,15 @@ int gs_pair(PSERVER_DATA server, char* pin) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
char pairing_secret[16 + SIGNATURE_LEN];
|
||||
|
||||
if (strlen(result) / 2 > sizeof(pairing_secret)) {
|
||||
gs_error = "Pairing secret too big";
|
||||
ret = GS_FAILED;
|
||||
size_t pairing_secret_len = strlen(result) / 2;
|
||||
if (pairing_secret_len <= 16) {
|
||||
ret = GS_INVALID;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
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, SIGNATURE_LEN, plaincert)) {
|
||||
pairing_secret = malloc(pairing_secret_len);
|
||||
hex_to_bytes(result, pairing_secret, pairing_secret_len*2);
|
||||
if (!verifySignature(pairing_secret, 16, pairing_secret+16, pairing_secret_len-16, plaincert)) {
|
||||
gs_error = "MITM attack detected";
|
||||
ret = GS_FAILED;
|
||||
goto cleanup;
|
||||
@ -606,15 +604,15 @@ int gs_pair(PSERVER_DATA server, char* pin) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
char client_pairing_secret[sizeof(client_secret_data) + SIGNATURE_LEN];
|
||||
char client_pairing_secret_hex[SIZEOF_AS_HEX_STR(client_pairing_secret)];
|
||||
client_pairing_secret = malloc(sizeof(client_secret_data) + s_len);
|
||||
client_pairing_secret_hex = malloc(LEN_AS_HEX_STR(sizeof(client_secret_data) + s_len));
|
||||
memcpy(client_pairing_secret, client_secret_data, sizeof(client_secret_data));
|
||||
memcpy(client_pairing_secret + sizeof(client_secret_data), signature, SIGNATURE_LEN);
|
||||
bytes_to_hex(client_pairing_secret, client_pairing_secret_hex, sizeof(client_secret_data) + SIGNATURE_LEN);
|
||||
memcpy(client_pairing_secret + sizeof(client_secret_data), signature, s_len);
|
||||
bytes_to_hex(client_pairing_secret, client_pairing_secret_hex, sizeof(client_secret_data) + s_len);
|
||||
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse(uuid, uuid_str);
|
||||
snprintf(url, sizeof(url), "http://%s:%u/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&clientpairingsecret=%s", server->serverInfo.address, server->httpPort, unique_id, uuid_str, client_pairing_secret_hex);
|
||||
snprintf(url, url_max_len, "http://%s:%u/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&clientpairingsecret=%s", server->serverInfo.address, server->httpPort, unique_id, uuid_str, client_pairing_secret_hex);
|
||||
if ((ret = http_request(url, data)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
@ -633,7 +631,7 @@ int gs_pair(PSERVER_DATA server, char* pin) {
|
||||
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse(uuid, uuid_str);
|
||||
snprintf(url, sizeof(url), "https://%s:%u/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&phrase=pairchallenge", server->serverInfo.address, server->httpsPort, unique_id, uuid_str);
|
||||
snprintf(url, url_max_len, "https://%s:%u/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&phrase=pairchallenge", server->serverInfo.address, server->httpsPort, unique_id, uuid_str);
|
||||
if ((ret = http_request(url, data)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
@ -656,8 +654,13 @@ int gs_pair(PSERVER_DATA server, char* pin) {
|
||||
if (ret != GS_OK)
|
||||
gs_unpair(server);
|
||||
|
||||
if (result != NULL)
|
||||
free(result);
|
||||
free(url);
|
||||
free(plaincert);
|
||||
free(challenge_response);
|
||||
free(pairing_secret);
|
||||
free(client_pairing_secret);
|
||||
free(client_pairing_secret_hex);
|
||||
free(result);
|
||||
|
||||
http_free_data(data);
|
||||
|
||||
@ -738,9 +741,10 @@ int gs_start_app(PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, b
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse(uuid, uuid_str);
|
||||
int surround_info = SURROUNDAUDIOINFO_FROM_AUDIO_CONFIGURATION(config->audioConfiguration);
|
||||
snprintf(url, sizeof(url), "https://%s:%u/%s?uniqueid=%s&uuid=%s&appid=%d&mode=%dx%dx%d&additionalStates=1&sops=%d&rikey=%s&rikeyid=%d&localAudioPlayMode=%d&surroundAudioInfo=%d&remoteControllersBitmap=%d&gcmap=%d%s",
|
||||
snprintf(url, sizeof(url), "https://%s:%u/%s?uniqueid=%s&uuid=%s&appid=%d&mode=%dx%dx%d&additionalStates=1&sops=%d&rikey=%s&rikeyid=%d&localAudioPlayMode=%d&surroundAudioInfo=%d&remoteControllersBitmap=%d&gcmap=%d%s%s",
|
||||
server->serverInfo.address, server->httpsPort, server->currentGame ? "resume" : "launch", unique_id, uuid_str, appId, config->width, config->height, fps, sops, rikey_hex, rikeyid, localaudio, surround_info, gamepad_mask, gamepad_mask,
|
||||
(config->supportedVideoFormats & VIDEO_FORMAT_MASK_10BIT) ? "&hdrMode=1&clientHdrCapVersion=0&clientHdrCapSupportedFlagsInUint32=0&clientHdrCapMetaDataId=NV_STATIC_METADATA_TYPE_1&clientHdrCapDisplayData=0x0x0x0x0x0x0x0x0x0x0" : "");
|
||||
(config->supportedVideoFormats & VIDEO_FORMAT_MASK_10BIT) ? "&hdrMode=1&clientHdrCapVersion=0&clientHdrCapSupportedFlagsInUint32=0&clientHdrCapMetaDataId=NV_STATIC_METADATA_TYPE_1&clientHdrCapDisplayData=0x0x0x0x0x0x0x0x0x0x0" : "",
|
||||
LiGetLaunchUrlQueryParameters());
|
||||
if ((ret = http_request(url, data)) == GS_OK)
|
||||
server->currentGame = appId;
|
||||
else
|
||||
|
@ -51,10 +51,10 @@ int http_init(const char* keyDirectory, int logLevel) {
|
||||
return GS_FAILED;
|
||||
|
||||
char certificateFilePath[4096];
|
||||
sprintf(certificateFilePath, "%s/%s", keyDirectory, CERTIFICATE_FILE_NAME);
|
||||
snprintf(certificateFilePath, sizeof(certificateFilePath), "%s/%s", keyDirectory, CERTIFICATE_FILE_NAME);
|
||||
|
||||
char keyFilePath[4096];
|
||||
sprintf(&keyFilePath[0], "%s/%s", keyDirectory, KEY_FILE_NAME);
|
||||
snprintf(keyFilePath, sizeof(keyFilePath), "%s/%s", keyDirectory, KEY_FILE_NAME);
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
|
||||
curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1L);
|
||||
@ -73,6 +73,9 @@ int http_init(const char* keyDirectory, int logLevel) {
|
||||
int http_request(char* url, PHTTP_DATA data) {
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, data);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
#ifdef __FreeBSD__
|
||||
curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1);
|
||||
#endif
|
||||
|
||||
if (debug)
|
||||
printf("Request %s\n", url);
|
||||
|
@ -31,3 +31,6 @@ extern AUDIO_RENDERER_CALLBACKS audio_callbacks_sdl;
|
||||
extern AUDIO_RENDERER_CALLBACKS audio_callbacks_pulse;
|
||||
bool audio_pulse_init(char* audio_device);
|
||||
#endif
|
||||
#ifdef __FreeBSD__
|
||||
extern AUDIO_RENDERER_CALLBACKS audio_callbacks_oss;
|
||||
#endif
|
||||
|
105
src/audio/oss.c
Normal file
105
src/audio/oss.c
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015-2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Moonlight is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#include <sys/soundcard.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include "audio.h"
|
||||
|
||||
#include <opus_multistream.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static OpusMSDecoder* decoder;
|
||||
static short* pcmBuffer;
|
||||
static int samplesPerFrame;
|
||||
static int channelCount;
|
||||
static int fd = -1;
|
||||
|
||||
static int oss_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int arFlags) {
|
||||
int rc;
|
||||
decoder = opus_multistream_decoder_create(opusConfig->sampleRate, opusConfig->channelCount, opusConfig->streams, opusConfig->coupledStreams, opusConfig->mapping, &rc);
|
||||
|
||||
channelCount = opusConfig->channelCount;
|
||||
samplesPerFrame = opusConfig->samplesPerFrame;
|
||||
pcmBuffer = malloc(sizeof(short) * channelCount * samplesPerFrame);
|
||||
if (pcmBuffer == NULL)
|
||||
return -1;
|
||||
|
||||
const char* oss_name = "/dev/dsp";
|
||||
fd = open(oss_name, O_WRONLY);
|
||||
if (fd == -1) {
|
||||
printf("Open audio device /dev/dsp failed! error %d\n", errno);
|
||||
return -1;
|
||||
}
|
||||
// buffer size for fragment ,selector 12 is 4096;11 is 2048;10 is 1024; 13is 8192
|
||||
int frag = 12;
|
||||
if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frag) == -1)
|
||||
printf("Set fragment for /dev/dsp failed.");
|
||||
|
||||
int format = AFMT_S16_LE;
|
||||
int channels = opusConfig->channelCount;
|
||||
int rate = opusConfig->sampleRate;
|
||||
if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) == -1)
|
||||
printf("Set format for /dev/dsp failed.");
|
||||
if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1)
|
||||
printf("Set channels for /dev/dsp failed.");
|
||||
if (ioctl(fd, SNDCTL_DSP_SPEED, &rate) == -1)
|
||||
printf("Set sample rate for /dev/dsp failed.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void oss_renderer_cleanup() {
|
||||
if (decoder != NULL) {
|
||||
opus_multistream_decoder_destroy(decoder);
|
||||
decoder = NULL;
|
||||
}
|
||||
|
||||
if (pcmBuffer != NULL) {
|
||||
free(pcmBuffer);
|
||||
pcmBuffer = NULL;
|
||||
}
|
||||
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static void oss_renderer_decode_and_play_sample(char* data, int length) {
|
||||
int decodeLen = opus_multistream_decode(decoder, data, length, pcmBuffer, samplesPerFrame, 0);
|
||||
if (decodeLen > 0) {
|
||||
write(fd, pcmBuffer, decodeLen * channelCount * sizeof(short));
|
||||
} else if (decodeLen < 0) {
|
||||
printf("Opus error from decode: %d\n", decodeLen);
|
||||
}
|
||||
}
|
||||
|
||||
AUDIO_RENDERER_CALLBACKS audio_callbacks_oss = {
|
||||
.init = oss_renderer_init,
|
||||
.cleanup = oss_renderer_cleanup,
|
||||
.decodeAndPlaySample = oss_renderer_decode_and_play_sample,
|
||||
.capabilities = CAPABILITY_DIRECT_SUBMIT | CAPABILITY_SUPPORTS_ARBITRARY_AUDIO_DURATION,
|
||||
};
|
||||
#endif
|
30
src/config.c
30
src/config.c
@ -20,6 +20,7 @@
|
||||
#include "platform.h"
|
||||
#include "config.h"
|
||||
#include "util.h"
|
||||
#include "cpu.h"
|
||||
|
||||
#include "input/evdev.h"
|
||||
#include "audio/audio.h"
|
||||
@ -348,20 +349,19 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) {
|
||||
config->stream.streamingRemotely = STREAM_CFG_AUTO;
|
||||
config->stream.audioConfiguration = AUDIO_CONFIGURATION_STEREO;
|
||||
config->stream.supportedVideoFormats = SCM_H264;
|
||||
config->stream.encryptionFlags = ENCFLG_AUDIO;
|
||||
|
||||
#ifdef __arm__
|
||||
char cpuinfo[4096] = {};
|
||||
if (read_file("/proc/cpuinfo", cpuinfo, sizeof(cpuinfo) - 1) > 0) {
|
||||
// If this is a ARMv6 CPU (like the Pi 1), we'll assume it's not
|
||||
// powerful enough to handle audio encryption. The Pi 1 could
|
||||
// barely handle Opus decoding alone.
|
||||
if (strstr(cpuinfo, "ARMv6")) {
|
||||
config->stream.encryptionFlags = ENCFLG_NONE;
|
||||
printf("Disabling audio encryption on low performance CPU\n");
|
||||
}
|
||||
// Opt in for video encryption if the CPU has good AES performance
|
||||
if (has_fast_aes()) {
|
||||
config->stream.encryptionFlags = ENCFLG_ALL;
|
||||
}
|
||||
else if (has_slow_aes()) {
|
||||
// For extremely slow CPUs, opt out of audio encryption
|
||||
config->stream.encryptionFlags = ENCFLG_NONE;
|
||||
printf("Disabling encryption on low performance CPU\n");
|
||||
}
|
||||
else {
|
||||
config->stream.encryptionFlags = ENCFLG_AUDIO;
|
||||
}
|
||||
#endif
|
||||
|
||||
config->debug_level = 0;
|
||||
config->platform = "auto";
|
||||
@ -411,11 +411,11 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) {
|
||||
struct passwd *pw = getpwuid(getuid());
|
||||
const char *dir;
|
||||
if ((dir = getenv("XDG_CACHE_DIR")) != NULL)
|
||||
sprintf(config->key_dir, "%s" MOONLIGHT_PATH, dir);
|
||||
snprintf(config->key_dir, sizeof(config->key_dir), "%s" MOONLIGHT_PATH, dir);
|
||||
else if ((dir = getenv("HOME")) != NULL)
|
||||
sprintf(config->key_dir, "%s" DEFAULT_CACHE_DIR MOONLIGHT_PATH, dir);
|
||||
snprintf(config->key_dir, sizeof(config->key_dir), "%s" DEFAULT_CACHE_DIR MOONLIGHT_PATH, dir);
|
||||
else
|
||||
sprintf(config->key_dir, "%s" DEFAULT_CACHE_DIR MOONLIGHT_PATH, pw->pw_dir);
|
||||
snprintf(config->key_dir, sizeof(config->key_dir), "%s" DEFAULT_CACHE_DIR MOONLIGHT_PATH, pw->pw_dir);
|
||||
}
|
||||
|
||||
if (config->stream.bitrate == -1) {
|
||||
|
121
src/cpu.c
Normal file
121
src/cpu.c
Normal file
@ -0,0 +1,121 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Moonlight is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "cpu.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef HAVE_GETAUXVAL
|
||||
#include <sys/auxv.h>
|
||||
|
||||
#ifndef HWCAP2_AES
|
||||
#define HWCAP2_AES (1 << 0)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) && defined(__riscv)
|
||||
#if __has_include(<sys/hwprobe.h>)
|
||||
#include <sys/hwprobe.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
|
||||
#if __has_include(<asm/hwprobe.h>)
|
||||
#include <asm/hwprobe.h>
|
||||
#include <sys/syscall.h>
|
||||
#else
|
||||
#define __NR_riscv_hwprobe 258
|
||||
struct riscv_hwprobe {
|
||||
int64_t key;
|
||||
uint64_t value;
|
||||
};
|
||||
#define RISCV_HWPROBE_KEY_IMA_EXT_0 4
|
||||
#endif
|
||||
|
||||
// RISC-V Scalar AES [E]ncryption and [D]ecryption
|
||||
#ifndef RISCV_HWPROBE_EXT_ZKND
|
||||
#define RISCV_HWPROBE_EXT_ZKND (1 << 11)
|
||||
#define RISCV_HWPROBE_EXT_ZKNE (1 << 12)
|
||||
#endif
|
||||
|
||||
// RISC-V Vector AES
|
||||
#ifndef RISCV_HWPROBE_EXT_ZVKNED
|
||||
#define RISCV_HWPROBE_EXT_ZVKNED (1 << 21)
|
||||
#endif
|
||||
|
||||
static int __riscv_hwprobe(struct riscv_hwprobe *pairs, size_t pair_count,
|
||||
size_t cpu_count, unsigned long *cpus,
|
||||
unsigned int flags)
|
||||
{
|
||||
return syscall(__NR_riscv_hwprobe, pairs, pair_count, cpu_count, cpus, flags);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
bool has_fast_aes() {
|
||||
#if defined(HAVE_GETAUXVAL) && (defined(__arm__) || defined(__aarch64__))
|
||||
#if defined(__arm__) && defined(HWCAP2_AES)
|
||||
return !!(getauxval(AT_HWCAP2) & HWCAP2_AES);
|
||||
#elif defined(__aarch64__)
|
||||
return !!(getauxval(AT_HWCAP) & HWCAP_AES);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
#elif defined(HAVE_BICS_AES)
|
||||
return __builtin_cpu_supports("aes");
|
||||
#elif defined(__BUILTIN_CPU_SUPPORTS__) && defined(__powerpc__)
|
||||
return __builtin_cpu_supports("vcrypto");
|
||||
#elif defined(__linux__) && defined(__riscv)
|
||||
struct riscv_hwprobe pairs[1] = {
|
||||
{ RISCV_HWPROBE_KEY_IMA_EXT_0, 0 },
|
||||
};
|
||||
|
||||
// If this syscall is not implemented, we'll get -ENOSYS
|
||||
// and the value field will remain zero.
|
||||
__riscv_hwprobe(pairs, sizeof(pairs) / sizeof(struct riscv_hwprobe), 0, NULL, 0);
|
||||
|
||||
return (pairs[0].value & (RISCV_HWPROBE_EXT_ZKNE | RISCV_HWPROBE_EXT_ZKND)) ==
|
||||
(RISCV_HWPROBE_EXT_ZKNE | RISCV_HWPROBE_EXT_ZKND) ||
|
||||
(pairs[0].value & RISCV_HWPROBE_EXT_ZVKNED);
|
||||
#elif __SIZEOF_SIZE_T__ == 4
|
||||
#warning Unknown 32-bit platform. Assuming AES is slow on this CPU.
|
||||
return false;
|
||||
#else
|
||||
#warning Unknown 64-bit platform. Assuming AES is fast on this CPU.
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool has_slow_aes() {
|
||||
#ifdef __arm__
|
||||
char cpuinfo[4096] = {};
|
||||
if (read_file("/proc/cpuinfo", cpuinfo, sizeof(cpuinfo) - 1) > 0) {
|
||||
// If this is a ARMv6 CPU (like the Pi 1), we'll assume it's not
|
||||
// powerful enough to handle audio encryption. The Pi 1 could
|
||||
// barely handle Opus decoding alone.
|
||||
if (strstr(cpuinfo, "ARMv6")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
21
src/cpu.h
Normal file
21
src/cpu.h
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Moonlight is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
bool has_fast_aes(void);
|
||||
bool has_slow_aes(void);
|
@ -90,7 +90,7 @@ void cec_init() {
|
||||
g_iface.init_video_standalone(g_iface.connection);
|
||||
|
||||
cec_adapter devices[10];
|
||||
int8_t iDevicesFound = g_iface.find_adapters(g_iface.connection, devices, sizeof(devices) / sizeof(devices), NULL);
|
||||
int8_t iDevicesFound = g_iface.find_adapters(g_iface.connection, devices, sizeof(devices) / sizeof(devices[0]), NULL);
|
||||
|
||||
if (iDevicesFound <= 0) {
|
||||
fprintf(stderr, "No CEC devices found\n");
|
||||
|
@ -38,7 +38,11 @@
|
||||
#include <limits.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#ifdef __linux__
|
||||
#include <endian.h>
|
||||
#else
|
||||
#include <sys/endian.h>
|
||||
#endif
|
||||
#include <math.h>
|
||||
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
@ -66,8 +70,13 @@ struct input_device {
|
||||
int hats_state[3][2];
|
||||
int fd;
|
||||
char modifiers;
|
||||
#ifdef __linux__
|
||||
__s32 mouseDeltaX, mouseDeltaY, mouseVScroll, mouseHScroll;
|
||||
__s32 touchDownX, touchDownY, touchX, touchY;
|
||||
#else
|
||||
int32_t mouseDeltaX, mouseDeltaY, mouseVScroll, mouseHScroll;
|
||||
int32_t touchDownX, touchDownY, touchX, touchY;
|
||||
#endif
|
||||
struct timeval touchDownTime;
|
||||
struct timeval btnDownTime;
|
||||
short controllerId;
|
||||
@ -827,8 +836,10 @@ void evdev_create(const char* device, struct mapping* mappings, bool verbose, in
|
||||
libevdev_has_event_code(evdev, EV_ABS, ABS_RZ))) &&
|
||||
!libevdev_has_event_type(evdev, EV_KEY);
|
||||
bool is_gamepad =
|
||||
libevdev_has_event_code(evdev, EV_ABS, ABS_X) &&
|
||||
libevdev_has_event_code(evdev, EV_ABS, ABS_Y) &&
|
||||
((libevdev_has_event_code(evdev, EV_ABS, ABS_X) &&
|
||||
libevdev_has_event_code(evdev, EV_ABS, ABS_Y)) ||
|
||||
(libevdev_has_event_code(evdev, EV_ABS, ABS_HAT0X) &&
|
||||
libevdev_has_event_code(evdev, EV_ABS, ABS_HAT0Y))) &&
|
||||
(libevdev_has_event_code(evdev, EV_KEY, BTN_TRIGGER) ||
|
||||
libevdev_has_event_code(evdev, EV_KEY, BTN_A) ||
|
||||
libevdev_has_event_code(evdev, EV_KEY, BTN_1) ||
|
||||
@ -1009,9 +1020,9 @@ void evdev_map(char* device) {
|
||||
for (int i = 0; i < 16; i++)
|
||||
buf += sprintf(buf, "%02x", ((unsigned char*) guid)[i]);
|
||||
|
||||
struct mapping map;
|
||||
strncpy(map.name, name, sizeof(map.name));
|
||||
strncpy(map.guid, str_guid, sizeof(map.guid));
|
||||
struct mapping map = {0};
|
||||
strncpy(map.name, name, sizeof(map.name) - 1);
|
||||
strncpy(map.guid, str_guid, sizeof(map.guid) - 1);
|
||||
|
||||
libevdev_free(evdev);
|
||||
close(fd);
|
||||
|
@ -38,8 +38,8 @@ struct mapping* mapping_parse(char* mapping) {
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
strncpy(map->guid, guid, sizeof(map->guid));
|
||||
strncpy(map->name, name, sizeof(map->name));
|
||||
strncpy(map->guid, guid, sizeof(map->guid) - 1);
|
||||
strncpy(map->name, name, sizeof(map->name) - 1);
|
||||
|
||||
/* Initialize all mapping indices to -1 to ensure they won't match anything */
|
||||
memset(&map->abs_leftx, -1, offsetof(struct mapping, next) - offsetof(struct mapping, abs_leftx));
|
||||
@ -58,7 +58,7 @@ struct mapping* mapping_parse(char* mapping) {
|
||||
value++;
|
||||
}
|
||||
if (strcmp("platform", key) == 0)
|
||||
strncpy(map->platform, value, sizeof(map->platform));
|
||||
strncpy(map->platform, value, sizeof(map->platform) - 1);
|
||||
else if (sscanf(value, "b%d", &int_value) == 1) {
|
||||
if (strcmp("a", key) == 0)
|
||||
map->btn_a = int_value;
|
||||
@ -152,6 +152,8 @@ struct mapping* mapping_parse(char* mapping) {
|
||||
map->hat_dpdown = int_value;
|
||||
map->hat_dir_dpdown = direction_value;
|
||||
}
|
||||
} else if (strcmp("crc", key) == 0) {
|
||||
/* CRC is not supported */
|
||||
} else
|
||||
fprintf(stderr, "Can't map (%s)\n", option);
|
||||
} else if (ret == 0 && option[0] != '\n')
|
||||
|
284
src/input/sdl.c
284
src/input/sdl.c
@ -59,11 +59,232 @@ typedef struct _GAMEPAD_STATE {
|
||||
|
||||
static GAMEPAD_STATE gamepads[MAX_GAMEPADS];
|
||||
|
||||
static int keyboard_modifiers;
|
||||
static int activeGamepadMask = 0;
|
||||
|
||||
int sdl_gamepads = 0;
|
||||
|
||||
#define VK_0 0x30
|
||||
#define VK_A 0x41
|
||||
|
||||
// These are real Windows VK_* codes
|
||||
#ifndef VK_F1
|
||||
#define VK_F1 0x70
|
||||
#define VK_F13 0x7C
|
||||
#define VK_NUMPAD0 0x60
|
||||
#endif
|
||||
|
||||
int vk_for_sdl_scancode(SDL_Scancode scancode) {
|
||||
// Set keycode. We explicitly use scancode here because GFE will try to correct
|
||||
// for AZERTY layouts on the host but it depends on receiving VK_ values matching
|
||||
// a QWERTY layout to work.
|
||||
if (scancode >= SDL_SCANCODE_1 && scancode <= SDL_SCANCODE_9) {
|
||||
// SDL defines SDL_SCANCODE_0 > SDL_SCANCODE_9, so we need to handle that manually
|
||||
return (scancode - SDL_SCANCODE_1) + VK_0 + 1;
|
||||
}
|
||||
else if (scancode >= SDL_SCANCODE_A && scancode <= SDL_SCANCODE_Z) {
|
||||
return (scancode - SDL_SCANCODE_A) + VK_A;
|
||||
}
|
||||
else if (scancode >= SDL_SCANCODE_F1 && scancode <= SDL_SCANCODE_F12) {
|
||||
return (scancode - SDL_SCANCODE_F1) + VK_F1;
|
||||
}
|
||||
else if (scancode >= SDL_SCANCODE_F13 && scancode <= SDL_SCANCODE_F24) {
|
||||
return (scancode - SDL_SCANCODE_F13) + VK_F13;
|
||||
}
|
||||
else if (scancode >= SDL_SCANCODE_KP_1 && scancode <= SDL_SCANCODE_KP_9) {
|
||||
// SDL defines SDL_SCANCODE_KP_0 > SDL_SCANCODE_KP_9, so we need to handle that manually
|
||||
return (scancode - SDL_SCANCODE_KP_1) + VK_NUMPAD0 + 1;
|
||||
}
|
||||
else {
|
||||
switch (scancode) {
|
||||
case SDL_SCANCODE_BACKSPACE:
|
||||
return 0x08;
|
||||
|
||||
case SDL_SCANCODE_TAB:
|
||||
return 0x09;
|
||||
|
||||
case SDL_SCANCODE_CLEAR:
|
||||
return 0x0C;
|
||||
|
||||
case SDL_SCANCODE_KP_ENTER:
|
||||
case SDL_SCANCODE_RETURN:
|
||||
return 0x0D;
|
||||
|
||||
case SDL_SCANCODE_PAUSE:
|
||||
return 0x13;
|
||||
|
||||
case SDL_SCANCODE_CAPSLOCK:
|
||||
return 0x14;
|
||||
|
||||
case SDL_SCANCODE_ESCAPE:
|
||||
return 0x1B;
|
||||
|
||||
case SDL_SCANCODE_SPACE:
|
||||
return 0x20;
|
||||
|
||||
case SDL_SCANCODE_PAGEUP:
|
||||
return 0x21;
|
||||
|
||||
case SDL_SCANCODE_PAGEDOWN:
|
||||
return 0x22;
|
||||
|
||||
case SDL_SCANCODE_END:
|
||||
return 0x23;
|
||||
|
||||
case SDL_SCANCODE_HOME:
|
||||
return 0x24;
|
||||
|
||||
case SDL_SCANCODE_LEFT:
|
||||
return 0x25;
|
||||
|
||||
case SDL_SCANCODE_UP:
|
||||
return 0x26;
|
||||
|
||||
case SDL_SCANCODE_RIGHT:
|
||||
return 0x27;
|
||||
|
||||
case SDL_SCANCODE_DOWN:
|
||||
return 0x28;
|
||||
|
||||
case SDL_SCANCODE_SELECT:
|
||||
return 0x29;
|
||||
|
||||
case SDL_SCANCODE_EXECUTE:
|
||||
return 0x2B;
|
||||
|
||||
case SDL_SCANCODE_PRINTSCREEN:
|
||||
return 0x2C;
|
||||
|
||||
case SDL_SCANCODE_INSERT:
|
||||
return 0x2D;
|
||||
|
||||
case SDL_SCANCODE_DELETE:
|
||||
return 0x2E;
|
||||
|
||||
case SDL_SCANCODE_HELP:
|
||||
return 0x2F;
|
||||
|
||||
case SDL_SCANCODE_KP_0:
|
||||
// See comment above about why we only handle SDL_SCANCODE_KP_0 here
|
||||
return VK_NUMPAD0;
|
||||
|
||||
case SDL_SCANCODE_0:
|
||||
// See comment above about why we only handle SDL_SCANCODE_0 here
|
||||
return VK_0;
|
||||
|
||||
case SDL_SCANCODE_KP_MULTIPLY:
|
||||
return 0x6A;
|
||||
|
||||
case SDL_SCANCODE_KP_PLUS:
|
||||
return 0x6B;
|
||||
|
||||
case SDL_SCANCODE_KP_COMMA:
|
||||
return 0x6C;
|
||||
|
||||
case SDL_SCANCODE_KP_MINUS:
|
||||
return 0x6D;
|
||||
|
||||
case SDL_SCANCODE_KP_PERIOD:
|
||||
return 0x6E;
|
||||
|
||||
case SDL_SCANCODE_KP_DIVIDE:
|
||||
return 0x6F;
|
||||
|
||||
case SDL_SCANCODE_NUMLOCKCLEAR:
|
||||
return 0x90;
|
||||
|
||||
case SDL_SCANCODE_SCROLLLOCK:
|
||||
return 0x91;
|
||||
|
||||
case SDL_SCANCODE_LSHIFT:
|
||||
return 0xA0;
|
||||
|
||||
case SDL_SCANCODE_RSHIFT:
|
||||
return 0xA1;
|
||||
|
||||
case SDL_SCANCODE_LCTRL:
|
||||
return 0xA2;
|
||||
|
||||
case SDL_SCANCODE_RCTRL:
|
||||
return 0xA3;
|
||||
|
||||
case SDL_SCANCODE_LALT:
|
||||
return 0xA4;
|
||||
|
||||
case SDL_SCANCODE_RALT:
|
||||
return 0xA5;
|
||||
|
||||
case SDL_SCANCODE_LGUI:
|
||||
return 0x5B;
|
||||
|
||||
case SDL_SCANCODE_RGUI:
|
||||
return 0x5C;
|
||||
|
||||
case SDL_SCANCODE_APPLICATION:
|
||||
return 0x5D;
|
||||
|
||||
case SDL_SCANCODE_AC_BACK:
|
||||
return 0xA6;
|
||||
|
||||
case SDL_SCANCODE_AC_FORWARD:
|
||||
return 0xA7;
|
||||
|
||||
case SDL_SCANCODE_AC_REFRESH:
|
||||
return 0xA8;
|
||||
|
||||
case SDL_SCANCODE_AC_STOP:
|
||||
return 0xA9;
|
||||
|
||||
case SDL_SCANCODE_AC_SEARCH:
|
||||
return 0xAA;
|
||||
|
||||
case SDL_SCANCODE_AC_BOOKMARKS:
|
||||
return 0xAB;
|
||||
|
||||
case SDL_SCANCODE_AC_HOME:
|
||||
return 0xAC;
|
||||
|
||||
case SDL_SCANCODE_SEMICOLON:
|
||||
return 0xBA;
|
||||
|
||||
case SDL_SCANCODE_EQUALS:
|
||||
return 0xBB;
|
||||
|
||||
case SDL_SCANCODE_COMMA:
|
||||
return 0xBC;
|
||||
|
||||
case SDL_SCANCODE_MINUS:
|
||||
return 0xBD;
|
||||
|
||||
case SDL_SCANCODE_PERIOD:
|
||||
return 0xBE;
|
||||
|
||||
case SDL_SCANCODE_SLASH:
|
||||
return 0xBF;
|
||||
|
||||
case SDL_SCANCODE_GRAVE:
|
||||
return 0xC0;
|
||||
|
||||
case SDL_SCANCODE_LEFTBRACKET:
|
||||
return 0xDB;
|
||||
|
||||
case SDL_SCANCODE_BACKSLASH:
|
||||
return 0xDC;
|
||||
|
||||
case SDL_SCANCODE_RIGHTBRACKET:
|
||||
return 0xDD;
|
||||
|
||||
case SDL_SCANCODE_APOSTROPHE:
|
||||
return 0xDE;
|
||||
|
||||
case SDL_SCANCODE_NONUSBACKSLASH:
|
||||
return 0xE2;
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void send_controller_arrival(PGAMEPAD_STATE state) {
|
||||
#if SDL_VERSION_ATLEAST(2, 0, 18)
|
||||
unsigned int supportedButtonFlags = 0;
|
||||
@ -274,57 +495,30 @@ int sdlinput_handle_event(SDL_Window* window, SDL_Event* event) {
|
||||
return 0;
|
||||
case SDL_KEYDOWN:
|
||||
case SDL_KEYUP:
|
||||
button = event->key.keysym.sym;
|
||||
if (button >= 0x21 && button <= 0x2f)
|
||||
button = keyCodes1[button - 0x21];
|
||||
else if (button >= 0x3a && button <= 0x40)
|
||||
button = keyCodes2[button - 0x3a];
|
||||
else if (button >= 0x5b && button <= 0x60)
|
||||
button = keyCodes3[button - 0x5b];
|
||||
else if (button >= 0x40000039 && button < 0x40000039 + sizeof(keyCodes4))
|
||||
button = keyCodes4[button - 0x40000039];
|
||||
else if (button >= 0x400000E0 && button <= 0x400000E7)
|
||||
button = keyCodes5[button - 0x400000E0];
|
||||
else if (button >= 0x61 && button <= 0x7a)
|
||||
button -= 0x20;
|
||||
else if (button == 0x7f)
|
||||
button = 0x2e;
|
||||
button = vk_for_sdl_scancode(event->key.keysym.scancode);
|
||||
|
||||
int modifier = 0;
|
||||
switch (event->key.keysym.sym) {
|
||||
case SDLK_RSHIFT:
|
||||
case SDLK_LSHIFT:
|
||||
modifier = MODIFIER_SHIFT;
|
||||
break;
|
||||
case SDLK_RALT:
|
||||
case SDLK_LALT:
|
||||
modifier = MODIFIER_ALT;
|
||||
break;
|
||||
case SDLK_RCTRL:
|
||||
case SDLK_LCTRL:
|
||||
modifier = MODIFIER_CTRL;
|
||||
break;
|
||||
case SDLK_RGUI:
|
||||
case SDLK_LGUI:
|
||||
modifier = MODIFIER_META;
|
||||
break;
|
||||
int modifiers = 0;
|
||||
if (event->key.keysym.mod & KMOD_CTRL) {
|
||||
modifiers |= MODIFIER_CTRL;
|
||||
}
|
||||
if (event->key.keysym.mod & KMOD_ALT) {
|
||||
modifiers |= MODIFIER_ALT;
|
||||
}
|
||||
if (event->key.keysym.mod & KMOD_SHIFT) {
|
||||
modifiers |= MODIFIER_SHIFT;
|
||||
}
|
||||
if (event->key.keysym.mod & KMOD_GUI) {
|
||||
modifiers |= MODIFIER_META;
|
||||
}
|
||||
|
||||
if (modifier != 0) {
|
||||
if (event->type==SDL_KEYDOWN)
|
||||
keyboard_modifiers |= modifier;
|
||||
else
|
||||
keyboard_modifiers &= ~modifier;
|
||||
}
|
||||
|
||||
LiSendKeyboardEvent(0x80 << 8 | button, event->type==SDL_KEYDOWN?KEY_ACTION_DOWN:KEY_ACTION_UP, keyboard_modifiers);
|
||||
LiSendKeyboardEvent(0x80 << 8 | button, event->type==SDL_KEYDOWN?KEY_ACTION_DOWN:KEY_ACTION_UP, modifiers);
|
||||
|
||||
// Quit the stream if all the required quit keys are down
|
||||
if ((keyboard_modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS && event->key.keysym.sym == QUIT_KEY && event->type==SDL_KEYUP)
|
||||
if ((modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS && event->key.keysym.sym == QUIT_KEY && event->type==SDL_KEYUP)
|
||||
return SDL_QUIT_APPLICATION;
|
||||
else if ((keyboard_modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS && event->key.keysym.sym == FULLSCREEN_KEY && event->type==SDL_KEYUP)
|
||||
else if ((modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS && event->key.keysym.sym == FULLSCREEN_KEY && event->type==SDL_KEYUP)
|
||||
return SDL_TOGGLE_FULLSCREEN;
|
||||
else if ((keyboard_modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS && event->key.keysym.sym == UNGRAB_KEY && event->type==SDL_KEYUP)
|
||||
else if ((modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS && event->key.keysym.sym == UNGRAB_KEY && event->type==SDL_KEYUP)
|
||||
return SDL_GetRelativeMouseMode() ? SDL_MOUSE_UNGRAB : SDL_MOUSE_GRAB;
|
||||
break;
|
||||
case SDL_FINGERDOWN:
|
||||
|
@ -22,83 +22,6 @@
|
||||
|
||||
extern int sdl_gamepads;
|
||||
|
||||
static const short keyCodes1[] = {
|
||||
0, //SDLK_EXCLAIM
|
||||
0, //SDLK_QUOTEDBL
|
||||
0, //SDLK_HASH
|
||||
0, //SDLK_DOLLAR
|
||||
0, //SDLK_PERCENT
|
||||
0, //SDLK_AMPERSAND
|
||||
0xDE, //SDLK_QUOTE
|
||||
0, //SDLK_LEFTPAREN
|
||||
0, //SDLK_RIGHTPAREN
|
||||
0, //SDLK_ASTERISK
|
||||
0, //SDLK_PLUS
|
||||
0xBC, //SDLK_COMMA
|
||||
0xBD, //SDLK_MINUS
|
||||
0xBE, //SDLK_PERIOD
|
||||
0xBF, //SDLK_SLASH
|
||||
};
|
||||
|
||||
static const short keyCodes2[] = {
|
||||
0, //SDLK_COLON
|
||||
0xBA, //SDLK_SEMICOLON
|
||||
0, //SDLK_LESS
|
||||
0xBB, //SDLK_EQUALS
|
||||
0, //SDLK_GREATER
|
||||
0, //SDLK_QUESTION
|
||||
0, //SDLK_AT
|
||||
};
|
||||
|
||||
static const short keyCodes3[] = {
|
||||
0xDB, //SDLK_LEFTBRACKET
|
||||
0xDC, //SDLK_BACKSLASH
|
||||
0xDD, //SDLK_RIGHTBRACKET
|
||||
0, //SDLK_CARET
|
||||
0, //SDLK_UNDERSCORE
|
||||
0xC0, //SDLK_BACKQUOTE
|
||||
};
|
||||
|
||||
static const short keyCodes4[] = {
|
||||
0x14, //SDLK_CAPSLOCK
|
||||
0x70, //SDLK_F1
|
||||
0x71, //SDLK_F2
|
||||
0x72, //SDLK_F3
|
||||
0x73, //SDLK_F4
|
||||
0x74, //SDLK_F5
|
||||
0x75, //SDLK_F6
|
||||
0x76, //SDLK_F7
|
||||
0x77, //SDLK_F8
|
||||
0x78, //SDLK_F9
|
||||
0x79, //SDLK_F10
|
||||
0x7A, //SDLK_F11
|
||||
0x7B, //SDLK_F12
|
||||
0, //SDLK_PRINTSCREEN
|
||||
0x91, //SDLK_SCROLLLOCK
|
||||
0x13, //SDLK_PAUSE
|
||||
0x2D, //SDLK_INSERT
|
||||
0x24, //SDLK_HOME
|
||||
0x21, //SDLK_PAGEUP
|
||||
0, //Not used
|
||||
0x23, //SDLK_END
|
||||
0x22, //SDLK_PAGEDOWN
|
||||
0x27, //SDLK_RIGHT
|
||||
0x25, //SDLK_LEFT
|
||||
0x28, //SDLK_DOWN
|
||||
0x26, //SDLK_UP
|
||||
};
|
||||
|
||||
static const short keyCodes5[] = {
|
||||
0xA2, //SDLK_LCTRL
|
||||
0xA0, //SDLK_LSHIFT
|
||||
0xA4, //SDLK_LALT
|
||||
0x5B, //SDLK_LGUI
|
||||
0xA3, //SDLK_RCTRL
|
||||
0xA1, //SDLK_RSHIFT
|
||||
0xA5, //SDLK_RALT
|
||||
0x5C, //SDLK_RGUI
|
||||
};
|
||||
|
||||
void sdlinput_init(char* mappings);
|
||||
int sdlinput_handle_event(SDL_Window* window, SDL_Event* event);
|
||||
void sdlinput_rumble(unsigned short controller_id, unsigned short low_freq_motor, unsigned short high_freq_motor);
|
||||
|
@ -74,7 +74,7 @@ void loop_add_fd(int fd, FdHandler handler, int events) {
|
||||
|
||||
void loop_remove_fd(int fd) {
|
||||
numFds--;
|
||||
int fdindex;
|
||||
int fdindex = numFds;
|
||||
|
||||
for (int i=0;i<=numFds;i++) {
|
||||
if (fds[i].fd == fd) {
|
||||
|
@ -195,7 +195,7 @@ static void help() {
|
||||
printf("\t-4k\t\t\tUse 3840x2160 resolution\n");
|
||||
printf("\t-width <width>\t\tHorizontal resolution (default 1280)\n");
|
||||
printf("\t-height <height>\tVertical resolution (default 720)\n");
|
||||
#if defined(HAVE_PI) | defined(HAVE_MMAL)
|
||||
#ifdef HAVE_EMBEDDED
|
||||
printf("\t-rotate <angle>\tRotate display: 0/90/180/270 (default 0)\n");
|
||||
#endif
|
||||
printf("\t-fps <fps>\t\tSpecify the fps to use (default 60)\n");
|
||||
|
@ -202,6 +202,9 @@ AUDIO_RENDERER_CALLBACKS* platform_get_audio(enum platform system, char* audio_d
|
||||
#ifdef HAVE_ALSA
|
||||
return &audio_callbacks_alsa;
|
||||
#endif
|
||||
#ifdef __FreeBSD__
|
||||
return &audio_callbacks_oss;
|
||||
#endif
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
@ -63,4 +63,4 @@ bool ensure_buf_size(void **buf, size_t *buf_size, size_t required_size) {
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -23,3 +23,4 @@
|
||||
int write_bool(char *path, bool val);
|
||||
int read_file(char *path, char *output, int output_len);
|
||||
bool ensure_buf_size(void **buf, size_t *buf_size, size_t required_size);
|
||||
bool has_fast_aes(void);
|
||||
|
153
src/video/rk.c
153
src/video/rk.c
@ -28,7 +28,6 @@
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
@ -40,7 +39,7 @@
|
||||
|
||||
#include <rockchip/rk_mpi.h>
|
||||
|
||||
#define MAX_FRAMES 16
|
||||
#define MAX_FRAMES 3
|
||||
#define RK_H264 0x7
|
||||
#define RK_H265 0x1000004
|
||||
#define RK_AV1 0x1000008
|
||||
@ -159,33 +158,34 @@ void *display_thread(void *param) {
|
||||
while (!frm_eos) {
|
||||
int _fb_id;
|
||||
|
||||
ret = pthread_mutex_lock(&mutex);
|
||||
assert(!ret);
|
||||
pthread_mutex_lock(&mutex);
|
||||
while (fb_id == 0) {
|
||||
pthread_cond_wait(&cond, &mutex);
|
||||
assert(!ret);
|
||||
if (fb_id == 0 && frm_eos) {
|
||||
ret = pthread_mutex_unlock(&mutex);
|
||||
assert(!ret);
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
_fb_id = fb_id;
|
||||
|
||||
fb_id = 0;
|
||||
ret = pthread_mutex_unlock(&mutex);
|
||||
assert(!ret);
|
||||
pthread_mutex_unlock(&mutex);
|
||||
|
||||
if (atomic) {
|
||||
// We may need to modeset to apply colorspace changes when toggling HDR
|
||||
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "FB_ID", _fb_id);
|
||||
ret = drmModeAtomicCommit(fd, drm_request, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
|
||||
if (ret) {
|
||||
perror("drmModeAtomicCommit");
|
||||
}
|
||||
} else {
|
||||
ret = drmModeSetPlane(fd, plane_id, crtc_id, _fb_id, 0,
|
||||
fb_x, fb_y, fb_width, fb_height,
|
||||
0, 0, frm_width << 16, frm_height << 16);
|
||||
if (ret) {
|
||||
perror("drmModeSetPlane");
|
||||
}
|
||||
}
|
||||
assert(!ret);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@ -248,10 +248,11 @@ void *frame_thread(void *param) {
|
||||
dmcd.bpp = 8; // hor_stride is already adjusted for 10 vs 8 bit
|
||||
dmcd.width = hor_stride;
|
||||
dmcd.height = ver_stride * 2; // documentation say not v*2/3 but v*2 (additional info included)
|
||||
do {
|
||||
ret = ioctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &dmcd);
|
||||
} while (ret == -1 && (errno == EINTR || errno == EAGAIN));
|
||||
assert(!ret);
|
||||
ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &dmcd);
|
||||
if (ret) {
|
||||
perror("drmIoctl(DRM_IOCTL_MODE_CREATE_DUMB)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
assert(dmcd.pitch == dmcd.width);
|
||||
assert(dmcd.size == dmcd.pitch * dmcd.height);
|
||||
frame_to_drm[i].handle = dmcd.handle;
|
||||
@ -260,10 +261,11 @@ void *frame_thread(void *param) {
|
||||
struct drm_prime_handle dph = {0};
|
||||
dph.handle = dmcd.handle;
|
||||
dph.fd = -1;
|
||||
do {
|
||||
ret = ioctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &dph);
|
||||
} while (ret == -1 && (errno == EINTR || errno == EAGAIN));
|
||||
assert(!ret);
|
||||
ret = drmIoctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &dph);
|
||||
if (ret) {
|
||||
perror("drmIoctl(DRM_IOCTL_PRIME_HANDLE_TO_FD)");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
MppBufferInfo info = {0};
|
||||
info.type = MPP_BUFFER_TYPE_DRM;
|
||||
info.size = dmcd.width * dmcd.height;
|
||||
@ -281,7 +283,10 @@ void *frame_thread(void *param) {
|
||||
offsets[1] = pitches[0] * ver_stride;
|
||||
pitches[1] = dmcd.pitch;
|
||||
ret = drmModeAddFB2(fd, frm_width, frm_height, pixel_format, handles, pitches, offsets, &frame_to_drm[i].fb_id, 0);
|
||||
assert(!ret);
|
||||
if (ret) {
|
||||
perror("drmModeAddFB2");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
// register external frame group
|
||||
ret = mpi_api->control(mpi_ctx, MPP_DEC_SET_EXT_BUF_GROUP, mpi_frm_grp);
|
||||
@ -318,14 +323,10 @@ void *frame_thread(void *param) {
|
||||
}
|
||||
assert(i != MAX_FRAMES);
|
||||
// send DRM FB to display thread
|
||||
ret = pthread_mutex_lock(&mutex);
|
||||
assert(!ret);
|
||||
pthread_mutex_lock(&mutex);
|
||||
fb_id = frame_to_drm[i].fb_id;
|
||||
ret = pthread_cond_signal(&cond);
|
||||
assert(!ret);
|
||||
ret = pthread_mutex_unlock(&mutex);
|
||||
assert(!ret);
|
||||
|
||||
pthread_cond_signal(&cond);
|
||||
pthread_mutex_unlock(&mutex);
|
||||
} else {
|
||||
fprintf(stderr, "Frame no buff\n");
|
||||
}
|
||||
@ -382,7 +383,10 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte
|
||||
}
|
||||
|
||||
resources = drmModeGetResources(fd);
|
||||
assert(resources);
|
||||
if (!resources) {
|
||||
perror("drmModeGetResources");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// find active monitor
|
||||
for (i = 0; i < resources->count_connectors; ++i) {
|
||||
@ -430,7 +434,10 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte
|
||||
for (i = 0; i < resources->count_crtcs; ++i) {
|
||||
if (resources->crtcs[i] == encoder->crtc_id) {
|
||||
crtc = drmModeGetCrtc(fd, resources->crtcs[i]);
|
||||
assert(crtc);
|
||||
if (!crtc) {
|
||||
perror("drmModeGetCrtc");
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -441,15 +448,25 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte
|
||||
uint32_t crtc_bit = (1 << i);
|
||||
|
||||
ret = drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
|
||||
assert(!ret);
|
||||
if (ret) {
|
||||
perror("drmSetClientCap(DRM_CLIENT_CAP_UNIVERSAL_PLANES)");
|
||||
}
|
||||
if (atomic) {
|
||||
ret = drmSetClientCap(fd, DRM_CLIENT_CAP_ATOMIC, 1);
|
||||
assert(!ret);
|
||||
drm_request = drmModeAtomicAlloc();
|
||||
assert(drm_request);
|
||||
if (ret) {
|
||||
perror("drmSetClientCap(DRM_CLIENT_CAP_ATOMIC)");
|
||||
atomic = false;
|
||||
}
|
||||
else {
|
||||
drm_request = drmModeAtomicAlloc();
|
||||
assert(drm_request);
|
||||
}
|
||||
}
|
||||
plane_resources = drmModeGetPlaneResources(fd);
|
||||
assert(plane_resources);
|
||||
if (!plane_resources) {
|
||||
perror("drmModeGetPlaneResources");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// search for OVERLAY (for active connector, unused, NV12 support)
|
||||
for (i = 0; i < plane_resources->count_planes; i++) {
|
||||
@ -487,6 +504,7 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte
|
||||
}
|
||||
plane_props[j] = prop;
|
||||
if (!strcmp(prop->name, "type") && (props->prop_values[j] == DRM_PLANE_TYPE_OVERLAY ||
|
||||
props->prop_values[j] == DRM_PLANE_TYPE_CURSOR ||
|
||||
props->prop_values[j] == DRM_PLANE_TYPE_PRIMARY)) {
|
||||
plane_id = ovr->plane_id;
|
||||
}
|
||||
@ -504,7 +522,29 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte
|
||||
}
|
||||
drmModeFreePlane(ovr);
|
||||
}
|
||||
assert(plane_id);
|
||||
|
||||
if (!plane_id) {
|
||||
fprintf(stderr, "Unable to find suitable plane\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// DRM defines rotation in degrees counter-clockwise while we define
|
||||
// rotation in degrees clockwise, so we swap the 90 and 270 cases
|
||||
int displayRotation = drFlags & DISPLAY_ROTATE_MASK;
|
||||
switch (displayRotation) {
|
||||
case DISPLAY_ROTATE_90:
|
||||
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "rotation", DRM_MODE_ROTATE_270);
|
||||
break;
|
||||
case DISPLAY_ROTATE_180:
|
||||
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "rotation", DRM_MODE_ROTATE_180);
|
||||
break;
|
||||
case DISPLAY_ROTATE_270:
|
||||
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "rotation", DRM_MODE_ROTATE_90);
|
||||
break;
|
||||
default:
|
||||
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "rotation", DRM_MODE_ROTATE_0);
|
||||
break;
|
||||
}
|
||||
|
||||
// hide cursor by move in left lower corner
|
||||
drmModeMoveCursor(fd, crtc_id, 0, crtc_height);
|
||||
@ -537,15 +577,11 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte
|
||||
ret = mpi_api->control(mpi_ctx, MPP_SET_OUTPUT_BLOCK, ¶m);
|
||||
assert(!ret);
|
||||
|
||||
ret = pthread_mutex_init(&mutex, NULL);
|
||||
assert(!ret);
|
||||
ret = pthread_cond_init(&cond, NULL);
|
||||
assert(!ret);
|
||||
pthread_mutex_init(&mutex, NULL);
|
||||
pthread_cond_init(&cond, NULL);
|
||||
|
||||
ret = pthread_create(&tid_frame, NULL, frame_thread, NULL);
|
||||
assert(!ret);
|
||||
ret = pthread_create(&tid_display, NULL, display_thread, NULL);
|
||||
assert(!ret);
|
||||
pthread_create(&tid_frame, NULL, frame_thread, NULL);
|
||||
pthread_create(&tid_display, NULL, display_thread, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -556,26 +592,19 @@ void rk_cleanup() {
|
||||
int ret;
|
||||
|
||||
frm_eos = 1;
|
||||
ret = pthread_mutex_lock(&mutex);
|
||||
assert(!ret);
|
||||
ret = pthread_cond_signal(&cond);
|
||||
assert(!ret);
|
||||
ret = pthread_mutex_unlock(&mutex);
|
||||
assert(!ret);
|
||||
pthread_mutex_lock(&mutex);
|
||||
pthread_cond_signal(&cond);
|
||||
pthread_mutex_unlock(&mutex);
|
||||
|
||||
ret = pthread_join(tid_display, NULL);
|
||||
assert(!ret);
|
||||
pthread_join(tid_display, NULL);
|
||||
|
||||
ret = pthread_cond_destroy(&cond);
|
||||
assert(!ret);
|
||||
ret = pthread_mutex_destroy(&mutex);
|
||||
assert(!ret);
|
||||
pthread_cond_destroy(&cond);
|
||||
pthread_mutex_destroy(&mutex);
|
||||
|
||||
ret = mpi_api->reset(mpi_ctx);
|
||||
assert(!ret);
|
||||
|
||||
ret = pthread_join(tid_frame, NULL);
|
||||
assert(!ret);
|
||||
pthread_join(tid_frame, NULL);
|
||||
|
||||
if (mpi_frm_grp) {
|
||||
ret = mpp_buffer_group_put(mpi_frm_grp);
|
||||
@ -583,13 +612,15 @@ void rk_cleanup() {
|
||||
mpi_frm_grp = NULL;
|
||||
for (i = 0; i < MAX_FRAMES; i++) {
|
||||
ret = drmModeRmFB(fd, frame_to_drm[i].fb_id);
|
||||
assert(!ret);
|
||||
if (ret) {
|
||||
perror("drmModeRmFB");
|
||||
}
|
||||
struct drm_mode_destroy_dumb dmdd = {0};
|
||||
dmdd.handle = frame_to_drm[i].handle;
|
||||
do {
|
||||
ret = ioctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dmdd);
|
||||
} while (ret == -1 && (errno == EINTR || errno == EAGAIN));
|
||||
assert(!ret);
|
||||
ret = drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dmdd);
|
||||
if (ret) {
|
||||
perror("drmIoctl(DRM_IOCTL_MODE_DESTROY_DUMB)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
2
third_party/SDL_GameControllerDB
vendored
2
third_party/SDL_GameControllerDB
vendored
@ -1 +1 @@
|
||||
Subproject commit dbcf31a6709ec8354b5963b1bb411721e07bd846
|
||||
Subproject commit ae51c9942f0d985b2161d6b22986f041fc98ce5c
|
2
third_party/moonlight-common-c
vendored
2
third_party/moonlight-common-c
vendored
@ -1 +1 @@
|
||||
Subproject commit d457fbb48714d4ce79de2594180dfd73bc5f070c
|
||||
Subproject commit 8af4562af672dd6b9ed28553ead172984fd9a683
|
Loading…
x
Reference in New Issue
Block a user