Compare commits

..

No commits in common. "master" and "v2.5.1" have entirely different histories.

61 changed files with 1891 additions and 2354 deletions

View File

@ -1,14 +1,10 @@
cmake_minimum_required(VERSION 3.6) cmake_minimum_required(VERSION 3.1)
project(moonlight-embedded VERSION 2.7.0 LANGUAGES C) project(moonlight-embedded VERSION 2.5.1 LANGUAGES C)
SET(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake") SET(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
SET(CMAKE_C_STANDARD 99) SET(CMAKE_C_STANDARD 99)
include(${CMAKE_ROOT}/Modules/GNUInstallDirs.cmake) include(${CMAKE_ROOT}/Modules/GNUInstallDirs.cmake)
include(${CMAKE_SOURCE_DIR}/cmake/generate_version_header.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) aux_source_directory(./src SRC_LIST)
list(APPEND SRC_LIST ./src/input/evdev.c ./src/input/mapping.c ./src/input/udev.c) list(APPEND SRC_LIST ./src/input/evdev.c ./src/input/mapping.c ./src/input/udev.c)
@ -16,48 +12,25 @@ set(MOONLIGHT_DEFINITIONS)
find_package(ALSA) find_package(ALSA)
find_package(Opus REQUIRED) find_package(Opus REQUIRED)
find_package(Broadcom-OMX) find_package(Broadcom)
find_package(Freescale) find_package(Freescale)
find_package(Amlogic) find_package(Amlogic)
find_package(Rockchip) find_package(Rockchip)
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
option(ENABLE_SDL "Compile SDL support" ON)
option(ENABLE_FFMPEG "Compile FFMPEG support" ON)
option(ENABLE_X11 "Compile X11 support (requires ENABLE_FFMPEG)" ON)
option(ENABLE_CEC "Compile CEC support" ON)
option(ENABLE_PULSE "Compile PulseAudio support" ON)
pkg_check_modules(EVDEV REQUIRED libevdev) pkg_check_modules(EVDEV REQUIRED libevdev)
pkg_check_modules(UDEV REQUIRED libudev) pkg_check_modules(UDEV REQUIRED libudev)
if (ENABLE_SDL) pkg_check_modules(SDL sdl2>=2.0.4)
pkg_check_modules(SDL sdl2>=2.0.4) pkg_check_modules(AVCODEC libavcodec)
endif() pkg_check_modules(AVUTIL libavutil)
if (ENABLE_FFMPEG) pkg_check_modules(XLIB x11)
pkg_check_modules(AVCODEC libavcodec) pkg_check_modules(VDPAU vdpau)
pkg_check_modules(AVUTIL libavutil) pkg_check_modules(LIBVA libva)
pkg_check_modules(VDPAU vdpau) pkg_check_modules(LIBVA_X11 libva-x11)
pkg_check_modules(LIBVA libva) pkg_check_modules(PULSE libpulse-simple)
pkg_check_modules(EGL egl) pkg_check_modules(CEC libcec>=4)
pkg_check_modules(GLES glesv2) pkg_check_modules(EGL egl)
pkg_check_modules(GLES glesv2)
if (ENABLE_X11)
pkg_check_modules(XLIB x11)
pkg_check_modules(LIBVA_X11 libva-x11)
endif()
endif()
if (ENABLE_PULSE)
pkg_check_modules(PULSE libpulse-simple)
endif()
if (ENABLE_CEC)
pkg_check_modules(CEC libcec>=4)
endif()
pkg_check_modules(MMAL mmal)
if (NOT MMAL_FOUND)
find_package(MMAL)
endif()
set(VDPAU_ACCEL_FOUND FALSE) set(VDPAU_ACCEL_FOUND FALSE)
set(VA_ACCEL_FOUND FALSE) set(VA_ACCEL_FOUND FALSE)
@ -89,22 +62,6 @@ add_executable(moonlight ${SRC_LIST})
target_link_libraries(moonlight m) target_link_libraries(moonlight m)
target_link_libraries(moonlight gamestream) 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) if (CEC_FOUND)
list(APPEND MOONLIGHT_DEFINITIONS HAVE_LIBCEC) list(APPEND MOONLIGHT_DEFINITIONS HAVE_LIBCEC)
list(APPEND MOONLIGHT_OPTIONS CEC) list(APPEND MOONLIGHT_OPTIONS CEC)
@ -116,37 +73,35 @@ endif()
if(AMLOGIC_FOUND) if(AMLOGIC_FOUND)
list(APPEND MOONLIGHT_DEFINITIONS HAVE_AML) list(APPEND MOONLIGHT_DEFINITIONS HAVE_AML)
list(APPEND MOONLIGHT_OPTIONS AML) list(APPEND MOONLIGHT_OPTIONS AML)
add_library(moonlight-aml SHARED ./src/video/aml.c ./src/util.c ${ILCLIENT_SRC_LIST}) 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_include_directories(moonlight-aml PRIVATE ${AMLOGIC_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR})
target_link_libraries(moonlight-aml gamestream ${AMLOGIC_LIBRARIES}) target_link_libraries(moonlight-aml gamestream ${AMLOGIC_LIBRARIES})
set_property(TARGET moonlight-aml PROPERTY COMPILE_DEFINITIONS ${AMLOGIC_DEFINITIONS}) set_property(TARGET moonlight-aml PROPERTY COMPILE_DEFINITIONS ${AMLOGIC_DEFINITIONS})
install(TARGETS moonlight-aml DESTINATION ${CMAKE_INSTALL_LIBDIR}) install(TARGETS moonlight-aml DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif() endif()
if(BROADCOM-OMX_FOUND) if(BROADCOM_FOUND)
list(APPEND MOONLIGHT_DEFINITIONS HAVE_PI) list(APPEND MOONLIGHT_DEFINITIONS HAVE_PI)
list(APPEND MOONLIGHT_OPTIONS PI) list(APPEND MOONLIGHT_OPTIONS PI)
aux_source_directory(./third_party/ilclient ILCLIENT_SRC_LIST) aux_source_directory(./third_party/ilclient ILCLIENT_SRC_LIST)
add_library(moonlight-pi SHARED ./src/video/pi.c ./src/audio/omx.c ./src/util.c ${ILCLIENT_SRC_LIST}) 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_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}) target_link_libraries(moonlight-pi gamestream ${BROADCOM_OMX_LIBRARIES} ${OPUS_LIBRARY})
set_property(TARGET moonlight-pi PROPERTY COMPILE_DEFINITIONS ${BROADCOM_OMX_DEFINITIONS}) set_property(TARGET moonlight-pi PROPERTY COMPILE_DEFINITIONS ${BROADCOM_OMX_DEFINITIONS})
install(TARGETS moonlight-pi DESTINATION ${CMAKE_INSTALL_LIBDIR}) install(TARGETS moonlight-pi DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif()
if(MMAL_FOUND)
list(APPEND MOONLIGHT_DEFINITIONS HAVE_MMAL) list(APPEND MOONLIGHT_DEFINITIONS HAVE_MMAL)
list(APPEND MOONLIGHT_OPTIONS MMAL) list(APPEND MOONLIGHT_OPTIONS MMAL)
add_library(moonlight-mmal SHARED ./src/video/mmal.c ./src/util.c) add_library(moonlight-mmal SHARED ./src/video/mmal.c)
target_include_directories(moonlight-mmal PRIVATE ${MMAL_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR}) target_include_directories(moonlight-mmal PRIVATE ${BROADCOM_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR})
target_link_libraries(moonlight-mmal gamestream ${MMAL_LINK_LIBRARIES}) target_link_libraries(moonlight-mmal gamestream ${BROADCOM_MMAL_LIBRARIES})
install(TARGETS moonlight-mmal DESTINATION ${CMAKE_INSTALL_LIBDIR}) install(TARGETS moonlight-mmal DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif() endif()
if(FREESCALE_FOUND) if(FREESCALE_FOUND)
list(APPEND MOONLIGHT_DEFINITIONS HAVE_IMX) list(APPEND MOONLIGHT_DEFINITIONS HAVE_IMX)
list(APPEND MOONLIGHT_OPTIONS IMX) list(APPEND MOONLIGHT_OPTIONS IMX)
add_library(moonlight-imx SHARED ./src/video/imx.c ./src/video/imx_vpu.c ./src/util.c) add_library(moonlight-imx SHARED ./src/video/imx.c ./src/video/imx_vpu.c)
target_include_directories(moonlight-imx PRIVATE ${FREESCALE_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR}) target_include_directories(moonlight-imx PRIVATE ${FREESCALE_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR})
target_link_libraries(moonlight-imx gamestream ${FREESCALE_LIBRARIES}) target_link_libraries(moonlight-imx gamestream ${FREESCALE_LIBRARIES})
install(TARGETS moonlight-imx DESTINATION ${CMAKE_INSTALL_LIBDIR}) install(TARGETS moonlight-imx DESTINATION ${CMAKE_INSTALL_LIBDIR})
@ -155,7 +110,7 @@ endif()
if(ROCKCHIP_FOUND) if(ROCKCHIP_FOUND)
list(APPEND MOONLIGHT_DEFINITIONS HAVE_ROCKCHIP) list(APPEND MOONLIGHT_DEFINITIONS HAVE_ROCKCHIP)
list(APPEND MOONLIGHT_OPTIONS ROCKCHIP) list(APPEND MOONLIGHT_OPTIONS ROCKCHIP)
add_library(moonlight-rk SHARED ./src/video/rk.c ./src/util.c) add_library(moonlight-rk SHARED ./src/video/rk.c)
target_include_directories(moonlight-rk PRIVATE ${ROCKCHIP_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR}) target_include_directories(moonlight-rk PRIVATE ${ROCKCHIP_INCLUDE_DIRS} ${GAMESTREAM_INCLUDE_DIR} ${MOONLIGHT_COMMON_INCLUDE_DIR})
target_link_libraries(moonlight-rk gamestream ${ROCKCHIP_LIBRARIES}) target_link_libraries(moonlight-rk gamestream ${ROCKCHIP_LIBRARIES})
set_property(TARGET moonlight-rk PROPERTY COMPILE_DEFINITIONS ${ROCKCHIP_DEFINITIONS}) set_property(TARGET moonlight-rk PROPERTY COMPILE_DEFINITIONS ${ROCKCHIP_DEFINITIONS})
@ -211,12 +166,12 @@ if (PULSE_FOUND)
target_link_libraries(moonlight ${PULSE_LIBRARIES}) target_link_libraries(moonlight ${PULSE_LIBRARIES})
endif() endif()
if (AMLOGIC_FOUND OR BROADCOM-OMX_FOUND OR MMAL_FOUND OR FREESCALE_FOUND OR ROCKCHIP_FOUND OR X11_FOUND) if (AMLOGIC_FOUND OR BROADCOM_FOUND OR FREESCALE_FOUND OR ROCKCHIP_FOUND OR X11_FOUND)
list(APPEND MOONLIGHT_DEFINITIONS HAVE_EMBEDDED) list(APPEND MOONLIGHT_DEFINITIONS HAVE_EMBEDDED)
list(APPEND MOONLIGHT_OPTIONS EMBEDDED) list(APPEND MOONLIGHT_OPTIONS EMBEDDED)
endif() endif()
if(NOT AMLOGIC_FOUND AND NOT BROADCOM-OMX_FOUND AND NOT MMAL_FOUND AND NOT FREESCALE_FOUND AND NOT ROCKCHIP_FOUND AND NOT SOFTWARE_FOUND) if(NOT AMLOGIC_FOUND AND NOT BROADCOM_FOUND AND NOT FREESCALE_FOUND AND NOT ROCKCHIP_FOUND AND NOT SOFTWARE_FOUND)
message(FATAL_ERROR "No video output available") message(FATAL_ERROR "No video output available")
endif() endif()

View File

@ -2,14 +2,30 @@
[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/uaph3i3lfu7gl7m7/branch/master?svg=true)](https://ci.appveyor.com/project/cgutman/moonlight-embedded/branch/master) [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/uaph3i3lfu7gl7m7/branch/master?svg=true)](https://ci.appveyor.com/project/cgutman/moonlight-embedded/branch/master)
Moonlight Embedded is an open source client for [Sunshine](https://github.com/LizardByte/Sunshine) and NVIDIA GameStream for embedded Linux systems, like Raspberry Pi, CuBox-i and ODROID. Moonlight allows you to stream your full collection of games and applications from your PC to other devices to play them remotely. Moonlight Embedded is an open source implementation of NVIDIA's GameStream, as used by the NVIDIA Shield, but built for Linux.
Moonlight also has [PC](https://github.com/moonlight-stream/moonlight-qt), [Android](https://github.com/moonlight-stream/moonlight-android), and [iOS](https://github.com/moonlight-stream/moonlight-ios) clients. Moonlight Embedded allows you to stream your full collection of games from
your powerful Windows desktop to your (embedded) Linux system, like Raspberry Pi, CuBox-i and ODROID.
## Documentation ## Documentation
More information about installing and runnning Moonlight Embedded is available on the [wiki](https://github.com/moonlight-stream/moonlight-embedded/wiki). More information about installing and runnning Moonlight Embedded is available on the [wiki](https://github.com/moonlight-stream/moonlight-embedded/wiki).
## Requirements
* [GFE compatible](http://shield.nvidia.com/play-pc-games/) computer with GTX 600/700/900/1000 series GPU (for the PC you're streaming from)
* High-end wireless router (802.11n dual-band recommended) or wired network
* Geforce Experience 2.1.1 or higher
## Quick Start
* Ensure your GFE server and client are on the same network
* Turn on Shield Streaming in the GFE settings
* Pair Moonlight Embedded with the GFE server
* Accept the pairing confirmation on your PC
* Connect to the GFE Server with Moonlight Embedded
* Play games!
## Bugs ## Bugs
Please check the wiki and old bug reports before submitting a new bug report. Please check the wiki and old bug reports before submitting a new bug report.

View File

@ -12,7 +12,7 @@ environment:
BUILD_TARGET: raspbian BUILD_TARGET: raspbian
install: install:
- 'sudo apt update || true' - 'sudo apt update'
- 'sudo apt install -y $PACKAGES' - 'sudo apt install -y $PACKAGES'
- '[ "$BUILD_TARGET" != raspbian ] || docker run --rm --privileged multiarch/qemu-user-static --reset -p yes' - '[ "$BUILD_TARGET" != raspbian ] || docker run --rm --privileged multiarch/qemu-user-static --reset -p yes'
@ -21,7 +21,7 @@ before_build:
build_script: build_script:
- 'if [[ "$BUILD_TARGET" = ubuntu ]]; then mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX=/tmp .. && make -j$(nproc) && make install; fi' - 'if [[ "$BUILD_TARGET" = ubuntu ]]; then mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX=/tmp .. && make -j$(nproc) && make install; fi'
- 'if [[ "$BUILD_TARGET" = raspbian ]]; then git clone --recursive https://github.com/cgutman/moonlight-embedded-packaging.git && cd moonlight-embedded-packaging && sh -c "./build-rpi-buster.sh $APPVEYOR_REPO_COMMIT"; fi' - 'if [[ "$BUILD_TARGET" = raspbian ]]; then git clone --recursive https://github.com/cgutman/moonlight-embedded-packaging.git && cd moonlight-embedded-packaging && sh -c "./build-rpi.sh $APPVEYOR_REPO_COMMIT"; fi'
after_build: after_build:
- sh: '[ "$BUILD_TARGET" != raspbian ] || appveyor PushArtifact out_*/moonlight-embedded_*.deb' - sh: '[ "$BUILD_TARGET" != raspbian ] || appveyor PushArtifact out_*/moonlight-embedded_*.deb'

View File

@ -1,17 +1,29 @@
find_path(AMLOGIC_INCLUDE_DIR find_path(AMLOGIC_INCLUDE_DIR
NAMES codec.h NAMES codec.h
DOC "Amlogic include directory" DOC "Amlogic include directory"
PATHS /usr/local/include/amcodec /usr/osmc/include/amcodec /usr/include/amcodec /usr/include/) PATHS /usr/local/include/amcodec /usr/include/amcodec /usr/include/)
mark_as_advanced(AMLOGIC_INCLUDE_DIR) 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 find_library(AMCODEC_LIBRARY
NAMES libamcodec.so NAMES libamcodec.so
DOC "Path to Amlogic Video Codec Library" DOC "Path to Amlogic Video Codec Library"
PATHS /usr/lib/aml_libs /usr/osmc/lib /usr/local/lib /usr/lib) PATHS /usr/lib/aml_libs /usr/local/lib /usr/lib)
mark_as_advanced(AMCODEC_LIBRARY) mark_as_advanced(AMCODEC_LIBRARY)
include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake) include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Amlogic DEFAULT_MSG AMLOGIC_INCLUDE_DIR AMCODEC_LIBRARY) FIND_PACKAGE_HANDLE_STANDARD_ARGS(Amlogic DEFAULT_MSG AMLOGIC_INCLUDE_DIR AMCODEC_LIBRARY AMADEC_LIBRARY AMAVUTILS_LIBRARY)
set(AMLOGIC_LIBRARIES ${AMCODEC_LIBRARY}) set(AMLOGIC_LIBRARIES ${AMCODEC_LIBRARY} ${AMADEC_LIBRARY} ${AMAVUTILS_LIBRARY})
set(AMLOGIC_INCLUDE_DIRS ${AMLOGIC_INCLUDE_DIR}) set(AMLOGIC_INCLUDE_DIRS ${AMLOGIC_INCLUDE_DIR})

View File

@ -28,9 +28,28 @@ find_library(BCM_HOST_LIBRARY
PATHS /opt/vc/lib) PATHS /opt/vc/lib)
mark_as_advanced(BCM_HOST_LIBRARY) mark_as_advanced(BCM_HOST_LIBRARY)
find_library(MMAL_CORE_LIBRARY
NAMES libmmal_core.so
DOC "Path to MMAL Core Library"
PATHS /opt/vc/lib)
mark_as_advanced(MMAL_CORE_LIBRARY)
find_library(MMAL_UTIL_LIBRARY
NAMES libmmal_util.so
DOC "Path to MMAL Util Library"
PATHS /opt/vc/lib)
mark_as_advanced(MMAL_UTIL_LIBRARY)
find_library(MMAL_VC_CLIENT_LIBRARY
NAMES libmmal_vc_client.so
DOC "Path to MMAL Client Library"
PATHS /opt/vc/lib)
mark_as_advanced(MMAL_VC_CLIENT_LIBRARY)
include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake) include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Broadcom-OMX DEFAULT_MSG BROADCOM_INCLUDE_DIR VCOS_LIBRARY VCHIQ_LIBRARY OPENMAXIL_LIBRARY BCM_HOST_LIBRARY) FIND_PACKAGE_HANDLE_STANDARD_ARGS(Broadcom DEFAULT_MSG BROADCOM_INCLUDE_DIR VCOS_LIBRARY VCHIQ_LIBRARY OPENMAXIL_LIBRARY BCM_HOST_LIBRARY)
set(BROADCOM_OMX_LIBRARIES ${BCM_HOST_LIBRARY} ${OPENMAXIL_LIBRARY} ${VCHIQ_LIBRARY} ${VCOS_LIBRARY}) set(BROADCOM_OMX_LIBRARIES ${BCM_HOST_LIBRARY} ${OPENMAXIL_LIBRARY} ${VCHIQ_LIBRARY} ${VCOS_LIBRARY})
set(BROADCOM_MMAL_LIBRARIES ${BCM_HOST_LIBRARY} ${VCOS_LIBRARY} ${MMAL_CORE_LIBRARY} ${MMAL_UTIL_LIBRARY} ${MMAL_VC_CLIENT_LIBRARY})
set(BROADCOM_INCLUDE_DIRS ${BROADCOM_INCLUDE_DIR} ${BROADCOM_INCLUDE_DIR}/interface/vmcs_host/linux ${BROADCOM_INCLUDE_DIR}/interface/vcos/pthreads) set(BROADCOM_INCLUDE_DIRS ${BROADCOM_INCLUDE_DIR} ${BROADCOM_INCLUDE_DIR}/interface/vmcs_host/linux ${BROADCOM_INCLUDE_DIR}/interface/vcos/pthreads)
set(BROADCOM_OMX_DEFINITIONS USE_VCHIQ_ARM HAVE_LIBOPENMAX=2 OMX OMX_SKIP64BIT USE_EXTERNAL_OMX HAVE_LIBBCM_HOST USE_EXTERNAL_LIBBCM_HOST) set(BROADCOM_OMX_DEFINITIONS USE_VCHIQ_ARM HAVE_LIBOPENMAX=2 OMX OMX_SKIP64BIT USE_EXTERNAL_OMX HAVE_LIBBCM_HOST USE_EXTERNAL_LIBBCM_HOST)

View File

@ -1,77 +1,51 @@
# CMake - Cross Platform Makefile Generator # - Try to find LIBUUID
# Copyright 2000-2024 Kitware, Inc. and Contributors # Find LIBUUID headers, libraries and the answer to all questions.
# All rights reserved.
# #
# Distributed under the OSI-approved BSD 3-Clause License. See # LIBUUID_FOUND True if libuuid got found
# https://cmake.org/licensing for details. # 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.
# #
#[=======================================================================[.rst:
FindLibUUID
------------
Find LibUUID include directory and library. INCLUDE( FindPkgConfig )
Imported Targets IF ( LibUuid_FIND_REQUIRED )
^^^^^^^^^^^^^^^^ SET( _pkgconfig_REQUIRED "REQUIRED" )
ELSE( LibUuid_FIND_REQUIRED )
SET( _pkgconfig_REQUIRED "" )
ENDIF ( LibUuid_FIND_REQUIRED )
An :ref:`imported target <Imported targets>` named IF ( LIBUUID_MIN_VERSION )
``LibUUID::LibUUID`` is provided if LibUUID has been found. 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 )
Result Variables
^^^^^^^^^^^^^^^^
This module defines the following variables: IF( NOT LIBUUID_FOUND AND NOT PKG_CONFIG_FOUND )
FIND_PATH( LIBUUID_INCLUDE_DIRS uuid/uuid.h )
FIND_LIBRARY( LIBUUID_LIBRARIES uuid)
``LibUUID_FOUND`` # Report results
True if LibUUID was found, false otherwise. IF ( LIBUUID_LIBRARIES AND LIBUUID_INCLUDE_DIRS )
``LibUUID_INCLUDE_DIRS`` SET( LIBUUID_FOUND 1 )
Include directories needed to include LibUUID headers. IF ( NOT LIBUUID_FIND_QUIETLY )
``LibUUID_LIBRARIES`` MESSAGE( STATUS "Found libuuid: ${LIBUUID_LIBRARIES}" )
Libraries needed to link to LibUUID. 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 )
Cache Variables MARK_AS_ADVANCED( LIBUUID_LIBRARIES LIBUUID_INCLUDE_DIRS )
^^^^^^^^^^^^^^^
This module uses the following cache variables:
``LibUUID_LIBRARY``
The location of the LibUUID library file.
``LibUUID_INCLUDE_DIR``
The location of the LibUUID include directory containing ``uuid/uuid.h``.
The cache variables should not be used by project code.
They may be set by end users to point at LibUUID components.
#]=======================================================================]
#-----------------------------------------------------------------------------
find_library(LibUUID_LIBRARY
NAMES uuid
)
mark_as_advanced(LibUUID_LIBRARY)
find_path(LibUUID_INCLUDE_DIR
NAMES uuid/uuid.h
)
mark_as_advanced(LibUUID_INCLUDE_DIR)
#-----------------------------------------------------------------------------
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibUUID
FOUND_VAR LibUUID_FOUND
REQUIRED_VARS LibUUID_LIBRARY LibUUID_INCLUDE_DIR
)
set(LIBUUID_FOUND ${LibUUID_FOUND})
#-----------------------------------------------------------------------------
# Provide documented result variables and targets.
if(LibUUID_FOUND)
set(LibUUID_INCLUDE_DIRS ${LibUUID_INCLUDE_DIR})
set(LibUUID_LIBRARIES ${LibUUID_LIBRARY})
if(NOT TARGET LibUUID::LibUUID)
add_library(LibUUID::LibUUID UNKNOWN IMPORTED)
set_target_properties(LibUUID::LibUUID PROPERTIES
IMPORTED_LOCATION "${LibUUID_LIBRARY}"
INTERFACE_INCLUDE_DIRECTORIES "${LibUUID_INCLUDE_DIRS}"
)
endif()
endif()

View File

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

View File

@ -99,9 +99,9 @@ By default, 1392 is used on LAN and 1024 on WAN.
=item B<-codec> [I<CODEC>] =item B<-codec> [I<CODEC>]
Select codec to use. Select codec to use.
Can be 'auto', 'h264', 'h265', 'hevc', or 'av1'. Can be 'auto', 'h264', 'h265' or 'hevc'.
Not all video decoders support H.265/HEVC or AV1. Not all video decoders do support H.265/HEVC.
Will still use H.264 if server doesn't support HEVC or AV1. Will still use H.264 if server doesn't support HEVC.
=item B<-remote> [I<yes/no/auto>] =item B<-remote> [I<yes/no/auto>]

View File

@ -23,9 +23,9 @@ target_link_libraries(gamestream moonlight-common)
set_target_properties(gamestream PROPERTIES SOVERSION ${SO_VERSION} VERSION ${PROJECT_VERSION}) set_target_properties(gamestream PROPERTIES SOVERSION ${SO_VERSION} VERSION ${PROJECT_VERSION})
set_target_properties(moonlight-common 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_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}) target_link_libraries(gamestream ${CMAKE_THREAD_LIBS_INIT} ${CMAKE_DL_LIBS})

View File

@ -48,16 +48,11 @@
static char unique_id[UNIQUEID_CHARS+1]; static char unique_id[UNIQUEID_CHARS+1];
static X509 *cert; static X509 *cert;
static char cert_hex[8192]; static char cert_hex[4096];
static EVP_PKEY *privateKey; static EVP_PKEY *privateKey;
const char* gs_error; const char* gs_error;
#define LEN_AS_HEX_STR(x) ((x) * 2 + 1)
#define SIZEOF_AS_HEX_STR(x) LEN_AS_HEX_STR(sizeof(x))
#define UUID_STRLEN 37
static int mkdirtree(const char* directory) { static int mkdirtree(const char* directory) {
char buffer[PATH_MAX]; char buffer[PATH_MAX];
char* p = buffer; char* p = buffer;
@ -92,16 +87,16 @@ static int load_unique_id(const char* keyDirectory) {
snprintf(uniqueFilePath, PATH_MAX, "%s/%s", keyDirectory, UNIQUE_FILE_NAME); snprintf(uniqueFilePath, PATH_MAX, "%s/%s", keyDirectory, UNIQUE_FILE_NAME);
FILE *fd = fopen(uniqueFilePath, "r"); FILE *fd = fopen(uniqueFilePath, "r");
if (fd == NULL || fread(unique_id, UNIQUEID_CHARS, 1, fd) != UNIQUEID_CHARS) { if (fd == NULL) {
snprintf(unique_id,UNIQUEID_CHARS+1,"0123456789ABCDEF"); snprintf(unique_id,UNIQUEID_CHARS+1,"0123456789ABCDEF");
if (fd)
fclose(fd);
fd = fopen(uniqueFilePath, "w"); fd = fopen(uniqueFilePath, "w");
if (fd == NULL) if (fd == NULL)
return GS_FAILED; return GS_FAILED;
fwrite(unique_id, UNIQUEID_CHARS, 1, fd); fwrite(unique_id, UNIQUEID_CHARS, 1, fd);
} else {
fread(unique_id, UNIQUEID_CHARS, 1, fd);
} }
fclose(fd); fclose(fd);
unique_id[UNIQUEID_CHARS] = 0; unique_id[UNIQUEID_CHARS] = 0;
@ -164,129 +159,109 @@ static int load_cert(const char* keyDirectory) {
return GS_OK; return GS_OK;
} }
static int load_serverinfo(PSERVER_DATA server, bool https) {
uuid_t uuid;
char uuid_str[UUID_STRLEN];
char url[4096];
int ret = GS_INVALID;
char *pairedText = NULL;
char *currentGameText = NULL;
char *stateText = NULL;
char *serverCodecModeSupportText = NULL;
char *httpsPortText = NULL;
uuid_generate_random(uuid);
uuid_unparse(uuid, uuid_str);
snprintf(url, sizeof(url), "%s://%s:%d/serverinfo?uniqueid=%s&uuid=%s",
https ? "https" : "http", server->serverInfo.address, https ? server->httpsPort : server->httpPort, unique_id, uuid_str);
PHTTP_DATA data = http_create_data();
if (data == NULL) {
ret = GS_OUT_OF_MEMORY;
goto cleanup;
}
if (http_request(url, data) != GS_OK) {
ret = GS_IO_ERROR;
goto cleanup;
}
if (xml_status(data->memory, data->size) == GS_ERROR) {
ret = GS_ERROR;
goto cleanup;
}
if (xml_search(data->memory, data->size, "currentgame", &currentGameText) != GS_OK) {
goto cleanup;
}
if (xml_search(data->memory, data->size, "PairStatus", &pairedText) != GS_OK)
goto cleanup;
if (xml_search(data->memory, data->size, "appversion", (char**) &server->serverInfo.serverInfoAppVersion) != GS_OK)
goto cleanup;
if (xml_search(data->memory, data->size, "state", &stateText) != GS_OK)
goto cleanup;
if (xml_search(data->memory, data->size, "ServerCodecModeSupport", &serverCodecModeSupportText) != GS_OK)
goto cleanup;
if (xml_search(data->memory, data->size, "gputype", &server->gpuType) != GS_OK)
goto cleanup;
if (xml_search(data->memory, data->size, "GsVersion", &server->gsVersion) != GS_OK)
goto cleanup;
if (xml_search(data->memory, data->size, "GfeVersion", (char**) &server->serverInfo.serverInfoGfeVersion) != GS_OK)
goto cleanup;
if (xml_search(data->memory, data->size, "HttpsPort", &httpsPortText) != GS_OK)
goto cleanup;
if (xml_modelist(data->memory, data->size, &server->modes) != GS_OK)
goto cleanup;
// These fields are present on all version of GFE that this client supports
if (!strlen(currentGameText) || !strlen(pairedText) || !strlen(server->serverInfo.serverInfoAppVersion) || !strlen(stateText))
goto cleanup;
server->paired = pairedText != NULL && strcmp(pairedText, "1") == 0;
server->currentGame = currentGameText == NULL ? 0 : atoi(currentGameText);
server->serverInfo.serverCodecModeSupport = serverCodecModeSupportText == NULL ? SCM_H264 : atoi(serverCodecModeSupportText);
server->serverMajorVersion = atoi(server->serverInfo.serverInfoAppVersion);
server->isNvidiaSoftware = strstr(stateText, "MJOLNIR") != NULL;
server->httpsPort = atoi(httpsPortText);
if (!server->httpsPort)
server->httpsPort = 47984;
if (strstr(stateText, "_SERVER_BUSY") == NULL) {
// After GFE 2.8, current game remains set even after streaming
// has ended. We emulate the old behavior by forcing it to zero
// if streaming is not active.
server->currentGame = 0;
}
ret = GS_OK;
cleanup:
if (data != NULL)
http_free_data(data);
if (pairedText != NULL)
free(pairedText);
if (currentGameText != NULL)
free(currentGameText);
if (serverCodecModeSupportText != NULL)
free(serverCodecModeSupportText);
if (httpsPortText != NULL)
free(httpsPortText);
return ret;
}
static int load_server_status(PSERVER_DATA server) { static int load_server_status(PSERVER_DATA server) {
uuid_t uuid;
char uuid_str[37];
int ret; int ret;
char url[4096];
int i; int i;
/* Fetch the HTTPS port if we don't have one yet */ i = 0;
if (!server->httpsPort) { do {
ret = load_serverinfo(server, false); char *pairedText = NULL;
if (ret != GS_OK) char *currentGameText = NULL;
return ret; char *stateText = NULL;
} char *serverCodecModeSupportText = NULL;
// Modern GFE versions don't allow serverinfo to be fetched over HTTPS if the client ret = GS_INVALID;
// 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 uuid_generate_random(uuid);
// for everything because it doesn't accurately tell us if we're paired. uuid_unparse(uuid, uuid_str);
ret = GS_INVALID;
for (i = 0; i < 2 && ret != GS_OK; i++) { // Modern GFE versions don't allow serverinfo to be fetched over HTTPS if the client
ret = load_serverinfo(server, i == 0); // is not already paired. Since we can't pair without knowing the server version, we
} // make another request over HTTP if the HTTPS request fails. We can't just use HTTP
// for everything because it doesn't accurately tell us if we're paired.
snprintf(url, sizeof(url), "%s://%s:%d/serverinfo?uniqueid=%s&uuid=%s",
i == 0 ? "https" : "http", server->serverInfo.address, i == 0 ? 47984 : 47989, unique_id, uuid_str);
PHTTP_DATA data = http_create_data();
if (data == NULL) {
ret = GS_OUT_OF_MEMORY;
goto cleanup;
}
if (http_request(url, data) != GS_OK) {
ret = GS_IO_ERROR;
goto cleanup;
}
if (xml_status(data->memory, data->size) == GS_ERROR) {
ret = GS_ERROR;
goto cleanup;
}
if (xml_search(data->memory, data->size, "currentgame", &currentGameText) != GS_OK) {
goto cleanup;
}
if (xml_search(data->memory, data->size, "PairStatus", &pairedText) != GS_OK)
goto cleanup;
if (xml_search(data->memory, data->size, "appversion", (char**) &server->serverInfo.serverInfoAppVersion) != GS_OK)
goto cleanup;
if (xml_search(data->memory, data->size, "state", &stateText) != GS_OK)
goto cleanup;
if (xml_search(data->memory, data->size, "ServerCodecModeSupport", &serverCodecModeSupportText) != GS_OK)
goto cleanup;
if (xml_search(data->memory, data->size, "gputype", &server->gpuType) != GS_OK)
goto cleanup;
if (xml_search(data->memory, data->size, "GsVersion", &server->gsVersion) != GS_OK)
goto cleanup;
if (xml_search(data->memory, data->size, "GfeVersion", (char**) &server->serverInfo.serverInfoGfeVersion) != GS_OK)
goto cleanup;
if (xml_modelist(data->memory, data->size, &server->modes) != GS_OK)
goto cleanup;
// These fields are present on all version of GFE that this client supports
if (!strlen(currentGameText) || !strlen(pairedText) || !strlen(server->serverInfo.serverInfoAppVersion) || !strlen(stateText))
goto cleanup;
server->paired = pairedText != NULL && strcmp(pairedText, "1") == 0;
server->currentGame = currentGameText == NULL ? 0 : atoi(currentGameText);
server->supports4K = serverCodecModeSupportText != NULL;
server->serverMajorVersion = atoi(server->serverInfo.serverInfoAppVersion);
if (strstr(stateText, "_SERVER_BUSY") == NULL) {
// After GFE 2.8, current game remains set even after streaming
// has ended. We emulate the old behavior by forcing it to zero
// if streaming is not active.
server->currentGame = 0;
}
ret = GS_OK;
cleanup:
if (data != NULL)
http_free_data(data);
if (pairedText != NULL)
free(pairedText);
if (currentGameText != NULL)
free(currentGameText);
if (serverCodecModeSupportText != NULL)
free(serverCodecModeSupportText);
i++;
} while (ret != GS_OK && i < 2);
if (ret == GS_OK && !server->unsupported) { if (ret == GS_OK && !server->unsupported) {
if (server->serverMajorVersion > MAX_SUPPORTED_GFE_VERSION) { if (server->serverMajorVersion > MAX_SUPPORTED_GFE_VERSION) {
@ -308,12 +283,6 @@ static void bytes_to_hex(unsigned char *in, char *out, size_t len) {
out[len * 2] = 0; 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) { static int sign_it(const char *msg, size_t mlen, unsigned char **sig, size_t *slen, EVP_PKEY *pkey) {
int result = GS_FAILED; int result = GS_FAILED;
@ -381,42 +350,18 @@ static bool verifySignature(const char *data, int dataLength, char *signature, i
return result > 0; return result > 0;
} }
static void encrypt(const unsigned char *plaintext, int plaintextLen, const unsigned char *key, unsigned char *ciphertext) {
EVP_CIPHER_CTX* cipher = EVP_CIPHER_CTX_new();
EVP_EncryptInit(cipher, EVP_aes_128_ecb(), key, NULL);
EVP_CIPHER_CTX_set_padding(cipher, 0);
int ciphertextLen = 0;
EVP_EncryptUpdate(cipher, ciphertext, &ciphertextLen, plaintext, plaintextLen);
EVP_CIPHER_CTX_free(cipher);
}
static void decrypt(const unsigned char *ciphertext, int ciphertextLen, const unsigned char *key, unsigned char *plaintext) {
EVP_CIPHER_CTX* cipher = EVP_CIPHER_CTX_new();
EVP_DecryptInit(cipher, EVP_aes_128_ecb(), key, NULL);
EVP_CIPHER_CTX_set_padding(cipher, 0);
int plaintextLen = 0;
EVP_DecryptUpdate(cipher, plaintext, &plaintextLen, ciphertext, ciphertextLen);
EVP_CIPHER_CTX_free(cipher);
}
int gs_unpair(PSERVER_DATA server) { int gs_unpair(PSERVER_DATA server) {
int ret = GS_OK; int ret = GS_OK;
char url[4096]; char url[4096];
uuid_t uuid; uuid_t uuid;
char uuid_str[UUID_STRLEN]; char uuid_str[37];
PHTTP_DATA data = http_create_data(); PHTTP_DATA data = http_create_data();
if (data == NULL) if (data == NULL)
return GS_OUT_OF_MEMORY; return GS_OUT_OF_MEMORY;
uuid_generate_random(uuid); uuid_generate_random(uuid);
uuid_unparse(uuid, uuid_str); uuid_unparse(uuid, uuid_str);
snprintf(url, sizeof(url), "http://%s:%u/unpair?uniqueid=%s&uuid=%s", server->serverInfo.address, server->httpPort, unique_id, uuid_str); snprintf(url, sizeof(url), "http://%s:47989/unpair?uniqueid=%s&uuid=%s", server->serverInfo.address, unique_id, uuid_str);
ret = http_request(url, data); ret = http_request(url, data);
http_free_data(data); http_free_data(data);
@ -426,32 +371,29 @@ int gs_unpair(PSERVER_DATA server) {
int gs_pair(PSERVER_DATA server, char* pin) { int gs_pair(PSERVER_DATA server, char* pin) {
int ret = GS_OK; int ret = GS_OK;
char* result = NULL; char* result = NULL;
size_t url_max_len = 16384; char url[4096];
char* url = malloc(url_max_len);
uuid_t uuid; uuid_t uuid;
char uuid_str[UUID_STRLEN]; char uuid_str[37];
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) { if (server->paired) {
gs_error = "Already paired"; gs_error = "Already paired";
ret = GS_WRONG_STATE; return GS_WRONG_STATE;
goto cleanup; }
if (server->currentGame != 0) {
gs_error = "The computer is currently in a game. You must close the game before pairing";
return GS_WRONG_STATE;
} }
unsigned char salt_data[16]; unsigned char salt_data[16];
char salt_hex[SIZEOF_AS_HEX_STR(salt_data)]; char salt_hex[33];
RAND_bytes(salt_data, sizeof(salt_data)); RAND_bytes(salt_data, 16);
bytes_to_hex(salt_data, salt_hex, sizeof(salt_data)); bytes_to_hex(salt_data, salt_hex, 16);
uuid_generate_random(uuid); uuid_generate_random(uuid);
uuid_unparse(uuid, uuid_str); uuid_unparse(uuid, uuid_str);
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); snprintf(url, sizeof(url), "http://%s:47989/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&phrase=getservercert&salt=%s&clientcert=%s", server->serverInfo.address, unique_id, uuid_str, salt_hex, cert_hex);
data = http_create_data(); PHTTP_DATA data = http_create_data();
if (data == NULL) if (data == NULL)
return GS_OUT_OF_MEMORY; return GS_OUT_OF_MEMORY;
else if ((ret = http_request(url, data)) != GS_OK) else if ((ret = http_request(url, data)) != GS_OK)
@ -473,33 +415,43 @@ int gs_pair(PSERVER_DATA server, char* pin) {
if ((ret = xml_search(data->memory, data->size, "plaincert", &result)) != GS_OK) if ((ret = xml_search(data->memory, data->size, "plaincert", &result)) != GS_OK)
goto cleanup; goto cleanup;
if (strlen(result)/2 > 8191) {
gs_error = "Server certificate too big";
ret = GS_FAILED;
goto cleanup;
}
size_t plaincertlen = strlen(result)/2; char plaincert[8192];
plaincert = malloc(plaincertlen + 1); for (int count = 0; count < strlen(result); count += 2) {
hex_to_bytes(result, plaincert, plaincertlen*2); sscanf(&result[count], "%2hhx", &plaincert[count / 2]);
plaincert[plaincertlen] = 0; }
plaincert[strlen(result)/2] = '\0';
unsigned char salt_pin[sizeof(salt_data) + 4]; unsigned char salt_pin[20];
unsigned char aes_key[32]; // Must fit SHA256 unsigned char aes_key_hash[32];
memcpy(salt_pin, salt_data, sizeof(salt_data)); AES_KEY enc_key, dec_key;
memcpy(salt_pin+sizeof(salt_data), pin, 4); memcpy(salt_pin, salt_data, 16);
memcpy(salt_pin+16, pin, 4);
int hash_length = server->serverMajorVersion >= 7 ? 32 : 20; int hash_length = server->serverMajorVersion >= 7 ? 32 : 20;
if (server->serverMajorVersion >= 7) if (server->serverMajorVersion >= 7)
SHA256(salt_pin, sizeof(salt_pin), aes_key); SHA256(salt_pin, 20, aes_key_hash);
else else
SHA1(salt_pin, sizeof(salt_pin), aes_key); 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);
unsigned char challenge_data[16]; unsigned char challenge_data[16];
unsigned char challenge_enc[sizeof(challenge_data)]; unsigned char challenge_enc[16];
char challenge_hex[SIZEOF_AS_HEX_STR(challenge_enc)]; char challenge_hex[33];
RAND_bytes(challenge_data, sizeof(challenge_data)); RAND_bytes(challenge_data, 16);
encrypt(challenge_data, sizeof(challenge_data), aes_key, challenge_enc); AES_encrypt(challenge_data, challenge_enc, &enc_key);
bytes_to_hex(challenge_enc, challenge_hex, sizeof(challenge_enc)); bytes_to_hex(challenge_enc, challenge_hex, 16);
uuid_generate_random(uuid); uuid_generate_random(uuid);
uuid_unparse(uuid, uuid_str); uuid_unparse(uuid, uuid_str);
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); snprintf(url, sizeof(url), "http://%s:47989/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&clientchallenge=%s", server->serverInfo.address, unique_id, uuid_str, challenge_hex);
if ((ret = http_request(url, data)) != GS_OK) if ((ret = http_request(url, data)) != GS_OK)
goto cleanup; goto cleanup;
@ -523,42 +475,42 @@ int gs_pair(PSERVER_DATA server, char* pin) {
goto cleanup; goto cleanup;
} }
char challenge_response_data_enc[64]; char challenge_response_data_enc[48];
char challenge_response_data[sizeof(challenge_response_data_enc)]; char challenge_response_data[48];
for (int count = 0; count < strlen(result); count += 2) {
if (strlen(result) / 2 > sizeof(challenge_response_data_enc)) { sscanf(&result[count], "%2hhx", &challenge_response_data_enc[count / 2]);
gs_error = "Server challenge response too big";
ret = GS_FAILED;
goto cleanup;
} }
hex_to_bytes(result, challenge_response_data_enc, strlen(result)); for (int i = 0; i < 48; i += 16) {
decrypt(challenge_response_data_enc, sizeof(challenge_response_data_enc), aes_key, challenge_response_data); AES_decrypt(&challenge_response_data_enc[i], &challenge_response_data[i], &dec_key);
}
char client_secret_data[16]; char client_secret_data[16];
RAND_bytes(client_secret_data, sizeof(client_secret_data)); RAND_bytes(client_secret_data, 16);
const ASN1_BIT_STRING *asnSignature; const ASN1_BIT_STRING *asnSignature;
X509_get0_signature(&asnSignature, NULL, cert); X509_get0_signature(&asnSignature, NULL, cert);
challenge_response = malloc(16 + asnSignature->length + sizeof(client_secret_data)); char challenge_response[16 + 256 + 16];
char challenge_response_hash[32]; char challenge_response_hash[32];
char challenge_response_hash_enc[sizeof(challenge_response_hash)]; char challenge_response_hash_enc[32];
char challenge_response_hex[SIZEOF_AS_HEX_STR(challenge_response_hash_enc)]; char challenge_response_hex[65];
memcpy(challenge_response, challenge_response_data + hash_length, 16); memcpy(challenge_response, challenge_response_data + hash_length, 16);
memcpy(challenge_response + 16, asnSignature->data, asnSignature->length); memcpy(challenge_response + 16, asnSignature->data, 256);
memcpy(challenge_response + 16 + asnSignature->length, client_secret_data, sizeof(client_secret_data)); memcpy(challenge_response + 16 + 256, client_secret_data, 16);
if (server->serverMajorVersion >= 7) if (server->serverMajorVersion >= 7)
SHA256(challenge_response, 16 + asnSignature->length + sizeof(client_secret_data), challenge_response_hash); SHA256(challenge_response, 16 + 256 + 16, challenge_response_hash);
else else
SHA1(challenge_response, 16 + asnSignature->length + sizeof(client_secret_data), challenge_response_hash); SHA1(challenge_response, 16 + 256 + 16, challenge_response_hash);
encrypt(challenge_response_hash, sizeof(challenge_response_hash), aes_key, challenge_response_hash_enc); for (int i = 0; i < 32; i += 16) {
bytes_to_hex(challenge_response_hash_enc, challenge_response_hex, sizeof(challenge_response_hash_enc)); AES_encrypt(&challenge_response_hash[i], &challenge_response_hash_enc[i], &enc_key);
}
bytes_to_hex(challenge_response_hash_enc, challenge_response_hex, 32);
uuid_generate_random(uuid); uuid_generate_random(uuid);
uuid_unparse(uuid, uuid_str); uuid_unparse(uuid, uuid_str);
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); snprintf(url, sizeof(url), "http://%s:47989/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&serverchallengeresp=%s", server->serverInfo.address, unique_id, uuid_str, challenge_response_hex);
if ((ret = http_request(url, data)) != GS_OK) if ((ret = http_request(url, data)) != GS_OK)
goto cleanup; goto cleanup;
@ -582,15 +534,12 @@ int gs_pair(PSERVER_DATA server, char* pin) {
goto cleanup; goto cleanup;
} }
size_t pairing_secret_len = strlen(result) / 2; char pairing_secret[16 + 256];
if (pairing_secret_len <= 16) { for (int count = 0; count < strlen(result); count += 2) {
ret = GS_INVALID; sscanf(&result[count], "%2hhx", &pairing_secret[count / 2]);
goto cleanup;
} }
pairing_secret = malloc(pairing_secret_len); if (!verifySignature(pairing_secret, 16, pairing_secret+16, 256, plaincert)) {
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"; gs_error = "MITM attack detected";
ret = GS_FAILED; ret = GS_FAILED;
goto cleanup; goto cleanup;
@ -598,21 +547,21 @@ int gs_pair(PSERVER_DATA server, char* pin) {
unsigned char *signature = NULL; unsigned char *signature = NULL;
size_t s_len; size_t s_len;
if (sign_it(client_secret_data, sizeof(client_secret_data), &signature, &s_len, privateKey) != GS_OK) { if (sign_it(client_secret_data, 16, &signature, &s_len, privateKey) != GS_OK) {
gs_error = "Failed to sign data"; gs_error = "Failed to sign data";
ret = GS_FAILED; ret = GS_FAILED;
goto cleanup; goto cleanup;
} }
client_pairing_secret = malloc(sizeof(client_secret_data) + s_len); char client_pairing_secret[16 + 256];
client_pairing_secret_hex = malloc(LEN_AS_HEX_STR(sizeof(client_secret_data) + s_len)); char client_pairing_secret_hex[(16 + 256) * 2 + 1];
memcpy(client_pairing_secret, client_secret_data, sizeof(client_secret_data)); memcpy(client_pairing_secret, client_secret_data, 16);
memcpy(client_pairing_secret + sizeof(client_secret_data), signature, s_len); memcpy(client_pairing_secret + 16, signature, 256);
bytes_to_hex(client_pairing_secret, client_pairing_secret_hex, sizeof(client_secret_data) + s_len); bytes_to_hex(client_pairing_secret, client_pairing_secret_hex, 16 + 256);
uuid_generate_random(uuid); uuid_generate_random(uuid);
uuid_unparse(uuid, uuid_str); uuid_unparse(uuid, uuid_str);
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); snprintf(url, sizeof(url), "http://%s:47989/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&clientpairingsecret=%s", server->serverInfo.address, unique_id, uuid_str, client_pairing_secret_hex);
if ((ret = http_request(url, data)) != GS_OK) if ((ret = http_request(url, data)) != GS_OK)
goto cleanup; goto cleanup;
@ -631,7 +580,7 @@ int gs_pair(PSERVER_DATA server, char* pin) {
uuid_generate_random(uuid); uuid_generate_random(uuid);
uuid_unparse(uuid, uuid_str); uuid_unparse(uuid, 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); snprintf(url, sizeof(url), "https://%s:47984/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&phrase=pairchallenge", server->serverInfo.address, unique_id, uuid_str);
if ((ret = http_request(url, data)) != GS_OK) if ((ret = http_request(url, data)) != GS_OK)
goto cleanup; goto cleanup;
@ -654,23 +603,11 @@ int gs_pair(PSERVER_DATA server, char* pin) {
if (ret != GS_OK) if (ret != GS_OK)
gs_unpair(server); gs_unpair(server);
free(url); if (result != NULL)
free(plaincert); free(result);
free(challenge_response);
free(pairing_secret);
free(client_pairing_secret);
free(client_pairing_secret_hex);
free(result);
http_free_data(data); http_free_data(data);
// If we failed when attempting to pair with a game running, that's likely the issue.
// Sunshine supports pairing with an active session, but GFE does not.
if (ret != GS_OK && server->currentGame != 0) {
gs_error = "The computer is currently in a game. You must close the game before pairing.";
ret = GS_WRONG_STATE;
}
return ret; return ret;
} }
@ -678,14 +615,14 @@ int gs_applist(PSERVER_DATA server, PAPP_LIST *list) {
int ret = GS_OK; int ret = GS_OK;
char url[4096]; char url[4096];
uuid_t uuid; uuid_t uuid;
char uuid_str[UUID_STRLEN]; char uuid_str[37];
PHTTP_DATA data = http_create_data(); PHTTP_DATA data = http_create_data();
if (data == NULL) if (data == NULL)
return GS_OUT_OF_MEMORY; return GS_OUT_OF_MEMORY;
uuid_generate_random(uuid); uuid_generate_random(uuid);
uuid_unparse(uuid, uuid_str); uuid_unparse(uuid, uuid_str);
snprintf(url, sizeof(url), "https://%s:%u/applist?uniqueid=%s&uuid=%s", server->serverInfo.address, server->httpsPort, unique_id, uuid_str); snprintf(url, sizeof(url), "https://%s:47984/applist?uniqueid=%s&uuid=%s", server->serverInfo.address, unique_id, uuid_str);
if (http_request(url, data) != GS_OK) if (http_request(url, data) != GS_OK)
ret = GS_IO_ERROR; ret = GS_IO_ERROR;
else if (xml_status(data->memory, data->size) == GS_ERROR) else if (xml_status(data->memory, data->size) == GS_ERROR)
@ -701,12 +638,14 @@ int gs_start_app(PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, b
int ret = GS_OK; int ret = GS_OK;
uuid_t uuid; uuid_t uuid;
char* result = NULL; char* result = NULL;
char uuid_str[UUID_STRLEN]; char uuid_str[37];
PDISPLAY_MODE mode = server->modes; PDISPLAY_MODE mode = server->modes;
bool correct_mode = false; bool correct_mode = false;
bool supported_resolution = false;
while (mode != NULL) { while (mode != NULL) {
if (mode->width == config->width && mode->height == config->height) { if (mode->width == config->width && mode->height == config->height) {
supported_resolution = true;
if (mode->refresh == config->fps) if (mode->refresh == config->fps)
correct_mode = true; correct_mode = true;
} }
@ -717,34 +656,39 @@ int gs_start_app(PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, b
if (!correct_mode && !server->unsupported) if (!correct_mode && !server->unsupported)
return GS_NOT_SUPPORTED_MODE; return GS_NOT_SUPPORTED_MODE;
RAND_bytes(config->remoteInputAesKey, sizeof(config->remoteInputAesKey)); if (config->height >= 2160 && !server->supports4K)
memset(config->remoteInputAesIv, 0, sizeof(config->remoteInputAesIv)); return GS_NOT_SUPPORTED_4K;
RAND_bytes(config->remoteInputAesKey, 16);
memset(config->remoteInputAesIv, 0, 16);
// GFE somehow doesn't like this totally legit random number, so we have to generate one
RAND_bytes(config->remoteInputAesIv, 4);
srand(time(NULL));
char url[4096]; char url[4096];
u_int32_t rikeyid = 0; u_int32_t rikeyid = 0;
RAND_bytes(config->remoteInputAesIv, sizeof(rikeyid)); memcpy(&rikeyid, config->remoteInputAesIv, 4);
memcpy(&rikeyid, config->remoteInputAesIv, sizeof(rikeyid));
rikeyid = htonl(rikeyid); rikeyid = htonl(rikeyid);
char rikey_hex[SIZEOF_AS_HEX_STR(config->remoteInputAesKey)]; char rikey_hex[33];
bytes_to_hex(config->remoteInputAesKey, rikey_hex, sizeof(config->remoteInputAesKey)); bytes_to_hex(config->remoteInputAesKey, rikey_hex, 16);
PHTTP_DATA data = http_create_data(); PHTTP_DATA data = http_create_data();
if (data == NULL) if (data == NULL)
return GS_OUT_OF_MEMORY; return GS_OUT_OF_MEMORY;
// Using an FPS value over 60 causes SOPS to default to 720p60,
// so force it to 0 to ensure the correct resolution is set. We
// used to use 60 here but that locked the frame rate to 60 FPS
// on GFE 3.20.3.
int fps = (server->isNvidiaSoftware && config->fps > 60) ? 0 : config->fps;
uuid_generate_random(uuid); uuid_generate_random(uuid);
uuid_unparse(uuid, uuid_str); uuid_unparse(uuid, uuid_str);
int surround_info = SURROUNDAUDIOINFO_FROM_AUDIO_CONFIGURATION(config->audioConfiguration); int surround_info = SURROUNDAUDIOINFO_FROM_AUDIO_CONFIGURATION(config->audioConfiguration);
snprintf(url, sizeof(url), "https://%s:%u/%s?uniqueid=%s&uuid=%s&appid=%d&mode=%dx%dx%d&additionalStates=1&sops=%d&rikey=%s&rikeyid=%d&localAudioPlayMode=%d&surroundAudioInfo=%d&remoteControllersBitmap=%d&gcmap=%d%s%s", if (server->currentGame == 0) {
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, // Using an FPS value over 60 causes SOPS to default to 720p60,
(config->supportedVideoFormats & VIDEO_FORMAT_MASK_10BIT) ? "&hdrMode=1&clientHdrCapVersion=0&clientHdrCapSupportedFlagsInUint32=0&clientHdrCapMetaDataId=NV_STATIC_METADATA_TYPE_1&clientHdrCapDisplayData=0x0x0x0x0x0x0x0x0x0x0" : "", // so force it to 0 to ensure the correct resolution is set. We
LiGetLaunchUrlQueryParameters()); // used to use 60 here but that locked the frame rate to 60 FPS
// on GFE 3.20.3.
int fps = config->fps > 60 ? 0 : config->fps;
snprintf(url, sizeof(url), "https://%s:47984/launch?uniqueid=%s&uuid=%s&appid=%d&mode=%dx%dx%d&additionalStates=1&sops=%d&rikey=%s&rikeyid=%d&localAudioPlayMode=%d&surroundAudioInfo=%d&remoteControllersBitmap=%d&gcmap=%d", server->serverInfo.address, unique_id, uuid_str, appId, config->width, config->height, fps, sops, rikey_hex, rikeyid, localaudio, surround_info, gamepad_mask, gamepad_mask);
} else
snprintf(url, sizeof(url), "https://%s:47984/resume?uniqueid=%s&uuid=%s&rikey=%s&rikeyid=%d&surroundAudioInfo=%d", server->serverInfo.address, unique_id, uuid_str, rikey_hex, rikeyid, surround_info);
if ((ret = http_request(url, data)) == GS_OK) if ((ret = http_request(url, data)) == GS_OK)
server->currentGame = appId; server->currentGame = appId;
else else
@ -752,8 +696,7 @@ int gs_start_app(PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, b
if ((ret = xml_status(data->memory, data->size) != GS_OK)) if ((ret = xml_status(data->memory, data->size) != GS_OK))
goto cleanup; goto cleanup;
else if ((ret = xml_search(data->memory, data->size, "gamesession", &result)) != GS_OK && else if ((ret = xml_search(data->memory, data->size, "gamesession", &result)) != GS_OK)
(ret = xml_search(data->memory, data->size, "resume", &result)) != GS_OK)
goto cleanup; goto cleanup;
if (!strcmp(result, "0")) { if (!strcmp(result, "0")) {
@ -781,7 +724,7 @@ int gs_quit_app(PSERVER_DATA server) {
int ret = GS_OK; int ret = GS_OK;
char url[4096]; char url[4096];
uuid_t uuid; uuid_t uuid;
char uuid_str[UUID_STRLEN]; char uuid_str[37];
char* result = NULL; char* result = NULL;
PHTTP_DATA data = http_create_data(); PHTTP_DATA data = http_create_data();
if (data == NULL) if (data == NULL)
@ -789,7 +732,7 @@ int gs_quit_app(PSERVER_DATA server) {
uuid_generate_random(uuid); uuid_generate_random(uuid);
uuid_unparse(uuid, uuid_str); uuid_unparse(uuid, uuid_str);
snprintf(url, sizeof(url), "https://%s:%u/cancel?uniqueid=%s&uuid=%s", server->serverInfo.address, server->httpsPort, unique_id, uuid_str); snprintf(url, sizeof(url), "https://%s:47984/cancel?uniqueid=%s&uuid=%s", server->serverInfo.address, unique_id, uuid_str);
if ((ret = http_request(url, data)) != GS_OK) if ((ret = http_request(url, data)) != GS_OK)
goto cleanup; goto cleanup;
@ -811,7 +754,7 @@ int gs_quit_app(PSERVER_DATA server) {
return ret; return ret;
} }
int gs_init(PSERVER_DATA server, char *address, unsigned short httpPort, const char *keyDirectory, int log_level, bool unsupported) { int gs_init(PSERVER_DATA server, char *address, const char *keyDirectory, int log_level, bool unsupported) {
mkdirtree(keyDirectory); mkdirtree(keyDirectory);
if (load_unique_id(keyDirectory) != GS_OK) if (load_unique_id(keyDirectory) != GS_OK)
return GS_FAILED; return GS_FAILED;
@ -824,7 +767,5 @@ int gs_init(PSERVER_DATA server, char *address, unsigned short httpPort, const c
LiInitializeServerInformation(&server->serverInfo); LiInitializeServerInformation(&server->serverInfo);
server->serverInfo.address = address; server->serverInfo.address = address;
server->unsupported = unsupported; server->unsupported = unsupported;
server->httpPort = httpPort ? httpPort : 47989;
server->httpsPort = 0; /* Populated by load_server_status() */
return load_server_status(server); return load_server_status(server);
} }

View File

@ -31,18 +31,16 @@
typedef struct _SERVER_DATA { typedef struct _SERVER_DATA {
char* gpuType; char* gpuType;
bool paired; bool paired;
bool supports4K;
bool unsupported; bool unsupported;
bool isNvidiaSoftware;
int currentGame; int currentGame;
int serverMajorVersion; int serverMajorVersion;
char* gsVersion; char* gsVersion;
PDISPLAY_MODE modes; PDISPLAY_MODE modes;
SERVER_INFORMATION serverInfo; SERVER_INFORMATION serverInfo;
unsigned short httpPort;
unsigned short httpsPort;
} SERVER_DATA, *PSERVER_DATA; } SERVER_DATA, *PSERVER_DATA;
int gs_init(PSERVER_DATA server, char* address, unsigned short httpPort, const char *keyDirectory, int logLevel, bool unsupported); int gs_init(PSERVER_DATA server, char* address, const char *keyDirectory, int logLevel, bool unsupported);
int gs_start_app(PSERVER_DATA server, PSTREAM_CONFIGURATION config, int appId, bool sops, bool localaudio, int gamepad_mask); int gs_start_app(PSERVER_DATA server, PSTREAM_CONFIGURATION config, int appId, bool sops, bool localaudio, int gamepad_mask);
int gs_applist(PSERVER_DATA server, PAPP_LIST *app_list); int gs_applist(PSERVER_DATA server, PAPP_LIST *app_list);
int gs_unpair(PSERVER_DATA server); int gs_unpair(PSERVER_DATA server);

View File

@ -33,11 +33,6 @@
static AvahiSimplePoll *simple_poll = NULL; static AvahiSimplePoll *simple_poll = NULL;
struct cb_ctx {
char* address;
unsigned short* port;
};
static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) { static void client_callback(AvahiClient *c, AvahiClientState state, void *userdata) {
if (state == AVAHI_CLIENT_FAILURE) { if (state == AVAHI_CLIENT_FAILURE) {
gs_error = "Server connection failure"; gs_error = "Server connection failure";
@ -48,14 +43,12 @@ static void client_callback(AvahiClient *c, AvahiClientState state, void *userda
static void resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void *userdata) { static void resolve_callback(AvahiServiceResolver *r, AvahiIfIndex interface, AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, const char *domain, const char *host_name, const AvahiAddress *address, uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, void *userdata) {
if (event == AVAHI_RESOLVER_FOUND) { if (event == AVAHI_RESOLVER_FOUND) {
if (userdata != NULL) { if (userdata != NULL) {
struct cb_ctx* ctx = userdata; avahi_address_snprint(userdata, AVAHI_ADDRESS_STR_MAX, address);
avahi_address_snprint(ctx->address, AVAHI_ADDRESS_STR_MAX, address);
*ctx->port = port;
avahi_simple_poll_quit(simple_poll); avahi_simple_poll_quit(simple_poll);
} else { } else {
char strAddress[AVAHI_ADDRESS_STR_MAX]; char strAddress[AVAHI_ADDRESS_STR_MAX];
avahi_address_snprint(strAddress, sizeof(strAddress), address); avahi_address_snprint(strAddress, sizeof(strAddress), address);
printf(" %s (%s:%u)\n", host_name, strAddress, port); printf(" %s (%s)\n", host_name, strAddress);
} }
} }
@ -80,7 +73,7 @@ static void browse_callback(AvahiServiceBrowser *b, AvahiIfIndex interface, Avah
} }
} }
void gs_discover_server(char* dest, unsigned short* port) { void gs_discover_server(char* dest) {
AvahiClient *client = NULL; AvahiClient *client = NULL;
AvahiServiceBrowser *sb = NULL; AvahiServiceBrowser *sb = NULL;
@ -96,10 +89,7 @@ void gs_discover_server(char* dest, unsigned short* port) {
goto cleanup; goto cleanup;
} }
struct cb_ctx ctx; if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET, "_nvstream._tcp", NULL, 0, browse_callback, dest))) {
ctx.address = dest;
ctx.port = port;
if (!(sb = avahi_service_browser_new(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET, "_nvstream._tcp", NULL, 0, browse_callback, &ctx))) {
gs_error = "Failed to create service browser"; gs_error = "Failed to create service browser";
goto cleanup; goto cleanup;
} }

View File

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

View File

@ -26,6 +26,9 @@
static CURL *curl; static CURL *curl;
static const char *pCertFile = "./client.pem";
static const char *pKeyFile = "./key.pem";
static bool debug; static bool debug;
static size_t _write_curl(void *contents, size_t size, size_t nmemb, void *userp) static size_t _write_curl(void *contents, size_t size, size_t nmemb, void *userp)
@ -51,10 +54,10 @@ int http_init(const char* keyDirectory, int logLevel) {
return GS_FAILED; return GS_FAILED;
char certificateFilePath[4096]; char certificateFilePath[4096];
snprintf(certificateFilePath, sizeof(certificateFilePath), "%s/%s", keyDirectory, CERTIFICATE_FILE_NAME); sprintf(certificateFilePath, "%s/%s", keyDirectory, CERTIFICATE_FILE_NAME);
char keyFilePath[4096]; char keyFilePath[4096];
snprintf(keyFilePath, sizeof(keyFilePath), "%s/%s", keyDirectory, KEY_FILE_NAME); sprintf(&keyFilePath[0], "%s/%s", keyDirectory, KEY_FILE_NAME);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1L); curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1L);
@ -73,9 +76,6 @@ int http_init(const char* keyDirectory, int logLevel) {
int http_request(char* url, PHTTP_DATA data) { int http_request(char* url, PHTTP_DATA data) {
curl_easy_setopt(curl, CURLOPT_WRITEDATA, data); curl_easy_setopt(curl, CURLOPT_WRITEDATA, data);
curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_URL, url);
#ifdef __FreeBSD__
curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1);
#endif
if (debug) if (debug)
printf("Request %s\n", url); printf("Request %s\n", url);

View File

@ -39,6 +39,7 @@ CERT_KEY_PAIR mkcert_generate() {
EVP_PKEY *pkey = NULL; EVP_PKEY *pkey = NULL;
PKCS12 *p12 = NULL; PKCS12 *p12 = NULL;
CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
bio_err = BIO_new_fp(stderr, BIO_NOCLOSE); bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
OpenSSL_add_all_algorithms(); OpenSSL_add_all_algorithms();
@ -80,47 +81,71 @@ void mkcert_save(const char* certFile, const char* p12File, const char* keyPairF
} }
int mkcert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int years) { int mkcert(X509 **x509p, EVP_PKEY **pkeyp, int bits, int serial, int years) {
EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); X509 *x;
EVP_PKEY_keygen_init(ctx); EVP_PKEY *pk;
EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits); RSA *rsa;
X509_NAME *name = NULL;
// pk must be initialized on input if (*pkeyp == NULL) {
EVP_PKEY *pk = NULL;; if ((pk=EVP_PKEY_new()) == NULL) {
EVP_PKEY_keygen(ctx, &pk); abort();
return(0);
}
} else {
pk = *pkeyp;
}
EVP_PKEY_CTX_free(ctx); if (*x509p == NULL) {
if ((x = X509_new()) == NULL) {
goto err;
}
} else {
x = *x509p;
}
X509* cert = X509_new(); if ((rsa = RSA_new()) == NULL)
X509_set_version(cert, 2); goto err;
ASN1_INTEGER_set(X509_get_serialNumber(cert), serial);
#if OPENSSL_VERSION_NUMBER < 0x10100000L
X509_gmtime_adj(X509_get_notBefore(cert), 0);
X509_gmtime_adj(X509_get_notAfter(cert), 60 * 60 * 24 * 365 * years);
#else
ASN1_TIME* before = ASN1_STRING_dup(X509_get0_notBefore(cert));
ASN1_TIME* after = ASN1_STRING_dup(X509_get0_notAfter(cert));
X509_gmtime_adj(before, 0); BIGNUM* bne = BN_new();
X509_gmtime_adj(after, 60 * 60 * 24 * 365 * years); if (bne == NULL) {
abort();
X509_set1_notBefore(cert, before);
X509_set1_notAfter(cert, after);
ASN1_STRING_free(before);
ASN1_STRING_free(after);
#endif
X509_set_pubkey(cert, pk);
X509_NAME* name = X509_get_subject_name(cert);
X509_NAME_add_entry_by_txt(name,"CN", MBSTRING_ASC, (unsigned char*)"NVIDIA GameStream Client", -1, -1, 0);
X509_set_issuer_name(cert, name);
if (!X509_sign(cert, pk, EVP_sha256())) {
goto err; goto err;
} }
*x509p = cert; BN_set_word(bne, RSA_F4);
if (RSA_generate_key_ex(rsa, bits, bne, NULL) == 0) {
abort();
goto err;
}
if (!EVP_PKEY_assign_RSA(pk, rsa)) {
abort();
goto err;
}
X509_set_version(x, 2);
ASN1_INTEGER_set(X509_get_serialNumber(x), serial);
X509_gmtime_adj(X509_get_notBefore(x), 0);
X509_gmtime_adj(X509_get_notAfter(x), (long)60*60*24*365*years);
X509_set_pubkey(x, pk);
name = X509_get_subject_name(x);
/* This function creates and adds the entry, working out the
* correct string type and performing checks on its length.
*/
X509_NAME_add_entry_by_txt(name,"CN", MBSTRING_ASC, (unsigned char*)"NVIDIA GameStream Client", -1, -1, 0);
/* Its self signed so set the issuer name to be the same as the
* subject.
*/
X509_set_issuer_name(x, name);
if (!X509_sign(x, pk, EVP_sha256())) {
goto err;
}
*x509p = x;
*pkeyp = pk; *pkeyp = pk;
return(1); return(1);

View File

@ -31,9 +31,9 @@ void gs_sps_init(int width, int height) {
} }
void gs_sps_fix(PLENTRY sps, int flags, uint8_t* out_buf, uint32_t* out_offset) { void gs_sps_fix(PLENTRY sps, int flags, uint8_t* out_buf, uint32_t* out_offset) {
int start_len = sps->data[2] == 0x01 ? 3 : 4; const char naluHeader[] = {0x00, 0x00, 0x00, 0x01};
read_nal_unit(h264_stream, sps->data+start_len, sps->length-start_len); read_nal_unit(h264_stream, sps->data+4, sps->length-4);
// Some decoders rely on H264 level to decide how many buffers are needed // Some decoders rely on H264 level to decide how many buffers are needed
// Since we only need one frame buffered, we'll set level as low as we can // Since we only need one frame buffered, we'll set level as low as we can
@ -76,8 +76,8 @@ void gs_sps_fix(PLENTRY sps, int flags, uint8_t* out_buf, uint32_t* out_offset)
h264_stream->sps->vui.max_bits_per_mb_denom = 1; h264_stream->sps->vui.max_bits_per_mb_denom = 1;
} }
memcpy(out_buf+*out_offset, sps->data, start_len); memcpy(out_buf+*out_offset, naluHeader, 4);
*out_offset += start_len; *out_offset += 4;
*out_offset += write_nal_unit(h264_stream, out_buf+*out_offset, 128); *out_offset += write_nal_unit(h264_stream, out_buf+*out_offset, 128);
} }

View File

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

View File

@ -7,10 +7,6 @@
#height = 720 #height = 720
#fps = 60 #fps = 60
## Output rotation (independent of xrandr or framebuffer settings!)
## Allowed values: 0, 90, 180, 270
#rotate = 0
## Bitrate depends by default on resolution and fps ## Bitrate depends by default on resolution and fps
## Set to -1 to enable default ## Set to -1 to enable default
## 20Mbps (20000) for 1080p (60 fps) ## 20Mbps (20000) for 1080p (60 fps)

View File

@ -57,8 +57,8 @@ static int alsa_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGUR
snd_pcm_hw_params_t *hw_params; snd_pcm_hw_params_t *hw_params;
snd_pcm_sw_params_t *sw_params; snd_pcm_sw_params_t *sw_params;
snd_pcm_uframes_t period_size = (opusConfig->sampleRate * 20) / 1000; // 20 ms period snd_pcm_uframes_t period_size = samplesPerFrame * FRAME_BUFFER;
snd_pcm_uframes_t buffer_size = 3 * period_size; // 60 ms buffer snd_pcm_uframes_t buffer_size = 2 * period_size;
unsigned int sampleRate = opusConfig->sampleRate; unsigned int sampleRate = opusConfig->sampleRate;
char* audio_device = (char*) context; char* audio_device = (char*) context;
@ -84,7 +84,7 @@ static int alsa_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGUR
CHECK_RETURN(snd_pcm_sw_params_malloc(&sw_params)); CHECK_RETURN(snd_pcm_sw_params_malloc(&sw_params));
CHECK_RETURN(snd_pcm_sw_params_current(handle, sw_params)); CHECK_RETURN(snd_pcm_sw_params_current(handle, sw_params));
CHECK_RETURN(snd_pcm_sw_params_set_avail_min(handle, sw_params, period_size)); CHECK_RETURN(snd_pcm_sw_params_set_avail_min(handle, sw_params, period_size));
CHECK_RETURN(snd_pcm_sw_params_set_start_threshold(handle, sw_params, period_size)); CHECK_RETURN(snd_pcm_sw_params_set_start_threshold(handle, sw_params, 1));
CHECK_RETURN(snd_pcm_sw_params(handle, sw_params)); CHECK_RETURN(snd_pcm_sw_params(handle, sw_params));
snd_pcm_sw_params_free(sw_params); snd_pcm_sw_params_free(sw_params);
@ -115,17 +115,14 @@ static void alsa_renderer_decode_and_play_sample(char* data, int length) {
int decodeLen = opus_multistream_decode(decoder, data, length, pcmBuffer, samplesPerFrame, 0); int decodeLen = opus_multistream_decode(decoder, data, length, pcmBuffer, samplesPerFrame, 0);
if (decodeLen > 0) { if (decodeLen > 0) {
int rc = snd_pcm_writei(handle, pcmBuffer, decodeLen); int rc = snd_pcm_writei(handle, pcmBuffer, decodeLen);
if (rc < 0) { if (rc == -EPIPE)
rc = snd_pcm_recover(handle, rc, 0); snd_pcm_recover(handle, rc, 1);
if (rc == 0)
rc = snd_pcm_writei(handle, pcmBuffer, decodeLen);
}
if (rc<0) if (rc<0)
printf("Alsa error from writei: %d\n", rc); printf("Alsa error from writei: %d\n", rc);
else if (decodeLen != rc) else if (decodeLen != rc)
printf("Alsa shortm write, write %d frames\n", rc); printf("Alsa shortm write, write %d frames\n", rc);
} else if (decodeLen < 0) { } else {
printf("Opus error from decode: %d\n", decodeLen); printf("Opus error from decode: %d\n", decodeLen);
} }
} }

View File

@ -21,6 +21,8 @@
#include <Limelight.h> #include <Limelight.h>
#define FRAME_BUFFER 12
#ifdef HAVE_ALSA #ifdef HAVE_ALSA
extern AUDIO_RENDERER_CALLBACKS audio_callbacks_alsa; extern AUDIO_RENDERER_CALLBACKS audio_callbacks_alsa;
#endif #endif
@ -31,6 +33,3 @@ extern AUDIO_RENDERER_CALLBACKS audio_callbacks_sdl;
extern AUDIO_RENDERER_CALLBACKS audio_callbacks_pulse; extern AUDIO_RENDERER_CALLBACKS audio_callbacks_pulse;
bool audio_pulse_init(char* audio_device); bool audio_pulse_init(char* audio_device);
#endif #endif
#ifdef __FreeBSD__
extern AUDIO_RENDERER_CALLBACKS audio_callbacks_oss;
#endif

View File

@ -34,7 +34,7 @@ static int channelCount;
static int samplesPerFrame; static int samplesPerFrame;
static int omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int arFlags) { static int omx_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int arFlags) {
int rc; int rc, error;
OMX_ERRORTYPE err; OMX_ERRORTYPE err;
unsigned char omxMapping[AUDIO_CONFIGURATION_MAX_CHANNEL_COUNT]; unsigned char omxMapping[AUDIO_CONFIGURATION_MAX_CHANNEL_COUNT];
char* componentName = "audio_render"; char* componentName = "audio_render";
@ -203,7 +203,7 @@ static void omx_renderer_decode_and_play_sample(char* data, int length) {
if (r != OMX_ErrorNone) { if (r != OMX_ErrorNone) {
fprintf(stderr, "Empty buffer error\n"); fprintf(stderr, "Empty buffer error\n");
} }
} else if (decodeLen < 0) { } else {
printf("Opus error from decode: %d\n", decodeLen); printf("Opus error from decode: %d\n", decodeLen);
} }
} }

View File

@ -1,105 +0,0 @@
/*
* This file is part of Moonlight Embedded.
*
* Copyright (C) 2015-2017 Iwan Timmer
*
* Moonlight is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Moonlight is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef __FreeBSD__
#include <sys/soundcard.h>
#include <sys/ioctl.h>
#include "audio.h"
#include <opus_multistream.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static OpusMSDecoder* decoder;
static short* pcmBuffer;
static int samplesPerFrame;
static int channelCount;
static int fd = -1;
static int oss_renderer_init(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int arFlags) {
int rc;
decoder = opus_multistream_decoder_create(opusConfig->sampleRate, opusConfig->channelCount, opusConfig->streams, opusConfig->coupledStreams, opusConfig->mapping, &rc);
channelCount = opusConfig->channelCount;
samplesPerFrame = opusConfig->samplesPerFrame;
pcmBuffer = malloc(sizeof(short) * channelCount * samplesPerFrame);
if (pcmBuffer == NULL)
return -1;
const char* oss_name = "/dev/dsp";
fd = open(oss_name, O_WRONLY);
if (fd == -1) {
printf("Open audio device /dev/dsp failed! error %d\n", errno);
return -1;
}
// buffer size for fragment ,selector 12 is 4096;11 is 2048;10 is 1024; 13is 8192
int frag = 12;
if (ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &frag) == -1)
printf("Set fragment for /dev/dsp failed.");
int format = AFMT_S16_LE;
int channels = opusConfig->channelCount;
int rate = opusConfig->sampleRate;
if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) == -1)
printf("Set format for /dev/dsp failed.");
if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1)
printf("Set channels for /dev/dsp failed.");
if (ioctl(fd, SNDCTL_DSP_SPEED, &rate) == -1)
printf("Set sample rate for /dev/dsp failed.");
return 0;
}
static void oss_renderer_cleanup() {
if (decoder != NULL) {
opus_multistream_decoder_destroy(decoder);
decoder = NULL;
}
if (pcmBuffer != NULL) {
free(pcmBuffer);
pcmBuffer = NULL;
}
if (fd != -1) {
close(fd);
fd = -1;
}
}
static void oss_renderer_decode_and_play_sample(char* data, int length) {
int decodeLen = opus_multistream_decode(decoder, data, length, pcmBuffer, samplesPerFrame, 0);
if (decodeLen > 0) {
write(fd, pcmBuffer, decodeLen * channelCount * sizeof(short));
} else if (decodeLen < 0) {
printf("Opus error from decode: %d\n", decodeLen);
}
}
AUDIO_RENDERER_CALLBACKS audio_callbacks_oss = {
.init = oss_renderer_init,
.cleanup = oss_renderer_cleanup,
.decodeAndPlaySample = oss_renderer_decode_and_play_sample,
.capabilities = CAPABILITY_DIRECT_SUBMIT | CAPABILITY_SUPPORTS_ARBITRARY_AUDIO_DURATION,
};
#endif

View File

@ -101,7 +101,7 @@ static void pulse_renderer_decode_and_play_sample(char* data, int length) {
if (rc<0) if (rc<0)
printf("Pulseaudio error: %s\n", pa_strerror(error)); printf("Pulseaudio error: %s\n", pa_strerror(error));
} else if (decodeLen < 0) { } else {
printf("Opus error from decode: %d\n", decodeLen); printf("Opus error from decode: %d\n", decodeLen);
} }
} }

View File

@ -82,7 +82,7 @@ static void sdl_renderer_decode_and_play_sample(char* data, int length) {
int decodeLen = opus_multistream_decode(decoder, data, length, pcmBuffer, samplesPerFrame, 0); int decodeLen = opus_multistream_decode(decoder, data, length, pcmBuffer, samplesPerFrame, 0);
if (decodeLen > 0) { if (decodeLen > 0) {
SDL_QueueAudio(dev, pcmBuffer, decodeLen * channelCount * sizeof(short)); SDL_QueueAudio(dev, pcmBuffer, decodeLen * channelCount * sizeof(short));
} else if (decodeLen < 0) { } else {
printf("Opus error from decode: %d\n", decodeLen); printf("Opus error from decode: %d\n", decodeLen);
} }
} }

View File

@ -17,10 +17,8 @@
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>. * along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
*/ */
#include "platform.h"
#include "config.h" #include "config.h"
#include "util.h" #include "util.h"
#include "cpu.h"
#include "input/evdev.h" #include "input/evdev.h"
#include "audio/audio.h" #include "audio/audio.h"
@ -74,9 +72,6 @@ static struct option long_options[] = {
{"verbose", no_argument, NULL, 'z'}, {"verbose", no_argument, NULL, 'z'},
{"debug", no_argument, NULL, 'Z'}, {"debug", no_argument, NULL, 'Z'},
{"nomouseemulation", no_argument, NULL, '4'}, {"nomouseemulation", no_argument, NULL, '4'},
{"pin", required_argument, NULL, '5'},
{"port", required_argument, NULL, '6'},
{"hdr", no_argument, NULL, '7'},
{0, 0, 0, 0}, {0, 0, 0, 0},
}; };
@ -227,8 +222,6 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) {
config->codec = CODEC_H264; config->codec = CODEC_H264;
else if (strcasecmp(value, "h265") == 0 || strcasecmp(value, "hevc") == 0) else if (strcasecmp(value, "h265") == 0 || strcasecmp(value, "hevc") == 0)
config->codec = CODEC_HEVC; config->codec = CODEC_HEVC;
else if (strcasecmp(value, "av1") == 0)
config->codec = CODEC_AV1;
break; break;
case 'y': case 'y':
config->unsupported = false; config->unsupported = false;
@ -251,15 +244,6 @@ static void parse_argument(int c, char* value, PCONFIGURATION config) {
case '4': case '4':
config->mouse_emulation = false; config->mouse_emulation = false;
break; break;
case '5':
config->pin = atoi(value);
break;
case '6':
config->port = atoi(value);
break;
case '7':
config->hdr = true;
break;
case 1: case 1:
if (config->action == NULL) if (config->action == NULL)
config->action = value; config->action = value;
@ -348,20 +332,21 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) {
config->stream.packetSize = 1392; config->stream.packetSize = 1392;
config->stream.streamingRemotely = STREAM_CFG_AUTO; config->stream.streamingRemotely = STREAM_CFG_AUTO;
config->stream.audioConfiguration = AUDIO_CONFIGURATION_STEREO; config->stream.audioConfiguration = AUDIO_CONFIGURATION_STEREO;
config->stream.supportedVideoFormats = SCM_H264; config->stream.supportsHevc = false;
config->stream.encryptionFlags = ENCFLG_AUDIO;
// Opt in for video encryption if the CPU has good AES performance #ifdef __arm__
if (has_fast_aes()) { char cpuinfo[4096] = {};
config->stream.encryptionFlags = ENCFLG_ALL; 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
else if (has_slow_aes()) { // powerful enough to handle audio encryption. The Pi 1 could
// For extremely slow CPUs, opt out of audio encryption // barely handle Opus decoding alone.
config->stream.encryptionFlags = ENCFLG_NONE; if (strstr(cpuinfo, "ARMv6")) {
printf("Disabling encryption on low performance CPU\n"); config->stream.encryptionFlags = ENCFLG_NONE;
} printf("Disabling audio encryption on low performance CPU\n");
else { }
config->stream.encryptionFlags = ENCFLG_AUDIO;
} }
#endif
config->debug_level = 0; config->debug_level = 0;
config->platform = "auto"; config->platform = "auto";
@ -379,9 +364,6 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) {
config->mouse_emulation = true; config->mouse_emulation = true;
config->rotate = 0; config->rotate = 0;
config->codec = CODEC_UNSPECIFIED; config->codec = CODEC_UNSPECIFIED;
config->hdr = false;
config->pin = 0;
config->port = 47989;
config->inputsCount = 0; config->inputsCount = 0;
config->mapping = get_path("gamecontrollerdb.txt", getenv("XDG_DATA_DIRS")); config->mapping = get_path("gamecontrollerdb.txt", getenv("XDG_DATA_DIRS"));
@ -399,7 +381,7 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) {
} else { } else {
int option_index = 0; int option_index = 0;
int c; int c;
while ((c = getopt_long_only(argc, argv, "-abc:d:efg:h:i:j:k:lm:no:p:q:r:s:tu:v:w:xy45:6:7", long_options, &option_index)) != -1) { while ((c = getopt_long_only(argc, argv, "-abc:d:efg:h:i:j:k:lm:no:p:q:r:s:tu:v:w:xy4", long_options, &option_index)) != -1) {
parse_argument(c, optarg, config); parse_argument(c, optarg, config);
} }
} }
@ -411,32 +393,19 @@ void config_parse(int argc, char* argv[], PCONFIGURATION config) {
struct passwd *pw = getpwuid(getuid()); struct passwd *pw = getpwuid(getuid());
const char *dir; const char *dir;
if ((dir = getenv("XDG_CACHE_DIR")) != NULL) if ((dir = getenv("XDG_CACHE_DIR")) != NULL)
snprintf(config->key_dir, sizeof(config->key_dir), "%s" MOONLIGHT_PATH, dir); sprintf(config->key_dir, "%s" MOONLIGHT_PATH, dir);
else if ((dir = getenv("HOME")) != NULL) else if ((dir = getenv("HOME")) != NULL)
snprintf(config->key_dir, sizeof(config->key_dir), "%s" DEFAULT_CACHE_DIR MOONLIGHT_PATH, dir); sprintf(config->key_dir, "%s" DEFAULT_CACHE_DIR MOONLIGHT_PATH, dir);
else else
snprintf(config->key_dir, sizeof(config->key_dir), "%s" DEFAULT_CACHE_DIR MOONLIGHT_PATH, pw->pw_dir); sprintf(config->key_dir, "%s" DEFAULT_CACHE_DIR MOONLIGHT_PATH, pw->pw_dir);
} }
if (config->stream.bitrate == -1) { if (config->stream.bitrate == -1) {
// This table prefers 16:10 resolutions because they are if (config->stream.height >= 1080 && config->stream.fps >= 60)
// only slightly more pixels than the 16:9 equivalents, so config->stream.bitrate = 20000;
// we don't want to bump those 16:10 resolutions up to the else if (config->stream.height >= 1080 || config->stream.fps >= 60)
// next 16:9 slot. config->stream.bitrate = 10000;
else
if (config->stream.width * config->stream.height <= 640 * 360) { config->stream.bitrate = 5000;
config->stream.bitrate = (int)(1000 * (config->stream.fps / 30.0));
} else if (config->stream.width * config->stream.height <= 854 * 480) {
config->stream.bitrate = (int)(1500 * (config->stream.fps / 30.0));
} else if (config->stream.width * config->stream.height <= 1366 * 768) {
// This covers 1280x720 and 1280x800 too
config->stream.bitrate = (int)(5000 * (config->stream.fps / 30.0));
} else if (config->stream.width * config->stream.height <= 1920 * 1200) {
config->stream.bitrate = (int)(10000 * (config->stream.fps / 30.0));
} else if (config->stream.width * config->stream.height <= 2560 * 1600) {
config->stream.bitrate = (int)(20000 * (config->stream.fps / 30.0));
} else /* if (config->stream.width * config->stream.height <= 3840 * 2160) */ {
config->stream.bitrate = (int)(40000 * (config->stream.fps / 30.0));
}
} }
} }

View File

@ -23,6 +23,8 @@
#define MAX_INPUTS 6 #define MAX_INPUTS 6
enum codecs { CODEC_UNSPECIFIED, CODEC_H264, CODEC_HEVC };
typedef struct _CONFIGURATION { typedef struct _CONFIGURATION {
STREAM_CONFIGURATION stream; STREAM_CONFIGURATION stream;
int debug_level; int debug_level;
@ -45,9 +47,6 @@ typedef struct _CONFIGURATION {
char* inputs[MAX_INPUTS]; char* inputs[MAX_INPUTS];
int inputsCount; int inputsCount;
enum codecs codec; enum codecs codec;
bool hdr;
int pin;
unsigned short port;
} CONFIGURATION, *PCONFIGURATION; } CONFIGURATION, *PCONFIGURATION;
extern bool inputAdded; extern bool inputAdded;

View File

@ -23,21 +23,13 @@
#include <stdarg.h> #include <stdarg.h>
#include <signal.h> #include <signal.h>
#ifdef HAVE_SDL
#include <SDL.h>
#endif
pthread_t main_thread_id = 0; pthread_t main_thread_id = 0;
bool connection_debug; bool connection_debug;
ConnListenerRumble rumble_handler = NULL; ConnListenerRumble rumble_handler = NULL;
ConnListenerRumbleTriggers rumble_triggers_handler = NULL;
ConnListenerSetMotionEventState set_motion_event_state_handler = NULL;
ConnListenerSetControllerLED set_controller_led_handler = NULL;
static void connection_terminated(int errorCode) { static void connection_terminated(int errorCode) {
switch (errorCode) { switch (errorCode) {
case ML_ERROR_GRACEFUL_TERMINATION: case ML_ERROR_GRACEFUL_TERMINATION:
printf("Connection has been terminated gracefully.\n");
break; break;
case ML_ERROR_NO_VIDEO_TRAFFIC: case ML_ERROR_NO_VIDEO_TRAFFIC:
printf("No video received from host. Check the host PC's firewall and port forwarding rules.\n"); printf("No video received from host. Check the host PC's firewall and port forwarding rules.\n");
@ -56,12 +48,6 @@ static void connection_terminated(int errorCode) {
break; break;
} }
#ifdef HAVE_SDL
SDL_Event event;
event.type = SDL_QUIT;
SDL_PushEvent(&event);
#endif
if (main_thread_id != 0) if (main_thread_id != 0)
pthread_kill(main_thread_id, SIGTERM); pthread_kill(main_thread_id, SIGTERM);
} }
@ -78,21 +64,6 @@ static void rumble(unsigned short controllerNumber, unsigned short lowFreqMotor,
rumble_handler(controllerNumber, lowFreqMotor, highFreqMotor); rumble_handler(controllerNumber, lowFreqMotor, highFreqMotor);
} }
static void rumble_triggers(unsigned short controllerNumber, unsigned short leftTrigger, unsigned short rightTrigger) {
if (rumble_handler)
rumble_triggers_handler(controllerNumber, leftTrigger, rightTrigger);
}
static void set_motion_event_state(unsigned short controllerNumber, unsigned char motionType, unsigned short reportRateHz) {
if (set_motion_event_state_handler)
set_motion_event_state_handler(controllerNumber, motionType, reportRateHz);
}
static void set_controller_led(unsigned short controllerNumber, unsigned char r, unsigned char g, unsigned char b) {
if (set_controller_led_handler)
set_controller_led_handler(controllerNumber, r, g, b);
}
static void connection_status_update(int status) { static void connection_status_update(int status) {
switch (status) { switch (status) {
case CONN_STATUS_OKAY: case CONN_STATUS_OKAY:
@ -112,9 +83,5 @@ CONNECTION_LISTENER_CALLBACKS connection_callbacks = {
.connectionTerminated = connection_terminated, .connectionTerminated = connection_terminated,
.logMessage = connection_log_message, .logMessage = connection_log_message,
.rumble = rumble, .rumble = rumble,
.connectionStatusUpdate = connection_status_update, .connectionStatusUpdate = connection_status_update
.setHdrMode = NULL,
.rumbleTriggers = rumble_triggers,
.setMotionEventState = set_motion_event_state,
.setControllerLED = set_controller_led,
}; };

View File

@ -26,6 +26,3 @@ extern CONNECTION_LISTENER_CALLBACKS connection_callbacks;
extern pthread_t main_thread_id; extern pthread_t main_thread_id;
extern bool connection_debug; extern bool connection_debug;
extern ConnListenerRumble rumble_handler; extern ConnListenerRumble rumble_handler;
extern ConnListenerRumbleTriggers rumble_triggers_handler;
extern ConnListenerSetMotionEventState set_motion_event_state_handler;
extern ConnListenerSetControllerLED set_controller_led_handler;

121
src/cpu.c
View File

@ -1,121 +0,0 @@
/*
* 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;
}

View File

@ -1,21 +0,0 @@
/*
* This file is part of Moonlight Embedded.
*
* Moonlight is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* Moonlight is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdbool.h>
bool has_fast_aes(void);
bool has_slow_aes(void);

View File

@ -90,7 +90,7 @@ void cec_init() {
g_iface.init_video_standalone(g_iface.connection); g_iface.init_video_standalone(g_iface.connection);
cec_adapter devices[10]; cec_adapter devices[10];
int8_t iDevicesFound = g_iface.find_adapters(g_iface.connection, devices, sizeof(devices) / sizeof(devices[0]), NULL); int8_t iDevicesFound = g_iface.find_adapters(g_iface.connection, devices, sizeof(devices) / sizeof(devices), NULL);
if (iDevicesFound <= 0) { if (iDevicesFound <= 0) {
fprintf(stderr, "No CEC devices found\n"); fprintf(stderr, "No CEC devices found\n");

View File

@ -38,11 +38,7 @@
#include <limits.h> #include <limits.h>
#include <unistd.h> #include <unistd.h>
#include <pthread.h> #include <pthread.h>
#ifdef __linux__
#include <endian.h> #include <endian.h>
#else
#include <sys/endian.h>
#endif
#include <math.h> #include <math.h>
#if __BYTE_ORDER == __LITTLE_ENDIAN #if __BYTE_ORDER == __LITTLE_ENDIAN
@ -70,19 +66,14 @@ struct input_device {
int hats_state[3][2]; int hats_state[3][2];
int fd; int fd;
char modifiers; char modifiers;
#ifdef __linux__ __s32 mouseDeltaX, mouseDeltaY, mouseScroll;
__s32 mouseDeltaX, mouseDeltaY, mouseVScroll, mouseHScroll;
__s32 touchDownX, touchDownY, touchX, touchY; __s32 touchDownX, touchDownY, touchX, touchY;
#else
int32_t mouseDeltaX, mouseDeltaY, mouseVScroll, mouseHScroll;
int32_t touchDownX, touchDownY, touchX, touchY;
#endif
struct timeval touchDownTime; struct timeval touchDownTime;
struct timeval btnDownTime; struct timeval btnDownTime;
short controllerId; short controllerId;
int haptic_effect_id; int haptic_effect_id;
int buttonFlags; int buttonFlags;
unsigned char leftTrigger, rightTrigger; char leftTrigger, rightTrigger;
short leftStickX, leftStickY; short leftStickX, leftStickY;
short rightStickX, rightStickY; short rightStickX, rightStickY;
bool gamepadModified; bool gamepadModified;
@ -114,9 +105,6 @@ static const int hat_constants[3][3] = {{HAT_UP | HAT_LEFT, HAT_UP, HAT_UP | HAT
// Determines the maximum motion amount before allowing movement // Determines the maximum motion amount before allowing movement
#define MOUSE_EMULATION_DEADZONE 2 #define MOUSE_EMULATION_DEADZONE 2
// Limited by number of bits in activeGamepadMask
#define MAX_GAMEPADS 16
static struct input_device* devices = NULL; static struct input_device* devices = NULL;
static int numDevices = 0; static int numDevices = 0;
static int assignedControllerIds = 0; static int assignedControllerIds = 0;
@ -179,10 +167,6 @@ static void evdev_remove(int devindex) {
pthread_join(devices[devindex].meThread, NULL); pthread_join(devices[devindex].meThread, NULL);
} }
libevdev_free(devices[devindex].dev);
loop_remove_fd(devices[devindex].fd);
close(devices[devindex].fd);
if (devindex != numDevices && numDevices > 0) if (devindex != numDevices && numDevices > 0)
memcpy(&devices[devindex], &devices[numDevices], sizeof(struct input_device)); memcpy(&devices[devindex], &devices[numDevices], sizeof(struct input_device));
} }
@ -205,7 +189,7 @@ static short evdev_convert_value(struct input_event *ev, struct input_device *de
return (long long)(ev->value - (ev->value>parms->avg?parms->flat*2:0) - parms->min) * (SHRT_MAX-SHRT_MIN) / (parms->max-parms->min-parms->flat*2) + SHRT_MIN; 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 unsigned char evdev_convert_value_byte(struct input_event *ev, struct input_device *dev, struct input_abs_parms *parms, char halfaxis) { static char evdev_convert_value_byte(struct input_event *ev, struct input_device *dev, struct input_abs_parms *parms, char halfaxis) {
if (parms->max == 0 && parms->min == 0) { if (parms->max == 0 && parms->min == 0) {
fprintf(stderr, "Axis not found: %d\n", ev->code); fprintf(stderr, "Axis not found: %d\n", ev->code);
return 0; return 0;
@ -256,70 +240,12 @@ void *HandleMouseEmulation(void* param)
deltaY = pow((float)rawY / 32767.0f * MOUSE_EMULATION_MOTION_MULTIPLIER, 3); deltaY = pow((float)rawY / 32767.0f * MOUSE_EMULATION_MOTION_MULTIPLIER, 3);
// Enforce deadzones // Enforce deadzones
deltaX = fabs(deltaX) > MOUSE_EMULATION_DEADZONE ? deltaX - MOUSE_EMULATION_DEADZONE : 0; deltaX = abs(deltaX) > MOUSE_EMULATION_DEADZONE ? deltaX - MOUSE_EMULATION_DEADZONE : 0;
deltaY = fabs(deltaY) > MOUSE_EMULATION_DEADZONE ? deltaY - MOUSE_EMULATION_DEADZONE : 0; deltaY = abs(deltaY) > MOUSE_EMULATION_DEADZONE ? deltaY - MOUSE_EMULATION_DEADZONE : 0;
if (deltaX != 0 || deltaY != 0) if (deltaX != 0 || deltaY != 0)
LiSendMouseMoveEvent(deltaX, -deltaY); LiSendMouseMoveEvent(deltaX, -deltaY);
} }
return NULL;
}
#define SET_BTN_FLAG(x, y) supportedButtonFlags |= (x >= 0) ? y : 0
static void send_controller_arrival(struct input_device *dev) {
unsigned char type = LI_CTYPE_UNKNOWN;
unsigned int supportedButtonFlags = 0;
unsigned short capabilities = 0;
switch (libevdev_get_id_vendor(dev->dev)) {
case 0x045e: // Microsoft
type = LI_CTYPE_XBOX;
break;
case 0x054c: // Sony
type = LI_CTYPE_PS;
break;
case 0x057e: // Nintendo
type = LI_CTYPE_NINTENDO;
break;
}
const char* name = libevdev_get_name(dev->dev);
if (name && type == LI_CTYPE_UNKNOWN) {
// Try to guess based on the name
if (strstr(name, "Xbox") || strstr(name, "X-Box") || strstr(name, "XBox") || strstr(name, "XBOX")) {
type = LI_CTYPE_XBOX;
}
}
SET_BTN_FLAG(dev->map->btn_a, A_FLAG);
SET_BTN_FLAG(dev->map->btn_b, B_FLAG);
SET_BTN_FLAG(dev->map->btn_x, X_FLAG);
SET_BTN_FLAG(dev->map->btn_y, Y_FLAG);
SET_BTN_FLAG(dev->map->btn_back, BACK_FLAG);
SET_BTN_FLAG(dev->map->btn_start, PLAY_FLAG);
SET_BTN_FLAG(dev->map->btn_guide, SPECIAL_FLAG);
SET_BTN_FLAG(dev->map->btn_leftstick, LS_CLK_FLAG);
SET_BTN_FLAG(dev->map->btn_rightstick, RS_CLK_FLAG);
SET_BTN_FLAG(dev->map->btn_leftshoulder, LB_FLAG);
SET_BTN_FLAG(dev->map->btn_rightshoulder, RB_FLAG);
SET_BTN_FLAG(dev->map->btn_misc1, MISC_FLAG);
SET_BTN_FLAG(dev->map->btn_paddle1, PADDLE1_FLAG);
SET_BTN_FLAG(dev->map->btn_paddle2, PADDLE2_FLAG);
SET_BTN_FLAG(dev->map->btn_paddle3, PADDLE3_FLAG);
SET_BTN_FLAG(dev->map->btn_paddle4, PADDLE4_FLAG);
SET_BTN_FLAG(dev->map->btn_touchpad, TOUCHPAD_FLAG);
if (dev->map->abs_lefttrigger >= 0 && dev->map->abs_righttrigger >= 0)
capabilities |= LI_CCAP_ANALOG_TRIGGERS;
// TODO: Probe for this properly
capabilities |= LI_CCAP_RUMBLE;
LiSendControllerArrivalEvent(dev->controllerId, assignedControllerIds, type,
supportedButtonFlags, capabilities);
} }
static bool evdev_handle_event(struct input_event *ev, struct input_device *dev) { static bool evdev_handle_event(struct input_event *ev, struct input_device *dev) {
@ -345,17 +271,13 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev)
dev->mouseDeltaX = 0; dev->mouseDeltaX = 0;
dev->mouseDeltaY = 0; dev->mouseDeltaY = 0;
} }
if (dev->mouseVScroll != 0) { if (dev->mouseScroll != 0) {
LiSendScrollEvent(dev->mouseVScroll); LiSendScrollEvent(dev->mouseScroll);
dev->mouseVScroll = 0; dev->mouseScroll = 0;
}
if (dev->mouseHScroll != 0) {
LiSendHScrollEvent(dev->mouseHScroll);
dev->mouseHScroll = 0;
} }
if (dev->gamepadModified) { if (dev->gamepadModified) {
if (dev->controllerId < 0) { if (dev->controllerId < 0) {
for (int i = 0; i < MAX_GAMEPADS; i++) { for (int i = 0; i < 4; i++) {
if ((assignedControllerIds & (1 << i)) == 0) { if ((assignedControllerIds & (1 << i)) == 0) {
assignedControllerIds |= (1 << i); assignedControllerIds |= (1 << i);
dev->controllerId = i; dev->controllerId = i;
@ -366,9 +288,6 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev)
//Use id 0 when too many gamepads are connected //Use id 0 when too many gamepads are connected
if (dev->controllerId < 0) if (dev->controllerId < 0)
dev->controllerId = 0; dev->controllerId = 0;
// Send controller arrival event to the host
send_controller_arrival(dev);
} }
// Send event only if mouse emulation is disabled. // Send event only if mouse emulation is disabled.
if (dev->mouseEmulation == false) if (dev->mouseEmulation == false)
@ -418,7 +337,7 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev)
LiSendKeyboardEvent(code, ev->value?KEY_ACTION_DOWN:KEY_ACTION_UP, dev->modifiers); LiSendKeyboardEvent(code, ev->value?KEY_ACTION_DOWN:KEY_ACTION_UP, dev->modifiers);
} else { } else {
int mouseCode = 0; int mouseCode = 0;
int gamepadCode = 0; short gamepadCode = 0;
int index = dev->key_map[ev->code]; int index = dev->key_map[ev->code];
switch (ev->code) { switch (ev->code) {
@ -492,18 +411,6 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev)
gamepadCode = BACK_FLAG; gamepadCode = BACK_FLAG;
else if (index == dev->map->btn_guide) else if (index == dev->map->btn_guide)
gamepadCode = SPECIAL_FLAG; gamepadCode = SPECIAL_FLAG;
else if (index == dev->map->btn_misc1)
gamepadCode = MISC_FLAG;
else if (index == dev->map->btn_paddle1)
gamepadCode = PADDLE1_FLAG;
else if (index == dev->map->btn_paddle2)
gamepadCode = PADDLE2_FLAG;
else if (index == dev->map->btn_paddle3)
gamepadCode = PADDLE3_FLAG;
else if (index == dev->map->btn_paddle4)
gamepadCode = PADDLE4_FLAG;
else if (index == dev->map->btn_touchpad)
gamepadCode = TOUCHPAD_FLAG;
} }
if (mouseCode != 0) { if (mouseCode != 0) {
@ -574,11 +481,8 @@ static bool evdev_handle_event(struct input_event *ev, struct input_device *dev)
case REL_Y: case REL_Y:
dev->mouseDeltaY = ev->value; dev->mouseDeltaY = ev->value;
break; break;
case REL_HWHEEL:
dev->mouseHScroll = ev->value;
break;
case REL_WHEEL: case REL_WHEEL:
dev->mouseVScroll = ev->value; dev->mouseScroll = ev->value;
break; break;
} }
break; break;
@ -836,10 +740,8 @@ void evdev_create(const char* device, struct mapping* mappings, bool verbose, in
libevdev_has_event_code(evdev, EV_ABS, ABS_RZ))) && libevdev_has_event_code(evdev, EV_ABS, ABS_RZ))) &&
!libevdev_has_event_type(evdev, EV_KEY); !libevdev_has_event_type(evdev, EV_KEY);
bool is_gamepad = bool is_gamepad =
((libevdev_has_event_code(evdev, EV_ABS, ABS_X) && 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_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_TRIGGER) ||
libevdev_has_event_code(evdev, EV_KEY, BTN_A) || libevdev_has_event_code(evdev, EV_KEY, BTN_A) ||
libevdev_has_event_code(evdev, EV_KEY, BTN_1) || libevdev_has_event_code(evdev, EV_KEY, BTN_1) ||
@ -1020,9 +922,9 @@ void evdev_map(char* device) {
for (int i = 0; i < 16; i++) for (int i = 0; i < 16; i++)
buf += sprintf(buf, "%02x", ((unsigned char*) guid)[i]); buf += sprintf(buf, "%02x", ((unsigned char*) guid)[i]);
struct mapping map = {0}; struct mapping map;
strncpy(map.name, name, sizeof(map.name) - 1); strncpy(map.name, libevdev_get_name(evdev), sizeof(map.name));
strncpy(map.guid, str_guid, sizeof(map.guid) - 1); strncpy(map.guid, str_guid, sizeof(map.guid));
libevdev_free(evdev); libevdev_free(evdev);
close(fd); close(fd);

View File

@ -38,8 +38,8 @@ struct mapping* mapping_parse(char* mapping) {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
strncpy(map->guid, guid, sizeof(map->guid) - 1); strncpy(map->guid, guid, sizeof(map->guid));
strncpy(map->name, name, sizeof(map->name) - 1); strncpy(map->name, name, sizeof(map->name));
/* Initialize all mapping indices to -1 to ensure they won't match anything */ /* 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)); 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++; value++;
} }
if (strcmp("platform", key) == 0) if (strcmp("platform", key) == 0)
strncpy(map->platform, value, sizeof(map->platform) - 1); strncpy(map->platform, value, sizeof(map->platform));
else if (sscanf(value, "b%d", &int_value) == 1) { else if (sscanf(value, "b%d", &int_value) == 1) {
if (strcmp("a", key) == 0) if (strcmp("a", key) == 0)
map->btn_a = int_value; map->btn_a = int_value;
@ -94,18 +94,6 @@ struct mapping* mapping_parse(char* mapping) {
map->btn_lefttrigger = int_value; map->btn_lefttrigger = int_value;
else if (strcmp("righttrigger", key) == 0) else if (strcmp("righttrigger", key) == 0)
map->btn_righttrigger = int_value; map->btn_righttrigger = int_value;
else if (strcmp("misc1", key) == 0)
map->btn_misc1 = int_value;
else if (strcmp("paddle1", key) == 0)
map->btn_paddle1 = int_value;
else if (strcmp("paddle2", key) == 0)
map->btn_paddle2 = int_value;
else if (strcmp("paddle3", key) == 0)
map->btn_paddle3 = int_value;
else if (strcmp("paddle4", key) == 0)
map->btn_paddle4 = int_value;
else if (strcmp("touchpad", key) == 0)
map->btn_touchpad = int_value;
} else if (sscanf(value, "a%d%c", &int_value, &flag) >= 1) { } else if (sscanf(value, "a%d%c", &int_value, &flag) >= 1) {
if (strcmp("leftx", key) == 0) { if (strcmp("leftx", key) == 0) {
map->abs_leftx = int_value; map->abs_leftx = int_value;
@ -152,8 +140,6 @@ struct mapping* mapping_parse(char* mapping) {
map->hat_dpdown = int_value; map->hat_dpdown = int_value;
map->hat_dir_dpdown = direction_value; map->hat_dir_dpdown = direction_value;
} }
} else if (strcmp("crc", key) == 0) {
/* CRC is not supported */
} else } else
fprintf(stderr, "Can't map (%s)\n", option); fprintf(stderr, "Can't map (%s)\n", option);
} else if (ret == 0 && option[0] != '\n') } else if (ret == 0 && option[0] != '\n')
@ -227,11 +213,5 @@ void mapping_print(struct mapping* map) {
print_abs("righttrigger", map->abs_righttrigger); print_abs("righttrigger", map->abs_righttrigger);
print_btn("lefttrigger", map->btn_lefttrigger); print_btn("lefttrigger", map->btn_lefttrigger);
print_btn("righttrigger", map->btn_righttrigger); print_btn("righttrigger", map->btn_righttrigger);
print_btn("misc1", map->btn_misc1);
print_btn("paddle1", map->btn_paddle1);
print_btn("paddle2", map->btn_paddle2);
print_btn("paddle3", map->btn_paddle3);
print_btn("paddle4", map->btn_paddle4);
print_btn("touchpad", map->btn_touchpad);
printf("platform:Linux\n"); printf("platform:Linux\n");
} }

View File

@ -28,9 +28,9 @@ struct mapping {
bool reverse_leftx, reverse_lefty; bool reverse_leftx, reverse_lefty;
bool reverse_rightx, reverse_righty; bool reverse_rightx, reverse_righty;
char halfaxis_lefttrigger, halfaxis_righttrigger; bool halfaxis_lefttrigger, halfaxis_righttrigger;
char halfaxis_dpright, halfaxis_dpleft; bool halfaxis_dpright, halfaxis_dpleft;
char halfaxis_dpup, halfaxis_dpdown; bool halfaxis_dpup, halfaxis_dpdown;
/* abs_leftx must be the first member of the list of mapping indices! */ /* abs_leftx must be the first member of the list of mapping indices! */
short abs_leftx, abs_lefty; short abs_leftx, abs_lefty;
@ -45,9 +45,6 @@ struct mapping {
short btn_back, btn_start, btn_guide; short btn_back, btn_start, btn_guide;
short btn_leftstick, btn_rightstick; short btn_leftstick, btn_rightstick;
short btn_leftshoulder, btn_rightshoulder; short btn_leftshoulder, btn_rightshoulder;
short btn_misc1;
short btn_paddle1, btn_paddle2, btn_paddle3, btn_paddle4;
short btn_touchpad;
short abs_lefttrigger, abs_righttrigger; short abs_lefttrigger, abs_righttrigger;
short btn_lefttrigger, btn_righttrigger; short btn_lefttrigger, btn_righttrigger;

View File

@ -26,406 +26,60 @@
#define QUIT_KEY SDLK_q #define QUIT_KEY SDLK_q
#define QUIT_BUTTONS (PLAY_FLAG|BACK_FLAG|LB_FLAG|RB_FLAG) #define QUIT_BUTTONS (PLAY_FLAG|BACK_FLAG|LB_FLAG|RB_FLAG)
#define FULLSCREEN_KEY SDLK_f #define FULLSCREEN_KEY SDLK_f
#define UNGRAB_KEY SDLK_z
static const int SDL_TO_LI_BUTTON_MAP[] = {
A_FLAG, B_FLAG, X_FLAG, Y_FLAG,
BACK_FLAG, SPECIAL_FLAG, PLAY_FLAG,
LS_CLK_FLAG, RS_CLK_FLAG,
LB_FLAG, RB_FLAG,
UP_FLAG, DOWN_FLAG, LEFT_FLAG, RIGHT_FLAG,
MISC_FLAG,
PADDLE1_FLAG, PADDLE2_FLAG, PADDLE3_FLAG, PADDLE4_FLAG,
TOUCHPAD_FLAG,
};
typedef struct _GAMEPAD_STATE { typedef struct _GAMEPAD_STATE {
unsigned char leftTrigger, rightTrigger; char leftTrigger, rightTrigger;
short leftStickX, leftStickY; short leftStickX, leftStickY;
short rightStickX, rightStickY; short rightStickX, rightStickY;
int buttons; int buttons;
SDL_JoystickID sdl_id; SDL_JoystickID sdl_id;
SDL_GameController* controller;
#if !SDL_VERSION_ATLEAST(2, 0, 9)
SDL_Haptic* haptic; SDL_Haptic* haptic;
int haptic_effect_id; int haptic_effect_id;
#endif
short id; short id;
bool initialized; bool initialized;
} GAMEPAD_STATE, *PGAMEPAD_STATE; } GAMEPAD_STATE, *PGAMEPAD_STATE;
// Limited by number of bits in activeGamepadMask static GAMEPAD_STATE gamepads[4];
#define MAX_GAMEPADS 16
static GAMEPAD_STATE gamepads[MAX_GAMEPADS];
static int keyboard_modifiers;
static int activeGamepadMask = 0; static int activeGamepadMask = 0;
int sdl_gamepads = 0; int sdl_gamepads = 0;
#define VK_0 0x30 static PGAMEPAD_STATE get_gamepad(SDL_JoystickID sdl_id) {
#define VK_A 0x41 for (int i = 0;i<4;i++) {
// These are real Windows VK_* codes
#ifndef VK_F1
#define VK_F1 0x70
#define VK_F13 0x7C
#define VK_NUMPAD0 0x60
#endif
int vk_for_sdl_scancode(SDL_Scancode scancode) {
// Set keycode. We explicitly use scancode here because GFE will try to correct
// for AZERTY layouts on the host but it depends on receiving VK_ values matching
// a QWERTY layout to work.
if (scancode >= SDL_SCANCODE_1 && scancode <= SDL_SCANCODE_9) {
// SDL defines SDL_SCANCODE_0 > SDL_SCANCODE_9, so we need to handle that manually
return (scancode - SDL_SCANCODE_1) + VK_0 + 1;
}
else if (scancode >= SDL_SCANCODE_A && scancode <= SDL_SCANCODE_Z) {
return (scancode - SDL_SCANCODE_A) + VK_A;
}
else if (scancode >= SDL_SCANCODE_F1 && scancode <= SDL_SCANCODE_F12) {
return (scancode - SDL_SCANCODE_F1) + VK_F1;
}
else if (scancode >= SDL_SCANCODE_F13 && scancode <= SDL_SCANCODE_F24) {
return (scancode - SDL_SCANCODE_F13) + VK_F13;
}
else if (scancode >= SDL_SCANCODE_KP_1 && scancode <= SDL_SCANCODE_KP_9) {
// SDL defines SDL_SCANCODE_KP_0 > SDL_SCANCODE_KP_9, so we need to handle that manually
return (scancode - SDL_SCANCODE_KP_1) + VK_NUMPAD0 + 1;
}
else {
switch (scancode) {
case SDL_SCANCODE_BACKSPACE:
return 0x08;
case SDL_SCANCODE_TAB:
return 0x09;
case SDL_SCANCODE_CLEAR:
return 0x0C;
case SDL_SCANCODE_KP_ENTER:
case SDL_SCANCODE_RETURN:
return 0x0D;
case SDL_SCANCODE_PAUSE:
return 0x13;
case SDL_SCANCODE_CAPSLOCK:
return 0x14;
case SDL_SCANCODE_ESCAPE:
return 0x1B;
case SDL_SCANCODE_SPACE:
return 0x20;
case SDL_SCANCODE_PAGEUP:
return 0x21;
case SDL_SCANCODE_PAGEDOWN:
return 0x22;
case SDL_SCANCODE_END:
return 0x23;
case SDL_SCANCODE_HOME:
return 0x24;
case SDL_SCANCODE_LEFT:
return 0x25;
case SDL_SCANCODE_UP:
return 0x26;
case SDL_SCANCODE_RIGHT:
return 0x27;
case SDL_SCANCODE_DOWN:
return 0x28;
case SDL_SCANCODE_SELECT:
return 0x29;
case SDL_SCANCODE_EXECUTE:
return 0x2B;
case SDL_SCANCODE_PRINTSCREEN:
return 0x2C;
case SDL_SCANCODE_INSERT:
return 0x2D;
case SDL_SCANCODE_DELETE:
return 0x2E;
case SDL_SCANCODE_HELP:
return 0x2F;
case SDL_SCANCODE_KP_0:
// See comment above about why we only handle SDL_SCANCODE_KP_0 here
return VK_NUMPAD0;
case SDL_SCANCODE_0:
// See comment above about why we only handle SDL_SCANCODE_0 here
return VK_0;
case SDL_SCANCODE_KP_MULTIPLY:
return 0x6A;
case SDL_SCANCODE_KP_PLUS:
return 0x6B;
case SDL_SCANCODE_KP_COMMA:
return 0x6C;
case SDL_SCANCODE_KP_MINUS:
return 0x6D;
case SDL_SCANCODE_KP_PERIOD:
return 0x6E;
case SDL_SCANCODE_KP_DIVIDE:
return 0x6F;
case SDL_SCANCODE_NUMLOCKCLEAR:
return 0x90;
case SDL_SCANCODE_SCROLLLOCK:
return 0x91;
case SDL_SCANCODE_LSHIFT:
return 0xA0;
case SDL_SCANCODE_RSHIFT:
return 0xA1;
case SDL_SCANCODE_LCTRL:
return 0xA2;
case SDL_SCANCODE_RCTRL:
return 0xA3;
case SDL_SCANCODE_LALT:
return 0xA4;
case SDL_SCANCODE_RALT:
return 0xA5;
case SDL_SCANCODE_LGUI:
return 0x5B;
case SDL_SCANCODE_RGUI:
return 0x5C;
case SDL_SCANCODE_APPLICATION:
return 0x5D;
case SDL_SCANCODE_AC_BACK:
return 0xA6;
case SDL_SCANCODE_AC_FORWARD:
return 0xA7;
case SDL_SCANCODE_AC_REFRESH:
return 0xA8;
case SDL_SCANCODE_AC_STOP:
return 0xA9;
case SDL_SCANCODE_AC_SEARCH:
return 0xAA;
case SDL_SCANCODE_AC_BOOKMARKS:
return 0xAB;
case SDL_SCANCODE_AC_HOME:
return 0xAC;
case SDL_SCANCODE_SEMICOLON:
return 0xBA;
case SDL_SCANCODE_EQUALS:
return 0xBB;
case SDL_SCANCODE_COMMA:
return 0xBC;
case SDL_SCANCODE_MINUS:
return 0xBD;
case SDL_SCANCODE_PERIOD:
return 0xBE;
case SDL_SCANCODE_SLASH:
return 0xBF;
case SDL_SCANCODE_GRAVE:
return 0xC0;
case SDL_SCANCODE_LEFTBRACKET:
return 0xDB;
case SDL_SCANCODE_BACKSLASH:
return 0xDC;
case SDL_SCANCODE_RIGHTBRACKET:
return 0xDD;
case SDL_SCANCODE_APOSTROPHE:
return 0xDE;
case SDL_SCANCODE_NONUSBACKSLASH:
return 0xE2;
default:
return 0;
}
}
}
static void send_controller_arrival(PGAMEPAD_STATE state) {
#if SDL_VERSION_ATLEAST(2, 0, 18)
unsigned int supportedButtonFlags = 0;
unsigned short capabilities = 0;
unsigned char type = LI_CTYPE_UNKNOWN;
for (int i = 0; i < SDL_arraysize(SDL_TO_LI_BUTTON_MAP); i++) {
if (SDL_GameControllerHasButton(state->controller, (SDL_GameControllerButton)i)) {
supportedButtonFlags |= SDL_TO_LI_BUTTON_MAP[i];
}
}
if (SDL_GameControllerGetBindForAxis(state->controller, SDL_CONTROLLER_AXIS_TRIGGERLEFT).bindType == SDL_CONTROLLER_BINDTYPE_AXIS ||
SDL_GameControllerGetBindForAxis(state->controller, SDL_CONTROLLER_AXIS_TRIGGERRIGHT).bindType == SDL_CONTROLLER_BINDTYPE_AXIS)
capabilities |= LI_CCAP_ANALOG_TRIGGERS;
if (SDL_GameControllerHasRumble(state->controller))
capabilities |= LI_CCAP_RUMBLE;
if (SDL_GameControllerHasRumbleTriggers(state->controller))
capabilities |= LI_CCAP_TRIGGER_RUMBLE;
if (SDL_GameControllerGetNumTouchpads(state->controller) > 0)
capabilities |= LI_CCAP_TOUCHPAD;
if (SDL_GameControllerHasSensor(state->controller, SDL_SENSOR_ACCEL))
capabilities |= LI_CCAP_ACCEL;
if (SDL_GameControllerHasSensor(state->controller, SDL_SENSOR_GYRO))
capabilities |= LI_CCAP_GYRO;
if (SDL_GameControllerHasLED(state->controller))
capabilities |= LI_CCAP_RGB_LED;
switch (SDL_GameControllerGetType(state->controller)) {
case SDL_CONTROLLER_TYPE_XBOX360:
case SDL_CONTROLLER_TYPE_XBOXONE:
type = LI_CTYPE_XBOX;
break;
case SDL_CONTROLLER_TYPE_PS3:
case SDL_CONTROLLER_TYPE_PS4:
case SDL_CONTROLLER_TYPE_PS5:
type = LI_CTYPE_PS;
break;
case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO:
#if SDL_VERSION_ATLEAST(2, 24, 0)
case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT:
case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT:
case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR:
#endif
type = LI_CTYPE_NINTENDO;
break;
}
LiSendControllerArrivalEvent(state->id, activeGamepadMask, type, supportedButtonFlags, capabilities);
#endif
}
static PGAMEPAD_STATE get_gamepad(SDL_JoystickID sdl_id, bool add) {
// See if a gamepad already exists
for (int i = 0;i<MAX_GAMEPADS;i++) {
if (gamepads[i].initialized && gamepads[i].sdl_id == sdl_id)
return &gamepads[i];
}
if (!add)
return NULL;
for (int i = 0;i<MAX_GAMEPADS;i++) {
if (!gamepads[i].initialized) { if (!gamepads[i].initialized) {
gamepads[i].sdl_id = sdl_id; gamepads[i].sdl_id = sdl_id;
gamepads[i].id = i; gamepads[i].id = i;
gamepads[i].initialized = true; gamepads[i].initialized = true;
activeGamepadMask |= (1 << i); activeGamepadMask |= (1 << i);
return &gamepads[i]; return &gamepads[i];
} } else if (gamepads[i].sdl_id == sdl_id)
return &gamepads[i];
} }
return &gamepads[0]; return &gamepads[0];
} }
static void add_gamepad(int joystick_index) { static void init_gamepad(int joystick_index) {
SDL_GameController* controller = SDL_GameControllerOpen(joystick_index); if (SDL_IsGameController(joystick_index)) {
if (!controller) { sdl_gamepads++;
fprintf(stderr, "Could not open gamecontroller %i: %s\n", joystick_index, SDL_GetError()); SDL_GameController* controller = SDL_GameControllerOpen(joystick_index);
return; if (!controller) {
} fprintf(stderr, "Could not open gamecontroller %i: %s\n", joystick_index, SDL_GetError());
return;
SDL_Joystick* joystick = SDL_GameControllerGetJoystick(controller);
SDL_JoystickID joystick_id = SDL_JoystickInstanceID(joystick);
// Check if we have already set up a state for this gamepad
PGAMEPAD_STATE state = get_gamepad(joystick_id, false);
if (state) {
// This was probably a gamepad added during initialization, so we've already
// got state set up. However, we still need to inform the host about it, since
// we couldn't do that during initialization (since we weren't connected yet).
send_controller_arrival(state);
SDL_GameControllerClose(controller);
return;
}
// Create a new gamepad state
state = get_gamepad(joystick_id, true);
state->controller = controller;
#if !SDL_VERSION_ATLEAST(2, 0, 9)
state->haptic = SDL_HapticOpenFromJoystick(joystick);
if (haptic && (SDL_HapticQuery(state->haptic) & SDL_HAPTIC_LEFTRIGHT) == 0) {
SDL_HapticClose(state->haptic);
state->haptic = NULL;
}
state->haptic_effect_id = -1;
#endif
// Send the controller arrival event to the host
send_controller_arrival(state);
sdl_gamepads++;
}
static void remove_gamepad(SDL_JoystickID sdl_id) {
for (int i = 0;i<MAX_GAMEPADS;i++) {
if (gamepads[i].initialized && gamepads[i].sdl_id == sdl_id) {
#if !SDL_VERSION_ATLEAST(2, 0, 9)
if (gamepads[i].haptic_effect_id >= 0) {
SDL_HapticDestroyEffect(gamepads[i].haptic, gamepads[i].haptic_effect_id);
}
if (gamepads[i].haptic) {
SDL_HapticClose(gamepads[i].haptic);
}
#endif
SDL_GameControllerClose(gamepads[i].controller);
// This will cause disconnection of the virtual controller on the host PC
activeGamepadMask &= ~(1 << i);
LiSendMultiControllerEvent(i, activeGamepadMask, 0, 0, 0, 0, 0, 0, 0);
memset(&gamepads[i], 0, sizeof(*gamepads));
sdl_gamepads--;
break;
} }
SDL_Joystick* joystick = SDL_GameControllerGetJoystick(controller);
SDL_Haptic* haptic = SDL_HapticOpenFromJoystick(joystick);
if (haptic && (SDL_HapticQuery(haptic) & SDL_HAPTIC_LEFTRIGHT) == 0) {
SDL_HapticClose(haptic);
haptic = NULL;
}
SDL_JoystickID sdl_id = SDL_JoystickInstanceID(joystick);
PGAMEPAD_STATE state = get_gamepad(joystick_index);
state->haptic = haptic;
state->haptic_effect_id = -1;
} }
} }
@ -433,41 +87,22 @@ void sdlinput_init(char* mappings) {
memset(gamepads, 0, sizeof(gamepads)); memset(gamepads, 0, sizeof(gamepads));
SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER); SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER);
#if !SDL_VERSION_ATLEAST(2, 0, 9)
SDL_InitSubSystem(SDL_INIT_HAPTIC); SDL_InitSubSystem(SDL_INIT_HAPTIC);
#endif
SDL_GameControllerAddMappingsFromFile(mappings); SDL_GameControllerAddMappingsFromFile(mappings);
// Add game controllers here to ensure an accurate count for (int i = 0; i < SDL_NumJoysticks(); ++i)
// goes to the host when starting a new session. init_gamepad(i);
for (int i = 0; i < SDL_NumJoysticks(); ++i) {
if (SDL_IsGameController(i))
add_gamepad(i);
}
} }
int sdlinput_handle_event(SDL_Window* window, SDL_Event* event) { int sdlinput_handle_event(SDL_Event* event) {
int button = 0; int button = 0;
unsigned char touchEventType;
PGAMEPAD_STATE gamepad; PGAMEPAD_STATE gamepad;
switch (event->type) { switch (event->type) {
case SDL_MOUSEMOTION: case SDL_MOUSEMOTION:
if (SDL_GetRelativeMouseMode()) LiSendMouseMoveEvent(event->motion.xrel, event->motion.yrel);
LiSendMouseMoveEvent(event->motion.xrel, event->motion.yrel);
else {
int w, h;
SDL_GetWindowSize(window, &w, &h);
LiSendMousePositionEvent(event->motion.x, event->motion.y, w, h);
}
break; break;
case SDL_MOUSEWHEEL: case SDL_MOUSEWHEEL:
#if SDL_VERSION_ATLEAST(2, 0, 18)
LiSendHighResHScrollEvent((short)(event->wheel.preciseX * 120)); // WHEEL_DELTA
LiSendHighResScrollEvent((short)(event->wheel.preciseY * 120)); // WHEEL_DELTA
#else
LiSendHScrollEvent(event->wheel.x);
LiSendScrollEvent(event->wheel.y); LiSendScrollEvent(event->wheel.y);
#endif
break; break;
case SDL_MOUSEBUTTONUP: case SDL_MOUSEBUTTONUP:
case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONDOWN:
@ -492,63 +127,64 @@ int sdlinput_handle_event(SDL_Window* window, SDL_Event* event) {
if (button != 0) if (button != 0)
LiSendMouseButtonEvent(event->type==SDL_MOUSEBUTTONDOWN?BUTTON_ACTION_PRESS:BUTTON_ACTION_RELEASE, button); LiSendMouseButtonEvent(event->type==SDL_MOUSEBUTTONDOWN?BUTTON_ACTION_PRESS:BUTTON_ACTION_RELEASE, button);
return 0; return SDL_MOUSE_GRAB;
case SDL_KEYDOWN: case SDL_KEYDOWN:
case SDL_KEYUP: case SDL_KEYUP:
button = vk_for_sdl_scancode(event->key.keysym.scancode); 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;
int modifiers = 0; int modifier = 0;
if (event->key.keysym.mod & KMOD_CTRL) { switch (event->key.keysym.sym) {
modifiers |= MODIFIER_CTRL; case SDLK_RSHIFT:
} case SDLK_LSHIFT:
if (event->key.keysym.mod & KMOD_ALT) { modifier = MODIFIER_SHIFT;
modifiers |= MODIFIER_ALT; break;
} case SDLK_RALT:
if (event->key.keysym.mod & KMOD_SHIFT) { case SDLK_LALT:
modifiers |= MODIFIER_SHIFT; modifier = MODIFIER_ALT;
} break;
if (event->key.keysym.mod & KMOD_GUI) { case SDLK_RCTRL:
modifiers |= MODIFIER_META; case SDLK_LCTRL:
modifier = MODIFIER_CTRL;
break;
case SDLK_RGUI:
case SDLK_LGUI:
modifier = MODIFIER_META;
break;
} }
LiSendKeyboardEvent(0x80 << 8 | button, event->type==SDL_KEYDOWN?KEY_ACTION_DOWN:KEY_ACTION_UP, modifiers); if (modifier != 0) {
if (event->type==SDL_KEYDOWN)
keyboard_modifiers |= modifier;
else
keyboard_modifiers &= ~modifier;
}
// Quit the stream if all the required quit keys are down // Quit the stream if all the required quit keys are down
if ((modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS && event->key.keysym.sym == QUIT_KEY && event->type==SDL_KEYUP) if ((keyboard_modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS && event->key.keysym.sym == QUIT_KEY && event->type==SDL_KEYUP)
return SDL_QUIT_APPLICATION; return SDL_QUIT_APPLICATION;
else if ((modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS && event->key.keysym.sym == FULLSCREEN_KEY && event->type==SDL_KEYUP) else if ((keyboard_modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS && event->key.keysym.sym == FULLSCREEN_KEY && event->type==SDL_KEYUP)
return SDL_TOGGLE_FULLSCREEN; return SDL_TOGGLE_FULLSCREEN;
else if ((modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS && event->key.keysym.sym == UNGRAB_KEY && event->type==SDL_KEYUP) else if ((keyboard_modifiers & ACTION_MODIFIERS) == ACTION_MODIFIERS)
return SDL_GetRelativeMouseMode() ? SDL_MOUSE_UNGRAB : SDL_MOUSE_GRAB; return SDL_MOUSE_UNGRAB;
break;
case SDL_FINGERDOWN:
case SDL_FINGERMOTION:
case SDL_FINGERUP:
switch (event->type) {
case SDL_FINGERDOWN:
touchEventType = LI_TOUCH_EVENT_DOWN;
break;
case SDL_FINGERMOTION:
touchEventType = LI_TOUCH_EVENT_MOVE;
break;
case SDL_FINGERUP:
touchEventType = LI_TOUCH_EVENT_UP;
break;
default:
return SDL_NOTHING;
}
// These are already window-relative normalized coordinates, so we just need to clamp them LiSendKeyboardEvent(0x80 << 8 | button, event->type==SDL_KEYDOWN?KEY_ACTION_DOWN:KEY_ACTION_UP, keyboard_modifiers);
event->tfinger.x = SDL_max(SDL_min(1.0f, event->tfinger.x), 0.0f);
event->tfinger.y = SDL_max(SDL_min(1.0f, event->tfinger.y), 0.0f);
LiSendTouchEvent(touchEventType, event->tfinger.fingerId, event->tfinger.x, event->tfinger.y,
event->tfinger.pressure, 0.0f, 0.0f, LI_ROT_UNKNOWN);
break; break;
case SDL_CONTROLLERAXISMOTION: case SDL_CONTROLLERAXISMOTION:
gamepad = get_gamepad(event->caxis.which, false); gamepad = get_gamepad(event->caxis.which);
if (!gamepad)
return SDL_NOTHING;
switch (event->caxis.axis) { switch (event->caxis.axis) {
case SDL_CONTROLLER_AXIS_LEFTX: case SDL_CONTROLLER_AXIS_LEFTX:
gamepad->leftStickX = event->caxis.value; gamepad->leftStickX = event->caxis.value;
@ -575,86 +211,76 @@ int sdlinput_handle_event(SDL_Window* window, SDL_Event* event) {
break; break;
case SDL_CONTROLLERBUTTONDOWN: case SDL_CONTROLLERBUTTONDOWN:
case SDL_CONTROLLERBUTTONUP: case SDL_CONTROLLERBUTTONUP:
gamepad = get_gamepad(event->cbutton.which, false); gamepad = get_gamepad(event->cbutton.which);
if (!gamepad) switch (event->cbutton.button) {
case SDL_CONTROLLER_BUTTON_A:
button = A_FLAG;
break;
case SDL_CONTROLLER_BUTTON_B:
button = B_FLAG;
break;
case SDL_CONTROLLER_BUTTON_Y:
button = Y_FLAG;
break;
case SDL_CONTROLLER_BUTTON_X:
button = X_FLAG;
break;
case SDL_CONTROLLER_BUTTON_DPAD_UP:
button = UP_FLAG;
break;
case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
button = DOWN_FLAG;
break;
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
button = RIGHT_FLAG;
break;
case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
button = LEFT_FLAG;
break;
case SDL_CONTROLLER_BUTTON_BACK:
button = BACK_FLAG;
break;
case SDL_CONTROLLER_BUTTON_START:
button = PLAY_FLAG;
break;
case SDL_CONTROLLER_BUTTON_GUIDE:
button = SPECIAL_FLAG;
break;
case SDL_CONTROLLER_BUTTON_LEFTSTICK:
button = LS_CLK_FLAG;
break;
case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
button = RS_CLK_FLAG;
break;
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
button = LB_FLAG;
break;
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
button = RB_FLAG;
break;
default:
return SDL_NOTHING; return SDL_NOTHING;
if (event->cbutton.button >= SDL_arraysize(SDL_TO_LI_BUTTON_MAP)) }
return SDL_NOTHING;
if (event->type == SDL_CONTROLLERBUTTONDOWN) if (event->type == SDL_CONTROLLERBUTTONDOWN)
gamepad->buttons |= SDL_TO_LI_BUTTON_MAP[event->cbutton.button]; gamepad->buttons |= button;
else else
gamepad->buttons &= ~SDL_TO_LI_BUTTON_MAP[event->cbutton.button]; gamepad->buttons &= ~button;
if ((gamepad->buttons & QUIT_BUTTONS) == QUIT_BUTTONS) if ((gamepad->buttons & QUIT_BUTTONS) == QUIT_BUTTONS)
return SDL_QUIT_APPLICATION; return SDL_QUIT_APPLICATION;
LiSendMultiControllerEvent(gamepad->id, activeGamepadMask, gamepad->buttons, gamepad->leftTrigger, gamepad->rightTrigger, gamepad->leftStickX, gamepad->leftStickY, gamepad->rightStickX, gamepad->rightStickY); LiSendMultiControllerEvent(gamepad->id, activeGamepadMask, gamepad->buttons, gamepad->leftTrigger, gamepad->rightTrigger, gamepad->leftStickX, gamepad->leftStickY, gamepad->rightStickX, gamepad->rightStickY);
break; break;
case SDL_CONTROLLERDEVICEADDED:
add_gamepad(event->cdevice.which);
break;
case SDL_CONTROLLERDEVICEREMOVED:
remove_gamepad(event->cdevice.which);
break;
#if SDL_VERSION_ATLEAST(2, 0, 14)
case SDL_CONTROLLERSENSORUPDATE:
gamepad = get_gamepad(event->csensor.which, false);
if (!gamepad)
return SDL_NOTHING;
switch (event->csensor.sensor) {
case SDL_SENSOR_ACCEL:
LiSendControllerMotionEvent(gamepad->id, LI_MOTION_TYPE_ACCEL, event->csensor.data[0], event->csensor.data[1], event->csensor.data[2]);
break;
case SDL_SENSOR_GYRO:
// Convert rad/s to deg/s
LiSendControllerMotionEvent(gamepad->id, LI_MOTION_TYPE_GYRO,
event->csensor.data[0] * 57.2957795f,
event->csensor.data[1] * 57.2957795f,
event->csensor.data[2] * 57.2957795f);
break;
}
break;
case SDL_CONTROLLERTOUCHPADDOWN:
case SDL_CONTROLLERTOUCHPADUP:
case SDL_CONTROLLERTOUCHPADMOTION:
gamepad = get_gamepad(event->ctouchpad.which, false);
if (!gamepad)
return SDL_NOTHING;
switch (event->type) {
case SDL_CONTROLLERTOUCHPADDOWN:
touchEventType = LI_TOUCH_EVENT_DOWN;
break;
case SDL_CONTROLLERTOUCHPADUP:
touchEventType = LI_TOUCH_EVENT_UP;
break;
case SDL_CONTROLLERTOUCHPADMOTION:
touchEventType = LI_TOUCH_EVENT_MOVE;
break;
default:
return SDL_NOTHING;
}
LiSendControllerTouchEvent(gamepad->id, touchEventType, event->ctouchpad.finger,
event->ctouchpad.x, event->ctouchpad.y, event->ctouchpad.pressure);
break;
#endif
} }
return SDL_NOTHING; return SDL_NOTHING;
} }
void sdlinput_rumble(unsigned short controller_id, unsigned short low_freq_motor, unsigned short high_freq_motor) { void sdlinput_rumble(unsigned short controller_id, unsigned short low_freq_motor, unsigned short high_freq_motor) {
if (controller_id >= MAX_GAMEPADS) if (controller_id >= 4)
return; return;
PGAMEPAD_STATE state = &gamepads[controller_id]; PGAMEPAD_STATE state = &gamepads[controller_id];
if (!state->initialized)
return;
#if SDL_VERSION_ATLEAST(2, 0, 9)
SDL_GameControllerRumble(state->controller, low_freq_motor, high_freq_motor, 30000);
#else
SDL_Haptic* haptic = state->haptic; SDL_Haptic* haptic = state->haptic;
if (!haptic) if (!haptic)
return; return;
@ -677,45 +303,4 @@ void sdlinput_rumble(unsigned short controller_id, unsigned short low_freq_motor
state->haptic_effect_id = SDL_HapticNewEffect(haptic, &effect); state->haptic_effect_id = SDL_HapticNewEffect(haptic, &effect);
if (state->haptic_effect_id >= 0) if (state->haptic_effect_id >= 0)
SDL_HapticRunEffect(haptic, state->haptic_effect_id, 1); SDL_HapticRunEffect(haptic, state->haptic_effect_id, 1);
#endif
}
void sdlinput_rumble_triggers(unsigned short controller_id, unsigned short left_trigger, unsigned short right_trigger) {
PGAMEPAD_STATE state = &gamepads[controller_id];
if (!state->initialized)
return;
#if SDL_VERSION_ATLEAST(2, 0, 14)
SDL_GameControllerRumbleTriggers(state->controller, left_trigger, right_trigger, 30000);
#endif
}
void sdlinput_set_motion_event_state(unsigned short controller_id, unsigned char motion_type, unsigned short report_rate_hz) {
PGAMEPAD_STATE state = &gamepads[controller_id];
if (!state->initialized)
return;
#if SDL_VERSION_ATLEAST(2, 0, 14)
switch (motion_type) {
case LI_MOTION_TYPE_ACCEL:
SDL_GameControllerSetSensorEnabled(state->controller, SDL_SENSOR_ACCEL, report_rate_hz ? SDL_TRUE : SDL_FALSE);
break;
case LI_MOTION_TYPE_GYRO:
SDL_GameControllerSetSensorEnabled(state->controller, SDL_SENSOR_GYRO, report_rate_hz ? SDL_TRUE : SDL_FALSE);
break;
}
#endif
}
void sdlinput_set_controller_led(unsigned short controller_id, unsigned char r, unsigned char g, unsigned char b) {
PGAMEPAD_STATE state = &gamepads[controller_id];
if (!state->initialized)
return;
#if SDL_VERSION_ATLEAST(2, 0, 14)
SDL_GameControllerSetLED(state->controller, r, g, b);
#endif
} }

View File

@ -22,9 +22,83 @@
extern int sdl_gamepads; 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); void sdlinput_init(char* mappings);
int sdlinput_handle_event(SDL_Window* window, SDL_Event* event); int sdlinput_handle_event(SDL_Event* event);
void sdlinput_rumble(unsigned short controller_id, unsigned short low_freq_motor, unsigned short high_freq_motor); void sdlinput_rumble(unsigned short controller_id, unsigned short low_freq_motor, unsigned short high_freq_motor);
void sdlinput_rumble_triggers(unsigned short controller_id, unsigned short left_trigger, unsigned short right_trigger);
void sdlinput_set_motion_event_state(unsigned short controller_id, unsigned char motion_type, unsigned short report_rate_hz);
void sdlinput_set_controller_led(unsigned short controller_id, unsigned char r, unsigned char g, unsigned char b);

View File

@ -36,6 +36,7 @@ static struct mapping* defaultMappings;
static struct udev *udev; static struct udev *udev;
static struct udev_monitor *udev_mon; static struct udev_monitor *udev_mon;
static int udev_fd;
static int inputRotate; static int inputRotate;
static int udev_handle(int fd) { static int udev_handle(int fd) {
@ -91,11 +92,10 @@ void udev_init(bool autoload, struct mapping* mappings, bool verbose, int rotate
defaultMappings = mappings; defaultMappings = mappings;
inputRotate = rotate; inputRotate = rotate;
loop_add_fd(udev_monitor_get_fd(udev_mon), &udev_handle, POLLIN); int udev_fd = udev_monitor_get_fd(udev_mon);
loop_add_fd(udev_fd, &udev_handle, POLLIN);
} }
void udev_destroy() { void evdev_destroy() {
loop_remove_fd(udev_monitor_get_fd(udev_mon));
udev_monitor_unref(udev_mon);
udev_unref(udev); udev_unref(udev);
} }

View File

@ -20,4 +20,4 @@
#include "mapping.h" #include "mapping.h"
void udev_init(bool autoload, struct mapping* mappings, bool verbose, int rotate); void udev_init(bool autoload, struct mapping* mappings, bool verbose, int rotate);
void udev_destroy(); void evdev_destroy();

View File

@ -111,12 +111,6 @@ static int x11_handler(int fd) {
case Button5: case Button5:
LiSendScrollEvent(-1); LiSendScrollEvent(-1);
break; break;
case 6:
LiSendHScrollEvent(-1);
break;
case 7:
LiSendHScrollEvent(1);
break;
case 8: case 8:
button = BUTTON_X1; button = BUTTON_X1;
break; break;

View File

@ -38,8 +38,7 @@ static int sigFd;
static int loop_sig_handler(int fd) { static int loop_sig_handler(int fd) {
struct signalfd_siginfo info; struct signalfd_siginfo info;
if (read(fd, &info, sizeof(info)) != sizeof(info)) read(fd, &info, sizeof(info));
return LOOP_RETURN;
switch (info.ssi_signo) { switch (info.ssi_signo) {
case SIGINT: case SIGINT:
case SIGTERM: case SIGTERM:
@ -74,9 +73,9 @@ void loop_add_fd(int fd, FdHandler handler, int events) {
void loop_remove_fd(int fd) { void loop_remove_fd(int fd) {
numFds--; numFds--;
int fdindex = numFds; int fdindex;
for (int i=0;i<=numFds;i++) { for (int i=0;i<numFds;i++) {
if (fds[i].fd == fd) { if (fds[i].fd == fd) {
fdindex = i; fdindex = i;
break; break;

View File

@ -20,8 +20,8 @@
#include "loop.h" #include "loop.h"
#include "connection.h" #include "connection.h"
#include "configuration.h" #include "configuration.h"
#include "platform.h"
#include "config.h" #include "config.h"
#include "platform.h"
#include "sdl.h" #include "sdl.h"
#include "audio/audio.h" #include "audio/audio.h"
@ -96,7 +96,7 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform sys
gamepads += sdl_gamepads; gamepads += sdl_gamepads;
#endif #endif
int gamepad_mask = 0; int gamepad_mask = 0;
for (int i = 0; i < gamepads; i++) for (int i = 0; i < gamepads && i < 4; i++)
gamepad_mask = (gamepad_mask << 1) + 1; gamepad_mask = (gamepad_mask << 1) + 1;
int ret = gs_start_app(server, &config->stream, appId, config->sops, config->localaudio, gamepad_mask); int ret = gs_start_app(server, &config->stream, appId, config->sops, config->localaudio, gamepad_mask);
@ -119,8 +119,6 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform sys
drFlags |= DISPLAY_FULLSCREEN; drFlags |= DISPLAY_FULLSCREEN;
switch (config->rotate) { switch (config->rotate) {
case 0:
break;
case 90: case 90:
drFlags |= DISPLAY_ROTATE_90; drFlags |= DISPLAY_ROTATE_90;
break; break;
@ -130,8 +128,6 @@ static void stream(PSERVER_DATA server, PCONFIGURATION config, enum platform sys
case 270: case 270:
drFlags |= DISPLAY_ROTATE_270; drFlags |= DISPLAY_ROTATE_270;
break; break;
default:
printf("Ignoring invalid rotation value: %d\n", config->rotate);
} }
if (config->debug_level > 0) { if (config->debug_level > 0) {
@ -195,14 +191,13 @@ static void help() {
printf("\t-4k\t\t\tUse 3840x2160 resolution\n"); printf("\t-4k\t\t\tUse 3840x2160 resolution\n");
printf("\t-width <width>\t\tHorizontal resolution (default 1280)\n"); printf("\t-width <width>\t\tHorizontal resolution (default 1280)\n");
printf("\t-height <height>\tVertical resolution (default 720)\n"); printf("\t-height <height>\tVertical resolution (default 720)\n");
#ifdef HAVE_EMBEDDED #if defined(HAVE_PI) | defined(HAVE_MMAL)
printf("\t-rotate <angle>\tRotate display: 0/90/180/270 (default 0)\n"); printf("\t-rotate <height>\tRotate display: 0/90/180/270 (default 0)\n");
#endif #endif
printf("\t-fps <fps>\t\tSpecify the fps to use (default 60)\n"); printf("\t-fps <fps>\t\tSpecify the fps to use (default 60)\n");
printf("\t-bitrate <bitrate>\tSpecify the bitrate in Kbps\n"); printf("\t-bitrate <bitrate>\tSpecify the bitrate in Kbps\n");
printf("\t-packetsize <size>\tSpecify the maximum packetsize in bytes\n"); printf("\t-packetsize <size>\tSpecify the maximum packetsize in bytes\n");
printf("\t-codec <codec>\t\tSelect used codec: auto/h264/h265/av1 (default auto)\n"); printf("\t-codec <codec>\t\tSelect used codec: auto/h264/h265 (default auto)\n");
printf("\t-hdr\t\tEnable HDR streaming (experimental, requires host and device support)\n");
printf("\t-remote <yes/no/auto>\t\t\tEnable optimizations for WAN streaming (default auto)\n"); printf("\t-remote <yes/no/auto>\t\t\tEnable optimizations for WAN streaming (default auto)\n");
printf("\t-app <app>\t\tName of app to stream\n"); printf("\t-app <app>\t\tName of app to stream\n");
printf("\t-nosops\t\t\tDon't allow GFE to modify game settings\n"); printf("\t-nosops\t\t\tDon't allow GFE to modify game settings\n");
@ -264,7 +259,7 @@ int main(int argc, char* argv[]) {
} }
config.address[0] = 0; config.address[0] = 0;
printf("Searching for server...\n"); printf("Searching for server...\n");
gs_discover_server(config.address, &config.port); gs_discover_server(config.address);
if (config.address[0] == 0) { if (config.address[0] == 0) {
fprintf(stderr, "Autodiscovery failed. Specify an IP address next time.\n"); fprintf(stderr, "Autodiscovery failed. Specify an IP address next time.\n");
exit(-1); exit(-1);
@ -277,10 +272,10 @@ int main(int argc, char* argv[]) {
config_file_parse(host_config_file, &config); config_file_parse(host_config_file, &config);
SERVER_DATA server; SERVER_DATA server;
printf("Connecting to %s...\n", config.address); printf("Connect to %s...\n", config.address);
int ret; int ret;
if ((ret = gs_init(&server, config.address, config.port, config.key_dir, config.debug_level, config.unsupported)) == GS_OUT_OF_MEMORY) { if ((ret = gs_init(&server, config.address, config.key_dir, config.debug_level, config.unsupported)) == GS_OUT_OF_MEMORY) {
fprintf(stderr, "Not enough memory\n"); fprintf(stderr, "Not enough memory\n");
exit(-1); exit(-1);
} else if (ret == GS_ERROR) { } else if (ret == GS_ERROR) {
@ -297,10 +292,8 @@ int main(int argc, char* argv[]) {
exit(-1); exit(-1);
} }
if (config.debug_level > 0) { if (config.debug_level > 0)
printf("GPU: %s, GFE: %s (%s, %s)\n", server.gpuType, server.serverInfo.serverInfoGfeVersion, server.gsVersion, server.serverInfo.serverInfoAppVersion); printf("GPU: %s, GFE: %s (%s, %s)\n", server.gpuType, server.serverInfo.serverInfoGfeVersion, server.gsVersion, server.serverInfo.serverInfoAppVersion);
printf("Server codec flags: 0x%x\n", server.serverInfo.serverCodecModeSupport);
}
if (strcmp("list", config.action) == 0) { if (strcmp("list", config.action) == 0) {
pair_check(&server); pair_check(&server);
@ -318,23 +311,7 @@ int main(int argc, char* argv[]) {
fprintf(stderr, "You can't select a audio device for SDL\n"); fprintf(stderr, "You can't select a audio device for SDL\n");
exit(-1); exit(-1);
} }
config.stream.supportsHevc = config.codec != CODEC_H264 && (config.codec == CODEC_HEVC || platform_supports_hevc(system));
config.stream.supportedVideoFormats = VIDEO_FORMAT_H264;
if (config.codec == CODEC_HEVC || (config.codec == CODEC_UNSPECIFIED && platform_prefers_codec(system, CODEC_HEVC))) {
config.stream.supportedVideoFormats |= VIDEO_FORMAT_H265;
if (config.hdr)
config.stream.supportedVideoFormats |= VIDEO_FORMAT_H265_MAIN10;
}
if (config.codec == CODEC_AV1 || (config.codec == CODEC_UNSPECIFIED && platform_prefers_codec(system, CODEC_AV1))) {
config.stream.supportedVideoFormats |= VIDEO_FORMAT_AV1_MAIN8;
if (config.hdr)
config.stream.supportedVideoFormats |= VIDEO_FORMAT_AV1_MAIN10;
}
if (config.hdr && !(config.stream.supportedVideoFormats & VIDEO_FORMAT_MASK_10BIT)) {
fprintf(stderr, "HDR streaming requires HEVC or AV1 codec\n");
exit(-1);
}
#ifdef HAVE_SDL #ifdef HAVE_SDL
if (system == SDL) if (system == SDL)
@ -364,7 +341,7 @@ int main(int argc, char* argv[]) {
for (int i=0;i<config.inputsCount;i++) { for (int i=0;i<config.inputsCount;i++) {
if (config.debug_level > 0) if (config.debug_level > 0)
printf("Adding input device %s...\n", config.inputs[i]); printf("Add input %s...\n", config.inputs[i]);
evdev_create(config.inputs[i], mappings, config.debug_level > 0, config.rotate); evdev_create(config.inputs[i], mappings, config.debug_level > 0, config.rotate);
} }
@ -385,9 +362,6 @@ int main(int argc, char* argv[]) {
sdlinput_init(config.mapping); sdlinput_init(config.mapping);
rumble_handler = sdlinput_rumble; rumble_handler = sdlinput_rumble;
rumble_triggers_handler = sdlinput_rumble_triggers;
set_motion_event_state_handler = sdlinput_set_motion_event_state;
set_controller_led_handler = sdlinput_set_controller_led;
} }
#endif #endif
} }
@ -395,13 +369,8 @@ int main(int argc, char* argv[]) {
stream(&server, &config, system); stream(&server, &config, system);
} else if (strcmp("pair", config.action) == 0) { } else if (strcmp("pair", config.action) == 0) {
char pin[5]; char pin[5];
if (config.pin > 0 && config.pin <= 9999) { sprintf(pin, "%d%d%d%d", (int)random() % 10, (int)random() % 10, (int)random() % 10, (int)random() % 10);
sprintf(pin, "%04d", config.pin);
} else {
sprintf(pin, "%d%d%d%d", (unsigned)random() % 10, (unsigned)random() % 10, (unsigned)random() % 10, (unsigned)random() % 10);
}
printf("Please enter the following PIN on the target PC: %s\n", pin); printf("Please enter the following PIN on the target PC: %s\n", pin);
fflush(stdout);
if (gs_pair(&server, &pin[0]) != GS_OK) { if (gs_pair(&server, &pin[0]) != GS_OK) {
fprintf(stderr, "Failed to pair to server: %s\n", gs_error); fprintf(stderr, "Failed to pair to server: %s\n", gs_error);
} else { } else {

View File

@ -61,7 +61,7 @@ enum platform platform_check(char* name) {
#endif #endif
#ifdef HAVE_AML #ifdef HAVE_AML
if (std || strcmp(name, "aml") == 0) { if (std || strcmp(name, "aml") == 0) {
void *handle = dlopen("libmoonlight-aml.so", RTLD_LAZY | RTLD_GLOBAL); void *handle = dlopen("libmoonlight-aml.so", RTLD_NOW | RTLD_GLOBAL);
if (handle != NULL && access("/dev/amvideo", F_OK) != -1) if (handle != NULL && access("/dev/amvideo", F_OK) != -1)
return AML; return AML;
} }
@ -87,18 +87,13 @@ enum platform platform_check(char* name) {
if (init == INIT_VDPAU) if (init == INIT_VDPAU)
return X11_VDPAU; return X11_VDPAU;
#endif #endif
#ifdef HAVE_SDL
return SDL;
#else
return X11; return X11;
#endif
} }
#endif #endif
#ifdef HAVE_SDL #ifdef HAVE_SDL
if (std || strcmp(name, "sdl") == 0) if (std || strcmp(name, "sdl") == 0)
return SDL; return SDL;
#endif #endif
if (strcmp(name, "fake") == 0) if (strcmp(name, "fake") == 0)
return FAKE; return FAKE;
@ -109,14 +104,13 @@ void platform_start(enum platform system) {
switch (system) { switch (system) {
#ifdef HAVE_AML #ifdef HAVE_AML
case AML: case AML:
write_bool("/sys/class/graphics/fb0/blank", true); blank_fb("/sys/class/graphics/fb0/blank", true);
write_bool("/sys/class/graphics/fb1/blank", true); blank_fb("/sys/class/graphics/fb1/blank", true);
write_bool("/sys/class/video/disable_video", false);
break; break;
#endif #endif
#if defined(HAVE_PI) || defined(HAVE_MMAL) #if defined(HAVE_PI) | defined(HAVE_MMAL)
case PI: case PI:
write_bool("/sys/class/graphics/fb0/blank", true); blank_fb("/sys/class/graphics/fb0/blank", true);
break; break;
#endif #endif
} }
@ -126,13 +120,13 @@ void platform_stop(enum platform system) {
switch (system) { switch (system) {
#ifdef HAVE_AML #ifdef HAVE_AML
case AML: case AML:
write_bool("/sys/class/graphics/fb0/blank", false); blank_fb("/sys/class/graphics/fb0/blank", false);
write_bool("/sys/class/graphics/fb1/blank", false); blank_fb("/sys/class/graphics/fb1/blank", false);
break; break;
#endif #endif
#if defined(HAVE_PI) || defined(HAVE_MMAL) #if defined(HAVE_PI) | defined(HAVE_MMAL)
case PI: case PI:
write_bool("/sys/class/graphics/fb0/blank", false); blank_fb("/sys/class/graphics/fb0/blank", false);
break; break;
#endif #endif
} }
@ -202,29 +196,15 @@ AUDIO_RENDERER_CALLBACKS* platform_get_audio(enum platform system, char* audio_d
#ifdef HAVE_ALSA #ifdef HAVE_ALSA
return &audio_callbacks_alsa; return &audio_callbacks_alsa;
#endif #endif
#ifdef __FreeBSD__
return &audio_callbacks_oss;
#endif
} }
return NULL; return NULL;
} }
bool platform_prefers_codec(enum platform system, enum codecs codec) { bool platform_supports_hevc(enum platform system) {
switch (codec) { switch (system) {
case CODEC_H264: case AML:
// H.264 is always supported case RK:
return true; return true;
case CODEC_HEVC:
switch (system) {
case AML:
case RK:
case X11_VAAPI:
case X11_VDPAU:
return true;
}
return false;
case CODEC_AV1:
return false;
} }
return false; return false;
} }

View File

@ -27,12 +27,11 @@
#define IS_EMBEDDED(SYSTEM) SYSTEM != SDL #define IS_EMBEDDED(SYSTEM) SYSTEM != SDL
enum platform { NONE, SDL, X11, X11_VDPAU, X11_VAAPI, PI, MMAL, IMX, AML, RK, FAKE }; enum platform { NONE, SDL, X11, X11_VDPAU, X11_VAAPI, PI, MMAL, IMX, AML, RK, FAKE };
enum codecs { CODEC_UNSPECIFIED, CODEC_H264, CODEC_HEVC, CODEC_AV1 };
enum platform platform_check(char*); enum platform platform_check(char*);
PDECODER_RENDERER_CALLBACKS platform_get_video(enum platform system); PDECODER_RENDERER_CALLBACKS platform_get_video(enum platform system);
PAUDIO_RENDERER_CALLBACKS platform_get_audio(enum platform system, char* audio_device); PAUDIO_RENDERER_CALLBACKS platform_get_audio(enum platform system, char* audio_device);
bool platform_prefers_codec(enum platform system, enum codecs codec); bool platform_supports_hevc(enum platform system);
char* platform_name(enum platform system); char* platform_name(enum platform system);
void platform_start(enum platform system); void platform_start(enum platform system);

View File

@ -50,6 +50,7 @@ void sdl_init(int width, int height, bool fullscreen) {
exit(1); exit(1);
} }
SDL_SetRelativeMouseMode(SDL_TRUE);
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC); renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
if (!renderer) { if (!renderer) {
printf("SDL_CreateRenderer failed: %s\n", SDL_GetError()); printf("SDL_CreateRenderer failed: %s\n", SDL_GetError());
@ -71,25 +72,19 @@ void sdl_init(int width, int height, bool fullscreen) {
void sdl_loop() { void sdl_loop() {
SDL_Event event; SDL_Event event;
SDL_SetRelativeMouseMode(SDL_TRUE);
while(!done && SDL_WaitEvent(&event)) { while(!done && SDL_WaitEvent(&event)) {
switch (sdlinput_handle_event(window, &event)) { switch (sdlinput_handle_event(&event)) {
case SDL_QUIT_APPLICATION: case SDL_QUIT_APPLICATION:
done = true; done = true;
break; break;
case SDL_TOGGLE_FULLSCREEN: case SDL_TOGGLE_FULLSCREEN:
fullscreen_flags ^= SDL_WINDOW_FULLSCREEN; fullscreen_flags ^= SDL_WINDOW_FULLSCREEN;
SDL_SetWindowFullscreen(window, fullscreen_flags); SDL_SetWindowFullscreen(window, fullscreen_flags);
break;
case SDL_MOUSE_GRAB: case SDL_MOUSE_GRAB:
SDL_ShowCursor(SDL_ENABLE);
SDL_SetRelativeMouseMode(SDL_TRUE); SDL_SetRelativeMouseMode(SDL_TRUE);
break; break;
case SDL_MOUSE_UNGRAB: case SDL_MOUSE_UNGRAB:
SDL_SetRelativeMouseMode(SDL_FALSE); SDL_SetRelativeMouseMode(SDL_FALSE);
SDL_ShowCursor(SDL_DISABLE);
break; break;
default: default:
if (event.type == SDL_QUIT) if (event.type == SDL_QUIT)

View File

@ -24,15 +24,14 @@
#include <fcntl.h> #include <fcntl.h>
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
#include <stdlib.h>
int write_bool(char *path, bool val) { int blank_fb(char *path, bool clear) {
int fd = open(path, O_RDWR); int fd = open(path, O_RDWR);
if(fd >= 0) { if(fd >= 0) {
int ret = write(fd, val ? "1" : "0", 1); int ret = write(fd, clear ? "1" : "0", 1);
if (ret < 0) if (ret < 0)
fprintf(stderr, "Failed to write %d to %s: %d\n", val ? 1 : 0, path, ret); fprintf(stderr, "Failed to clear framebuffer %s: %d\n", path, ret);
close(fd); close(fd);
return 0; return 0;
@ -51,16 +50,3 @@ int read_file(char *path, char* output, int output_len) {
return -1; return -1;
} }
bool ensure_buf_size(void **buf, size_t *buf_size, size_t required_size) {
if (*buf_size >= required_size)
return false;
*buf_size = required_size;
*buf = realloc(*buf, *buf_size);
if (!*buf) {
fprintf(stderr, "Failed to allocate %zu bytes\n", *buf_size);
abort();
}
return true;
}

View File

@ -18,9 +18,6 @@
*/ */
#include <stdbool.h> #include <stdbool.h>
#include <stddef.h>
int write_bool(char *path, bool val); int blank_fb(char *path, bool clear);
int read_file(char *path, char *output, int output_len); int read_file(char *path, char *output, int output_len);
bool ensure_buf_size(void **buf, size_t *buf_size, size_t required_size);
bool has_fast_aes(void);

View File

@ -24,101 +24,48 @@
#include <stdio.h> #include <stdio.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <codec.h> #include <codec.h>
#include <errno.h>
#include <pthread.h>
#include <linux/videodev2.h>
#include "../util.h"
#include "video.h"
#define SYNC_OUTSIDE 0x02 #define SYNC_OUTSIDE 0x02
#define UCODE_IP_ONLY_PARAM 0x08 #define UCODE_IP_ONLY_PARAM 0x08
#define MAX_WRITE_ATTEMPTS 5
#define EAGAIN_SLEEP_TIME 2 * 1000
static codec_para_t codecParam = { 0 }; static codec_para_t codecParam = { 0 };
static pthread_t displayThread;
static int videoFd = -1;
static volatile bool done = false;
void *pkt_buf = NULL;
size_t pkt_buf_size = 0;
void* aml_display_thread(void* unused) {
while (!done) {
struct v4l2_buffer vbuf = { 0 };
vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (ioctl(videoFd, VIDIOC_DQBUF, &vbuf) < 0) {
if (errno == EAGAIN) {
usleep(500);
continue;
}
fprintf(stderr, "VIDIOC_DQBUF failed: %d\n", errno);
break;
}
if (ioctl(videoFd, VIDIOC_QBUF, &vbuf) < 0) {
fprintf(stderr, "VIDIOC_QBUF failed: %d\n", errno);
break;
}
}
printf("Display thread terminated\n");
return NULL;
}
int aml_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { int aml_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
codecParam.stream_type = STREAM_TYPE_ES_VIDEO;
codecParam.has_video = 1;
codecParam.noblock = 0;
codecParam.am_sysinfo.param = 0;
codecParam.handle = -1; switch (videoFormat) {
codecParam.cntl_handle = -1; case VIDEO_FORMAT_H264:
codecParam.audio_utils_handle = -1; if (width > 1920 || height > 1080) {
codecParam.sub_handle = -1; codecParam.video_type = VFORMAT_H264_4K2K;
codecParam.has_video = 1; codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_H264_4K2K;
codecParam.noblock = 0; } else {
codecParam.stream_type = STREAM_TYPE_ES_VIDEO; codecParam.video_type = VFORMAT_H264;
codecParam.am_sysinfo.param = 0; codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_H264;
#ifdef STREAM_TYPE_FRAME // Workaround for decoding special case of C1, 1080p, H264
codecParam.dec_mode = STREAM_TYPE_FRAME; int major, minor;
#endif struct utsname name;
uname(&name);
#ifdef FRAME_BASE_PATH_AMLVIDEO_AMVIDEO int ret = sscanf(name.release, "%d.%d", &major, &minor);
codecParam.video_path = FRAME_BASE_PATH_AMLVIDEO_AMVIDEO; if (!(major > 3 || (major == 3 && minor >= 14)) && width == 1920 && height == 1080)
#endif codecParam.am_sysinfo.param = (void*) UCODE_IP_ONLY_PARAM;
}
if (videoFormat & VIDEO_FORMAT_MASK_H264) { break;
if (width > 1920 || height > 1080) { case VIDEO_FORMAT_H265:
codecParam.video_type = VFORMAT_H264_4K2K; codecParam.video_type = VFORMAT_HEVC;
codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_H264_4K2K; codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_HEVC;
} else { break;
codecParam.video_type = VFORMAT_H264; default:
codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_H264; printf("Video format not supported\n");
return -1;
// Workaround for decoding special case of C1, 1080p, H264
int major, minor;
struct utsname name;
uname(&name);
int ret = sscanf(name.release, "%d.%d", &major, &minor);
if (ret == 2 && !(major > 3 || (major == 3 && minor >= 14)) && width == 1920 && height == 1080)
codecParam.am_sysinfo.param = (void*) UCODE_IP_ONLY_PARAM;
}
} else if (videoFormat & VIDEO_FORMAT_MASK_H265) {
codecParam.video_type = VFORMAT_HEVC;
codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_HEVC;
#ifdef CODEC_TAG_AV1
} else if (videoFormat & VIDEO_FORMAT_MASK_AV1) {
codecParam.video_type = VFORMAT_AV1;
codecParam.am_sysinfo.format = VIDEO_DEC_FORMAT_AV1;
#endif
} else {
printf("Video format not supported\n");
return -1;
} }
codecParam.am_sysinfo.width = width; codecParam.am_sysinfo.width = width;
@ -137,83 +84,33 @@ int aml_setup(int videoFormat, int width, int height, int redrawRate, void* cont
return -2; return -2;
} }
char vfm_map[2048] = {};
char* eol;
if (read_file("/sys/class/vfm/map", vfm_map, sizeof(vfm_map) - 1) > 0 && (eol = strchr(vfm_map, '\n'))) {
*eol = 0;
// If amlvideo is in the pipeline, we must spawn a display thread
printf("VFM map: %s\n", vfm_map);
if (strstr(vfm_map, "amlvideo")) {
printf("Using display thread for amlvideo pipeline\n");
videoFd = open("/dev/video10", O_RDONLY | O_NONBLOCK);
if (videoFd < 0) {
fprintf(stderr, "Failed to open video device: %d\n", errno);
return -3;
}
pthread_create(&displayThread, NULL, aml_display_thread, NULL);
}
}
ensure_buf_size(&pkt_buf, &pkt_buf_size, INITIAL_DECODER_BUFFER_SIZE);
return 0; return 0;
} }
void aml_cleanup() { void aml_cleanup() {
if (videoFd >= 0) {
done = true;
pthread_join(displayThread, NULL);
close(videoFd);
}
codec_close(&codecParam); codec_close(&codecParam);
free(pkt_buf);
} }
int aml_submit_decode_unit(PDECODE_UNIT decodeUnit) { int aml_submit_decode_unit(PDECODE_UNIT decodeUnit) {
int result = DR_OK;
ensure_buf_size(&pkt_buf, &pkt_buf_size, decodeUnit->fullLength);
int written = 0, length = 0, errCounter = 0, api;
PLENTRY entry = decodeUnit->bufferList; PLENTRY entry = decodeUnit->bufferList;
do { while (entry != NULL) {
memcpy(pkt_buf+length, entry->data, entry->length); int api = codec_write(&codecParam, entry->data, entry->length);
length += entry->length; if (api != entry->length) {
entry = entry->next; fprintf(stderr, "codec_write error: %x\n", api);
} while (entry != NULL); codec_reset(&codecParam);
result = DR_NEED_IDR;
codec_checkin_pts(&codecParam, decodeUnit->presentationTimeMs); break;
while (length > 0) {
api = codec_write(&codecParam, pkt_buf+written, length);
if (api < 0) {
if (errno != EAGAIN) {
fprintf(stderr, "codec_write() error: %x %d\n", errno, api);
codec_reset(&codecParam);
break;
} else {
if (++errCounter == MAX_WRITE_ATTEMPTS) {
fprintf(stderr, "codec_write() timeout\n");
break;
}
usleep(EAGAIN_SLEEP_TIME);
}
} else {
written += api;
length -= api;
} }
}
return length ? DR_NEED_IDR : DR_OK; entry = entry->next;
}
return result;
} }
DECODER_RENDERER_CALLBACKS decoder_callbacks_aml = { DECODER_RENDERER_CALLBACKS decoder_callbacks_aml = {
.setup = aml_setup, .setup = aml_setup,
.cleanup = aml_cleanup, .cleanup = aml_cleanup,
.submitDecodeUnit = aml_submit_decode_unit, .submitDecodeUnit = aml_submit_decode_unit,
.capabilities = CAPABILITY_DIRECT_SUBMIT | CAPABILITY_SLICES_PER_FRAME(8),
// We may delay in aml_submit_decode_unit() for a while, so we can't set CAPABILITY_DIRECT_SUBMIT
.capabilities = CAPABILITY_REFERENCE_FRAME_INVALIDATION_HEVC,
}; };

View File

@ -143,6 +143,7 @@ void egl_init(EGLNativeDisplayType native_display, NativeWindowType native_windo
GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER); GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_shader, 1, &vertex_source, NULL); glShaderSource(vertex_shader, 1, &vertex_source, NULL);
glCompileShader(vertex_shader); glCompileShader(vertex_shader);
GLint maxLength = 0;
GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_shader, 1, &fragment_source, NULL); glShaderSource(fragment_shader, 1, &fragment_source, NULL);

View File

@ -33,7 +33,7 @@
// General decoder and renderer state // General decoder and renderer state
static AVPacket* pkt; static AVPacket* pkt;
static const AVCodec* decoder; static AVCodec* decoder;
static AVCodecContext* decoder_ctx; static AVCodecContext* decoder_ctx;
static AVFrame** dec_frames; static AVFrame** dec_frames;
@ -60,72 +60,13 @@ int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer
} }
ffmpeg_decoder = perf_lvl & VAAPI_ACCELERATION ? VAAPI : SOFTWARE; ffmpeg_decoder = perf_lvl & VAAPI_ACCELERATION ? VAAPI : SOFTWARE;
switch (videoFormat) {
for (int try = 0; try < 6; try++) { case VIDEO_FORMAT_H264:
if (videoFormat & VIDEO_FORMAT_MASK_H264) { decoder = avcodec_find_decoder_by_name("h264");
if (ffmpeg_decoder == SOFTWARE) { break;
if (try == 0) decoder = avcodec_find_decoder_by_name("h264_nvv4l2"); // Tegra case VIDEO_FORMAT_H265:
if (try == 1) decoder = avcodec_find_decoder_by_name("h264_nvmpi"); // Tegra decoder = avcodec_find_decoder_by_name("hevc");
if (try == 2) decoder = avcodec_find_decoder_by_name("h264_omx"); // VisionFive break;
if (try == 3) decoder = avcodec_find_decoder_by_name("h264_v4l2m2m"); // Stateful V4L2
}
if (try == 4) decoder = avcodec_find_decoder_by_name("h264"); // Software and hwaccel
} else if (videoFormat & VIDEO_FORMAT_MASK_H265) {
if (ffmpeg_decoder == SOFTWARE) {
if (try == 0) decoder = avcodec_find_decoder_by_name("hevc_nvv4l2"); // Tegra
if (try == 1) decoder = avcodec_find_decoder_by_name("hevc_nvmpi"); // Tegra
if (try == 2) decoder = avcodec_find_decoder_by_name("hevc_omx"); // VisionFive
if (try == 3) decoder = avcodec_find_decoder_by_name("hevc_v4l2m2m"); // Stateful V4L2
}
if (try == 4) decoder = avcodec_find_decoder_by_name("hevc"); // Software and hwaccel
} else if (videoFormat & VIDEO_FORMAT_MASK_AV1) {
if (ffmpeg_decoder == SOFTWARE) {
if (try == 0) decoder = avcodec_find_decoder_by_name("libdav1d");
}
if (try == 1) decoder = avcodec_find_decoder_by_name("av1"); // Hwaccel
} else {
printf("Video format not supported\n");
return -1;
}
// Skip this decoder if it isn't compiled into FFmpeg
if (!decoder) {
continue;
}
decoder_ctx = avcodec_alloc_context3(decoder);
if (decoder_ctx == NULL) {
printf("Couldn't allocate context\n");
return -1;
}
// Use low delay decoding
decoder_ctx->flags |= AV_CODEC_FLAG_LOW_DELAY;
// Allow display of corrupt frames and frames missing references
decoder_ctx->flags |= AV_CODEC_FLAG_OUTPUT_CORRUPT;
decoder_ctx->flags2 |= AV_CODEC_FLAG2_SHOW_ALL;
// Report decoding errors to allow us to request a key frame
decoder_ctx->err_recognition = AV_EF_EXPLODE;
if (perf_lvl & SLICE_THREADING) {
decoder_ctx->thread_type = FF_THREAD_SLICE;
decoder_ctx->thread_count = thread_count;
} else {
decoder_ctx->thread_count = 1;
}
decoder_ctx->width = width;
decoder_ctx->height = height;
decoder_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
int err = avcodec_open2(decoder_ctx, decoder, NULL);
if (err < 0) {
printf("Couldn't open codec: %s\n", decoder->name);
avcodec_free_context(&decoder_ctx);
continue;
}
} }
if (decoder == NULL) { if (decoder == NULL) {
@ -133,7 +74,38 @@ int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer
return -1; return -1;
} }
printf("Using FFmpeg decoder: %s\n", decoder->name); decoder_ctx = avcodec_alloc_context3(decoder);
if (decoder_ctx == NULL) {
printf("Couldn't allocate context");
return -1;
}
// Use low delay decoding
decoder_ctx->flags |= AV_CODEC_FLAG_LOW_DELAY;
// Allow display of corrupt frames and frames missing references
decoder_ctx->flags |= AV_CODEC_FLAG_OUTPUT_CORRUPT;
decoder_ctx->flags2 |= AV_CODEC_FLAG2_SHOW_ALL;
// Report decoding errors to allow us to request a key frame
decoder_ctx->err_recognition = AV_EF_EXPLODE;
if (perf_lvl & SLICE_THREADING) {
decoder_ctx->thread_type = FF_THREAD_SLICE;
decoder_ctx->thread_count = thread_count;
} else {
decoder_ctx->thread_count = 1;
}
decoder_ctx->width = width;
decoder_ctx->height = height;
decoder_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
int err = avcodec_open2(decoder_ctx, decoder, NULL);
if (err < 0) {
printf("Couldn't open codec");
return err;
}
dec_frames_cnt = buffer_count; dec_frames_cnt = buffer_count;
dec_frames = malloc(buffer_count * sizeof(AVFrame*)); dec_frames = malloc(buffer_count * sizeof(AVFrame*));
@ -163,7 +135,9 @@ int ffmpeg_init(int videoFormat, int width, int height, int perf_lvl, int buffer
void ffmpeg_destroy(void) { void ffmpeg_destroy(void) {
av_packet_free(&pkt); av_packet_free(&pkt);
if (decoder_ctx) { if (decoder_ctx) {
avcodec_free_context(&decoder_ctx); avcodec_close(decoder_ctx);
av_free(decoder_ctx);
decoder_ctx = NULL;
} }
if (dec_frames) { if (dec_frames) {
for (int i = 0; i < dec_frames_cnt; i++) { for (int i = 0; i < dec_frames_cnt; i++) {

View File

@ -65,7 +65,6 @@ int vaapi_init_lib() {
int vaapi_init(AVCodecContext* decoder_ctx) { int vaapi_init(AVCodecContext* decoder_ctx) {
decoder_ctx->get_format = va_get_format; decoder_ctx->get_format = va_get_format;
decoder_ctx->get_buffer2 = va_get_buffer; decoder_ctx->get_buffer2 = va_get_buffer;
return 0;
} }
void vaapi_queue(AVFrame* dec_frame, Window win, int width, int height) { void vaapi_queue(AVFrame* dec_frame, Window win, int width, int height) {

View File

@ -42,7 +42,7 @@
#include <linux/v4l2-controls.h> #include <linux/v4l2-controls.h>
#include <linux/videodev2.h> #include <linux/videodev2.h>
#define MIN_FRAME_BUFFER_COUNT 18 #define MIN_FRAME_BUFFER_COUNT 18;
#define THRESHOLD 2 #define THRESHOLD 2

View File

@ -210,7 +210,7 @@ bool vpu_decode(PDECODE_UNIT decodeUnit) {
while (vpu_IsBusy()) { while (vpu_IsBusy()) {
if (loop_id > 50) { if (loop_id > 50) {
vpu_SWReset(handle, 0); vpu_SWReset(handle, 0);
fprintf(stderr, "VPU busy timeout expired\n"); fprintf(stderr, "VPU is too long busy\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
vpu_WaitForInt(100); vpu_WaitForInt(100);

View File

@ -48,6 +48,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <interface/mmal/vc/mmal_vc_api.h> #include <interface/mmal/vc/mmal_vc_api.h>
#include <interface/vcos/vcos.h> #include <interface/vcos/vcos.h>
#define MAX_DECODE_UNIT_SIZE 262144
#define ALIGN(x, a) (((x)+(a)-1)&~((a)-1)) #define ALIGN(x, a) (((x)+(a)-1)&~((a)-1))
static VCOS_SEMAPHORE_T semaphore; static VCOS_SEMAPHORE_T semaphore;
@ -78,6 +80,8 @@ static void output_callback(MMAL_PORT_T *port, MMAL_BUFFER_HEADER_T *buf) {
} }
static int decoder_renderer_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { static int decoder_renderer_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
MMAL_STATUS_T status;
if (videoFormat != VIDEO_FORMAT_H264) { if (videoFormat != VIDEO_FORMAT_H264) {
fprintf(stderr, "Video format not supported\n"); fprintf(stderr, "Video format not supported\n");
return -1; return -1;
@ -112,7 +116,7 @@ static int decoder_renderer_setup(int videoFormat, int width, int height, int re
} }
decoder->input[0]->buffer_num = 5; decoder->input[0]->buffer_num = 5;
decoder->input[0]->buffer_size = INITIAL_DECODER_BUFFER_SIZE; decoder->input[0]->buffer_size = MAX_DECODE_UNIT_SIZE;
pool_in = mmal_port_pool_create(decoder->input[0], decoder->input[0]->buffer_num, decoder->output[0]->buffer_size); pool_in = mmal_port_pool_create(decoder->input[0], decoder->input[0]->buffer_num, decoder->output[0]->buffer_size);
MMAL_ES_FORMAT_T *format_out = decoder->output[0]->format; MMAL_ES_FORMAT_T *format_out = decoder->output[0]->format;

View File

@ -41,11 +41,13 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <ilclient.h> #include <ilclient.h>
#include <bcm_host.h> #include <bcm_host.h>
#define MAX_DECODE_UNIT_SIZE 262144
static TUNNEL_T tunnel[2]; static TUNNEL_T tunnel[2];
static COMPONENT_T *list[3]; static COMPONENT_T *list[3];
static ILCLIENT_T *client; static ILCLIENT_T *client;
static COMPONENT_T *video_decode = NULL, *video_render = NULL; static COMPONENT_T *video_decode = NULL, *video_scheduler = NULL, *video_render = NULL;
static int port_settings_changed; static int port_settings_changed;
static int first_packet; static int first_packet;
@ -60,6 +62,8 @@ static int decoder_renderer_setup(int videoFormat, int width, int height, int re
gs_sps_init(width, height); gs_sps_init(width, height);
OMX_VIDEO_PARAM_PORTFORMATTYPE format; OMX_VIDEO_PARAM_PORTFORMATTYPE format;
OMX_TIME_CONFIG_CLOCKSTATETYPE cstate;
COMPONENT_T *clock = NULL;
memset(list, 0, sizeof(list)); memset(list, 0, sizeof(list));
memset(tunnel, 0, sizeof(tunnel)); memset(tunnel, 0, sizeof(tunnel));
@ -128,6 +132,13 @@ static int decoder_renderer_setup(int videoFormat, int width, int height, int re
latencyTarget.nInterFactor = 500; latencyTarget.nInterFactor = 500;
latencyTarget.nAdjCap = 20; latencyTarget.nAdjCap = 20;
OMX_CONFIG_DISPLAYREGIONTYPE displayRegion;
displayRegion.nSize = sizeof(OMX_PARAM_PORTDEFINITIONTYPE);
displayRegion.nVersion.nVersion = OMX_VERSION;
displayRegion.nPortIndex = 90;
displayRegion.fullscreen = OMX_TRUE;
displayRegion.mode = OMX_DISPLAY_SET_FULLSCREEN;
if(OMX_SetParameter(ILC_GET_HANDLE(video_render), OMX_IndexConfigLatencyTarget, &latencyTarget) != OMX_ErrorNone) { if(OMX_SetParameter(ILC_GET_HANDLE(video_render), OMX_IndexConfigLatencyTarget, &latencyTarget) != OMX_ErrorNone) {
fprintf(stderr, "Failed to set video render parameters\n"); fprintf(stderr, "Failed to set video render parameters\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@ -168,7 +179,7 @@ static int decoder_renderer_setup(int videoFormat, int width, int height, int re
} }
// Increase the buffer size to fit the largest possible frame // Increase the buffer size to fit the largest possible frame
port.nBufferSize = INITIAL_DECODER_BUFFER_SIZE; port.nBufferSize = MAX_DECODE_UNIT_SIZE;
if(OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamPortDefinition, &port) == OMX_ErrorNone && if(OMX_SetParameter(ILC_GET_HANDLE(video_decode), OMX_IndexParamPortDefinition, &port) == OMX_ErrorNone &&
ilclient_enable_port_buffers(video_decode, 130, NULL, NULL, NULL) == 0) { ilclient_enable_port_buffers(video_decode, 130, NULL, NULL, NULL) == 0) {
@ -186,6 +197,8 @@ static int decoder_renderer_setup(int videoFormat, int width, int height, int re
} }
static void decoder_renderer_cleanup() { static void decoder_renderer_cleanup() {
int status = 0;
OMX_BUFFERHEADERTYPE *buf; OMX_BUFFERHEADERTYPE *buf;
if((buf = ilclient_get_input_buffer(video_decode, 130, 1)) == NULL){ if((buf = ilclient_get_input_buffer(video_decode, 130, 1)) == NULL){
fprintf(stderr, "Can't get video buffer\n"); fprintf(stderr, "Can't get video buffer\n");

View File

@ -18,8 +18,7 @@
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>. * along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
*/ */
#include "video.h" #include <Limelight.h>
#include "../util.h"
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -28,6 +27,7 @@
#include <errno.h> #include <errno.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h> #include <fcntl.h>
#include <pthread.h> #include <pthread.h>
#include <unistd.h> #include <unistd.h>
@ -35,80 +35,28 @@
#include <xf86drm.h> #include <xf86drm.h>
#include <xf86drmMode.h> #include <xf86drmMode.h>
#include <libdrm/drm_fourcc.h> #include <libdrm/drm_fourcc.h>
#include <linux/videodev2.h>
#include <rockchip/rk_mpi.h> #include <rockchip/rk_mpi.h>
#define MAX_FRAMES 3 #define READ_BUF_SIZE 0x00100000
#define RK_H264 0x7 #define MAX_FRAMES 16
#define RK_H265 0x1000004 #define RK_H264 7
#define RK_AV1 0x1000008 #define RK_H265 16777220
// Vendor-defined 10-bit format code used prior to 5.10 #ifndef DRM_FORMAT_NV12_10
#ifndef DRM_FORMAT_NA12 #define DRM_FORMAT_NV12_10 fourcc_code('N', 'A', '1', '2')
#define DRM_FORMAT_NA12 fourcc_code('N', 'A', '1', '2')
#endif #endif
// Upstreamed 10-bit format code used on 5.10+ kernels
#ifndef DRM_FORMAT_NV15
#define DRM_FORMAT_NV15 fourcc_code('N', 'V', '1', '5')
#endif
// Values for "Colorspace" connector property
#ifndef DRM_MODE_COLORIMETRY_DEFAULT
#define DRM_MODE_COLORIMETRY_DEFAULT 0
#endif
#ifndef DRM_MODE_COLORIMETRY_BT2020_RGB
#define DRM_MODE_COLORIMETRY_BT2020_RGB 9
#endif
// HDR structs copied from linux include/linux/hdmi.h for older libdrm versions
struct rk_hdr_metadata_infoframe
{
uint8_t eotf;
uint8_t metadata_type;
struct
{
uint16_t x, y;
} display_primaries[3];
struct
{
uint16_t x, y;
} white_point;
uint16_t max_display_mastering_luminance;
uint16_t min_display_mastering_luminance;
uint16_t max_cll;
uint16_t max_fall;
};
struct rk_hdr_output_metadata
{
uint32_t metadata_type;
union {
struct rk_hdr_metadata_infoframe hdmi_metadata_type1;
};
};
void *pkt_buf = NULL; void *pkt_buf = NULL;
size_t pkt_buf_size = 0;
int fd; int fd;
int fb_id; int fb_id;
uint32_t plane_id, crtc_id, conn_id, hdr_metadata_blob_id, pixel_format; uint32_t plane_id, crtc_id;
int frm_eos; int frm_eos;
int crtc_width; int crtc_width;
int crtc_height; int crtc_height;
RK_U32 frm_width; RK_U32 frm_width;
RK_U32 frm_height; RK_U32 frm_height;
int fb_x, fb_y, fb_width, fb_height; int fb_x, fb_y, fb_width, fb_height;
bool atomic;
uint8_t last_colorspace = 0xFF;
bool last_hdr_state = false;
pthread_t tid_frame, tid_display; pthread_t tid_frame, tid_display;
pthread_mutex_t mutex; pthread_mutex_t mutex;
@ -121,12 +69,6 @@ drmModeRes *resources = NULL;
drmModePlaneRes *plane_resources = NULL; drmModePlaneRes *plane_resources = NULL;
drmModeCrtcPtr crtc = {0}; drmModeCrtcPtr crtc = {0};
drmModePropertyPtr hdr_metadata_prop = NULL;
drmModeAtomicReqPtr drm_request = NULL;
drmModePropertyPtr plane_props[32];
drmModePropertyPtr conn_props[32];
MppCtx mpi_ctx; MppCtx mpi_ctx;
MppApi *mpi_api; MppApi *mpi_api;
MppPacket mpi_packet; MppPacket mpi_packet;
@ -138,57 +80,36 @@ struct {
uint32_t handle; uint32_t handle;
} frame_to_drm[MAX_FRAMES]; } frame_to_drm[MAX_FRAMES];
int set_property(uint32_t id, uint32_t type, drmModePropertyPtr *props, char *name, uint64_t value) {
while (*props) {
if (!strcasecmp(name, (*props)->name)) {
if (atomic)
return drmModeAtomicAddProperty(drm_request, id, (*props)->prop_id, value);
else
return drmModeObjectSetProperty(fd, id, type, (*props)->prop_id, value);
}
props++;
}
fprintf(stderr, "Property '%s' not found\n", name);
return -EINVAL;
}
void *display_thread(void *param) { void *display_thread(void *param) {
int ret; int ret;
while (!frm_eos) { while (!frm_eos) {
int _fb_id; int _fb_id;
pthread_mutex_lock(&mutex); ret = pthread_mutex_lock(&mutex);
assert(!ret);
while (fb_id == 0) { while (fb_id == 0) {
pthread_cond_wait(&cond, &mutex); pthread_cond_wait(&cond, &mutex);
assert(!ret);
if (fb_id == 0 && frm_eos) { if (fb_id == 0 && frm_eos) {
pthread_mutex_unlock(&mutex); ret = pthread_mutex_unlock(&mutex);
assert(!ret);
return NULL; return NULL;
} }
} }
_fb_id = fb_id; _fb_id = fb_id;
fb_id = 0; fb_id = 0;
pthread_mutex_unlock(&mutex); ret = pthread_mutex_unlock(&mutex);
assert(!ret);
if (atomic) { // show DRM FB in overlay plane (auto vsynced/atomic !)
// We may need to modeset to apply colorspace changes when toggling HDR ret = drmModeSetPlane(fd, plane_id, crtc_id, _fb_id, 0,
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "FB_ID", _fb_id); fb_x, fb_y, fb_width, fb_height,
ret = drmModeAtomicCommit(fd, drm_request, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL); 0, 0, frm_width << 16, frm_height << 16);
if (ret) { assert(!ret);
perror("drmModeAtomicCommit");
}
} else {
ret = drmModeSetPlane(fd, plane_id, crtc_id, _fb_id, 0,
fb_x, fb_y, fb_width, fb_height,
0, 0, frm_width << 16, frm_height << 16);
if (ret) {
perror("drmModeSetPlane");
}
}
} }
return NULL;
} }
void *frame_thread(void *param) { void *frame_thread(void *param) {
@ -245,27 +166,25 @@ void *frame_thread(void *param) {
// new DRM buffer // new DRM buffer
struct drm_mode_create_dumb dmcd = {0}; struct drm_mode_create_dumb dmcd = {0};
dmcd.bpp = 8; // hor_stride is already adjusted for 10 vs 8 bit dmcd.bpp = fmt == MPP_FMT_YUV420SP ? 8:10;
dmcd.width = hor_stride; dmcd.width = hor_stride;
dmcd.height = ver_stride * 2; // documentation say not v*2/3 but v*2 (additional info included) dmcd.height = ver_stride * 2; // documentation say not v*2/3 but v*2 (additional info included)
ret = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &dmcd); do {
if (ret) { ret = ioctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &dmcd);
perror("drmIoctl(DRM_IOCTL_MODE_CREATE_DUMB)"); } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
exit(EXIT_FAILURE); assert(!ret);
} assert(dmcd.pitch == (fmt == MPP_FMT_YUV420SP?hor_stride:hor_stride * 10 / 8));
assert(dmcd.pitch == dmcd.width); assert(dmcd.size == (fmt == MPP_FMT_YUV420SP?hor_stride:hor_stride * 10 / 8) * ver_stride * 2);
assert(dmcd.size == dmcd.pitch * dmcd.height);
frame_to_drm[i].handle = dmcd.handle; frame_to_drm[i].handle = dmcd.handle;
// commit DRM buffer to frame group // commit DRM buffer to frame group
struct drm_prime_handle dph = {0}; struct drm_prime_handle dph = {0};
dph.handle = dmcd.handle; dph.handle = dmcd.handle;
dph.fd = -1; dph.fd = -1;
ret = drmIoctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &dph); do {
if (ret) { ret = ioctl(fd, DRM_IOCTL_PRIME_HANDLE_TO_FD, &dph);
perror("drmIoctl(DRM_IOCTL_PRIME_HANDLE_TO_FD)"); } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
exit(EXIT_FAILURE); assert(!ret);
}
MppBufferInfo info = {0}; MppBufferInfo info = {0};
info.type = MPP_BUFFER_TYPE_DRM; info.type = MPP_BUFFER_TYPE_DRM;
info.size = dmcd.width * dmcd.height; info.size = dmcd.width * dmcd.height;
@ -278,37 +197,17 @@ void *frame_thread(void *param) {
uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0}; uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
handles[0] = frame_to_drm[i].handle; handles[0] = frame_to_drm[i].handle;
offsets[0] = 0; offsets[0] = 0;
pitches[0] = dmcd.pitch; pitches[0] = hor_stride;
handles[1] = frame_to_drm[i].handle; handles[1] = frame_to_drm[i].handle;
offsets[1] = pitches[0] * ver_stride; offsets[1] = hor_stride * ver_stride;
pitches[1] = dmcd.pitch; pitches[1] = hor_stride;
ret = drmModeAddFB2(fd, frm_width, frm_height, pixel_format, handles, pitches, offsets, &frame_to_drm[i].fb_id, 0); ret = drmModeAddFB2(fd, frm_width, frm_height, fmt == MPP_FMT_YUV420SP ? DRM_FORMAT_NV12:DRM_FORMAT_NV12_10, handles, pitches, offsets, &frame_to_drm[i].fb_id, 0);
if (ret) { assert(!ret);
perror("drmModeAddFB2");
exit(EXIT_FAILURE);
}
} }
// register external frame group // register external frame group
ret = mpi_api->control(mpi_ctx, MPP_DEC_SET_EXT_BUF_GROUP, mpi_frm_grp); ret = mpi_api->control(mpi_ctx, MPP_DEC_SET_EXT_BUF_GROUP, mpi_frm_grp);
ret = mpi_api->control(mpi_ctx, MPP_DEC_SET_INFO_CHANGE_READY, NULL); ret = mpi_api->control(mpi_ctx, MPP_DEC_SET_INFO_CHANGE_READY, NULL);
// Set atomic properties for the plane prior to the first commit
if (atomic) {
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "CRTC_ID", crtc_id);
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "SRC_X", 0 << 16);
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "SRC_Y", 0 << 16);
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "SRC_W", frm_width << 16);
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "SRC_H", frm_height << 16);
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "CRTC_X", fb_x);
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "CRTC_Y", fb_y);
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "CRTC_W", fb_width);
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "CRTC_H", fb_height);
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "ZPOS", 0);
}
// Set properties on the connector
set_property(conn_id, DRM_MODE_OBJECT_CONNECTOR, conn_props, "allm_enable", 1); // HDMI ALLM (Game Mode)
set_property(conn_id, DRM_MODE_OBJECT_CONNECTOR, conn_props, "Colorspace", last_hdr_state ? DRM_MODE_COLORIMETRY_BT2020_RGB : DRM_MODE_COLORIMETRY_DEFAULT);
} else { } else {
// regular frame received // regular frame received
@ -323,10 +222,14 @@ void *frame_thread(void *param) {
} }
assert(i != MAX_FRAMES); assert(i != MAX_FRAMES);
// send DRM FB to display thread // send DRM FB to display thread
pthread_mutex_lock(&mutex); ret = pthread_mutex_lock(&mutex);
assert(!ret);
fb_id = frame_to_drm[i].fb_id; fb_id = frame_to_drm[i].fb_id;
pthread_cond_signal(&cond); ret = pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex); assert(!ret);
ret = pthread_mutex_unlock(&mutex);
assert(!ret);
} else { } else {
fprintf(stderr, "Frame no buff\n"); fprintf(stderr, "Frame no buff\n");
} }
@ -351,42 +254,29 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte
int ret; int ret;
int i; int i;
int j; int j;
int format; int format = 0;
if (videoFormat & VIDEO_FORMAT_MASK_H264) { switch (videoFormat) {
format = RK_H264; case VIDEO_FORMAT_H264:
} else if (videoFormat & VIDEO_FORMAT_MASK_H265) { format = RK_H264;
format = RK_H265; break;
} else if (videoFormat & VIDEO_FORMAT_MASK_AV1) { case VIDEO_FORMAT_H265:
format = RK_AV1; format = RK_H265;
} else { break;
fprintf(stderr, "Video format not supported\n"); default:
return -1; fprintf(stderr, "Video format not supported\n");
return -1;
} }
// We need atomic plane properties for HDR, but atomic seems to perform quite bad
// on RK3588 for some reason. The performance of commits seems to go down the longer
// the stream runs. We'll use the legacy API for non-HDR streams as a workaround.
atomic = !!(videoFormat & VIDEO_FORMAT_MASK_10BIT);
MppCodingType mpp_type = (MppCodingType)format; MppCodingType mpp_type = (MppCodingType)format;
ret = mpp_check_support_format(MPP_CTX_DEC, mpp_type); ret = mpp_check_support_format(MPP_CTX_DEC, mpp_type);
if (ret) { assert(!ret);
fprintf(stderr, "Selected video format is not supported\n");
return -1;
}
fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC); fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
if (fd < 0) { assert(fd >= 0);
fprintf(stderr, "Unable to open card0: %d\n", errno);
return -1;
}
resources = drmModeGetResources(fd); resources = drmModeGetResources(fd);
if (!resources) { assert(resources);
perror("drmModeGetResources");
return -1;
}
// find active monitor // find active monitor
for (i = 0; i < resources->count_connectors; ++i) { for (i = 0; i < resources->count_connectors; ++i) {
@ -401,24 +291,6 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte
} }
assert(i < resources->count_connectors); assert(i < resources->count_connectors);
conn_id = connector->connector_id;
{
drmModeObjectPropertiesPtr props = drmModeObjectGetProperties(fd, conn_id, DRM_MODE_OBJECT_CONNECTOR);
assert(props->count_props < sizeof(conn_props) / sizeof(conn_props[0]));
for (j = 0; j < props->count_props; j++) {
drmModePropertyPtr prop = drmModeGetProperty(fd, props->props[j]);
if (!prop) {
continue;
}
if (!strcmp(prop->name, "HDR_OUTPUT_METADATA")) {
hdr_metadata_prop = prop;
}
conn_props[j] = prop;
}
drmModeFreeObjectProperties(props);
}
for (i = 0; i < resources->count_encoders; ++i) { for (i = 0; i < resources->count_encoders; ++i) {
encoder = drmModeGetEncoder(fd, resources->encoders[i]); encoder = drmModeGetEncoder(fd, resources->encoders[i]);
if (!encoder) { if (!encoder) {
@ -434,10 +306,7 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte
for (i = 0; i < resources->count_crtcs; ++i) { for (i = 0; i < resources->count_crtcs; ++i) {
if (resources->crtcs[i] == encoder->crtc_id) { if (resources->crtcs[i] == encoder->crtc_id) {
crtc = drmModeGetCrtc(fd, resources->crtcs[i]); crtc = drmModeGetCrtc(fd, resources->crtcs[i]);
if (!crtc) { assert(crtc);
perror("drmModeGetCrtc");
continue;
}
break; break;
} }
} }
@ -448,25 +317,9 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte
uint32_t crtc_bit = (1 << i); uint32_t crtc_bit = (1 << i);
ret = drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); ret = drmSetClientCap(fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
if (ret) { assert(!ret);
perror("drmSetClientCap(DRM_CLIENT_CAP_UNIVERSAL_PLANES)");
}
if (atomic) {
ret = drmSetClientCap(fd, DRM_CLIENT_CAP_ATOMIC, 1);
if (ret) {
perror("drmSetClientCap(DRM_CLIENT_CAP_ATOMIC)");
atomic = false;
}
else {
drm_request = drmModeAtomicAlloc();
assert(drm_request);
}
}
plane_resources = drmModeGetPlaneResources(fd); plane_resources = drmModeGetPlaneResources(fd);
if (!plane_resources) { assert(plane_resources);
perror("drmModeGetPlaneResources");
return -1;
}
// search for OVERLAY (for active connector, unused, NV12 support) // search for OVERLAY (for active connector, unused, NV12 support)
for (i = 0; i < plane_resources->count_planes; i++) { for (i = 0; i < plane_resources->count_planes; i++) {
@ -475,19 +328,11 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte
continue; continue;
} }
for (j = 0; j < ovr->count_formats; j++) { for (j = 0; j < ovr->count_formats; j++) {
if (videoFormat & VIDEO_FORMAT_MASK_10BIT) { if (ovr->formats[j] == DRM_FORMAT_NV12) {
// 10-bit formats use NA12 (vendor-defined) or NV15 (upstreamed in 5.10+)
if (ovr->formats[j] == DRM_FORMAT_NA12 || ovr->formats[j] == DRM_FORMAT_NV15) {
break;
}
} else if (ovr->formats[j] == DRM_FORMAT_NV12) {
// 8-bit formats always use NV12
break; break;
} }
} }
if (j < ovr->count_formats) { if (j == ovr->count_formats) {
pixel_format = ovr->formats[j];
} else {
continue; continue;
} }
if ((ovr->possible_crtcs & crtc_bit) && !ovr->crtc_id) { if ((ovr->possible_crtcs & crtc_bit) && !ovr->crtc_id) {
@ -496,70 +341,37 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte
continue; continue;
} }
assert(props->count_props < sizeof(plane_props) / sizeof(plane_props[0])); for (j = 0; j < props->count_props && !plane_id; j++) {
for (j = 0; j < props->count_props; j++) {
drmModePropertyPtr prop = drmModeGetProperty(fd, props->props[j]); drmModePropertyPtr prop = drmModeGetProperty(fd, props->props[j]);
if (!prop) { if (!prop) {
continue; continue;
} }
plane_props[j] = prop; if (!strcmp(prop->name, "type") && props->prop_values[j] == DRM_PLANE_TYPE_OVERLAY) {
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; plane_id = ovr->plane_id;
} }
drmModeFreeProperty(prop);
} }
drmModeFreeObjectProperties(props);
if (plane_id) { if (plane_id) {
drmModeFreeObjectProperties(props); break;
break;
} else {
for (j = 0; j < props->count_props; j++) {
drmModeFreeProperty(plane_props[j]);
plane_props[j] = NULL;
}
drmModeFreeObjectProperties(props);
} }
} }
drmModeFreePlane(ovr); 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 // hide cursor by move in left lower corner
drmModeMoveCursor(fd, crtc_id, 0, crtc_height); drmModeMoveCursor(fd, crtc_id, 0, crtc_height);
// MPI SETUP // MPI SETUP
ensure_buf_size(&pkt_buf, &pkt_buf_size, INITIAL_DECODER_BUFFER_SIZE); pkt_buf = malloc(READ_BUF_SIZE);
ret = mpp_packet_init(&mpi_packet, pkt_buf, pkt_buf_size); assert(pkt_buf);
ret = mpp_packet_init(&mpi_packet, pkt_buf, READ_BUF_SIZE);
assert(!ret); assert(!ret);
ret = mpp_create(&mpi_ctx, &mpi_api); ret = mpp_create(&mpi_ctx, &mpi_api);
if (ret) { assert(!ret);
fprintf(stderr, "mpp_create() failed: %d\n", ret);
return -1;
}
// decoder split mode (multi-data-input) need to be set before init // decoder split mode (multi-data-input) need to be set before init
int param = 1; int param = 1;
@ -567,21 +379,22 @@ int rk_setup(int videoFormat, int width, int height, int redrawRate, void* conte
assert(!ret); assert(!ret);
ret = mpp_init(mpi_ctx, MPP_CTX_DEC, mpp_type); ret = mpp_init(mpi_ctx, MPP_CTX_DEC, mpp_type);
if (ret) { assert(!ret);
fprintf(stderr, "mpp_init() failed: %d\n", ret);
return -1;
}
// set blocked read on Frame Thread // set blocked read on Frame Thread
param = MPP_POLL_BLOCK; param = MPP_POLL_BLOCK;
ret = mpi_api->control(mpi_ctx, MPP_SET_OUTPUT_BLOCK, &param); ret = mpi_api->control(mpi_ctx, MPP_SET_OUTPUT_BLOCK, &param);
assert(!ret); assert(!ret);
pthread_mutex_init(&mutex, NULL); ret = pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&cond, NULL); assert(!ret);
ret = pthread_cond_init(&cond, NULL);
assert(!ret);
pthread_create(&tid_frame, NULL, frame_thread, NULL); ret = pthread_create(&tid_frame, NULL, frame_thread, NULL);
pthread_create(&tid_display, NULL, display_thread, NULL); assert(!ret);
ret = pthread_create(&tid_display, NULL, display_thread, NULL);
assert(!ret);
return 0; return 0;
} }
@ -592,19 +405,26 @@ void rk_cleanup() {
int ret; int ret;
frm_eos = 1; frm_eos = 1;
pthread_mutex_lock(&mutex); ret = pthread_mutex_lock(&mutex);
pthread_cond_signal(&cond); assert(!ret);
pthread_mutex_unlock(&mutex); ret = pthread_cond_signal(&cond);
assert(!ret);
ret = pthread_mutex_unlock(&mutex);
assert(!ret);
pthread_join(tid_display, NULL); ret = pthread_join(tid_display, NULL);
assert(!ret);
pthread_cond_destroy(&cond); ret = pthread_cond_destroy(&cond);
pthread_mutex_destroy(&mutex); assert(!ret);
ret = pthread_mutex_destroy(&mutex);
assert(!ret);
ret = mpi_api->reset(mpi_ctx); ret = mpi_api->reset(mpi_ctx);
assert(!ret); assert(!ret);
pthread_join(tid_frame, NULL); ret = pthread_join(tid_frame, NULL);
assert(!ret);
if (mpi_frm_grp) { if (mpi_frm_grp) {
ret = mpp_buffer_group_put(mpi_frm_grp); ret = mpp_buffer_group_put(mpi_frm_grp);
@ -612,15 +432,13 @@ void rk_cleanup() {
mpi_frm_grp = NULL; mpi_frm_grp = NULL;
for (i = 0; i < MAX_FRAMES; i++) { for (i = 0; i < MAX_FRAMES; i++) {
ret = drmModeRmFB(fd, frame_to_drm[i].fb_id); ret = drmModeRmFB(fd, frame_to_drm[i].fb_id);
if (ret) { assert(!ret);
perror("drmModeRmFB");
}
struct drm_mode_destroy_dumb dmdd = {0}; struct drm_mode_destroy_dumb dmdd = {0};
dmdd.handle = frame_to_drm[i].handle; dmdd.handle = frame_to_drm[i].handle;
ret = drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dmdd); do {
if (ret) { ret = ioctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &dmdd);
perror("drmIoctl(DRM_IOCTL_MODE_DESTROY_DUMB)"); } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
} assert(!ret);
} }
} }
@ -628,130 +446,37 @@ void rk_cleanup() {
mpp_destroy(mpi_ctx); mpp_destroy(mpi_ctx);
free(pkt_buf); free(pkt_buf);
// Undo the connector-wide changes we performed
if (atomic)
drmModeAtomicSetCursor(drm_request, 0);
set_property(conn_id, DRM_MODE_OBJECT_CONNECTOR, conn_props, "HDR_OUTPUT_METADATA", 0);
set_property(conn_id, DRM_MODE_OBJECT_CONNECTOR, conn_props, "allm_enable", 0);
set_property(conn_id, DRM_MODE_OBJECT_CONNECTOR, conn_props, "Colorspace", DRM_MODE_COLORIMETRY_DEFAULT);
if (atomic)
drmModeAtomicCommit(fd, drm_request, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
if (hdr_metadata_blob_id) {
drmModeDestroyPropertyBlob(fd, hdr_metadata_blob_id);
hdr_metadata_blob_id = 0;
}
if (atomic)
drmModeAtomicFree(drm_request);
drmModeFreePlane(ovr); drmModeFreePlane(ovr);
drmModeFreePlaneResources(plane_resources); drmModeFreePlaneResources(plane_resources);
drmModeFreeEncoder(encoder); drmModeFreeEncoder(encoder);
drmModeFreeConnector(connector); drmModeFreeConnector(connector);
drmModeFreeCrtc(crtc); drmModeFreeCrtc(crtc);
drmModeFreeResources(resources); drmModeFreeResources(resources);
close(fd); close(fd);
} }
int rk_submit_decode_unit(PDECODE_UNIT decodeUnit) { int rk_submit_decode_unit(PDECODE_UNIT decodeUnit) {
int result = DR_OK; int result = DR_OK;
PLENTRY entry = decodeUnit->bufferList;
int length = 0;
if (ensure_buf_size(&pkt_buf, &pkt_buf_size, decodeUnit->fullLength)) { if (decodeUnit->fullLength < READ_BUF_SIZE) {
// Buffer was reallocated, so update the mpp_packet accordingly PLENTRY entry = decodeUnit->bufferList;
mpp_packet_set_data(mpi_packet, pkt_buf); int length = 0;
mpp_packet_set_size(mpi_packet, pkt_buf_size); while (entry != NULL) {
} memcpy(pkt_buf+length, entry->data, entry->length);
length += entry->length;
while (entry != NULL) { entry = entry->next;
memcpy(pkt_buf+length, entry->data, entry->length);
length += entry->length;
entry = entry->next;
}
mpp_packet_set_pos(mpi_packet, pkt_buf);
mpp_packet_set_length(mpi_packet, length);
if (last_hdr_state != decodeUnit->hdrActive) {
if (hdr_metadata_prop != NULL) {
int err;
if (hdr_metadata_blob_id) {
drmModeDestroyPropertyBlob(fd, hdr_metadata_blob_id);
hdr_metadata_blob_id = 0;
}
if (decodeUnit->hdrActive) {
struct rk_hdr_output_metadata outputMetadata;
SS_HDR_METADATA sunshineHdrMetadata;
// Sunshine will have HDR metadata but GFE will not
if (!LiGetHdrMetadata(&sunshineHdrMetadata)) {
memset(&sunshineHdrMetadata, 0, sizeof(sunshineHdrMetadata));
}
outputMetadata.metadata_type = 0; // HDMI_STATIC_METADATA_TYPE1
outputMetadata.hdmi_metadata_type1.eotf = 2; // SMPTE ST 2084
outputMetadata.hdmi_metadata_type1.metadata_type = 0; // Static Metadata Type 1
for (int i = 0; i < 3; i++) {
outputMetadata.hdmi_metadata_type1.display_primaries[i].x = sunshineHdrMetadata.displayPrimaries[i].x;
outputMetadata.hdmi_metadata_type1.display_primaries[i].y = sunshineHdrMetadata.displayPrimaries[i].y;
}
outputMetadata.hdmi_metadata_type1.white_point.x = sunshineHdrMetadata.whitePoint.x;
outputMetadata.hdmi_metadata_type1.white_point.y = sunshineHdrMetadata.whitePoint.y;
outputMetadata.hdmi_metadata_type1.max_display_mastering_luminance = sunshineHdrMetadata.maxDisplayLuminance;
outputMetadata.hdmi_metadata_type1.min_display_mastering_luminance = sunshineHdrMetadata.minDisplayLuminance;
outputMetadata.hdmi_metadata_type1.max_cll = sunshineHdrMetadata.maxContentLightLevel;
outputMetadata.hdmi_metadata_type1.max_fall = sunshineHdrMetadata.maxFrameAverageLightLevel;
err = drmModeCreatePropertyBlob(fd, &outputMetadata, sizeof(outputMetadata), &hdr_metadata_blob_id);
if (err < 0) {
hdr_metadata_blob_id = 0;
fprintf(stderr, "Failed to create HDR metadata blob: %d\n", errno);
}
}
err = drmModeObjectSetProperty(fd, conn_id, DRM_MODE_OBJECT_CONNECTOR, hdr_metadata_prop->prop_id, hdr_metadata_blob_id);
if (err < 0) {
fprintf(stderr, "Failed to set HDR metadata: %d\n", errno);
} else {
printf("Set display HDR mode: %s\n", decodeUnit->hdrActive ? "active" : "inactive");
}
} else {
fprintf(stderr, "HDR_OUTPUT_METADATA property is not supported by your display/kernel. Do you have an HDR display connected?\n");
} }
if (length) {
mpp_packet_set_pos(mpi_packet, pkt_buf);
mpp_packet_set_length(mpi_packet, length);
// Adjust plane EOTF property while (MPP_OK != mpi_api->decode_put_packet(mpi_ctx, mpi_packet))
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "EOTF", decodeUnit->hdrActive ? 2 : 0); // PQ or SDR ;
}
if (last_colorspace != decodeUnit->colorspace) {
uint32_t v4l2_colorspace;
switch (decodeUnit->colorspace) {
default:
fprintf(stderr, "Unknown frame colorspace: %d\n", decodeUnit->colorspace);
/* fall-through */
case COLORSPACE_REC_601:
v4l2_colorspace = V4L2_COLORSPACE_SMPTE170M;
break;
case COLORSPACE_REC_709:
v4l2_colorspace = V4L2_COLORSPACE_REC709;
break;
case COLORSPACE_REC_2020:
v4l2_colorspace = V4L2_COLORSPACE_BT2020;
break;
} }
set_property(plane_id, DRM_MODE_OBJECT_PLANE, plane_props, "COLOR_SPACE", v4l2_colorspace);
} }
last_colorspace = decodeUnit->colorspace;
last_hdr_state = decodeUnit->hdrActive;
while (MPP_OK != mpi_api->decode_put_packet(mpi_ctx, mpi_packet));
return result; return result;
} }

View File

@ -21,7 +21,6 @@
#include "ffmpeg.h" #include "ffmpeg.h"
#include "../sdl.h" #include "../sdl.h"
#include "../util.h"
#include <SDL.h> #include <SDL.h>
#include <SDL_thread.h> #include <SDL_thread.h>
@ -29,10 +28,10 @@
#include <unistd.h> #include <unistd.h>
#include <stdbool.h> #include <stdbool.h>
#define DECODER_BUFFER_SIZE 92*1024
#define SLICES_PER_FRAME 4 #define SLICES_PER_FRAME 4
static void* ffmpeg_buffer; static char* ffmpeg_buffer;
static size_t ffmpeg_buffer_size;
static int sdl_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { static int sdl_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
if (ffmpeg_init(videoFormat, width, height, SLICE_THREADING, SDL_BUFFER_FRAMES, SLICES_PER_FRAME) < 0) { if (ffmpeg_init(videoFormat, width, height, SLICE_THREADING, SDL_BUFFER_FRAMES, SLICES_PER_FRAME) < 0) {
@ -40,7 +39,12 @@ static int sdl_setup(int videoFormat, int width, int height, int redrawRate, voi
return -1; return -1;
} }
ensure_buf_size(&ffmpeg_buffer, &ffmpeg_buffer_size, INITIAL_DECODER_BUFFER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE); ffmpeg_buffer = malloc(DECODER_BUFFER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE);
if (ffmpeg_buffer == NULL) {
fprintf(stderr, "Not enough memory\n");
ffmpeg_destroy();
return -1;
}
return 0; return 0;
} }
@ -50,31 +54,36 @@ static void sdl_cleanup() {
} }
static int sdl_submit_decode_unit(PDECODE_UNIT decodeUnit) { static int sdl_submit_decode_unit(PDECODE_UNIT decodeUnit) {
PLENTRY entry = decodeUnit->bufferList; if (decodeUnit->fullLength < DECODER_BUFFER_SIZE) {
int length = 0; PLENTRY entry = decodeUnit->bufferList;
int length = 0;
while (entry != NULL) {
memcpy(ffmpeg_buffer+length, entry->data, entry->length);
length += entry->length;
entry = entry->next;
}
ffmpeg_decode(ffmpeg_buffer, length);
ensure_buf_size(&ffmpeg_buffer, &ffmpeg_buffer_size, decodeUnit->fullLength + AV_INPUT_BUFFER_PADDING_SIZE); if (SDL_LockMutex(mutex) == 0) {
AVFrame* frame = ffmpeg_get_frame(false);
if (frame != NULL) {
sdlNextFrame++;
while (entry != NULL) { SDL_Event event;
memcpy(ffmpeg_buffer+length, entry->data, entry->length); event.type = SDL_USEREVENT;
length += entry->length; event.user.code = SDL_CODE_FRAME;
entry = entry->next; event.user.data1 = &frame->data;
event.user.data2 = &frame->linesize;
SDL_PushEvent(&event);
}
SDL_UnlockMutex(mutex);
} else
fprintf(stderr, "Couldn't lock mutex\n");
} else {
fprintf(stderr, "Video decode buffer too small");
exit(1);
} }
ffmpeg_decode(ffmpeg_buffer, length);
SDL_LockMutex(mutex);
AVFrame* frame = ffmpeg_get_frame(false);
if (frame != NULL) {
sdlNextFrame++;
SDL_Event event;
event.type = SDL_USEREVENT;
event.user.code = SDL_CODE_FRAME;
event.user.data1 = &frame->data;
event.user.data2 = &frame->linesize;
SDL_PushEvent(&event);
}
SDL_UnlockMutex(mutex);
return DR_OK; return DR_OK;
} }
@ -83,5 +92,5 @@ DECODER_RENDERER_CALLBACKS decoder_callbacks_sdl = {
.setup = sdl_setup, .setup = sdl_setup,
.cleanup = sdl_cleanup, .cleanup = sdl_cleanup,
.submitDecodeUnit = sdl_submit_decode_unit, .submitDecodeUnit = sdl_submit_decode_unit,
.capabilities = CAPABILITY_SLICES_PER_FRAME(SLICES_PER_FRAME) | CAPABILITY_REFERENCE_FRAME_INVALIDATION_HEVC | CAPABILITY_DIRECT_SUBMIT, .capabilities = CAPABILITY_SLICES_PER_FRAME(SLICES_PER_FRAME) | CAPABILITY_REFERENCE_FRAME_INVALIDATION_AVC | CAPABILITY_DIRECT_SUBMIT,
}; };

View File

@ -33,8 +33,6 @@
#define INIT_VDPAU 2 #define INIT_VDPAU 2
#define INIT_VAAPI 3 #define INIT_VAAPI 3
#define INITIAL_DECODER_BUFFER_SIZE (256*1024)
#ifdef HAVE_X11 #ifdef HAVE_X11
int x11_init(bool vdpau, bool vaapi); int x11_init(bool vdpau, bool vaapi);
extern DECODER_RENDERER_CALLBACKS decoder_callbacks_x11; extern DECODER_RENDERER_CALLBACKS decoder_callbacks_x11;

View File

@ -26,7 +26,6 @@
#include "../input/x11.h" #include "../input/x11.h"
#include "../loop.h" #include "../loop.h"
#include "../util.h"
#include <X11/Xatom.h> #include <X11/Xatom.h>
#include <X11/Xutil.h> #include <X11/Xutil.h>
@ -38,12 +37,12 @@
#include <fcntl.h> #include <fcntl.h>
#include <poll.h> #include <poll.h>
#define DECODER_BUFFER_SIZE 92*1024
#define X11_VDPAU_ACCELERATION ENABLE_HARDWARE_ACCELERATION_1 #define X11_VDPAU_ACCELERATION ENABLE_HARDWARE_ACCELERATION_1
#define X11_VAAPI_ACCELERATION ENABLE_HARDWARE_ACCELERATION_2 #define X11_VAAPI_ACCELERATION ENABLE_HARDWARE_ACCELERATION_2
#define SLICES_PER_FRAME 4 #define SLICES_PER_FRAME 4
static void* ffmpeg_buffer = NULL; static char* ffmpeg_buffer = NULL;
static size_t ffmpeg_buffer_size = 0;
static Display *display = NULL; static Display *display = NULL;
static Window window; static Window window;
@ -83,7 +82,11 @@ int x11_init(bool vdpau, bool vaapi) {
} }
int x11_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) { int x11_setup(int videoFormat, int width, int height, int redrawRate, void* context, int drFlags) {
ensure_buf_size(&ffmpeg_buffer, &ffmpeg_buffer_size, INITIAL_DECODER_BUFFER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE); ffmpeg_buffer = malloc(DECODER_BUFFER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE);
if (ffmpeg_buffer == NULL) {
fprintf(stderr, "Not enough memory\n");
return -1;
}
if (!display) { if (!display) {
fprintf(stderr, "Error: failed to open X display.\n"); fprintf(stderr, "Error: failed to open X display.\n");
@ -164,23 +167,20 @@ void x11_cleanup() {
} }
int x11_submit_decode_unit(PDECODE_UNIT decodeUnit) { int x11_submit_decode_unit(PDECODE_UNIT decodeUnit) {
PLENTRY entry = decodeUnit->bufferList; if (decodeUnit->fullLength < DECODER_BUFFER_SIZE) {
int length = 0; PLENTRY entry = decodeUnit->bufferList;
int length = 0;
ensure_buf_size(&ffmpeg_buffer, &ffmpeg_buffer_size, decodeUnit->fullLength + AV_INPUT_BUFFER_PADDING_SIZE); while (entry != NULL) {
memcpy(ffmpeg_buffer+length, entry->data, entry->length);
while (entry != NULL) { length += entry->length;
memcpy(ffmpeg_buffer+length, entry->data, entry->length); entry = entry->next;
length += entry->length; }
entry = entry->next; ffmpeg_decode(ffmpeg_buffer, length);
AVFrame* frame = ffmpeg_get_frame(true);
if (frame != NULL)
write(pipefd[1], &frame, sizeof(void*));
} }
ffmpeg_decode(ffmpeg_buffer, length);
AVFrame* frame = ffmpeg_get_frame(true);
if (frame != NULL)
write(pipefd[1], &frame, sizeof(void*));
return DR_OK; return DR_OK;
} }
@ -188,7 +188,7 @@ DECODER_RENDERER_CALLBACKS decoder_callbacks_x11 = {
.setup = x11_setup, .setup = x11_setup,
.cleanup = x11_cleanup, .cleanup = x11_cleanup,
.submitDecodeUnit = x11_submit_decode_unit, .submitDecodeUnit = x11_submit_decode_unit,
.capabilities = CAPABILITY_SLICES_PER_FRAME(SLICES_PER_FRAME) | CAPABILITY_REFERENCE_FRAME_INVALIDATION_HEVC | CAPABILITY_DIRECT_SUBMIT, .capabilities = CAPABILITY_SLICES_PER_FRAME(SLICES_PER_FRAME) | CAPABILITY_REFERENCE_FRAME_INVALIDATION_AVC | CAPABILITY_DIRECT_SUBMIT,
}; };
DECODER_RENDERER_CALLBACKS decoder_callbacks_x11_vdpau = { DECODER_RENDERER_CALLBACKS decoder_callbacks_x11_vdpau = {

@ -1 +1 @@
Subproject commit ae51c9942f0d985b2161d6b22986f041fc98ce5c Subproject commit 68d3f1e5d5e204a5975834c5a6cae572bb075445

View File

@ -24,11 +24,16 @@
#include <stdint.h> #include <stdint.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h>
#include "bs.h" #include "bs.h"
#include "h264_stream.h" #include "h264_stream.h"
#include "h264_sei.h" #include "h264_sei.h"
FILE* h264_dbgfile = NULL;
#define printf(...) fprintf((h264_dbgfile == NULL ? stdout : h264_dbgfile), __VA_ARGS__)
/** /**
Calculate the log base 2 of the argument, rounded up. Calculate the log base 2 of the argument, rounded up.
Zero or negative arguments return zero Zero or negative arguments return zero
@ -549,7 +554,7 @@ void read_pic_parameter_set_rbsp(h264_stream_t* h, bs_t* b)
if( 1 ) { have_more_data = more_rbsp_data(h, b); } if( 1 ) { have_more_data = more_rbsp_data(h, b); }
if( 0 ) if( 0 )
{ {
have_more_data = pps->transform_8x8_mode_flag | pps->pic_scaling_matrix_present_flag | (pps->second_chroma_qp_index_offset != 0); have_more_data = pps->transform_8x8_mode_flag | pps->pic_scaling_matrix_present_flag | pps->second_chroma_qp_index_offset != 0;
} }
if( have_more_data ) if( have_more_data )
@ -1436,7 +1441,7 @@ void write_pic_parameter_set_rbsp(h264_stream_t* h, bs_t* b)
if( 0 ) { have_more_data = more_rbsp_data(h, b); } if( 0 ) { have_more_data = more_rbsp_data(h, b); }
if( 1 ) if( 1 )
{ {
have_more_data = pps->transform_8x8_mode_flag | pps->pic_scaling_matrix_present_flag | (pps->second_chroma_qp_index_offset != 0); have_more_data = pps->transform_8x8_mode_flag | pps->pic_scaling_matrix_present_flag | pps->second_chroma_qp_index_offset != 0;
} }
if( have_more_data ) if( have_more_data )
@ -1894,3 +1899,890 @@ void write_dec_ref_pic_marking(h264_stream_t* h, bs_t* b)
} }
} }
} }
void read_debug_seq_parameter_set_rbsp(h264_stream_t* h, bs_t* b);
void read_debug_scaling_list(bs_t* b, int* scalingList, int sizeOfScalingList, int* useDefaultScalingMatrixFlag );
void read_debug_vui_parameters(h264_stream_t* h, bs_t* b);
void read_debug_hrd_parameters(h264_stream_t* h, bs_t* b);
void read_debug_pic_parameter_set_rbsp(h264_stream_t* h, bs_t* b);
void read_debug_sei_rbsp(h264_stream_t* h, bs_t* b);
void read_debug_sei_message(h264_stream_t* h, bs_t* b);
void read_debug_access_unit_delimiter_rbsp(h264_stream_t* h, bs_t* b);
void read_debug_end_of_seq_rbsp(h264_stream_t* h, bs_t* b);
void read_debug_end_of_stream_rbsp(h264_stream_t* h, bs_t* b);
void read_debug_filler_data_rbsp(h264_stream_t* h, bs_t* b);
void read_debug_slice_layer_rbsp(h264_stream_t* h, bs_t* b);
void read_debug_rbsp_slice_trailing_bits(h264_stream_t* h, bs_t* b);
void read_debug_rbsp_trailing_bits(h264_stream_t* h, bs_t* b);
void read_debug_slice_header(h264_stream_t* h, bs_t* b);
void read_debug_ref_pic_list_reordering(h264_stream_t* h, bs_t* b);
void read_debug_pred_weight_table(h264_stream_t* h, bs_t* b);
void read_debug_dec_ref_pic_marking(h264_stream_t* h, bs_t* b);
//7.3.1 NAL unit syntax
int read_debug_nal_unit(h264_stream_t* h, uint8_t* buf, int size)
{
nal_t* nal = h->nal;
int nal_size = size;
int rbsp_size = size;
uint8_t* rbsp_buf = (uint8_t*)calloc(1, rbsp_size);
if( 1 )
{
int rc = nal_to_rbsp(buf, &nal_size, rbsp_buf, &rbsp_size);
if (rc < 0) { free(rbsp_buf); return -1; } // handle conversion error
}
if( 0 )
{
rbsp_size = size*3/4; // NOTE this may have to be slightly smaller (3/4 smaller, worst case) in order to be guaranteed to fit
}
bs_t* b = bs_new(rbsp_buf, rbsp_size);
printf("%d.%d: ", b->p - b->start, b->bits_left); int forbidden_zero_bit = bs_read_u(b, 1); printf("forbidden_zero_bit: %d \n", forbidden_zero_bit);
printf("%d.%d: ", b->p - b->start, b->bits_left); nal->nal_ref_idc = bs_read_u(b, 2); printf("nal->nal_ref_idc: %d \n", nal->nal_ref_idc);
printf("%d.%d: ", b->p - b->start, b->bits_left); nal->nal_unit_type = bs_read_u(b, 5); printf("nal->nal_unit_type: %d \n", nal->nal_unit_type);
switch ( nal->nal_unit_type )
{
case NAL_UNIT_TYPE_CODED_SLICE_IDR:
case NAL_UNIT_TYPE_CODED_SLICE_NON_IDR:
case NAL_UNIT_TYPE_CODED_SLICE_AUX:
read_debug_slice_layer_rbsp(h, b);
break;
#ifdef HAVE_SEI
case NAL_UNIT_TYPE_SEI:
read_debug_sei_rbsp(h, b);
break;
#endif
case NAL_UNIT_TYPE_SPS:
read_debug_seq_parameter_set_rbsp(h, b);
break;
case NAL_UNIT_TYPE_PPS:
read_debug_pic_parameter_set_rbsp(h, b);
break;
case NAL_UNIT_TYPE_AUD:
read_debug_access_unit_delimiter_rbsp(h, b);
break;
case NAL_UNIT_TYPE_END_OF_SEQUENCE:
read_debug_end_of_seq_rbsp(h, b);
break;
case NAL_UNIT_TYPE_END_OF_STREAM:
read_debug_end_of_stream_rbsp(h, b);
break;
case NAL_UNIT_TYPE_FILLER:
case NAL_UNIT_TYPE_SPS_EXT:
case NAL_UNIT_TYPE_UNSPECIFIED:
case NAL_UNIT_TYPE_CODED_SLICE_DATA_PARTITION_A:
case NAL_UNIT_TYPE_CODED_SLICE_DATA_PARTITION_B:
case NAL_UNIT_TYPE_CODED_SLICE_DATA_PARTITION_C:
default:
return -1;
}
if (bs_overrun(b)) { bs_free(b); free(rbsp_buf); return -1; }
if( 0 )
{
// now get the actual size used
rbsp_size = bs_pos(b);
int rc = rbsp_to_nal(rbsp_buf, &rbsp_size, buf, &nal_size);
if (rc < 0) { bs_free(b); free(rbsp_buf); return -1; }
}
bs_free(b);
free(rbsp_buf);
return nal_size;
}
//7.3.2.1 Sequence parameter set RBSP syntax
void read_debug_seq_parameter_set_rbsp(h264_stream_t* h, bs_t* b)
{
int i;
sps_t* sps = h->sps;
if( 1 )
{
memset(sps, 0, sizeof(sps_t));
sps->chroma_format_idc = 1;
}
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->profile_idc = bs_read_u8(b); printf("sps->profile_idc: %d \n", sps->profile_idc);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->constraint_set0_flag = bs_read_u1(b); printf("sps->constraint_set0_flag: %d \n", sps->constraint_set0_flag);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->constraint_set1_flag = bs_read_u1(b); printf("sps->constraint_set1_flag: %d \n", sps->constraint_set1_flag);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->constraint_set2_flag = bs_read_u1(b); printf("sps->constraint_set2_flag: %d \n", sps->constraint_set2_flag);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->constraint_set3_flag = bs_read_u1(b); printf("sps->constraint_set3_flag: %d \n", sps->constraint_set3_flag);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->constraint_set4_flag = bs_read_u1(b); printf("sps->constraint_set4_flag: %d \n", sps->constraint_set4_flag);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->constraint_set5_flag = bs_read_u1(b); printf("sps->constraint_set5_flag: %d \n", sps->constraint_set5_flag);
printf("%d.%d: ", b->p - b->start, b->bits_left); int reserved_zero_2bits = bs_read_u(b, 2); printf("reserved_zero_2bits: %d \n", reserved_zero_2bits);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->level_idc = bs_read_u8(b); printf("sps->level_idc: %d \n", sps->level_idc);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->seq_parameter_set_id = bs_read_ue(b); printf("sps->seq_parameter_set_id: %d \n", sps->seq_parameter_set_id);
if( sps->profile_idc == 100 || sps->profile_idc == 110 ||
sps->profile_idc == 122 || sps->profile_idc == 144 )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->chroma_format_idc = bs_read_ue(b); printf("sps->chroma_format_idc: %d \n", sps->chroma_format_idc);
if( sps->chroma_format_idc == 3 )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->residual_colour_transform_flag = bs_read_u1(b); printf("sps->residual_colour_transform_flag: %d \n", sps->residual_colour_transform_flag);
}
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->bit_depth_luma_minus8 = bs_read_ue(b); printf("sps->bit_depth_luma_minus8: %d \n", sps->bit_depth_luma_minus8);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->bit_depth_chroma_minus8 = bs_read_ue(b); printf("sps->bit_depth_chroma_minus8: %d \n", sps->bit_depth_chroma_minus8);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->qpprime_y_zero_transform_bypass_flag = bs_read_u1(b); printf("sps->qpprime_y_zero_transform_bypass_flag: %d \n", sps->qpprime_y_zero_transform_bypass_flag);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->seq_scaling_matrix_present_flag = bs_read_u1(b); printf("sps->seq_scaling_matrix_present_flag: %d \n", sps->seq_scaling_matrix_present_flag);
if( sps->seq_scaling_matrix_present_flag )
{
for( i = 0; i < 8; i++ )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->seq_scaling_list_present_flag[ i ] = bs_read_u1(b); printf("sps->seq_scaling_list_present_flag[ i ]: %d \n", sps->seq_scaling_list_present_flag[ i ]);
if( sps->seq_scaling_list_present_flag[ i ] )
{
if( i < 6 )
{
read_debug_scaling_list( b, sps->ScalingList4x4[ i ], 16,
&( sps->UseDefaultScalingMatrix4x4Flag[ i ] ) );
}
else
{
read_debug_scaling_list( b, sps->ScalingList8x8[ i - 6 ], 64,
&( sps->UseDefaultScalingMatrix8x8Flag[ i - 6 ] ) );
}
}
}
}
}
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->log2_max_frame_num_minus4 = bs_read_ue(b); printf("sps->log2_max_frame_num_minus4: %d \n", sps->log2_max_frame_num_minus4);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->pic_order_cnt_type = bs_read_ue(b); printf("sps->pic_order_cnt_type: %d \n", sps->pic_order_cnt_type);
if( sps->pic_order_cnt_type == 0 )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->log2_max_pic_order_cnt_lsb_minus4 = bs_read_ue(b); printf("sps->log2_max_pic_order_cnt_lsb_minus4: %d \n", sps->log2_max_pic_order_cnt_lsb_minus4);
}
else if( sps->pic_order_cnt_type == 1 )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->delta_pic_order_always_zero_flag = bs_read_u1(b); printf("sps->delta_pic_order_always_zero_flag: %d \n", sps->delta_pic_order_always_zero_flag);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->offset_for_non_ref_pic = bs_read_se(b); printf("sps->offset_for_non_ref_pic: %d \n", sps->offset_for_non_ref_pic);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->offset_for_top_to_bottom_field = bs_read_se(b); printf("sps->offset_for_top_to_bottom_field: %d \n", sps->offset_for_top_to_bottom_field);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->num_ref_frames_in_pic_order_cnt_cycle = bs_read_ue(b); printf("sps->num_ref_frames_in_pic_order_cnt_cycle: %d \n", sps->num_ref_frames_in_pic_order_cnt_cycle);
for( i = 0; i < sps->num_ref_frames_in_pic_order_cnt_cycle; i++ )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->offset_for_ref_frame[ i ] = bs_read_se(b); printf("sps->offset_for_ref_frame[ i ]: %d \n", sps->offset_for_ref_frame[ i ]);
}
}
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->num_ref_frames = bs_read_ue(b); printf("sps->num_ref_frames: %d \n", sps->num_ref_frames);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->gaps_in_frame_num_value_allowed_flag = bs_read_u1(b); printf("sps->gaps_in_frame_num_value_allowed_flag: %d \n", sps->gaps_in_frame_num_value_allowed_flag);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->pic_width_in_mbs_minus1 = bs_read_ue(b); printf("sps->pic_width_in_mbs_minus1: %d \n", sps->pic_width_in_mbs_minus1);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->pic_height_in_map_units_minus1 = bs_read_ue(b); printf("sps->pic_height_in_map_units_minus1: %d \n", sps->pic_height_in_map_units_minus1);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->frame_mbs_only_flag = bs_read_u1(b); printf("sps->frame_mbs_only_flag: %d \n", sps->frame_mbs_only_flag);
if( !sps->frame_mbs_only_flag )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->mb_adaptive_frame_field_flag = bs_read_u1(b); printf("sps->mb_adaptive_frame_field_flag: %d \n", sps->mb_adaptive_frame_field_flag);
}
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->direct_8x8_inference_flag = bs_read_u1(b); printf("sps->direct_8x8_inference_flag: %d \n", sps->direct_8x8_inference_flag);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->frame_cropping_flag = bs_read_u1(b); printf("sps->frame_cropping_flag: %d \n", sps->frame_cropping_flag);
if( sps->frame_cropping_flag )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->frame_crop_left_offset = bs_read_ue(b); printf("sps->frame_crop_left_offset: %d \n", sps->frame_crop_left_offset);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->frame_crop_right_offset = bs_read_ue(b); printf("sps->frame_crop_right_offset: %d \n", sps->frame_crop_right_offset);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->frame_crop_top_offset = bs_read_ue(b); printf("sps->frame_crop_top_offset: %d \n", sps->frame_crop_top_offset);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->frame_crop_bottom_offset = bs_read_ue(b); printf("sps->frame_crop_bottom_offset: %d \n", sps->frame_crop_bottom_offset);
}
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui_parameters_present_flag = bs_read_u1(b); printf("sps->vui_parameters_present_flag: %d \n", sps->vui_parameters_present_flag);
if( sps->vui_parameters_present_flag )
{
read_debug_vui_parameters(h, b);
}
read_debug_rbsp_trailing_bits(h, b);
if( 1 )
{
memcpy(h->sps_table[sps->seq_parameter_set_id], h->sps, sizeof(sps_t));
}
}
//7.3.2.1.1 Scaling list syntax
void read_debug_scaling_list(bs_t* b, int* scalingList, int sizeOfScalingList, int* useDefaultScalingMatrixFlag )
{
// NOTE need to be able to set useDefaultScalingMatrixFlag when reading, hence passing as pointer
int lastScale = 8;
int nextScale = 8;
int delta_scale;
for( int j = 0; j < sizeOfScalingList; j++ )
{
if( nextScale != 0 )
{
if( 0 )
{
nextScale = scalingList[ j ];
if (useDefaultScalingMatrixFlag[0]) { nextScale = 0; }
delta_scale = (nextScale - lastScale) % 256 ;
}
printf("%d.%d: ", b->p - b->start, b->bits_left); delta_scale = bs_read_se(b); printf("delta_scale: %d \n", delta_scale);
if( 1 )
{
nextScale = ( lastScale + delta_scale + 256 ) % 256;
useDefaultScalingMatrixFlag[0] = ( j == 0 && nextScale == 0 );
}
}
if( 1 )
{
scalingList[ j ] = ( nextScale == 0 ) ? lastScale : nextScale;
}
lastScale = scalingList[ j ];
}
}
//Appendix E.1.1 VUI parameters syntax
void read_debug_vui_parameters(h264_stream_t* h, bs_t* b)
{
sps_t* sps = h->sps;
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.aspect_ratio_info_present_flag = bs_read_u1(b); printf("sps->vui.aspect_ratio_info_present_flag: %d \n", sps->vui.aspect_ratio_info_present_flag);
if( sps->vui.aspect_ratio_info_present_flag )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.aspect_ratio_idc = bs_read_u8(b); printf("sps->vui.aspect_ratio_idc: %d \n", sps->vui.aspect_ratio_idc);
if( sps->vui.aspect_ratio_idc == SAR_Extended )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.sar_width = bs_read_u(b, 16); printf("sps->vui.sar_width: %d \n", sps->vui.sar_width);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.sar_height = bs_read_u(b, 16); printf("sps->vui.sar_height: %d \n", sps->vui.sar_height);
}
}
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.overscan_info_present_flag = bs_read_u1(b); printf("sps->vui.overscan_info_present_flag: %d \n", sps->vui.overscan_info_present_flag);
if( sps->vui.overscan_info_present_flag )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.overscan_appropriate_flag = bs_read_u1(b); printf("sps->vui.overscan_appropriate_flag: %d \n", sps->vui.overscan_appropriate_flag);
}
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.video_signal_type_present_flag = bs_read_u1(b); printf("sps->vui.video_signal_type_present_flag: %d \n", sps->vui.video_signal_type_present_flag);
if( sps->vui.video_signal_type_present_flag )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.video_format = bs_read_u(b, 3); printf("sps->vui.video_format: %d \n", sps->vui.video_format);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.video_full_range_flag = bs_read_u1(b); printf("sps->vui.video_full_range_flag: %d \n", sps->vui.video_full_range_flag);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.colour_description_present_flag = bs_read_u1(b); printf("sps->vui.colour_description_present_flag: %d \n", sps->vui.colour_description_present_flag);
if( sps->vui.colour_description_present_flag )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.colour_primaries = bs_read_u8(b); printf("sps->vui.colour_primaries: %d \n", sps->vui.colour_primaries);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.transfer_characteristics = bs_read_u8(b); printf("sps->vui.transfer_characteristics: %d \n", sps->vui.transfer_characteristics);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.matrix_coefficients = bs_read_u8(b); printf("sps->vui.matrix_coefficients: %d \n", sps->vui.matrix_coefficients);
}
}
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.chroma_loc_info_present_flag = bs_read_u1(b); printf("sps->vui.chroma_loc_info_present_flag: %d \n", sps->vui.chroma_loc_info_present_flag);
if( sps->vui.chroma_loc_info_present_flag )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.chroma_sample_loc_type_top_field = bs_read_ue(b); printf("sps->vui.chroma_sample_loc_type_top_field: %d \n", sps->vui.chroma_sample_loc_type_top_field);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.chroma_sample_loc_type_bottom_field = bs_read_ue(b); printf("sps->vui.chroma_sample_loc_type_bottom_field: %d \n", sps->vui.chroma_sample_loc_type_bottom_field);
}
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.timing_info_present_flag = bs_read_u1(b); printf("sps->vui.timing_info_present_flag: %d \n", sps->vui.timing_info_present_flag);
if( sps->vui.timing_info_present_flag )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.num_units_in_tick = bs_read_u(b, 32); printf("sps->vui.num_units_in_tick: %d \n", sps->vui.num_units_in_tick);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.time_scale = bs_read_u(b, 32); printf("sps->vui.time_scale: %d \n", sps->vui.time_scale);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.fixed_frame_rate_flag = bs_read_u1(b); printf("sps->vui.fixed_frame_rate_flag: %d \n", sps->vui.fixed_frame_rate_flag);
}
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.nal_hrd_parameters_present_flag = bs_read_u1(b); printf("sps->vui.nal_hrd_parameters_present_flag: %d \n", sps->vui.nal_hrd_parameters_present_flag);
if( sps->vui.nal_hrd_parameters_present_flag )
{
read_debug_hrd_parameters(h, b);
}
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.vcl_hrd_parameters_present_flag = bs_read_u1(b); printf("sps->vui.vcl_hrd_parameters_present_flag: %d \n", sps->vui.vcl_hrd_parameters_present_flag);
if( sps->vui.vcl_hrd_parameters_present_flag )
{
read_debug_hrd_parameters(h, b);
}
if( sps->vui.nal_hrd_parameters_present_flag || sps->vui.vcl_hrd_parameters_present_flag )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.low_delay_hrd_flag = bs_read_u1(b); printf("sps->vui.low_delay_hrd_flag: %d \n", sps->vui.low_delay_hrd_flag);
}
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.pic_struct_present_flag = bs_read_u1(b); printf("sps->vui.pic_struct_present_flag: %d \n", sps->vui.pic_struct_present_flag);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.bitstream_restriction_flag = bs_read_u1(b); printf("sps->vui.bitstream_restriction_flag: %d \n", sps->vui.bitstream_restriction_flag);
if( sps->vui.bitstream_restriction_flag )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.motion_vectors_over_pic_boundaries_flag = bs_read_u1(b); printf("sps->vui.motion_vectors_over_pic_boundaries_flag: %d \n", sps->vui.motion_vectors_over_pic_boundaries_flag);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.max_bytes_per_pic_denom = bs_read_ue(b); printf("sps->vui.max_bytes_per_pic_denom: %d \n", sps->vui.max_bytes_per_pic_denom);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.max_bits_per_mb_denom = bs_read_ue(b); printf("sps->vui.max_bits_per_mb_denom: %d \n", sps->vui.max_bits_per_mb_denom);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.log2_max_mv_length_horizontal = bs_read_ue(b); printf("sps->vui.log2_max_mv_length_horizontal: %d \n", sps->vui.log2_max_mv_length_horizontal);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.log2_max_mv_length_vertical = bs_read_ue(b); printf("sps->vui.log2_max_mv_length_vertical: %d \n", sps->vui.log2_max_mv_length_vertical);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.num_reorder_frames = bs_read_ue(b); printf("sps->vui.num_reorder_frames: %d \n", sps->vui.num_reorder_frames);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->vui.max_dec_frame_buffering = bs_read_ue(b); printf("sps->vui.max_dec_frame_buffering: %d \n", sps->vui.max_dec_frame_buffering);
}
}
//Appendix E.1.2 HRD parameters syntax
void read_debug_hrd_parameters(h264_stream_t* h, bs_t* b)
{
sps_t* sps = h->sps;
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->hrd.cpb_cnt_minus1 = bs_read_ue(b); printf("sps->hrd.cpb_cnt_minus1: %d \n", sps->hrd.cpb_cnt_minus1);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->hrd.bit_rate_scale = bs_read_u(b, 4); printf("sps->hrd.bit_rate_scale: %d \n", sps->hrd.bit_rate_scale);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->hrd.cpb_size_scale = bs_read_u(b, 4); printf("sps->hrd.cpb_size_scale: %d \n", sps->hrd.cpb_size_scale);
for( int SchedSelIdx = 0; SchedSelIdx <= sps->hrd.cpb_cnt_minus1; SchedSelIdx++ )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->hrd.bit_rate_value_minus1[ SchedSelIdx ] = bs_read_ue(b); printf("sps->hrd.bit_rate_value_minus1[ SchedSelIdx ]: %d \n", sps->hrd.bit_rate_value_minus1[ SchedSelIdx ]);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->hrd.cpb_size_value_minus1[ SchedSelIdx ] = bs_read_ue(b); printf("sps->hrd.cpb_size_value_minus1[ SchedSelIdx ]: %d \n", sps->hrd.cpb_size_value_minus1[ SchedSelIdx ]);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->hrd.cbr_flag[ SchedSelIdx ] = bs_read_u1(b); printf("sps->hrd.cbr_flag[ SchedSelIdx ]: %d \n", sps->hrd.cbr_flag[ SchedSelIdx ]);
}
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->hrd.initial_cpb_removal_delay_length_minus1 = bs_read_u(b, 5); printf("sps->hrd.initial_cpb_removal_delay_length_minus1: %d \n", sps->hrd.initial_cpb_removal_delay_length_minus1);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->hrd.cpb_removal_delay_length_minus1 = bs_read_u(b, 5); printf("sps->hrd.cpb_removal_delay_length_minus1: %d \n", sps->hrd.cpb_removal_delay_length_minus1);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->hrd.dpb_output_delay_length_minus1 = bs_read_u(b, 5); printf("sps->hrd.dpb_output_delay_length_minus1: %d \n", sps->hrd.dpb_output_delay_length_minus1);
printf("%d.%d: ", b->p - b->start, b->bits_left); sps->hrd.time_offset_length = bs_read_u(b, 5); printf("sps->hrd.time_offset_length: %d \n", sps->hrd.time_offset_length);
}
/*
UNIMPLEMENTED
//7.3.2.1.2 Sequence parameter set extension RBSP syntax
int read_debug_seq_parameter_set_extension_rbsp(bs_t* b, sps_ext_t* sps_ext) {
printf("%d.%d: ", b->p - b->start, b->bits_left); seq_parameter_set_id = bs_read_ue(b); printf("seq_parameter_set_id: %d \n", seq_parameter_set_id);
printf("%d.%d: ", b->p - b->start, b->bits_left); aux_format_idc = bs_read_ue(b); printf("aux_format_idc: %d \n", aux_format_idc);
if( aux_format_idc != 0 ) {
printf("%d.%d: ", b->p - b->start, b->bits_left); bit_depth_aux_minus8 = bs_read_ue(b); printf("bit_depth_aux_minus8: %d \n", bit_depth_aux_minus8);
printf("%d.%d: ", b->p - b->start, b->bits_left); alpha_incr_flag = bs_read_u1(b); printf("alpha_incr_flag: %d \n", alpha_incr_flag);
alpha_opaque_value = bs_read_debug_u(v);
alpha_transparent_value = bs_read_debug_u(v);
}
printf("%d.%d: ", b->p - b->start, b->bits_left); additional_extension_flag = bs_read_u1(b); printf("additional_extension_flag: %d \n", additional_extension_flag);
read_debug_rbsp_trailing_bits();
}
*/
//7.3.2.2 Picture parameter set RBSP syntax
void read_debug_pic_parameter_set_rbsp(h264_stream_t* h, bs_t* b)
{
pps_t* pps = h->pps;
if( 1 )
{
memset(pps, 0, sizeof(pps_t));
}
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->pic_parameter_set_id = bs_read_ue(b); printf("pps->pic_parameter_set_id: %d \n", pps->pic_parameter_set_id);
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->seq_parameter_set_id = bs_read_ue(b); printf("pps->seq_parameter_set_id: %d \n", pps->seq_parameter_set_id);
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->entropy_coding_mode_flag = bs_read_u1(b); printf("pps->entropy_coding_mode_flag: %d \n", pps->entropy_coding_mode_flag);
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->pic_order_present_flag = bs_read_u1(b); printf("pps->pic_order_present_flag: %d \n", pps->pic_order_present_flag);
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->num_slice_groups_minus1 = bs_read_ue(b); printf("pps->num_slice_groups_minus1: %d \n", pps->num_slice_groups_minus1);
if( pps->num_slice_groups_minus1 > 0 )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->slice_group_map_type = bs_read_ue(b); printf("pps->slice_group_map_type: %d \n", pps->slice_group_map_type);
if( pps->slice_group_map_type == 0 )
{
for( int i_group = 0; i_group <= pps->num_slice_groups_minus1; i_group++ )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->run_length_minus1[ i_group ] = bs_read_ue(b); printf("pps->run_length_minus1[ i_group ]: %d \n", pps->run_length_minus1[ i_group ]);
}
}
else if( pps->slice_group_map_type == 2 )
{
for( int i_group = 0; i_group < pps->num_slice_groups_minus1; i_group++ )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->top_left[ i_group ] = bs_read_ue(b); printf("pps->top_left[ i_group ]: %d \n", pps->top_left[ i_group ]);
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->bottom_right[ i_group ] = bs_read_ue(b); printf("pps->bottom_right[ i_group ]: %d \n", pps->bottom_right[ i_group ]);
}
}
else if( pps->slice_group_map_type == 3 ||
pps->slice_group_map_type == 4 ||
pps->slice_group_map_type == 5 )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->slice_group_change_direction_flag = bs_read_u1(b); printf("pps->slice_group_change_direction_flag: %d \n", pps->slice_group_change_direction_flag);
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->slice_group_change_rate_minus1 = bs_read_ue(b); printf("pps->slice_group_change_rate_minus1: %d \n", pps->slice_group_change_rate_minus1);
}
else if( pps->slice_group_map_type == 6 )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->pic_size_in_map_units_minus1 = bs_read_ue(b); printf("pps->pic_size_in_map_units_minus1: %d \n", pps->pic_size_in_map_units_minus1);
for( int i = 0; i <= pps->pic_size_in_map_units_minus1; i++ )
{
int v = intlog2( pps->num_slice_groups_minus1 + 1 );
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->slice_group_id[ i ] = bs_read_u(b, v); printf("pps->slice_group_id[ i ]: %d \n", pps->slice_group_id[ i ]);
}
}
}
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->num_ref_idx_l0_active_minus1 = bs_read_ue(b); printf("pps->num_ref_idx_l0_active_minus1: %d \n", pps->num_ref_idx_l0_active_minus1);
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->num_ref_idx_l1_active_minus1 = bs_read_ue(b); printf("pps->num_ref_idx_l1_active_minus1: %d \n", pps->num_ref_idx_l1_active_minus1);
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->weighted_pred_flag = bs_read_u1(b); printf("pps->weighted_pred_flag: %d \n", pps->weighted_pred_flag);
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->weighted_bipred_idc = bs_read_u(b, 2); printf("pps->weighted_bipred_idc: %d \n", pps->weighted_bipred_idc);
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->pic_init_qp_minus26 = bs_read_se(b); printf("pps->pic_init_qp_minus26: %d \n", pps->pic_init_qp_minus26);
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->pic_init_qs_minus26 = bs_read_se(b); printf("pps->pic_init_qs_minus26: %d \n", pps->pic_init_qs_minus26);
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->chroma_qp_index_offset = bs_read_se(b); printf("pps->chroma_qp_index_offset: %d \n", pps->chroma_qp_index_offset);
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->deblocking_filter_control_present_flag = bs_read_u1(b); printf("pps->deblocking_filter_control_present_flag: %d \n", pps->deblocking_filter_control_present_flag);
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->constrained_intra_pred_flag = bs_read_u1(b); printf("pps->constrained_intra_pred_flag: %d \n", pps->constrained_intra_pred_flag);
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->redundant_pic_cnt_present_flag = bs_read_u1(b); printf("pps->redundant_pic_cnt_present_flag: %d \n", pps->redundant_pic_cnt_present_flag);
int have_more_data = 0;
if( 1 ) { have_more_data = more_rbsp_data(h, b); }
if( 0 )
{
have_more_data = pps->transform_8x8_mode_flag | pps->pic_scaling_matrix_present_flag | pps->second_chroma_qp_index_offset != 0;
}
if( have_more_data )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->transform_8x8_mode_flag = bs_read_u1(b); printf("pps->transform_8x8_mode_flag: %d \n", pps->transform_8x8_mode_flag);
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->pic_scaling_matrix_present_flag = bs_read_u1(b); printf("pps->pic_scaling_matrix_present_flag: %d \n", pps->pic_scaling_matrix_present_flag);
if( pps->pic_scaling_matrix_present_flag )
{
for( int i = 0; i < 6 + 2* pps->transform_8x8_mode_flag; i++ )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->pic_scaling_list_present_flag[ i ] = bs_read_u1(b); printf("pps->pic_scaling_list_present_flag[ i ]: %d \n", pps->pic_scaling_list_present_flag[ i ]);
if( pps->pic_scaling_list_present_flag[ i ] )
{
if( i < 6 )
{
read_debug_scaling_list( b, pps->ScalingList4x4[ i ], 16,
&( pps->UseDefaultScalingMatrix4x4Flag[ i ] ) );
}
else
{
read_debug_scaling_list( b, pps->ScalingList8x8[ i - 6 ], 64,
&( pps->UseDefaultScalingMatrix8x8Flag[ i - 6 ] ) );
}
}
}
}
printf("%d.%d: ", b->p - b->start, b->bits_left); pps->second_chroma_qp_index_offset = bs_read_se(b); printf("pps->second_chroma_qp_index_offset: %d \n", pps->second_chroma_qp_index_offset);
}
read_debug_rbsp_trailing_bits(h, b);
if( 1 )
{
memcpy(h->pps, h->pps_table[pps->pic_parameter_set_id], sizeof(pps_t));
}
}
#ifdef HAVE_SEI
//7.3.2.3 Supplemental enhancement information RBSP syntax
void read_debug_sei_rbsp(h264_stream_t* h, bs_t* b)
{
if( 1 )
{
for( int i = 0; i < h->num_seis; i++ )
{
sei_free(h->seis[i]);
}
h->num_seis = 0;
do {
h->num_seis++;
h->seis = (sei_t**)realloc(h->seis, h->num_seis * sizeof(sei_t*));
h->seis[h->num_seis - 1] = sei_new();
h->sei = h->seis[h->num_seis - 1];
read_debug_sei_message(h, b);
} while( more_rbsp_data(h, b) );
}
if( 0 )
{
for (int i = 0; i < h->num_seis; i++)
{
h->sei = h->seis[i];
read_debug_sei_message(h, b);
}
h->sei = NULL;
}
read_debug_rbsp_trailing_bits(h, b);
}
//7.3.2.3.1 Supplemental enhancement information message syntax
void read_debug_sei_message(h264_stream_t* h, bs_t* b)
{
if( 0 )
{
_write_ff_coded_number(b, h->sei->payloadType);
_write_ff_coded_number(b, h->sei->payloadSize);
}
if( 1 )
{
h->sei->payloadType = _read_ff_coded_number(b);
h->sei->payloadSize = _read_ff_coded_number(b);
}
read_debug_sei_payload( h, b, h->sei->payloadType, h->sei->payloadSize );
}
#endif
//7.3.2.4 Access unit delimiter RBSP syntax
void read_debug_access_unit_delimiter_rbsp(h264_stream_t* h, bs_t* b)
{
printf("%d.%d: ", b->p - b->start, b->bits_left); h->aud->primary_pic_type = bs_read_u(b, 3); printf("h->aud->primary_pic_type: %d \n", h->aud->primary_pic_type);
read_debug_rbsp_trailing_bits(h, b);
}
//7.3.2.5 End of sequence RBSP syntax
void read_debug_end_of_seq_rbsp(h264_stream_t* h, bs_t* b)
{
}
//7.3.2.6 End of stream RBSP syntax
void read_debug_end_of_stream_rbsp(h264_stream_t* h, bs_t* b)
{
}
//7.3.2.7 Filler data RBSP syntax
void read_debug_filler_data_rbsp(h264_stream_t* h, bs_t* b)
{
while( bs_next_bits(b, 8) == 0xFF )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); int ff_byte = bs_read_u(b, 8); printf("ff_byte: %d \n", ff_byte);
}
read_debug_rbsp_trailing_bits(h, b);
}
//7.3.2.8 Slice layer without partitioning RBSP syntax
void read_debug_slice_layer_rbsp(h264_stream_t* h, bs_t* b)
{
read_debug_slice_header(h, b);
slice_data_rbsp_t* slice_data = h->slice_data;
if ( slice_data != NULL )
{
if ( slice_data->rbsp_buf != NULL ) free( slice_data->rbsp_buf );
uint8_t *sptr = b->p + (!!b->bits_left); // CABAC-specific: skip alignment bits, if there are any
slice_data->rbsp_size = b->end - sptr;
slice_data->rbsp_buf = (uint8_t*)malloc(slice_data->rbsp_size);
memcpy( slice_data->rbsp_buf, sptr, slice_data->rbsp_size );
// ugly hack: since next NALU starts at byte border, we are going to be padded by trailing_bits;
return;
}
// FIXME should read or skip data
//slice_data( ); /* all categories of slice_data( ) syntax */
read_debug_rbsp_slice_trailing_bits(h, b);
}
/*
// UNIMPLEMENTED
//7.3.2.9.1 Slice data partition A RBSP syntax
slice_data_partition_a_layer_rbsp( ) {
read_debug_slice_header( ); // only category 2
slice_id = bs_read_debug_ue(b)
read_debug_slice_data( ); // only category 2
read_debug_rbsp_slice_trailing_bits( ); // only category 2
}
//7.3.2.9.2 Slice data partition B RBSP syntax
slice_data_partition_b_layer_rbsp( ) {
printf("%d.%d: ", b->p - b->start, b->bits_left); slice_id = bs_read_ue(b); printf("slice_id: %d \n", slice_id); // only category 3
if( redundant_pic_cnt_present_flag )
printf("%d.%d: ", b->p - b->start, b->bits_left); redundant_pic_cnt = bs_read_ue(b); printf("redundant_pic_cnt: %d \n", redundant_pic_cnt);
read_debug_slice_data( ); // only category 3
read_debug_rbsp_slice_trailing_bits( ); // only category 3
}
//7.3.2.9.3 Slice data partition C RBSP syntax
slice_data_partition_c_layer_rbsp( ) {
printf("%d.%d: ", b->p - b->start, b->bits_left); slice_id = bs_read_ue(b); printf("slice_id: %d \n", slice_id); // only category 4
if( redundant_pic_cnt_present_flag )
printf("%d.%d: ", b->p - b->start, b->bits_left); redundant_pic_cnt = bs_read_ue(b); printf("redundant_pic_cnt: %d \n", redundant_pic_cnt);
read_debug_slice_data( ); // only category 4
rbsp_slice_trailing_bits( ); // only category 4
}
*/
//7.3.2.10 RBSP slice trailing bits syntax
void read_debug_rbsp_slice_trailing_bits(h264_stream_t* h, bs_t* b)
{
read_debug_rbsp_trailing_bits(h, b);
if( h->pps->entropy_coding_mode_flag )
{
while( more_rbsp_trailing_data(h, b) )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); int cabac_zero_word = bs_read_u(b, 16); printf("cabac_zero_word: %d \n", cabac_zero_word);
}
}
}
//7.3.2.11 RBSP trailing bits syntax
void read_debug_rbsp_trailing_bits(h264_stream_t* h, bs_t* b)
{
printf("%d.%d: ", b->p - b->start, b->bits_left); int rbsp_stop_one_bit = bs_read_u(b, 1); printf("rbsp_stop_one_bit: %d \n", rbsp_stop_one_bit);
while( !bs_byte_aligned(b) )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); int rbsp_alignment_zero_bit = bs_read_u(b, 1); printf("rbsp_alignment_zero_bit: %d \n", rbsp_alignment_zero_bit);
}
}
//7.3.3 Slice header syntax
void read_debug_slice_header(h264_stream_t* h, bs_t* b)
{
slice_header_t* sh = h->sh;
if( 1 )
{
memset(sh, 0, sizeof(slice_header_t));
}
nal_t* nal = h->nal;
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->first_mb_in_slice = bs_read_ue(b); printf("sh->first_mb_in_slice: %d \n", sh->first_mb_in_slice);
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->slice_type = bs_read_ue(b); printf("sh->slice_type: %d \n", sh->slice_type);
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pic_parameter_set_id = bs_read_ue(b); printf("sh->pic_parameter_set_id: %d \n", sh->pic_parameter_set_id);
// TODO check existence, otherwise fail
pps_t* pps = h->pps;
sps_t* sps = h->sps;
memcpy(h->pps_table[sh->pic_parameter_set_id], h->pps, sizeof(pps_t));
memcpy(h->sps_table[pps->seq_parameter_set_id], h->sps, sizeof(sps_t));
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->frame_num = bs_read_u(b, sps->log2_max_frame_num_minus4 + 4 ); printf("sh->frame_num: %d \n", sh->frame_num); // was u(v)
if( !sps->frame_mbs_only_flag )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->field_pic_flag = bs_read_u1(b); printf("sh->field_pic_flag: %d \n", sh->field_pic_flag);
if( sh->field_pic_flag )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->bottom_field_flag = bs_read_u1(b); printf("sh->bottom_field_flag: %d \n", sh->bottom_field_flag);
}
}
if( nal->nal_unit_type == 5 )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->idr_pic_id = bs_read_ue(b); printf("sh->idr_pic_id: %d \n", sh->idr_pic_id);
}
if( sps->pic_order_cnt_type == 0 )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pic_order_cnt_lsb = bs_read_u(b, sps->log2_max_pic_order_cnt_lsb_minus4 + 4 ); printf("sh->pic_order_cnt_lsb: %d \n", sh->pic_order_cnt_lsb); // was u(v)
if( pps->pic_order_present_flag && !sh->field_pic_flag )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->delta_pic_order_cnt_bottom = bs_read_se(b); printf("sh->delta_pic_order_cnt_bottom: %d \n", sh->delta_pic_order_cnt_bottom);
}
}
if( sps->pic_order_cnt_type == 1 && !sps->delta_pic_order_always_zero_flag )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->delta_pic_order_cnt[ 0 ] = bs_read_se(b); printf("sh->delta_pic_order_cnt[ 0 ]: %d \n", sh->delta_pic_order_cnt[ 0 ]);
if( pps->pic_order_present_flag && !sh->field_pic_flag )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->delta_pic_order_cnt[ 1 ] = bs_read_se(b); printf("sh->delta_pic_order_cnt[ 1 ]: %d \n", sh->delta_pic_order_cnt[ 1 ]);
}
}
if( pps->redundant_pic_cnt_present_flag )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->redundant_pic_cnt = bs_read_ue(b); printf("sh->redundant_pic_cnt: %d \n", sh->redundant_pic_cnt);
}
if( is_slice_type( sh->slice_type, SH_SLICE_TYPE_B ) )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->direct_spatial_mv_pred_flag = bs_read_u1(b); printf("sh->direct_spatial_mv_pred_flag: %d \n", sh->direct_spatial_mv_pred_flag);
}
if( is_slice_type( sh->slice_type, SH_SLICE_TYPE_P ) || is_slice_type( sh->slice_type, SH_SLICE_TYPE_SP ) || is_slice_type( sh->slice_type, SH_SLICE_TYPE_B ) )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->num_ref_idx_active_override_flag = bs_read_u1(b); printf("sh->num_ref_idx_active_override_flag: %d \n", sh->num_ref_idx_active_override_flag);
if( sh->num_ref_idx_active_override_flag )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->num_ref_idx_l0_active_minus1 = bs_read_ue(b); printf("sh->num_ref_idx_l0_active_minus1: %d \n", sh->num_ref_idx_l0_active_minus1); // FIXME does this modify the pps?
if( is_slice_type( sh->slice_type, SH_SLICE_TYPE_B ) )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->num_ref_idx_l1_active_minus1 = bs_read_ue(b); printf("sh->num_ref_idx_l1_active_minus1: %d \n", sh->num_ref_idx_l1_active_minus1);
}
}
}
read_debug_ref_pic_list_reordering(h, b);
if( ( pps->weighted_pred_flag && ( is_slice_type( sh->slice_type, SH_SLICE_TYPE_P ) || is_slice_type( sh->slice_type, SH_SLICE_TYPE_SP ) ) ) ||
( pps->weighted_bipred_idc == 1 && is_slice_type( sh->slice_type, SH_SLICE_TYPE_B ) ) )
{
read_debug_pred_weight_table(h, b);
}
if( nal->nal_ref_idc != 0 )
{
read_debug_dec_ref_pic_marking(h, b);
}
if( pps->entropy_coding_mode_flag && ! is_slice_type( sh->slice_type, SH_SLICE_TYPE_I ) && ! is_slice_type( sh->slice_type, SH_SLICE_TYPE_SI ) )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->cabac_init_idc = bs_read_ue(b); printf("sh->cabac_init_idc: %d \n", sh->cabac_init_idc);
}
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->slice_qp_delta = bs_read_se(b); printf("sh->slice_qp_delta: %d \n", sh->slice_qp_delta);
if( is_slice_type( sh->slice_type, SH_SLICE_TYPE_SP ) || is_slice_type( sh->slice_type, SH_SLICE_TYPE_SI ) )
{
if( is_slice_type( sh->slice_type, SH_SLICE_TYPE_SP ) )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->sp_for_switch_flag = bs_read_u1(b); printf("sh->sp_for_switch_flag: %d \n", sh->sp_for_switch_flag);
}
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->slice_qs_delta = bs_read_se(b); printf("sh->slice_qs_delta: %d \n", sh->slice_qs_delta);
}
if( pps->deblocking_filter_control_present_flag )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->disable_deblocking_filter_idc = bs_read_ue(b); printf("sh->disable_deblocking_filter_idc: %d \n", sh->disable_deblocking_filter_idc);
if( sh->disable_deblocking_filter_idc != 1 )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->slice_alpha_c0_offset_div2 = bs_read_se(b); printf("sh->slice_alpha_c0_offset_div2: %d \n", sh->slice_alpha_c0_offset_div2);
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->slice_beta_offset_div2 = bs_read_se(b); printf("sh->slice_beta_offset_div2: %d \n", sh->slice_beta_offset_div2);
}
}
if( pps->num_slice_groups_minus1 > 0 &&
pps->slice_group_map_type >= 3 && pps->slice_group_map_type <= 5)
{
int v = intlog2( pps->pic_size_in_map_units_minus1 + pps->slice_group_change_rate_minus1 + 1 );
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->slice_group_change_cycle = bs_read_u(b, v); printf("sh->slice_group_change_cycle: %d \n", sh->slice_group_change_cycle); // FIXME add 2?
}
}
//7.3.3.1 Reference picture list reordering syntax
void read_debug_ref_pic_list_reordering(h264_stream_t* h, bs_t* b)
{
slice_header_t* sh = h->sh;
// FIXME should be an array
if( ! is_slice_type( sh->slice_type, SH_SLICE_TYPE_I ) && ! is_slice_type( sh->slice_type, SH_SLICE_TYPE_SI ) )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->rplr.ref_pic_list_reordering_flag_l0 = bs_read_u1(b); printf("sh->rplr.ref_pic_list_reordering_flag_l0: %d \n", sh->rplr.ref_pic_list_reordering_flag_l0);
if( sh->rplr.ref_pic_list_reordering_flag_l0 )
{
int n = -1;
do
{
n++;
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->rplr.reorder_l0.reordering_of_pic_nums_idc[ n ] = bs_read_ue(b); printf("sh->rplr.reorder_l0.reordering_of_pic_nums_idc[ n ]: %d \n", sh->rplr.reorder_l0.reordering_of_pic_nums_idc[ n ]);
if( sh->rplr.reorder_l0.reordering_of_pic_nums_idc[ n ] == 0 ||
sh->rplr.reorder_l0.reordering_of_pic_nums_idc[ n ] == 1 )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->rplr.reorder_l0.abs_diff_pic_num_minus1[ n ] = bs_read_ue(b); printf("sh->rplr.reorder_l0.abs_diff_pic_num_minus1[ n ]: %d \n", sh->rplr.reorder_l0.abs_diff_pic_num_minus1[ n ]);
}
else if( sh->rplr.reorder_l0.reordering_of_pic_nums_idc[ n ] == 2 )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->rplr.reorder_l0.long_term_pic_num[ n ] = bs_read_ue(b); printf("sh->rplr.reorder_l0.long_term_pic_num[ n ]: %d \n", sh->rplr.reorder_l0.long_term_pic_num[ n ]);
}
} while( sh->rplr.reorder_l0.reordering_of_pic_nums_idc[ n ] != 3 && ! bs_eof(b) );
}
}
if( is_slice_type( sh->slice_type, SH_SLICE_TYPE_B ) )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->rplr.ref_pic_list_reordering_flag_l1 = bs_read_u1(b); printf("sh->rplr.ref_pic_list_reordering_flag_l1: %d \n", sh->rplr.ref_pic_list_reordering_flag_l1);
if( sh->rplr.ref_pic_list_reordering_flag_l1 )
{
int n = -1;
do
{
n++;
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->rplr.reorder_l1.reordering_of_pic_nums_idc[ n ] = bs_read_ue(b); printf("sh->rplr.reorder_l1.reordering_of_pic_nums_idc[ n ]: %d \n", sh->rplr.reorder_l1.reordering_of_pic_nums_idc[ n ]);
if( sh->rplr.reorder_l1.reordering_of_pic_nums_idc[ n ] == 0 ||
sh->rplr.reorder_l1.reordering_of_pic_nums_idc[ n ] == 1 )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->rplr.reorder_l1.abs_diff_pic_num_minus1[ n ] = bs_read_ue(b); printf("sh->rplr.reorder_l1.abs_diff_pic_num_minus1[ n ]: %d \n", sh->rplr.reorder_l1.abs_diff_pic_num_minus1[ n ]);
}
else if( sh->rplr.reorder_l1.reordering_of_pic_nums_idc[ n ] == 2 )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->rplr.reorder_l1.long_term_pic_num[ n ] = bs_read_ue(b); printf("sh->rplr.reorder_l1.long_term_pic_num[ n ]: %d \n", sh->rplr.reorder_l1.long_term_pic_num[ n ]);
}
} while( sh->rplr.reorder_l1.reordering_of_pic_nums_idc[ n ] != 3 && ! bs_eof(b) );
}
}
}
//7.3.3.2 Prediction weight table syntax
void read_debug_pred_weight_table(h264_stream_t* h, bs_t* b)
{
slice_header_t* sh = h->sh;
sps_t* sps = h->sps;
pps_t* pps = h->pps;
int i, j;
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.luma_log2_weight_denom = bs_read_ue(b); printf("sh->pwt.luma_log2_weight_denom: %d \n", sh->pwt.luma_log2_weight_denom);
if( sps->chroma_format_idc != 0 )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.chroma_log2_weight_denom = bs_read_ue(b); printf("sh->pwt.chroma_log2_weight_denom: %d \n", sh->pwt.chroma_log2_weight_denom);
}
for( i = 0; i <= pps->num_ref_idx_l0_active_minus1; i++ )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.luma_weight_l0_flag[i] = bs_read_u1(b); printf("sh->pwt.luma_weight_l0_flag[i]: %d \n", sh->pwt.luma_weight_l0_flag[i]);
if( sh->pwt.luma_weight_l0_flag[i] )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.luma_weight_l0[ i ] = bs_read_se(b); printf("sh->pwt.luma_weight_l0[ i ]: %d \n", sh->pwt.luma_weight_l0[ i ]);
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.luma_offset_l0[ i ] = bs_read_se(b); printf("sh->pwt.luma_offset_l0[ i ]: %d \n", sh->pwt.luma_offset_l0[ i ]);
}
if ( sps->chroma_format_idc != 0 )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.chroma_weight_l0_flag[i] = bs_read_u1(b); printf("sh->pwt.chroma_weight_l0_flag[i]: %d \n", sh->pwt.chroma_weight_l0_flag[i]);
if( sh->pwt.chroma_weight_l0_flag[i] )
{
for( j =0; j < 2; j++ )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.chroma_weight_l0[ i ][ j ] = bs_read_se(b); printf("sh->pwt.chroma_weight_l0[ i ][ j ]: %d \n", sh->pwt.chroma_weight_l0[ i ][ j ]);
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.chroma_offset_l0[ i ][ j ] = bs_read_se(b); printf("sh->pwt.chroma_offset_l0[ i ][ j ]: %d \n", sh->pwt.chroma_offset_l0[ i ][ j ]);
}
}
}
}
if( is_slice_type( sh->slice_type, SH_SLICE_TYPE_B ) )
{
for( i = 0; i <= pps->num_ref_idx_l1_active_minus1; i++ )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.luma_weight_l1_flag[i] = bs_read_u1(b); printf("sh->pwt.luma_weight_l1_flag[i]: %d \n", sh->pwt.luma_weight_l1_flag[i]);
if( sh->pwt.luma_weight_l1_flag[i] )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.luma_weight_l1[ i ] = bs_read_se(b); printf("sh->pwt.luma_weight_l1[ i ]: %d \n", sh->pwt.luma_weight_l1[ i ]);
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.luma_offset_l1[ i ] = bs_read_se(b); printf("sh->pwt.luma_offset_l1[ i ]: %d \n", sh->pwt.luma_offset_l1[ i ]);
}
if( sps->chroma_format_idc != 0 )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.chroma_weight_l1_flag[i] = bs_read_u1(b); printf("sh->pwt.chroma_weight_l1_flag[i]: %d \n", sh->pwt.chroma_weight_l1_flag[i]);
if( sh->pwt.chroma_weight_l1_flag[i] )
{
for( j = 0; j < 2; j++ )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.chroma_weight_l1[ i ][ j ] = bs_read_se(b); printf("sh->pwt.chroma_weight_l1[ i ][ j ]: %d \n", sh->pwt.chroma_weight_l1[ i ][ j ]);
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->pwt.chroma_offset_l1[ i ][ j ] = bs_read_se(b); printf("sh->pwt.chroma_offset_l1[ i ][ j ]: %d \n", sh->pwt.chroma_offset_l1[ i ][ j ]);
}
}
}
}
}
}
//7.3.3.3 Decoded reference picture marking syntax
void read_debug_dec_ref_pic_marking(h264_stream_t* h, bs_t* b)
{
slice_header_t* sh = h->sh;
// FIXME should be an array
if( h->nal->nal_unit_type == 5 )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->drpm.no_output_of_prior_pics_flag = bs_read_u1(b); printf("sh->drpm.no_output_of_prior_pics_flag: %d \n", sh->drpm.no_output_of_prior_pics_flag);
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->drpm.long_term_reference_flag = bs_read_u1(b); printf("sh->drpm.long_term_reference_flag: %d \n", sh->drpm.long_term_reference_flag);
}
else
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->drpm.adaptive_ref_pic_marking_mode_flag = bs_read_u1(b); printf("sh->drpm.adaptive_ref_pic_marking_mode_flag: %d \n", sh->drpm.adaptive_ref_pic_marking_mode_flag);
if( sh->drpm.adaptive_ref_pic_marking_mode_flag )
{
int n = -1;
do
{
n++;
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->drpm.memory_management_control_operation[ n ] = bs_read_ue(b); printf("sh->drpm.memory_management_control_operation[ n ]: %d \n", sh->drpm.memory_management_control_operation[ n ]);
if( sh->drpm.memory_management_control_operation[ n ] == 1 ||
sh->drpm.memory_management_control_operation[ n ] == 3 )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->drpm.difference_of_pic_nums_minus1[ n ] = bs_read_ue(b); printf("sh->drpm.difference_of_pic_nums_minus1[ n ]: %d \n", sh->drpm.difference_of_pic_nums_minus1[ n ]);
}
if(sh->drpm.memory_management_control_operation[ n ] == 2 )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->drpm.long_term_pic_num[ n ] = bs_read_ue(b); printf("sh->drpm.long_term_pic_num[ n ]: %d \n", sh->drpm.long_term_pic_num[ n ]);
}
if( sh->drpm.memory_management_control_operation[ n ] == 3 ||
sh->drpm.memory_management_control_operation[ n ] == 6 )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->drpm.long_term_frame_idx[ n ] = bs_read_ue(b); printf("sh->drpm.long_term_frame_idx[ n ]: %d \n", sh->drpm.long_term_frame_idx[ n ]);
}
if( sh->drpm.memory_management_control_operation[ n ] == 4 )
{
printf("%d.%d: ", b->p - b->start, b->bits_left); sh->drpm.max_long_term_frame_idx_plus1[ n ] = bs_read_ue(b); printf("sh->drpm.max_long_term_frame_idx_plus1[ n ]: %d \n", sh->drpm.max_long_term_frame_idx_plus1[ n ]);
}
} while( sh->drpm.memory_management_control_operation[ n ] != 0 && ! bs_eof(b) );
}
}
}

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