Merge branch 'master' of github.com:FreeRDP/FreeRDP
This commit is contained in:
commit
bfca674f2d
2
.gitignore
vendored
2
.gitignore
vendored
@ -20,6 +20,8 @@ LICENSE.txt
|
||||
*Config.cmake
|
||||
*ConfigVersion.cmake
|
||||
include/freerdp/version.h
|
||||
include/freerdp/build-config.h
|
||||
buildflags.h
|
||||
|
||||
*.a.objlist.cmake
|
||||
*.a.objlist
|
||||
|
@ -60,7 +60,7 @@ set(CPACK_PACKAGE_ICON "${CMAKE_SOURCE_DIR}/resources\\\\FreeRDP_Install.bmp")
|
||||
set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/resources\\\\FreeRDP_Icon_96px.ico")
|
||||
set(CPACK_NSIS_MUI_UNICON "${CMAKE_SOURCE_DIR}/resource\\\\FreeRDP_Icon_96px.ico")
|
||||
|
||||
set(CPACK_COMPONENTS_ALL client server libraries headers)
|
||||
set(CPACK_COMPONENTS_ALL client server libraries headers symbols tools)
|
||||
|
||||
if(MSVC)
|
||||
if(MSVC_RUNTIME STREQUAL "dynamic")
|
||||
@ -85,6 +85,12 @@ set(CPACK_COMPONENT_LIBRARIES_GROUP "Runtime")
|
||||
set(CPACK_COMPONENT_HEADERS_DISPLAY_NAME "Headers")
|
||||
set(CPACK_COMPONENT_HEADERS_GROUP "Development")
|
||||
|
||||
set(CPACK_COMPONENT_SYMBOLS_DISPLAY_NAME "Symbols")
|
||||
set(CPACK_COMPONENT_SYMBOLS_GROUP "Development")
|
||||
|
||||
set(CPACK_COMPONENT_TOOLS_DISPLAY_NAME "Tools")
|
||||
set(CPACK_COMPONENT_TOOLS_GROUP "Applications")
|
||||
|
||||
set(CPACK_COMPONENT_GROUP_RUNTIME_DESCRIPTION "Runtime")
|
||||
set(CPACK_COMPONENT_GROUP_APPLICATIONS_DESCRIPTION "Applications")
|
||||
set(CPACK_COMPONENT_GROUP_DEVELOPMENT_DESCRIPTION "Development")
|
||||
|
211
CMakeLists.txt
211
CMakeLists.txt
@ -68,29 +68,40 @@ if ($ENV{BUILD_NUMBER})
|
||||
set(BUILD_NUMBER $ENV{BUILD_NUMBER})
|
||||
endif()
|
||||
set(WITH_LIBRARY_VERSIONING "ON")
|
||||
set(FREERDP_VERSION_MAJOR "1")
|
||||
set(FREERDP_VERSION_MINOR "2")
|
||||
set(FREERDP_VERSION_REVISION "5")
|
||||
set(FREERDP_VERSION_MAJOR "2")
|
||||
set(FREERDP_VERSION_MINOR "0")
|
||||
set(FREERDP_VERSION_REVISION "0")
|
||||
set(FREERDP_VERSION_SUFFIX "dev")
|
||||
set(FREERDP_API_VERSION "${FREERDP_VERSION_MAJOR}.${FREERDP_VERSION_MINOR}")
|
||||
set(FREERDP_VERSION "${FREERDP_API_VERSION}.${FREERDP_VERSION_REVISION}")
|
||||
set(FREERDP_API_VERSION "${FREERDP_VERSION_MAJOR}")
|
||||
set(FREERDP_VERSION "${FREERDP_VERSION_MAJOR}.${FREERDP_VERSION_MINOR}.${FREERDP_VERSION_REVISION}")
|
||||
if (FREERDP_VERSION_SUFFIX)
|
||||
set(FREERDP_VERSION_FULL "${FREERDP_VERSION}-${FREERDP_VERSION_SUFFIX}")
|
||||
else()
|
||||
set(FREERDP_VERSION_FULL "${FREERDP_VERSION}")
|
||||
endif()
|
||||
set(FREERDP_INCLUDE_DIR "include/freerdp${FREERDP_VERSION_MAJOR}/")
|
||||
|
||||
# Make paths absolute
|
||||
if (CMAKE_INSTALL_PREFIX)
|
||||
get_filename_component(CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" ABSOLUTE)
|
||||
endif()
|
||||
if (FREERDP_EXTERNAL_PATH)
|
||||
get_filename_component (FREERDP_EXTERNAL_PATH "${FREERDP_EXTERNAL_PATH}" ABSOLUTE)
|
||||
endif()
|
||||
|
||||
# Allow to search the host machine for git
|
||||
if(ANDROID OR IOS)
|
||||
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH)
|
||||
endif(ANDROID OR IOS)
|
||||
|
||||
include(GetGitRevisionDescription)
|
||||
git_get_exact_tag(GIT_REVISION --tags --always)
|
||||
|
||||
if (${GIT_REVISION} STREQUAL "n/a")
|
||||
git_rev_parse(GIT_REVISION --short)
|
||||
git_rev_parse (GIT_REVISION --short)
|
||||
endif()
|
||||
if(ANDROID OR IOS)
|
||||
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
|
||||
SET (CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
|
||||
endif(ANDROID OR IOS)
|
||||
|
||||
message(STATUS "Git Revision ${GIT_REVISION}")
|
||||
@ -104,7 +115,7 @@ if(NOT CMAKE_BUILD_TYPE)
|
||||
endif()
|
||||
|
||||
if(NOT DEFINED BUILD_SHARED_LIBS)
|
||||
if(ANDROID OR IOS OR APPLE)
|
||||
if(IOS OR APPLE)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
else()
|
||||
set(BUILD_SHARED_LIBS ON)
|
||||
@ -246,6 +257,30 @@ if(${CMAKE_C_COMPILER_ID} STREQUAL "Clang")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Enable address sanitizer, where supported and when required
|
||||
if(${CMAKE_C_COMPILER_ID} STREQUAL "Clang" OR CMAKE_COMPILER_IS_GNUCC)
|
||||
if(WITH_SANITIZE_ADDRESS)
|
||||
if (DEFINED CMAKE_REQUIRED_FLAGS)
|
||||
set(SAVE_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS})
|
||||
endif()
|
||||
set(CMAKE_REQUIRED_FLAGS "-fsanitize=address")
|
||||
CHECK_C_COMPILER_FLAG ("-fsanitize=address" fsanitize-address)
|
||||
if(fsanitize-address)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address")
|
||||
endif()
|
||||
if (DEFINED SAVE_CMAKE_REQUIRED_FLAGS)
|
||||
set(CMAKE_REQUIRED_FLAGS ${SAVE_CMAKE_REQUIRED_FLAGS})
|
||||
else()
|
||||
unset(CMAKE_REQUIRED_FLAGS)
|
||||
endif()
|
||||
|
||||
CHECK_C_COMPILER_FLAG ("-fno-omit-frame-pointer" fno-omit-frame-pointer)
|
||||
if(fno-omit-frame-pointer)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fno-omit-frame-pointer")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
# Remove previous warning definitions,
|
||||
# NMake is otherwise complaining.
|
||||
@ -274,11 +309,11 @@ if(MSVC)
|
||||
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR})
|
||||
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR})
|
||||
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
else()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Zi")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi")
|
||||
endif()
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||
else()
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Zi")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zi")
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
@ -287,6 +322,15 @@ if(WIN32)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DWIN32_LEAN_AND_MEAN")
|
||||
|
||||
set(CMAKE_USE_RELATIVE_PATH ON)
|
||||
if (${CMAKE_GENERATOR} MATCHES "NMake Makefile*")
|
||||
set(CMAKE_PDB_BINARY_DIR ${CMAKE_BINARY_DIR})
|
||||
elseif (${CMAKE_GENERATOR} MATCHES "Visual Studio*")
|
||||
set(CMAKE_PDB_BINARY_DIR "${CMAKE_BINARY_DIR}/\${CMAKE_INSTALL_CONFIG_NAME}")
|
||||
else()
|
||||
message(FATAL "Unknown generator ${CMAKE_GENERATOR}")
|
||||
endif()
|
||||
|
||||
# Set product and vendor for dll and exe version information.
|
||||
set(RC_VERSION_VENDOR ${VENDOR})
|
||||
set(RC_VERSION_PRODUCT ${PRODUCT})
|
||||
@ -327,17 +371,16 @@ if(NOT IOS)
|
||||
check_include_files(fcntl.h HAVE_FCNTL_H)
|
||||
check_include_files(unistd.h HAVE_UNISTD_H)
|
||||
check_include_files(execinfo.h HAVE_EXECINFO_H)
|
||||
check_include_files(stdint.h HAVE_STDINT_H)
|
||||
check_include_files(inttypes.h HAVE_INTTYPES_H)
|
||||
check_include_files(sys/modem.h HAVE_SYS_MODEM_H)
|
||||
check_include_files(sys/filio.h HAVE_SYS_FILIO_H)
|
||||
check_include_files(sys/sockio.h HAVE_SYS_SOCKIO_H)
|
||||
check_include_files(sys/strtio.h HAVE_SYS_STRTIO_H)
|
||||
check_include_files(sys/select.h HAVE_SYS_SELECT_H)
|
||||
check_include_files(syslog.h HAVE_SYSLOG_H)
|
||||
else()
|
||||
set(HAVE_FCNTL_H 1)
|
||||
set(HAVE_UNISTD_H 1)
|
||||
set(HAVE_STDINT_H 1)
|
||||
set(HAVE_INTTYPES_H 1)
|
||||
set(HAVE_SYS_FILIO_H 1)
|
||||
endif()
|
||||
@ -369,9 +412,9 @@ if(APPLE)
|
||||
|
||||
# Temporarily disabled, causes the cmake script to be reexecuted, causing the compilation to fail.
|
||||
# Workaround: specify the parameter in the command-line
|
||||
# if(WITH_CLANG)
|
||||
# set(CMAKE_C_COMPILER "clang")
|
||||
# endif()
|
||||
# if(WITH_CLANG)
|
||||
# set(CMAKE_C_COMPILER "clang")
|
||||
# endif()
|
||||
|
||||
if (WITH_VERBOSE)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -v")
|
||||
@ -383,14 +426,18 @@ endif(APPLE)
|
||||
if(OPENBSD)
|
||||
set(WITH_MANPAGES "ON")
|
||||
set(WITH_ALSA "OFF")
|
||||
set(WITH_PULSE "OFF")
|
||||
set(WITH_OSS "ON")
|
||||
set(WITH_PULSE "OFF")
|
||||
set(WITH_OSS "ON")
|
||||
set(WITH_WAYLAND "OFF")
|
||||
endif()
|
||||
|
||||
# Android
|
||||
if(ANDROID)
|
||||
set(WITH_LIBRARY_VERSIONING "OFF")
|
||||
set(WITH_LIBRARY_VERSIONING "OFF")
|
||||
|
||||
if (${ANDROID_ABI} STREQUAL "armeabi")
|
||||
set (WITH_NEON OFF)
|
||||
endif()
|
||||
|
||||
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
|
||||
add_definitions(-DNDK_DEBUG=1)
|
||||
@ -402,40 +449,23 @@ if(ANDROID)
|
||||
endif()
|
||||
set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -llog")
|
||||
|
||||
if (NOT FREERDP_EXTERNAL_JPEG_PATH)
|
||||
if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/external/jpeg8d")
|
||||
set(FREERDP_EXTERNAL_JPEG_PATH "${CMAKE_CURRENT_SOURCE_DIR}/external/jpeg8d")
|
||||
if (NOT FREERDP_EXTERNAL_PATH)
|
||||
if (IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/external/")
|
||||
set (FREERDP_EXTERNAL_PATH "${CMAKE_CURRENT_SOURCE_DIR}/external/")
|
||||
else()
|
||||
message(STATUS "FREERDP_EXTERNAL_SSL_PATH not set! - Needs to be set if openssl is not found in the android NDK (which usually isn't)")
|
||||
message(STATUS "FREERDP_EXTERNAL_PATH not set!")
|
||||
endif()
|
||||
endif()
|
||||
if (NOT FREERDP_EXTERNAL_SSL_PATH)
|
||||
if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/external/openssl")
|
||||
set(FREERDP_EXTERNAL_SSL_PATH "${CMAKE_CURRENT_SOURCE_DIR}/external/openssl")
|
||||
else()
|
||||
message(STATUS "FREERDP_EXTERNAL_SSL_PATH not set! - Needs to be set if openssl is not found in the android NDK (which usually isn't)")
|
||||
endif()
|
||||
if(WITH_GPROF)
|
||||
if (NOT FREERDP_EXTERNAL_PROFILER_PATH)
|
||||
if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/external/android-ndk-profiler")
|
||||
set(FREERDP_EXTERNAL_PROFILER_PATH
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/external/android-ndk-profiler")
|
||||
else()
|
||||
message(STATUS "FREERDP_EXTERNAL_PROFILER_PATH not set!")
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ${FREERDP_EXTERNAL_SSL_PATH} ${FREERDP_EXTERNAL_JPEG_PATH})
|
||||
set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ${FREERDP_EXTERNAL_PROFILER_PATH})
|
||||
|
||||
set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/client/Android/FreeRDPCore/jni/${ANDROID_ABI})
|
||||
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/scripts/regenerate_jni_headers.sh.cmake
|
||||
${CMAKE_BINARY_DIR}/scripts/regenerate_jni_headers.sh @ONLY)
|
||||
list (APPEND CMAKE_INCLUDE_PATH ${FREERDP_EXTERNAL_PATH}/include)
|
||||
list (APPEND CMAKE_LIBRARY_PATH ${FREERDP_EXTERNAL_PATH}/${ANDROID_ABI}/ )
|
||||
set( CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH )
|
||||
set( CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH )
|
||||
|
||||
if (WITH_GPROF)
|
||||
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/scripts/gprof_generate.sh.cmake ${CMAKE_BINARY_DIR}/scripts/gprof_generate.sh @ONLY)
|
||||
CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/scripts/gprof_generate.sh.cmake
|
||||
${CMAKE_BINARY_DIR}/scripts/gprof_generate.sh @ONLY)
|
||||
endif(WITH_GPROF)
|
||||
|
||||
endif()
|
||||
|
||||
set(CMAKE_THREAD_PREFER_PTHREAD TRUE)
|
||||
@ -449,9 +479,9 @@ if(NOT WIN32)
|
||||
endif()
|
||||
|
||||
if(WITH_VALGRIND_MEMCHECK)
|
||||
check_include_files(valgrind/memcheck.h HAVE_VALGRIND_MEMCHECK_H)
|
||||
check_include_files(valgrind/memcheck.h HAVE_VALGRIND_MEMCHECK_H)
|
||||
else()
|
||||
unset(HAVE_VALGRIND_MEMCHECK_H CACHE)
|
||||
unset(HAVE_VALGRIND_MEMCHECK_H CACHE)
|
||||
endif()
|
||||
|
||||
if(UNIX OR CYGWIN)
|
||||
@ -589,6 +619,21 @@ if(APPLE)
|
||||
set(OPENSLES_FEATURE_TYPE "DISABLED")
|
||||
endif()
|
||||
|
||||
if(UNIX AND NOT ANDROID)
|
||||
set(WLOG_SYSTEMD_JOURNAL_FEATURE_TYPE "RECOMMENDED")
|
||||
set(WLOG_SYSTEMD_JOURNAL_FEATURE_PURPOSE "systemd journal appender")
|
||||
set(WLOG_SYSTEMD_JOURNAL_FEATURE_DESCRIPTION "allows to export wLog to systemd journal")
|
||||
|
||||
#include(Findlibsystemd)
|
||||
find_feature(libsystemd ${WLOG_SYSTEMD_JOURNAL_FEATURE_TYPE} ${WLOG_SYSTEMD_JOURNAL_FEATURE_PURPOSE} ${WLOG_SYSTEMD_JOURNAL_FEATURE_DESCRIPTION})
|
||||
|
||||
if(LIBSYSTEMD_FOUND)
|
||||
set(HAVE_JOURNALD_H TRUE)
|
||||
else()
|
||||
unset(HAVE_JOURNALD_H)
|
||||
endif()
|
||||
endif(UNIX AND NOT ANDROID)
|
||||
|
||||
if(ANDROID)
|
||||
set(X11_FEATURE_TYPE "DISABLED")
|
||||
set(WAYLAND_FEATURE_TYPE "DISABLED")
|
||||
@ -604,7 +649,6 @@ if(ANDROID)
|
||||
set(OPENSLES_FEATURE_TYPE "REQUIRED")
|
||||
endif()
|
||||
|
||||
|
||||
find_feature(X11 ${X11_FEATURE_TYPE} ${X11_FEATURE_PURPOSE} ${X11_FEATURE_DESCRIPTION})
|
||||
find_feature(Wayland ${WAYLAND_FEATURE_TYPE} ${WAYLAND_FEATURE_PURPOSE} ${WAYLAND_FEATURE_DESCRIPTION})
|
||||
find_feature(DirectFB ${DIRECTFB_FEATURE_TYPE} ${DIRECTFB_FEATURE_PURPOSE} ${DIRECTFB_FEATURE_DESCRIPTION})
|
||||
@ -677,8 +721,8 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DI
|
||||
|
||||
# RPATH configuration
|
||||
if(CMAKE_SKIP_RPATH)
|
||||
set(CMAKE_SKIP_RPATH FALSE)
|
||||
set(CMAKE_SKIP_INSTALL_RPATH TRUE)
|
||||
set(CMAKE_SKIP_RPATH FALSE)
|
||||
set(CMAKE_SKIP_INSTALL_RPATH TRUE)
|
||||
endif()
|
||||
set(CMAKE_SKIP_BUILD_RPATH FALSE)
|
||||
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
|
||||
@ -761,42 +805,22 @@ if(WITH_CHANNELS)
|
||||
add_subdirectory(channels)
|
||||
endif()
|
||||
|
||||
if(WITH_CLIENT)
|
||||
add_subdirectory(client)
|
||||
if (${CMAKE_VERSION} VERSION_LESS 2.8.12)
|
||||
set(PUBLIC_KEYWORD "")
|
||||
set(PRIVATE_KEYWORD "")
|
||||
else()
|
||||
set(PUBLIC_KEYWORD "PUBLIC")
|
||||
set(PRIVATE_KEYWORD "PRIVATE")
|
||||
endif()
|
||||
|
||||
if(WITH_CLIENT_COMMON OR WITH_CLIENT)
|
||||
add_subdirectory(client)
|
||||
endif()
|
||||
|
||||
if(WITH_SERVER)
|
||||
add_subdirectory(server)
|
||||
endif()
|
||||
|
||||
|
||||
# Exporting
|
||||
|
||||
if(${CMAKE_VERSION} VERSION_GREATER "2.8.10")
|
||||
|
||||
export(PACKAGE freerdp)
|
||||
|
||||
set(FREERDP_CMAKE_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}/cmake/FreeRDP")
|
||||
|
||||
set(FREERDP_INCLUDE_DIR "include")
|
||||
|
||||
# keep for legacy builds
|
||||
set(FREERDP_MONOLITHIC_BUILD OFF)
|
||||
|
||||
configure_package_config_file(FreeRDPConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/FreeRDPConfig.cmake
|
||||
INSTALL_DESTINATION ${FREERDP_CMAKE_INSTALL_DIR}
|
||||
PATH_VARS FREERDP_INCLUDE_DIR FREERDP_MONOLITHIC_BUILD)
|
||||
|
||||
write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/FreeRDPConfigVersion.cmake
|
||||
VERSION ${FREERDP_VERSION} COMPATIBILITY SameMajorVersion)
|
||||
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/FreeRDPConfig.cmake ${CMAKE_CURRENT_BINARY_DIR}/FreeRDPConfigVersion.cmake
|
||||
DESTINATION ${FREERDP_CMAKE_INSTALL_DIR})
|
||||
|
||||
install(EXPORT FreeRDPTargets DESTINATION ${FREERDP_CMAKE_INSTALL_DIR})
|
||||
|
||||
endif()
|
||||
|
||||
# Packaging
|
||||
|
||||
set(CMAKE_CPACK_INCLUDE_FILE "CMakeCPack.cmake")
|
||||
@ -813,13 +837,12 @@ endif()
|
||||
|
||||
include(${CMAKE_CPACK_INCLUDE_FILE})
|
||||
|
||||
set(FREERDP_PC_LIBS "-lfreerdp -lfreerdp-client")
|
||||
set(WINPR_PC_LIBS "-lwinpr")
|
||||
if (WITH_SERVER)
|
||||
set(FREERDP_PC_LIBS "${FREERDP_PC_LIBS} -lfreerdp-server")
|
||||
endif()
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/freerdp.pc.in ${CMAKE_CURRENT_BINARY_DIR}/freerdp.pc @ONLY)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/winpr.pc.in ${CMAKE_CURRENT_BINARY_DIR}/winpr.pc @ONLY)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/freerdp.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/winpr.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
|
||||
set(FREERDP_BUILD_CONFIG_LIST "")
|
||||
GET_CMAKE_PROPERTY(res VARIABLES)
|
||||
FOREACH(var ${res})
|
||||
IF (var MATCHES "^WITH_*|^BUILD_TESTING|^STATIC_CHANNELS|^HAVE_*")
|
||||
LIST(APPEND FREERDP_BUILD_CONFIG_LIST "${var}=${${var}}")
|
||||
ENDIF()
|
||||
ENDFOREACH()
|
||||
string(REPLACE ";" " " FREERDP_BUILD_CONFIG "${FREERDP_BUILD_CONFIG_LIST}")
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/buildflags.h.in ${CMAKE_CURRENT_BINARY_DIR}/buildflags.h)
|
||||
|
11
buildflags.h.in
Normal file
11
buildflags.h.in
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef _FREERDP_BUILD_FLAGS_H
|
||||
#define _FREERDP_BUILD_FLAGS_H
|
||||
|
||||
#define CFLAGS "${CMAKE_C_FLAGS}"
|
||||
#define COMPILER_ID "${CMAKE_C_COMPILER_ID}"
|
||||
#define COMPILER_VERSION "${CMAKE_C_COMPILER_VERSION}"
|
||||
#define TARGET_ARCH "${TARGET_ARCH}"
|
||||
#define BUILD_CONFIG "${FREERDP_BUILD_CONFIG}"
|
||||
#define BUILD_TYPE "${CMAKE_BUILD_TYPE}"
|
||||
|
||||
#endif /*_FREERDP_BUILD_FLAGS_H */
|
@ -154,11 +154,19 @@ macro(add_channel_client_subsystem _channel_prefix _channel_name _subsystem _typ
|
||||
endif()
|
||||
endmacro(add_channel_client_subsystem)
|
||||
|
||||
macro(add_channel_client_library _module_prefix _module_name _channel_name _dynamic _entry)
|
||||
if(${_dynamic} AND MSVC AND (NOT STATIC_CHANNELS))
|
||||
set(${_module_prefix}_SRCS ${${_module_prefix}_SRCS} module.def)
|
||||
endif()
|
||||
macro(channel_install _targets _destination _export_target)
|
||||
install(TARGETS ${_targets} DESTINATION ${_destination} EXPORT ${_export_target})
|
||||
endmacro(channel_install)
|
||||
|
||||
macro(server_channel_install _targets _destination)
|
||||
channel_install(${_targets} ${_destination} "FreeRDP-ServerTargets")
|
||||
endmacro(server_channel_install)
|
||||
|
||||
macro(client_channel_install _targets _destination)
|
||||
channel_install(${_targets} ${_destination} "FreeRDP-ClientTargets")
|
||||
endmacro(client_channel_install)
|
||||
|
||||
macro(add_channel_client_library _module_prefix _module_name _channel_name _dynamic _entry)
|
||||
if(${_dynamic} AND (NOT STATIC_CHANNELS))
|
||||
# On windows create dll version information.
|
||||
# Vendor, product and year are already set in top level CMakeLists.txt
|
||||
@ -178,19 +186,20 @@ macro(add_channel_client_library _module_prefix _module_name _channel_name _dyna
|
||||
endif()
|
||||
|
||||
add_library(${_module_name} ${${_module_prefix}_SRCS})
|
||||
client_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
||||
else()
|
||||
set(${_module_prefix}_STATIC ON PARENT_SCOPE)
|
||||
set(${_module_prefix}_NAME ${_module_name} PARENT_SCOPE)
|
||||
set(${_module_prefix}_CHANNEL ${_channel_name} PARENT_SCOPE)
|
||||
set(${_module_prefix}_ENTRY ${_entry} PARENT_SCOPE)
|
||||
add_library(${_module_name} STATIC ${${_module_prefix}_SRCS})
|
||||
if (${CMAKE_VERSION} VERSION_LESS 2.8.12)
|
||||
client_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
||||
endif()
|
||||
endif()
|
||||
endmacro(add_channel_client_library)
|
||||
|
||||
macro(add_channel_client_subsystem_library _module_prefix _module_name _channel_name _type _dynamic _entry)
|
||||
if(${_dynamic} AND MSVC AND (NOT STATIC_CHANNELS))
|
||||
set(${_module_prefix}_SRCS ${${_module_prefix}_SRCS} module.def)
|
||||
endif()
|
||||
if(${_dynamic} AND (NOT STATIC_CHANNELS))
|
||||
# On windows create dll version information.
|
||||
# Vendor, product and year are already set in top level CMakeLists.txt
|
||||
@ -210,18 +219,19 @@ macro(add_channel_client_subsystem_library _module_prefix _module_name _channel_
|
||||
endif()
|
||||
|
||||
add_library(${_module_name} ${${_module_prefix}_SRCS})
|
||||
client_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
||||
else()
|
||||
set(${_module_prefix}_STATIC ON PARENT_SCOPE)
|
||||
set(${_module_prefix}_NAME ${_module_name} PARENT_SCOPE)
|
||||
set(${_module_prefix}_TYPE ${_type} PARENT_SCOPE)
|
||||
add_library(${_module_name} STATIC ${${_module_prefix}_SRCS})
|
||||
if (${CMAKE_VERSION} VERSION_LESS 2.8.12)
|
||||
client_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
||||
endif()
|
||||
endif()
|
||||
endmacro(add_channel_client_subsystem_library)
|
||||
|
||||
macro(add_channel_server_library _module_prefix _module_name _channel_name _dynamic _entry)
|
||||
if(${_dynamic} AND MSVC AND (NOT STATIC_CHANNELS))
|
||||
set(${_module_prefix}_SRCS ${${_module_prefix}_SRCS} module.def)
|
||||
endif()
|
||||
if(${_dynamic} AND (NOT STATIC_CHANNELS))
|
||||
# On windows create dll version information.
|
||||
# Vendor, product and year are already set in top level CMakeLists.txt
|
||||
@ -240,12 +250,16 @@ macro(add_channel_server_library _module_prefix _module_name _channel_name _dyna
|
||||
endif()
|
||||
|
||||
add_library(${_module_name} ${${_module_prefix}_SRCS})
|
||||
server_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
||||
else()
|
||||
set(${_module_prefix}_STATIC ON PARENT_SCOPE)
|
||||
set(${_module_prefix}_NAME ${_module_name} PARENT_SCOPE)
|
||||
set(${_module_prefix}_CHANNEL ${_channel_name} PARENT_SCOPE)
|
||||
set(${_module_prefix}_ENTRY ${_entry} PARENT_SCOPE)
|
||||
add_library(${_module_name} STATIC ${${_module_prefix}_SRCS})
|
||||
if (${CMAKE_VERSION} VERSION_LESS 2.8.12)
|
||||
server_channel_install(${_module_name} ${FREERDP_ADDIN_PATH})
|
||||
endif()
|
||||
endif()
|
||||
endmacro(add_channel_server_library)
|
||||
|
||||
|
@ -25,11 +25,11 @@ include_directories(..)
|
||||
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DVCPluginEntry")
|
||||
|
||||
|
||||
|
||||
target_link_libraries(${MODULE_NAME} freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT STATIC_CHANNELS AND BUILD_SHARED_LIBS)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
||||
@ -52,3 +52,7 @@ endif()
|
||||
if(WITH_WINMM)
|
||||
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "winmm" "")
|
||||
endif()
|
||||
|
||||
if(WITH_MACAUDIO)
|
||||
add_channel_client_subsystem(${MODULE_PREFIX} ${CHANNEL_NAME} "mac" "")
|
||||
endif()
|
||||
|
@ -30,6 +30,3 @@ add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_N
|
||||
set(${MODULE_PREFIX}_LIBS freerdp ${ALSA_LIBRARIES})
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
* Copyright 2015 Armin Novak <armin.novak@thincast.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -195,7 +196,7 @@ static UINT audin_process_formats(IWTSVirtualChannelCallback* pChannelCallback,
|
||||
Stream_Read_UINT16(s, format.cbSize);
|
||||
format.data = Stream_Pointer(s);
|
||||
Stream_Seek(s, format.cbSize);
|
||||
|
||||
|
||||
DEBUG_DVC("wFormatTag=%d nChannels=%d nSamplesPerSec=%d "
|
||||
"nBlockAlign=%d wBitsPerSample=%d cbSize=%d",
|
||||
format.wFormatTag, format.nChannels, format.nSamplesPerSec,
|
||||
@ -310,7 +311,7 @@ static UINT audin_send_open_reply_pdu(IWTSVirtualChannelCallback* pChannelCallba
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_receive_wave_data(BYTE* data, int size, void* user_data)
|
||||
static UINT audin_receive_wave_data(const BYTE* data, int size, void* user_data)
|
||||
{
|
||||
UINT error;
|
||||
wStream* out;
|
||||
@ -521,7 +522,7 @@ static UINT audin_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
||||
IWTSVirtualChannel* pChannel, BYTE* Data, int* pbAccept,
|
||||
IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept,
|
||||
IWTSVirtualChannelCallback** ppCallback)
|
||||
{
|
||||
AUDIN_CHANNEL_CALLBACK* callback;
|
||||
@ -667,7 +668,7 @@ static UINT audin_load_device_plugin(IWTSPlugin* pPlugin, const char* name, ADDI
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT audin_set_subsystem(AUDIN_PLUGIN* audin, char* subsystem)
|
||||
static UINT audin_set_subsystem(AUDIN_PLUGIN* audin, char* subsystem)
|
||||
{
|
||||
free(audin->subsystem);
|
||||
audin->subsystem = _strdup(subsystem);
|
||||
@ -684,7 +685,7 @@ UINT audin_set_subsystem(AUDIN_PLUGIN* audin, char* subsystem)
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
UINT audin_set_device_name(AUDIN_PLUGIN* audin, char* device_name)
|
||||
static UINT audin_set_device_name(AUDIN_PLUGIN* audin, char* device_name)
|
||||
{
|
||||
free(audin->device_name);
|
||||
audin->device_name = _strdup(device_name);
|
||||
@ -696,7 +697,7 @@ UINT audin_set_device_name(AUDIN_PLUGIN* audin, char* device_name)
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
COMMAND_LINE_ARGUMENT_A audin_args[] =
|
||||
static COMMAND_LINE_ARGUMENT_A audin_args[] =
|
||||
{
|
||||
{ "sys", COMMAND_LINE_VALUE_REQUIRED, "<subsystem>", NULL, NULL, -1, NULL, "subsystem" },
|
||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "device" },
|
||||
@ -779,9 +780,38 @@ static BOOL audin_process_addin_args(IWTSPlugin* pPlugin, ADDIN_ARGV* args)
|
||||
*/
|
||||
UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||
{
|
||||
struct SubsystemEntry
|
||||
{
|
||||
char *subsystem;
|
||||
char *device;
|
||||
};
|
||||
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
ADDIN_ARGV* args;
|
||||
AUDIN_PLUGIN* audin;
|
||||
struct SubsystemEntry entries[] =
|
||||
{
|
||||
#if defined(WITH_PULSE)
|
||||
{"pulse", ""},
|
||||
#endif
|
||||
#if defined(WITH_OSS)
|
||||
{"oss", "default"},
|
||||
#endif
|
||||
#if defined(WITH_ALSA)
|
||||
{"alsa", "default"},
|
||||
#endif
|
||||
#if defined(WITH_OPENSLES)
|
||||
{"opensles", "default"},
|
||||
#endif
|
||||
#if defined(WITH_WINMM)
|
||||
{"winmm", "default"},
|
||||
#endif
|
||||
#if defined(WITH_MACAUDIO)
|
||||
{"mac", "default"},
|
||||
#endif
|
||||
{NULL,NULL}
|
||||
};
|
||||
struct SubsystemEntry *entry = &entries[0];
|
||||
|
||||
assert(pEntryPoints);
|
||||
assert(pEntryPoints->GetPlugin);
|
||||
@ -807,123 +837,26 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
|
||||
args = pEntryPoints->GetPluginData(pEntryPoints);
|
||||
audin->rdpcontext = ((freerdp*)((rdpSettings*) pEntryPoints->GetRdpSettings(pEntryPoints))->instance)->context;
|
||||
|
||||
if (error == CHANNEL_RC_OK)
|
||||
audin_process_addin_args((IWTSPlugin*) audin, args);
|
||||
else
|
||||
while (entry && entry->subsystem && !audin->device)
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterPlugin failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
if ((error = audin_set_subsystem(audin, entry->subsystem)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_set_subsystem for %s failed with error %lu!",
|
||||
entry->subsystem, error);
|
||||
}
|
||||
else if ((error = audin_set_device_name(audin, entry->device)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_set_device_name for %s failed with error %lu!",
|
||||
entry->subsystem, error);
|
||||
}
|
||||
else if ((error = audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_load_device_plugin %s failed with error %lu!",
|
||||
entry->subsystem, error);
|
||||
}
|
||||
|
||||
if (audin->subsystem && (error = audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args))) {
|
||||
WLog_ERR(TAG, "audin_load_device_plugin failed!");
|
||||
return error;
|
||||
}
|
||||
|
||||
#if defined(WITH_PULSE)
|
||||
if (!audin->device)
|
||||
{
|
||||
if ((error = audin_set_subsystem(audin, "pulse")))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_set_subsystem for pulse failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
if ((error = audin_set_device_name(audin, "")))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_set_device_name for pulse failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
if ((error = audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_load_device_plugin for pulse failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
entry++;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(WITH_OSS)
|
||||
if (!audin->device)
|
||||
{
|
||||
if ((error = audin_set_subsystem(audin, "oss")))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_set_subsystem for oss failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
if ((error = audin_set_device_name(audin, "default")))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_set_device_name for oss failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
if ((error = audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_load_device_plugin oss pulse failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(WITH_ALSA)
|
||||
if (!audin->device)
|
||||
{
|
||||
if ((error = audin_set_subsystem(audin, "alsa")))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_set_subsystem for alsa failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
if ((error = audin_set_device_name(audin, "default")))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_set_device_name for alsa failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
if ((error = audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_load_device_plugin oss alsa failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(WITH_OPENSLES)
|
||||
if (!audin->device)
|
||||
{
|
||||
if ((error = audin_set_subsystem(audin, "opensles")))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_set_subsystem for opensles failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
if ((error = audin_set_device_name(audin, "default")))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_set_device_name for opensles failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
if ((error = audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_load_device_plugin oss opensles failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(WITH_WINMM)
|
||||
if (!audin->device)
|
||||
{
|
||||
if ((error = audin_set_subsystem(audin, "winmm")))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_set_subsystem for winmm failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
if ((error = audin_set_device_name(audin, "default")))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_set_device_name for winmm failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
if ((error = audin_load_device_plugin((IWTSPlugin*) audin, audin->subsystem, args)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_load_device_plugin oss winmm failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (audin->device == NULL)
|
||||
{
|
||||
|
@ -1,7 +1,8 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# Android Client
|
||||
# FreeRDP cmake build script
|
||||
#
|
||||
# Copyright 2013 Armin Novak <armin.novak@thincast.com>
|
||||
# Copyright (c) 2015 Armin Novak <armin.novak@thincast.com>
|
||||
# Copyright (c) 2015 Thincast Technologies GmbH
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
@ -15,8 +16,16 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/Application.mk.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/Application.mk @ONLY)
|
||||
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/Android.mk.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/Android.mk @ONLY)
|
||||
define_channel_client_subsystem("audin" "mac" "")
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
audin_mac.c)
|
||||
|
||||
include_directories(..)
|
||||
include_directories(${MAC_INCLUDE_DIRS})
|
||||
|
||||
add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} "" TRUE "")
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS freerdp ${MAC_LIBRARIES})
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
461
channels/audin/client/mac/audin_mac.c
Normal file
461
channels/audin/client/mac/audin_mac.c
Normal file
@ -0,0 +1,461 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Audio Input Redirection Virtual Channel - Mac OS X implementation
|
||||
*
|
||||
* Copyright (c) 2015 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/string.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/debug.h>
|
||||
#include <winpr/cmdline.h>
|
||||
|
||||
#include <CoreAudio/CoreAudioTypes.h>
|
||||
#include <CoreAudio/CoreAudio.h>
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
#include <AudioToolbox/AudioQueue.h>
|
||||
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/codec/dsp.h>
|
||||
#include <freerdp/channels/rdpsnd.h>
|
||||
|
||||
#include "audin_main.h"
|
||||
|
||||
#define MAC_AUDIO_QUEUE_NUM_BUFFERS 100
|
||||
#define MAC_AUDIO_QUEUE_BUFFER_SIZE 32768
|
||||
|
||||
typedef struct _AudinMacDevice
|
||||
{
|
||||
IAudinDevice iface;
|
||||
|
||||
FREERDP_DSP_CONTEXT* dsp_context;
|
||||
|
||||
audinFormat format;
|
||||
UINT32 FramesPerPacket;
|
||||
int dev_unit;
|
||||
|
||||
AudinReceive receive;
|
||||
void* user_data;
|
||||
|
||||
rdpContext* rdpcontext;
|
||||
|
||||
bool isOpen;
|
||||
AudioQueueRef audioQueue;
|
||||
AudioStreamBasicDescription audioFormat;
|
||||
AudioQueueBufferRef audioBuffers[MAC_AUDIO_QUEUE_NUM_BUFFERS];
|
||||
} AudinMacDevice;
|
||||
|
||||
static AudioFormatID audin_mac_get_format(const audinFormat* format)
|
||||
{
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
return kAudioFormatLinearPCM;
|
||||
/*
|
||||
case WAVE_FORMAT_GSM610:
|
||||
return kAudioFormatMicrosoftGSM;
|
||||
case WAVE_FORMAT_ALAW:
|
||||
return kAudioFormatALaw;
|
||||
case WAVE_FORMAT_MULAW:
|
||||
return kAudioFormatULaw;
|
||||
case WAVE_FORMAT_AAC_MS:
|
||||
return kAudioFormatMPEG4AAC;
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
return kAudioFormatLinearPCM;
|
||||
*/
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static AudioFormatFlags audin_mac_get_flags_for_format(const audinFormat* format)
|
||||
{
|
||||
switch(format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_PCM:
|
||||
return kAudioFormatFlagIsSignedInteger;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL audin_mac_format_supported(IAudinDevice* device, audinFormat* format)
|
||||
{
|
||||
AudioFormatID req_fmt = 0;
|
||||
|
||||
if (device == NULL || format == NULL)
|
||||
return FALSE;
|
||||
|
||||
req_fmt = audin_mac_get_format(format);
|
||||
|
||||
if (req_fmt == 0)
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function description
|
||||
*
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT audin_mac_set_format(IAudinDevice* device, audinFormat* format, UINT32 FramesPerPacket)
|
||||
{
|
||||
AudinMacDevice* mac = (AudinMacDevice*)device;
|
||||
|
||||
if (device == NULL || format == NULL)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
mac->FramesPerPacket = FramesPerPacket;
|
||||
CopyMemory(&(mac->format), format, sizeof(audinFormat));
|
||||
|
||||
WLog_INFO(TAG, "Audio Format %s [channels=%d, samples=%d, bits=%d]",
|
||||
rdpsnd_get_audio_tag_string(format->wFormatTag),
|
||||
format->nChannels, format->nSamplesPerSec, format->wBitsPerSample);
|
||||
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
mac->FramesPerPacket *= 4; /* Compression ratio. */
|
||||
mac->format.wBitsPerSample *= 4;
|
||||
break;
|
||||
}
|
||||
|
||||
mac->audioFormat.mBitsPerChannel = mac->format.wBitsPerSample;
|
||||
mac->audioFormat.mChannelsPerFrame = mac->format.nChannels;
|
||||
mac->audioFormat.mFormatFlags = audin_mac_get_flags_for_format(format);
|
||||
mac->audioFormat.mFormatID = audin_mac_get_format(format);
|
||||
mac->audioFormat.mFramesPerPacket = 1;
|
||||
mac->audioFormat.mSampleRate = mac->format.nSamplesPerSec;
|
||||
mac->audioFormat.mBytesPerFrame =
|
||||
mac->audioFormat.mChannelsPerFrame * mac->audioFormat.mBitsPerChannel / 8;
|
||||
mac->audioFormat.mBytesPerPacket =
|
||||
mac->audioFormat.mBytesPerFrame * mac->audioFormat.mFramesPerPacket;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static void mac_audio_queue_input_cb(void *aqData,
|
||||
AudioQueueRef inAQ,
|
||||
AudioQueueBufferRef inBuffer,
|
||||
const AudioTimeStamp *inStartTime,
|
||||
UInt32 inNumPackets,
|
||||
const AudioStreamPacketDescription *inPacketDesc)
|
||||
{
|
||||
AudinMacDevice* mac = (AudinMacDevice*)aqData;
|
||||
UINT error;
|
||||
int encoded_size;
|
||||
const BYTE *encoded_data;
|
||||
BYTE *buffer = inBuffer->mAudioData;
|
||||
int buffer_size = inBuffer->mAudioDataByteSize;
|
||||
|
||||
(void)inAQ;
|
||||
(void)inStartTime;
|
||||
(void)inNumPackets;
|
||||
(void)inPacketDesc;
|
||||
|
||||
|
||||
/* Process. */
|
||||
switch (mac->format.wFormatTag) {
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
if (!mac->dsp_context->encode_ms_adpcm(mac->dsp_context,
|
||||
buffer, buffer_size, mac->format.nChannels, mac->format.nBlockAlign))
|
||||
{
|
||||
SetLastError(ERROR_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
encoded_data = mac->dsp_context->adpcm_buffer;
|
||||
encoded_size = mac->dsp_context->adpcm_size;
|
||||
break;
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
if (!mac->dsp_context->encode_ima_adpcm(mac->dsp_context,
|
||||
buffer, buffer_size, mac->format.nChannels, mac->format.nBlockAlign))
|
||||
{
|
||||
SetLastError(ERROR_INTERNAL_ERROR);
|
||||
break;
|
||||
}
|
||||
encoded_data = mac->dsp_context->adpcm_buffer;
|
||||
encoded_size = mac->dsp_context->adpcm_size;
|
||||
break;
|
||||
default:
|
||||
encoded_data = buffer;
|
||||
encoded_size = buffer_size;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((error = mac->receive(encoded_data, encoded_size, mac->user_data)))
|
||||
{
|
||||
WLog_ERR(TAG, "mac->receive failed with error %lu", error);
|
||||
SetLastError(ERROR_INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static UINT audin_mac_close(IAudinDevice *device)
|
||||
{
|
||||
UINT errCode = CHANNEL_RC_OK;
|
||||
char errString[1024];
|
||||
OSStatus devStat;
|
||||
AudinMacDevice *mac = (AudinMacDevice*)device;
|
||||
|
||||
if (device == NULL)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (mac->isOpen)
|
||||
{
|
||||
devStat = AudioQueueStop(mac->audioQueue, true);
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueStop failed with %s [%d]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
}
|
||||
mac->isOpen = false;
|
||||
}
|
||||
|
||||
if (mac->audioQueue)
|
||||
{
|
||||
devStat = AudioQueueDispose(mac->audioQueue, true);
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueDispose failed with %s [%d]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
}
|
||||
|
||||
mac->audioQueue = NULL;
|
||||
}
|
||||
|
||||
mac->receive = NULL;
|
||||
mac->user_data = NULL;
|
||||
|
||||
return errCode;
|
||||
}
|
||||
|
||||
static UINT audin_mac_open(IAudinDevice *device, AudinReceive receive, void *user_data) {
|
||||
AudinMacDevice *mac = (AudinMacDevice*)device;
|
||||
DWORD errCode;
|
||||
char errString[1024];
|
||||
OSStatus devStat;
|
||||
size_t index;
|
||||
|
||||
mac->receive = receive;
|
||||
mac->user_data = user_data;
|
||||
|
||||
devStat = AudioQueueNewInput(&(mac->audioFormat), mac_audio_queue_input_cb,
|
||||
mac, NULL, kCFRunLoopCommonModes, 0, &(mac->audioQueue));
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueNewInput failed with %s [%d]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
for (index = 0; index < MAC_AUDIO_QUEUE_NUM_BUFFERS; index++)
|
||||
{
|
||||
devStat = AudioQueueAllocateBuffer(mac->audioQueue, MAC_AUDIO_QUEUE_BUFFER_SIZE,
|
||||
&mac->audioBuffers[index]);
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueAllocateBuffer failed with %s [%d]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
devStat = AudioQueueEnqueueBuffer(mac->audioQueue,
|
||||
mac->audioBuffers[index],
|
||||
0,
|
||||
NULL);
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueEnqueueBuffer failed with %s [%d]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
|
||||
freerdp_dsp_context_reset_adpcm(mac->dsp_context);
|
||||
|
||||
devStat = AudioQueueStart(mac->audioQueue, NULL);
|
||||
if (devStat != 0)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "AudioQueueStart failed with %s [%d]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
goto err_out;
|
||||
}
|
||||
mac->isOpen = true;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
err_out:
|
||||
audin_mac_close(device);
|
||||
return CHANNEL_RC_INITIALIZATION_ERROR;
|
||||
}
|
||||
|
||||
static UINT audin_mac_free(IAudinDevice* device)
|
||||
{
|
||||
AudinMacDevice *mac = (AudinMacDevice*)device;
|
||||
|
||||
int error;
|
||||
|
||||
if (device == NULL)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if ((error = audin_mac_close(device)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_oss_close failed with error code %d!", error);
|
||||
}
|
||||
freerdp_dsp_context_free(mac->dsp_context);
|
||||
free(mac);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
static COMMAND_LINE_ARGUMENT_A audin_mac_args[] =
|
||||
{
|
||||
{ "dev", COMMAND_LINE_VALUE_REQUIRED, "<device>", NULL, NULL, -1, NULL, "audio device name" },
|
||||
{ NULL, 0, NULL, NULL, NULL, -1, NULL, NULL }
|
||||
};
|
||||
|
||||
static UINT audin_mac_parse_addin_args(AudinMacDevice *device, ADDIN_ARGV *args)
|
||||
{
|
||||
DWORD errCode;
|
||||
char errString[1024];
|
||||
int status;
|
||||
char* str_num, *eptr;
|
||||
DWORD flags;
|
||||
COMMAND_LINE_ARGUMENT_A* arg;
|
||||
AudinMacDevice* mac = (AudinMacDevice*)device;
|
||||
|
||||
if (args->argc == 1)
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
flags = COMMAND_LINE_SIGIL_NONE | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_IGN_UNKNOWN_KEYWORD;
|
||||
status = CommandLineParseArgumentsA(args->argc, (const char**)args->argv, audin_mac_args, flags, mac, NULL, NULL);
|
||||
|
||||
if (status < 0)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
arg = audin_mac_args;
|
||||
|
||||
do
|
||||
{
|
||||
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
|
||||
continue;
|
||||
|
||||
CommandLineSwitchStart(arg)
|
||||
CommandLineSwitchCase(arg, "dev")
|
||||
{
|
||||
str_num = _strdup(arg->Value);
|
||||
if (!str_num)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "_strdup failed with %s [%d]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
mac->dev_unit = strtol(str_num, &eptr, 10);
|
||||
|
||||
if (mac->dev_unit < 0 || *eptr != '\0')
|
||||
mac->dev_unit = -1;
|
||||
|
||||
free(str_num);
|
||||
}
|
||||
CommandLineSwitchEnd(arg)
|
||||
}
|
||||
while ((arg = CommandLineFindNextArgumentA(arg)) != NULL);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
#ifdef STATIC_CHANNELS
|
||||
#define freerdp_audin_client_subsystem_entry mac_freerdp_audin_client_subsystem_entry
|
||||
#endif
|
||||
|
||||
UINT freerdp_audin_client_subsystem_entry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
|
||||
{
|
||||
DWORD errCode;
|
||||
char errString[1024];
|
||||
ADDIN_ARGV *args;
|
||||
AudinMacDevice *mac;
|
||||
UINT error;
|
||||
|
||||
mac = (AudinMacDevice*)calloc(1, sizeof(AudinMacDevice));
|
||||
if (!mac)
|
||||
{
|
||||
errCode = GetLastError();
|
||||
WLog_ERR(TAG, "calloc failed with %s [%d]",
|
||||
winpr_strerror(errCode, errString, sizeof(errString)), errCode);
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
mac->iface.Open = audin_mac_open;
|
||||
mac->iface.FormatSupported = audin_mac_format_supported;
|
||||
mac->iface.SetFormat = audin_mac_set_format;
|
||||
mac->iface.Close = audin_mac_close;
|
||||
mac->iface.Free = audin_mac_free;
|
||||
mac->rdpcontext = pEntryPoints->rdpcontext;
|
||||
|
||||
mac->dev_unit = -1;
|
||||
args = pEntryPoints->args;
|
||||
|
||||
if ((error = audin_mac_parse_addin_args(mac, args)))
|
||||
{
|
||||
WLog_ERR(TAG, "audin_mac_parse_addin_args failed with %lu!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
mac->dsp_context = freerdp_dsp_context_new();
|
||||
if (!mac->dsp_context)
|
||||
{
|
||||
WLog_ERR(TAG, "freerdp_dsp_context_new failed!");
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
if ((error = pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) mac)))
|
||||
{
|
||||
WLog_ERR(TAG, "RegisterAudinDevice failed with error %lu!", error);
|
||||
goto error_out;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
|
||||
error_out:
|
||||
freerdp_dsp_context_free(mac->dsp_context);
|
||||
free(mac);
|
||||
return error;
|
||||
|
||||
}
|
@ -31,5 +31,3 @@ add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_N
|
||||
set(${MODULE_PREFIX}_LIBS freerdp ${OPENSLES_LIBRARIES})
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
@ -31,5 +31,3 @@ set(${MODULE_PREFIX}_LIBS freerdp ${OSS_LIBRARIES})
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
|
@ -30,5 +30,3 @@ add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_N
|
||||
set(${MODULE_PREFIX}_LIBS freerdp ${PULSE_LIBRARY})
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
@ -31,6 +31,8 @@ set(${MODULE_PREFIX}_LIBS freerdp winmm.lib)
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT STATIC_CHANNELS AND BUILD_SHARED_LIBS)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client/winmm")
|
||||
|
@ -26,6 +26,4 @@ add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE
|
||||
|
||||
target_link_libraries(${MODULE_NAME} freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <winpr/collections.h>
|
||||
|
||||
#include <freerdp/addin.h>
|
||||
#include <freerdp/build-config.h>
|
||||
#include <freerdp/client/channels.h>
|
||||
|
||||
#include "tables.h"
|
||||
@ -181,22 +182,22 @@ FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPSTR pszName, LPSTR pszSub
|
||||
|
||||
if (pszName && pszSubsystem && pszType)
|
||||
{
|
||||
sprintf_s(pszPattern, cchPattern, CMAKE_SHARED_LIBRARY_PREFIX"%s-client-%s-%s.%s",
|
||||
sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX"%s-client-%s-%s.%s",
|
||||
pszName, pszSubsystem, pszType, pszExtension);
|
||||
}
|
||||
else if (pszName && pszType)
|
||||
{
|
||||
sprintf_s(pszPattern, cchPattern, CMAKE_SHARED_LIBRARY_PREFIX"%s-client-?-%s.%s",
|
||||
sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX"%s-client-?-%s.%s",
|
||||
pszName, pszType, pszExtension);
|
||||
}
|
||||
else if (pszName)
|
||||
{
|
||||
sprintf_s(pszPattern, cchPattern, CMAKE_SHARED_LIBRARY_PREFIX"%s-client*.%s",
|
||||
sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX"%s-client*.%s",
|
||||
pszName, pszExtension);
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf_s(pszPattern, cchPattern, CMAKE_SHARED_LIBRARY_PREFIX"?-client*.%s",
|
||||
sprintf_s(pszPattern, cchPattern, FREERDP_SHARED_LIBRARY_PREFIX"?-client*.%s",
|
||||
pszExtension);
|
||||
}
|
||||
|
||||
|
@ -25,10 +25,6 @@ set(${MODULE_PREFIX}_SRCS
|
||||
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntry")
|
||||
|
||||
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS freerdp winpr)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
@ -46,7 +46,7 @@ const char* const CB_MSG_TYPE_STRINGS[] =
|
||||
"CB_CLIP_CAPS",
|
||||
"CB_FILECONTENTS_REQUEST",
|
||||
"CB_FILECONTENTS_RESPONSE",
|
||||
"CB_LOCK_CLIPDATA"
|
||||
"CB_LOCK_CLIPDATA",
|
||||
"CB_UNLOCK_CLIPDATA"
|
||||
};
|
||||
|
||||
@ -471,9 +471,9 @@ static UINT cliprdr_order_recv(cliprdrPlugin* cliprdr, wStream* s)
|
||||
Stream_Read_UINT16(s, msgFlags); /* msgFlags (2 bytes) */
|
||||
Stream_Read_UINT32(s, dataLen); /* dataLen (4 bytes) */
|
||||
|
||||
DEBUG_CLIPRDR("msgType: %s (%d), msgFlags: %d dataLen: %d",
|
||||
CB_MSG_TYPE_STRINGS[msgType], msgType, msgFlags, dataLen);
|
||||
#ifdef WITH_DEBUG_CLIPRDR
|
||||
WLog_DBG(TAG, "msgType: %s (%d), msgFlags: %d dataLen: %d",
|
||||
CB_MSG_TYPE_STRINGS[msgType], msgType, msgFlags, dataLen);
|
||||
winpr_HexDump(TAG, WLOG_DEBUG, Stream_Buffer(s), dataLen + 8);
|
||||
#endif
|
||||
|
||||
@ -910,7 +910,7 @@ UINT cliprdr_client_file_contents_response(CliprdrClientContext* context, CLIPRD
|
||||
if (fileContentsResponse->dwFlags & FILECONTENTS_SIZE)
|
||||
fileContentsResponse->cbRequested = sizeof(UINT64);
|
||||
|
||||
s = cliprdr_packet_new(CB_FILECONTENTS_REQUEST, 0,
|
||||
s = cliprdr_packet_new(CB_FILECONTENTS_RESPONSE, fileContentsResponse->msgFlags,
|
||||
4 + fileContentsResponse->cbRequested);
|
||||
|
||||
if (!s)
|
||||
|
@ -23,10 +23,6 @@ set(${MODULE_PREFIX}_SRCS
|
||||
|
||||
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntry")
|
||||
|
||||
|
||||
|
||||
target_link_libraries(${MODULE_NAME} freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")
|
||||
|
@ -193,7 +193,7 @@ static UINT cliprdr_server_format_list(CliprdrServerContext* context, CLIPRDR_FO
|
||||
CLIPRDR_FORMAT* format;
|
||||
CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*) context->handle;
|
||||
|
||||
if (!cliprdr->useLongFormatNames)
|
||||
if (!context->useLongFormatNames)
|
||||
{
|
||||
length = formatList->numFormats * 36;
|
||||
|
||||
@ -470,7 +470,7 @@ static UINT cliprdr_server_file_contents_response(CliprdrServerContext* context,
|
||||
if (fileContentsResponse->dwFlags & FILECONTENTS_SIZE)
|
||||
fileContentsResponse->cbRequested = sizeof(UINT64);
|
||||
|
||||
s = cliprdr_server_packet_new(CB_FILECONTENTS_REQUEST, 0,
|
||||
s = cliprdr_server_packet_new(CB_FILECONTENTS_RESPONSE, fileContentsResponse->msgFlags,
|
||||
4 + fileContentsResponse->cbRequested);
|
||||
|
||||
if (!s)
|
||||
@ -504,22 +504,21 @@ static UINT cliprdr_server_receive_general_capability(CliprdrServerContext* cont
|
||||
{
|
||||
UINT32 version;
|
||||
UINT32 generalFlags;
|
||||
CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*) context->handle;
|
||||
|
||||
Stream_Read_UINT32(s, version); /* version (4 bytes) */
|
||||
Stream_Read_UINT32(s, generalFlags); /* generalFlags (4 bytes) */
|
||||
|
||||
if (generalFlags & CB_USE_LONG_FORMAT_NAMES)
|
||||
cliprdr->useLongFormatNames = TRUE;
|
||||
if (context->useLongFormatNames)
|
||||
context->useLongFormatNames = (generalFlags & CB_USE_LONG_FORMAT_NAMES) ? TRUE : FALSE;
|
||||
|
||||
if (generalFlags & CB_STREAM_FILECLIP_ENABLED)
|
||||
cliprdr->streamFileClipEnabled = TRUE;
|
||||
if (context->streamFileClipEnabled)
|
||||
context->streamFileClipEnabled = (generalFlags & CB_STREAM_FILECLIP_ENABLED) ? TRUE : FALSE;
|
||||
|
||||
if (generalFlags & CB_FILECLIP_NO_FILE_PATHS)
|
||||
cliprdr->fileClipNoFilePaths = TRUE;
|
||||
if (context->fileClipNoFilePaths)
|
||||
context->fileClipNoFilePaths = (generalFlags & CB_FILECLIP_NO_FILE_PATHS) ? TRUE : FALSE;
|
||||
|
||||
if (generalFlags & CB_CAN_LOCK_CLIPDATA)
|
||||
cliprdr->canLockClipData = TRUE;
|
||||
if (context->canLockClipData)
|
||||
context->canLockClipData = (generalFlags & CB_CAN_LOCK_CLIPDATA) ? TRUE : FALSE;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
@ -634,7 +633,6 @@ static UINT cliprdr_server_receive_format_list(CliprdrServerContext* context, wS
|
||||
WCHAR* wszFormatName;
|
||||
CLIPRDR_FORMAT* formats = NULL;
|
||||
CLIPRDR_FORMAT_LIST formatList;
|
||||
CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*) context->handle;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
dataLen = header->dataLen;
|
||||
@ -654,7 +652,7 @@ static UINT cliprdr_server_receive_format_list(CliprdrServerContext* context, wS
|
||||
formatList.formats = NULL;
|
||||
formatList.numFormats = 0;
|
||||
}
|
||||
else if (!cliprdr->useLongFormatNames)
|
||||
else if (!context->useLongFormatNames)
|
||||
{
|
||||
formatList.numFormats = (dataLen / 36);
|
||||
|
||||
@ -957,7 +955,7 @@ static UINT cliprdr_server_receive_filecontents_request(CliprdrServerContext* co
|
||||
request.msgFlags = header->msgFlags;
|
||||
request.dataLen = header->dataLen;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 28)
|
||||
if (Stream_GetRemainingLength(s) < 24)
|
||||
{
|
||||
WLog_ERR(TAG, "not enought data in stream!");
|
||||
return ERROR_INVALID_DATA;
|
||||
@ -969,7 +967,10 @@ static UINT cliprdr_server_receive_filecontents_request(CliprdrServerContext* co
|
||||
Stream_Read_UINT32(s, request.nPositionLow); /* nPositionLow (4 bytes) */
|
||||
Stream_Read_UINT32(s, request.nPositionHigh); /* nPositionHigh (4 bytes) */
|
||||
Stream_Read_UINT32(s, request.cbRequested); /* cbRequested (4 bytes) */
|
||||
Stream_Read_UINT32(s, request.clipDataId); /* clipDataId (4 bytes) */
|
||||
if (Stream_GetRemainingLength(s) < 4) /* clipDataId (4 bytes) optional */
|
||||
request.clipDataId = 0;
|
||||
else
|
||||
Stream_Read_UINT32(s, request.clipDataId);
|
||||
|
||||
IFCALLRET(context->ClientFileContentsRequest, error, context, &request);
|
||||
if (error)
|
||||
@ -1005,9 +1006,9 @@ static UINT cliprdr_server_receive_filecontents_response(CliprdrServerContext* c
|
||||
response.cbRequested = header->dataLen - 4;
|
||||
response.requestedData = Stream_Pointer(s); /* requestedFileContentsData */
|
||||
|
||||
IFCALLRET(context->ServerFileContentsResponse, error, context, &response);
|
||||
IFCALLRET(context->ClientFileContentsResponse, error, context, &response);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "ServerFileContentsResponse failed with error %lu!", error);
|
||||
WLog_ERR(TAG, "ClientFileContentsResponse failed with error %lu!", error);
|
||||
|
||||
return error;
|
||||
}
|
||||
@ -1095,7 +1096,6 @@ static UINT cliprdr_server_init(CliprdrServerContext* context)
|
||||
CLIPRDR_CAPABILITIES capabilities;
|
||||
CLIPRDR_MONITOR_READY monitorReady;
|
||||
CLIPRDR_GENERAL_CAPABILITY_SET generalCapabilitySet;
|
||||
CliprdrServerPrivate* cliprdr = (CliprdrServerPrivate*) context->handle;
|
||||
UINT error;
|
||||
|
||||
ZeroMemory(&capabilities, sizeof(capabilities));
|
||||
@ -1103,9 +1103,18 @@ static UINT cliprdr_server_init(CliprdrServerContext* context)
|
||||
|
||||
generalFlags = 0;
|
||||
|
||||
if (cliprdr->useLongFormatNames)
|
||||
if (context->useLongFormatNames)
|
||||
generalFlags |= CB_USE_LONG_FORMAT_NAMES;
|
||||
|
||||
if (context->streamFileClipEnabled)
|
||||
generalFlags |= CB_STREAM_FILECLIP_ENABLED;
|
||||
|
||||
if (context->fileClipNoFilePaths)
|
||||
generalFlags |= CB_FILECLIP_NO_FILE_PATHS;
|
||||
|
||||
if (context->canLockClipData)
|
||||
generalFlags |= CB_CAN_LOCK_CLIPDATA;
|
||||
|
||||
capabilities.msgType = CB_CLIP_CAPS;
|
||||
capabilities.msgFlags = 0;
|
||||
capabilities.dataLen = 4 + CB_CAPSTYPE_GENERAL_LEN;
|
||||
@ -1409,12 +1418,6 @@ static UINT cliprdr_server_close(CliprdrServerContext* context)
|
||||
cliprdr->ChannelHandle = NULL;
|
||||
}
|
||||
|
||||
if (cliprdr->ChannelEvent)
|
||||
{
|
||||
CloseHandle(cliprdr->ChannelEvent);
|
||||
cliprdr->ChannelEvent = NULL;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
@ -1533,11 +1536,6 @@ CliprdrServerContext* cliprdr_server_context_new(HANDLE vcm)
|
||||
{
|
||||
cliprdr->vcm = vcm;
|
||||
|
||||
cliprdr->useLongFormatNames = TRUE;
|
||||
cliprdr->streamFileClipEnabled = TRUE;
|
||||
cliprdr->fileClipNoFilePaths = TRUE;
|
||||
cliprdr->canLockClipData = TRUE;
|
||||
|
||||
cliprdr->s = Stream_New(NULL, 4096);
|
||||
|
||||
if(!cliprdr->s)
|
||||
@ -1572,5 +1570,6 @@ void cliprdr_server_context_free(CliprdrServerContext* context)
|
||||
free(cliprdr->temporaryDirectory);
|
||||
}
|
||||
|
||||
free(context->handle);
|
||||
free(context);
|
||||
}
|
||||
|
@ -40,11 +40,6 @@ struct _cliprdr_server_private
|
||||
void* ChannelHandle;
|
||||
HANDLE ChannelEvent;
|
||||
|
||||
BOOL useLongFormatNames;
|
||||
BOOL streamFileClipEnabled;
|
||||
BOOL fileClipNoFilePaths;
|
||||
BOOL canLockClipData;
|
||||
|
||||
wStream* s;
|
||||
char* temporaryDirectory;
|
||||
};
|
||||
|
@ -31,6 +31,9 @@ set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr freerdp)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT STATIC_CHANNELS AND BUILD_SHARED_LIBS)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
@ -254,7 +254,7 @@ static UINT disp_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT disp_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
||||
IWTSVirtualChannel* pChannel, BYTE* Data, int* pbAccept,
|
||||
IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept,
|
||||
IWTSVirtualChannelCallback** ppCallback)
|
||||
{
|
||||
DISP_CHANNEL_CALLBACK* callback;
|
||||
|
@ -23,6 +23,5 @@ set(${MODULE_PREFIX}_SRCS
|
||||
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntry")
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
@ -436,7 +436,7 @@ static UINT dvcman_close_channel_iface(IWTSVirtualChannel* pChannel)
|
||||
UINT dvcman_create_channel(IWTSVirtualChannelManager* pChannelMgr, UINT32 ChannelId, const char* ChannelName)
|
||||
{
|
||||
int i;
|
||||
int bAccept;
|
||||
BOOL bAccept;
|
||||
DVCMAN_LISTENER* listener;
|
||||
DVCMAN_CHANNEL* channel;
|
||||
DrdynvcClientContext* context;
|
||||
@ -462,11 +462,11 @@ UINT dvcman_create_channel(IWTSVirtualChannelManager* pChannelMgr, UINT32 Channe
|
||||
channel->iface.Write = dvcman_write_channel;
|
||||
channel->iface.Close = dvcman_close_channel_iface;
|
||||
|
||||
bAccept = 1;
|
||||
bAccept = TRUE;
|
||||
pCallback = NULL;
|
||||
|
||||
if ((error = listener->listener_callback->OnNewChannelConnection(listener->listener_callback,
|
||||
(IWTSVirtualChannel*) channel, NULL, &bAccept, &pCallback)) == CHANNEL_RC_OK && bAccept == 1)
|
||||
(IWTSVirtualChannel*) channel, NULL, &bAccept, &pCallback)) == CHANNEL_RC_OK && bAccept)
|
||||
{
|
||||
WLog_DBG(TAG, "listener %s created new channel %d",
|
||||
listener->channel_name, channel->channel_id);
|
||||
@ -728,7 +728,7 @@ UINT drdynvc_send(drdynvcPlugin* drdynvc, wStream* s)
|
||||
UINT drdynvc_write_data(drdynvcPlugin* drdynvc, UINT32 ChannelId, BYTE* data, UINT32 dataSize)
|
||||
{
|
||||
wStream* data_out;
|
||||
unsigned long pos = 0;
|
||||
unsigned long pos;
|
||||
UINT32 cbChId;
|
||||
UINT32 cbLen;
|
||||
unsigned long chunkLength;
|
||||
@ -747,9 +747,9 @@ UINT drdynvc_write_data(drdynvcPlugin* drdynvc, UINT32 ChannelId, BYTE* data, UI
|
||||
Stream_SetPosition(data_out, 1);
|
||||
cbChId = drdynvc_write_variable_uint(data_out, ChannelId);
|
||||
|
||||
pos = Stream_GetPosition(data_out);
|
||||
if (dataSize == 0)
|
||||
{
|
||||
pos = Stream_GetPosition(data_out);
|
||||
Stream_SetPosition(data_out, 0);
|
||||
Stream_Write_UINT8(data_out, 0x40 | cbChId);
|
||||
Stream_SetPosition(data_out, pos);
|
||||
@ -758,7 +758,6 @@ UINT drdynvc_write_data(drdynvcPlugin* drdynvc, UINT32 ChannelId, BYTE* data, UI
|
||||
}
|
||||
else if (dataSize <= CHANNEL_CHUNK_LENGTH - pos)
|
||||
{
|
||||
pos = Stream_GetPosition(data_out);
|
||||
Stream_SetPosition(data_out, 0);
|
||||
Stream_Write_UINT8(data_out, 0x30 | cbChId);
|
||||
Stream_SetPosition(data_out, pos);
|
||||
|
@ -27,6 +27,5 @@ add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE
|
||||
|
||||
target_link_libraries(${MODULE_NAME} freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")
|
||||
|
@ -35,6 +35,9 @@ add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE
|
||||
|
||||
target_link_libraries(${MODULE_NAME} winpr freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT STATIC_CHANNELS AND BUILD_SHARED_LIBS)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
@ -29,6 +29,9 @@ add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE
|
||||
|
||||
target_link_libraries(${MODULE_NAME} freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT STATIC_CHANNELS AND BUILD_SHARED_LIBS)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
@ -96,7 +96,7 @@ static UINT echo_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT echo_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
||||
IWTSVirtualChannel* pChannel, BYTE* Data, int* pbAccept,
|
||||
IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept,
|
||||
IWTSVirtualChannelCallback** ppCallback)
|
||||
{
|
||||
ECHO_CHANNEL_CALLBACK* callback;
|
||||
|
@ -26,6 +26,5 @@ add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE
|
||||
|
||||
target_link_libraries(${MODULE_NAME} freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")
|
||||
|
@ -26,9 +26,4 @@ set(${MODULE_PREFIX}_SRCS
|
||||
add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntry")
|
||||
|
||||
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
@ -31,6 +31,5 @@ set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")
|
||||
|
@ -26,6 +26,9 @@ add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE
|
||||
|
||||
target_link_libraries(${MODULE_NAME} freerdp winpr)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT STATIC_CHANNELS AND BUILD_SHARED_LIBS)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
@ -48,6 +48,9 @@ endif()
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT STATIC_CHANNELS AND BUILD_SHARED_LIBS)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
@ -31,6 +31,5 @@ add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE
|
||||
|
||||
target_link_libraries(${MODULE_NAME} freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
@ -33,6 +33,5 @@ add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE
|
||||
|
||||
target_link_libraries(${MODULE_NAME} winpr freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
@ -6,6 +6,7 @@
|
||||
* Copyright 2010-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
* Copyright 2016 Armin Novak <armin.novak@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -42,6 +43,9 @@
|
||||
|
||||
void devman_device_free(DEVICE* device)
|
||||
{
|
||||
if (!device)
|
||||
return;
|
||||
|
||||
IFCALL(device->Free, device);
|
||||
}
|
||||
|
||||
@ -49,6 +53,9 @@ DEVMAN* devman_new(rdpdrPlugin* rdpdr)
|
||||
{
|
||||
DEVMAN* devman;
|
||||
|
||||
if (!rdpdr)
|
||||
return NULL;
|
||||
|
||||
devman = (DEVMAN*) calloc(1, sizeof(DEVMAN));
|
||||
|
||||
if (!devman)
|
||||
@ -84,6 +91,9 @@ void devman_unregister_device(DEVMAN* devman, void* key)
|
||||
{
|
||||
DEVICE* device;
|
||||
|
||||
if (!devman || !key)
|
||||
return;
|
||||
|
||||
device = (DEVICE*) ListDictionary_Remove(devman->devices, key);
|
||||
|
||||
if (device)
|
||||
@ -99,6 +109,9 @@ static UINT devman_register_device(DEVMAN* devman, DEVICE* device)
|
||||
{
|
||||
void* key = NULL;
|
||||
|
||||
if (!devman || !device)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
device->id = devman->id_sequence++;
|
||||
key = (void*) (size_t) device->id;
|
||||
|
||||
@ -115,6 +128,9 @@ DEVICE* devman_get_device_by_id(DEVMAN* devman, UINT32 id)
|
||||
DEVICE* device = NULL;
|
||||
void* key = (void*) (size_t) id;
|
||||
|
||||
if (!devman)
|
||||
return NULL;
|
||||
|
||||
device = (DEVICE*) ListDictionary_GetItemValue(devman->devices, key);
|
||||
|
||||
return device;
|
||||
@ -137,6 +153,9 @@ UINT devman_load_device_service(DEVMAN* devman, RDPDR_DEVICE* device, rdpContext
|
||||
DEVICE_SERVICE_ENTRY_POINTS ep;
|
||||
PDEVICE_SERVICE_ENTRY entry = NULL;
|
||||
|
||||
if (!devman || !device || !rdpcontext)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
|
||||
if (device->Type == RDPDR_DTYP_FILESYSTEM)
|
||||
ServiceName = DRIVE_SERVICE_NAME;
|
||||
else if (device->Type == RDPDR_DTYP_PRINT)
|
||||
|
@ -77,18 +77,28 @@ static UINT irp_complete(IRP* irp)
|
||||
return error;
|
||||
}
|
||||
|
||||
IRP* irp_new(DEVMAN* devman, wStream* s)
|
||||
IRP* irp_new(DEVMAN* devman, wStream* s, UINT* error)
|
||||
{
|
||||
IRP* irp;
|
||||
DEVICE* device;
|
||||
UINT32 DeviceId;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 20)
|
||||
{
|
||||
if (error)
|
||||
*error = ERROR_INVALID_DATA;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Stream_Read_UINT32(s, DeviceId); /* DeviceId (4 bytes) */
|
||||
device = devman_get_device_by_id(devman, DeviceId);
|
||||
|
||||
if (!device)
|
||||
{
|
||||
WLog_ERR(TAG, "devman_get_device_by_id failed!");
|
||||
WLog_WARN(TAG, "devman_get_device_by_id failed!");
|
||||
if (error)
|
||||
*error = CHANNEL_RC_OK;
|
||||
|
||||
return NULL;
|
||||
};
|
||||
|
||||
@ -97,6 +107,8 @@ IRP* irp_new(DEVMAN* devman, wStream* s)
|
||||
if (!irp)
|
||||
{
|
||||
WLog_ERR(TAG, "_aligned_malloc failed!");
|
||||
if (error)
|
||||
*error = CHANNEL_RC_NO_MEMORY;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -117,6 +129,8 @@ IRP* irp_new(DEVMAN* devman, wStream* s)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
_aligned_free(irp);
|
||||
if (error)
|
||||
*error = CHANNEL_RC_NO_MEMORY;
|
||||
return NULL;
|
||||
}
|
||||
Stream_Write_UINT16(irp->output, RDPDR_CTYP_CORE); /* Component (2 bytes) */
|
||||
@ -131,5 +145,8 @@ IRP* irp_new(DEVMAN* devman, wStream* s)
|
||||
irp->thread = NULL;
|
||||
irp->cancelled = FALSE;
|
||||
|
||||
if (error)
|
||||
*error = CHANNEL_RC_OK;
|
||||
|
||||
return irp;
|
||||
}
|
||||
|
@ -23,6 +23,6 @@
|
||||
|
||||
#include "rdpdr_main.h"
|
||||
|
||||
IRP* irp_new(DEVMAN* devman, wStream* s);
|
||||
IRP* irp_new(DEVMAN* devman, wStream* s, UINT* error);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_RDPDR_CLIENT_IRP_H */
|
||||
|
@ -4,8 +4,9 @@
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* Copyright 2010-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015-2016 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
* Copyright 2016 Armin Novak <armin.novak@thincast.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -60,12 +61,21 @@ static void rdpdr_write_general_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
}
|
||||
|
||||
/* Process device direction general capability set */
|
||||
static void rdpdr_process_general_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
static UINT rdpdr_process_general_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
{
|
||||
UINT16 capabilityLength;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 2)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, capabilityLength);
|
||||
|
||||
if (Stream_GetRemainingLength(s) < capabilityLength - 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Seek(s, capabilityLength - 4);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/* Output printer direction capability set */
|
||||
@ -75,12 +85,21 @@ static void rdpdr_write_printer_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
}
|
||||
|
||||
/* Process printer direction capability set */
|
||||
static void rdpdr_process_printer_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
static UINT rdpdr_process_printer_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
{
|
||||
UINT16 capabilityLength;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 2)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, capabilityLength);
|
||||
|
||||
if (Stream_GetRemainingLength(s) < capabilityLength - 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Seek(s, capabilityLength - 4);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/* Output port redirection capability set */
|
||||
@ -90,12 +109,21 @@ static void rdpdr_write_port_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
}
|
||||
|
||||
/* Process port redirection capability set */
|
||||
static void rdpdr_process_port_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
static UINT rdpdr_process_port_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
{
|
||||
UINT16 capabilityLength;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 2)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, capabilityLength);
|
||||
|
||||
if (Stream_GetRemainingLength(s) < capabilityLength - 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Seek(s, capabilityLength - 4);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/* Output drive redirection capability set */
|
||||
@ -105,12 +133,21 @@ static void rdpdr_write_drive_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
}
|
||||
|
||||
/* Process drive redirection capability set */
|
||||
static void rdpdr_process_drive_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
static UINT rdpdr_process_drive_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
{
|
||||
UINT16 capabilityLength;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 2)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, capabilityLength);
|
||||
|
||||
if (Stream_GetRemainingLength(s) < capabilityLength - 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Seek(s, capabilityLength - 4);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/* Output smart card redirection capability set */
|
||||
@ -120,53 +157,77 @@ static void rdpdr_write_smartcard_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
}
|
||||
|
||||
/* Process smartcard redirection capability set */
|
||||
static void rdpdr_process_smartcard_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
static UINT rdpdr_process_smartcard_capset(rdpdrPlugin* rdpdr, wStream* s)
|
||||
{
|
||||
UINT16 capabilityLength;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 2)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, capabilityLength);
|
||||
|
||||
if (Stream_GetRemainingLength(s) < capabilityLength - 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Seek(s, capabilityLength - 4);
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
void rdpdr_process_capability_request(rdpdrPlugin* rdpdr, wStream* s)
|
||||
UINT rdpdr_process_capability_request(rdpdrPlugin* rdpdr, wStream* s)
|
||||
{
|
||||
UINT status = CHANNEL_RC_OK;
|
||||
UINT16 i;
|
||||
UINT16 numCapabilities;
|
||||
UINT16 capabilityType;
|
||||
|
||||
if (!rdpdr || !s)
|
||||
return CHANNEL_RC_NULL_DATA;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, numCapabilities);
|
||||
Stream_Seek(s, 2); /* pad (2 bytes) */
|
||||
|
||||
for (i = 0; i < numCapabilities; i++)
|
||||
{
|
||||
if (Stream_GetRemainingLength(s) < sizeof(UINT16))
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, capabilityType);
|
||||
|
||||
switch (capabilityType)
|
||||
{
|
||||
case CAP_GENERAL_TYPE:
|
||||
rdpdr_process_general_capset(rdpdr, s);
|
||||
break;
|
||||
case CAP_GENERAL_TYPE:
|
||||
status = rdpdr_process_general_capset(rdpdr, s);
|
||||
break;
|
||||
|
||||
case CAP_PRINTER_TYPE:
|
||||
rdpdr_process_printer_capset(rdpdr, s);
|
||||
break;
|
||||
case CAP_PRINTER_TYPE:
|
||||
status = rdpdr_process_printer_capset(rdpdr, s);
|
||||
break;
|
||||
|
||||
case CAP_PORT_TYPE:
|
||||
rdpdr_process_port_capset(rdpdr, s);
|
||||
break;
|
||||
case CAP_PORT_TYPE:
|
||||
status = rdpdr_process_port_capset(rdpdr, s);
|
||||
break;
|
||||
|
||||
case CAP_DRIVE_TYPE:
|
||||
rdpdr_process_drive_capset(rdpdr, s);
|
||||
break;
|
||||
case CAP_DRIVE_TYPE:
|
||||
status = rdpdr_process_drive_capset(rdpdr, s);
|
||||
break;
|
||||
|
||||
case CAP_SMARTCARD_TYPE:
|
||||
rdpdr_process_smartcard_capset(rdpdr, s);
|
||||
break;
|
||||
case CAP_SMARTCARD_TYPE:
|
||||
status = rdpdr_process_smartcard_capset(rdpdr, s);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (status != CHANNEL_RC_OK)
|
||||
return status;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
#include "rdpdr_main.h"
|
||||
|
||||
void rdpdr_process_capability_request(rdpdrPlugin* rdpdr, wStream* s);
|
||||
UINT rdpdr_process_capability_request(rdpdrPlugin* rdpdr, wStream* s);
|
||||
UINT rdpdr_send_capability_response(rdpdrPlugin* rdpdr);
|
||||
|
||||
#endif /* FREERDP_CHANNEL_RDPDR_CLIENT_CAPABILITIES_H */
|
||||
|
@ -4,8 +4,9 @@
|
||||
*
|
||||
* Copyright 2010-2011 Vic Lee
|
||||
* Copyright 2010-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015-2016 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
* Copyright 2016 Armin Novak <armin.novak@thincast.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -81,7 +82,7 @@ static UINT rdpdr_send_device_list_remove_request(rdpdrPlugin* rdpdr, UINT32 cou
|
||||
UINT32 i;
|
||||
wStream* s;
|
||||
|
||||
s = Stream_New(NULL, 256);
|
||||
s = Stream_New(NULL, count * sizeof(UINT32) + 8);
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
@ -413,6 +414,8 @@ static UINT handle_hotplug(rdpdrPlugin* rdpdr)
|
||||
dev_array[size].path = word;
|
||||
dev_array[size++].to_add = TRUE;
|
||||
}
|
||||
else
|
||||
free (word);
|
||||
}
|
||||
free(line);
|
||||
}
|
||||
@ -427,7 +430,7 @@ static UINT handle_hotplug(rdpdrPlugin* rdpdr)
|
||||
BOOL dev_found = FALSE;
|
||||
|
||||
device_ext = (DEVICE_DRIVE_EXT *)ListDictionary_GetItemValue(rdpdr->devman->devices, (void *)keys[j]);
|
||||
if (!device_ext)
|
||||
if (!device_ext || !device_ext->path)
|
||||
continue;
|
||||
|
||||
/* not plugable device */
|
||||
@ -494,7 +497,6 @@ static UINT handle_hotplug(rdpdrPlugin* rdpdr)
|
||||
free(drive->Path);
|
||||
free(drive->Name);
|
||||
free(drive);
|
||||
error = CHANNEL_RC_NO_MEMORY;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
@ -502,7 +504,7 @@ static UINT handle_hotplug(rdpdrPlugin* rdpdr)
|
||||
|
||||
cleanup:
|
||||
for (i = 0; i < size; i++)
|
||||
free (dev_array[size].path);
|
||||
free (dev_array[i].path);
|
||||
|
||||
return error ? error : rdpdr_send_device_list_announce_request(rdpdr, TRUE);
|
||||
}
|
||||
@ -515,15 +517,15 @@ static void* drive_hotplug_thread_func(void* arg)
|
||||
struct timeval tv;
|
||||
int rv;
|
||||
UINT error;
|
||||
DWORD status;
|
||||
DWORD status;
|
||||
|
||||
rdpdr = (rdpdrPlugin*) arg;
|
||||
|
||||
if (!(rdpdr->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
|
||||
{
|
||||
WLog_ERR(TAG, "CreateEvent failed!");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto out;
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mfd = open("/proc/mounts", O_RDONLY, 0);
|
||||
@ -531,8 +533,8 @@ static void* drive_hotplug_thread_func(void* arg)
|
||||
if (mfd < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "ERROR: Unable to open /proc/mounts.");
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto out;
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
@ -543,18 +545,18 @@ static void* drive_hotplug_thread_func(void* arg)
|
||||
if ((error = handle_hotplug(rdpdr)))
|
||||
{
|
||||
WLog_ERR(TAG, "handle_hotplug failed with error %lu!", error);
|
||||
goto out;
|
||||
goto out;
|
||||
}
|
||||
|
||||
while ((rv = select(mfd+1, NULL, NULL, &rfds, &tv)) >= 0)
|
||||
{
|
||||
status = WaitForSingleObject(rdpdr->stopEvent, 0);
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", error);
|
||||
goto out;
|
||||
}
|
||||
status = WaitForSingleObject(rdpdr->stopEvent, 0);
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", error);
|
||||
goto out;
|
||||
}
|
||||
if (status == WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
@ -564,7 +566,7 @@ static void* drive_hotplug_thread_func(void* arg)
|
||||
if ((error = handle_hotplug(rdpdr)))
|
||||
{
|
||||
WLog_ERR(TAG, "handle_hotplug failed with error %lu!", error);
|
||||
goto out;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
@ -575,11 +577,11 @@ static void* drive_hotplug_thread_func(void* arg)
|
||||
}
|
||||
|
||||
out:
|
||||
if (error && rdpdr->rdpcontext)
|
||||
setChannelError(rdpdr->rdpcontext, error, "drive_hotplug_thread_func reported an error");
|
||||
if (error && rdpdr->rdpcontext)
|
||||
setChannelError(rdpdr->rdpcontext, error, "drive_hotplug_thread_func reported an error");
|
||||
|
||||
ExitThread((DWORD)error);
|
||||
return NULL;
|
||||
ExitThread((DWORD)error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -589,21 +591,22 @@ out:
|
||||
*/
|
||||
static UINT drive_hotplug_thread_terminate(rdpdrPlugin* rdpdr)
|
||||
{
|
||||
UINT error;
|
||||
UINT error;
|
||||
|
||||
if (rdpdr->hotplugThread)
|
||||
{
|
||||
if (rdpdr->stopEvent)
|
||||
SetEvent(rdpdr->stopEvent);
|
||||
|
||||
if (WaitForSingleObject(rdpdr->hotplugThread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
rdpdr->hotplugThread = NULL;
|
||||
}
|
||||
return CHANNEL_RC_OK;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -659,13 +662,18 @@ static UINT rdpdr_process_connect(rdpdrPlugin* rdpdr)
|
||||
return error;
|
||||
}
|
||||
|
||||
static void rdpdr_process_server_announce_request(rdpdrPlugin* rdpdr, wStream* s)
|
||||
static UINT rdpdr_process_server_announce_request(rdpdrPlugin* rdpdr, wStream* s)
|
||||
{
|
||||
if (Stream_GetRemainingLength(s) < 8)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, rdpdr->versionMajor);
|
||||
Stream_Read_UINT16(s, rdpdr->versionMinor);
|
||||
Stream_Read_UINT32(s, rdpdr->clientID);
|
||||
|
||||
rdpdr->sequenceId++;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -681,7 +689,7 @@ static UINT rdpdr_send_client_announce_reply(rdpdrPlugin* rdpdr)
|
||||
if (!s)
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_New failed!");
|
||||
return CHANNEL_RC_OK;
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
}
|
||||
|
||||
Stream_Write_UINT16(s, RDPDR_CTYP_CORE); /* Component (2 bytes) */
|
||||
@ -731,12 +739,15 @@ static UINT rdpdr_send_client_name_request(rdpdrPlugin* rdpdr)
|
||||
return rdpdr_send(rdpdr, s);
|
||||
}
|
||||
|
||||
static void rdpdr_process_server_clientid_confirm(rdpdrPlugin* rdpdr, wStream* s)
|
||||
static UINT rdpdr_process_server_clientid_confirm(rdpdrPlugin* rdpdr, wStream* s)
|
||||
{
|
||||
UINT16 versionMajor;
|
||||
UINT16 versionMinor;
|
||||
UINT32 clientID;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 8)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, versionMajor);
|
||||
Stream_Read_UINT16(s, versionMinor);
|
||||
Stream_Read_UINT32(s, clientID);
|
||||
@ -748,9 +759,9 @@ static void rdpdr_process_server_clientid_confirm(rdpdrPlugin* rdpdr, wStream* s
|
||||
}
|
||||
|
||||
if (clientID != rdpdr->clientID)
|
||||
{
|
||||
rdpdr->clientID = clientID;
|
||||
}
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -802,13 +813,13 @@ static UINT rdpdr_send_device_list_announce_request(rdpdrPlugin* rdpdr, BOOL use
|
||||
*/
|
||||
|
||||
if ((rdpdr->versionMinor == 0x0005) ||
|
||||
(device->type == RDPDR_DTYP_SMARTCARD) || userLoggedOn)
|
||||
(device->type == RDPDR_DTYP_SMARTCARD) || userLoggedOn)
|
||||
{
|
||||
data_len = (int) (device->data == NULL ? 0 : Stream_GetPosition(device->data));
|
||||
if (!Stream_EnsureRemainingCapacity(s, 20 + data_len))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
|
||||
Stream_Write_UINT32(s, device->type); /* deviceType */
|
||||
@ -857,20 +868,20 @@ static UINT rdpdr_process_irp(rdpdrPlugin* rdpdr, wStream* s)
|
||||
IRP* irp;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
irp = irp_new(rdpdr->devman, s);
|
||||
irp = irp_new(rdpdr->devman, s, &error);
|
||||
|
||||
if (!irp)
|
||||
{
|
||||
WLog_ERR(TAG, "irp_new failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
WLog_ERR(TAG, "irp_new failed with %lu!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
IFCALLRET(irp->device->IRPRequest, error, irp->device, irp);
|
||||
|
||||
if (error)
|
||||
WLog_ERR(TAG, "device->IRPRequest failed with error %lu", error);
|
||||
if (error)
|
||||
WLog_ERR(TAG, "device->IRPRequest failed with error %lu", error);
|
||||
|
||||
return error;
|
||||
return error;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -919,6 +930,12 @@ static UINT rdpdr_process_receive(rdpdrPlugin* rdpdr, wStream* s)
|
||||
UINT32 status;
|
||||
UINT error;
|
||||
|
||||
if (!rdpdr || !s)
|
||||
return CHANNEL_RC_NULL_DATA;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT16(s, component); /* Component (2 bytes) */
|
||||
Stream_Read_UINT16(s, packetId); /* PacketId (2 bytes) */
|
||||
|
||||
@ -926,70 +943,77 @@ static UINT rdpdr_process_receive(rdpdrPlugin* rdpdr, wStream* s)
|
||||
{
|
||||
switch (packetId)
|
||||
{
|
||||
case PAKID_CORE_SERVER_ANNOUNCE:
|
||||
rdpdr_process_server_announce_request(rdpdr, s);
|
||||
if ((error = rdpdr_send_client_announce_reply(rdpdr)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpdr_send_client_announce_reply failed with error %lu", error);
|
||||
return error;
|
||||
}
|
||||
if ((error = rdpdr_send_client_name_request(rdpdr)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpdr_send_client_name_request failed with error %lu", error);
|
||||
return error;
|
||||
}
|
||||
if ((error = rdpdr_process_init(rdpdr)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpdr_process_init failed with error %lu", error);
|
||||
return error;
|
||||
}
|
||||
break;
|
||||
case PAKID_CORE_SERVER_ANNOUNCE:
|
||||
if ((error = rdpdr_process_server_announce_request(rdpdr, s)))
|
||||
return error;
|
||||
if ((error = rdpdr_send_client_announce_reply(rdpdr)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpdr_send_client_announce_reply failed with error %lu", error);
|
||||
return error;
|
||||
}
|
||||
if ((error = rdpdr_send_client_name_request(rdpdr)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpdr_send_client_name_request failed with error %lu", error);
|
||||
return error;
|
||||
}
|
||||
if ((error = rdpdr_process_init(rdpdr)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpdr_process_init failed with error %lu", error);
|
||||
return error;
|
||||
}
|
||||
break;
|
||||
|
||||
case PAKID_CORE_SERVER_CAPABILITY:
|
||||
rdpdr_process_capability_request(rdpdr, s);
|
||||
if ((error = rdpdr_send_capability_response(rdpdr)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpdr_send_capability_response failed with error %lu", error);
|
||||
return error;
|
||||
}
|
||||
break;
|
||||
case PAKID_CORE_SERVER_CAPABILITY:
|
||||
if ((error = rdpdr_process_capability_request(rdpdr, s)))
|
||||
return error;
|
||||
if ((error = rdpdr_send_capability_response(rdpdr)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpdr_send_capability_response failed with error %lu", error);
|
||||
return error;
|
||||
}
|
||||
break;
|
||||
|
||||
case PAKID_CORE_CLIENTID_CONFIRM:
|
||||
rdpdr_process_server_clientid_confirm(rdpdr, s);
|
||||
if ((error = rdpdr_send_device_list_announce_request(rdpdr, FALSE)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpdr_send_device_list_announce_request failed with error %lu", error);
|
||||
return error;
|
||||
}
|
||||
break;
|
||||
case PAKID_CORE_CLIENTID_CONFIRM:
|
||||
if ((error = rdpdr_process_server_clientid_confirm(rdpdr, s)))
|
||||
return error;
|
||||
|
||||
case PAKID_CORE_USER_LOGGEDON:
|
||||
if ((error = rdpdr_send_device_list_announce_request(rdpdr, TRUE)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpdr_send_device_list_announce_request failed with error %lu", error);
|
||||
return error;
|
||||
}
|
||||
break;
|
||||
if ((error = rdpdr_send_device_list_announce_request(rdpdr, FALSE)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpdr_send_device_list_announce_request failed with error %lu", error);
|
||||
return error;
|
||||
}
|
||||
break;
|
||||
|
||||
case PAKID_CORE_DEVICE_REPLY:
|
||||
/* connect to a specific resource */
|
||||
Stream_Read_UINT32(s, deviceId);
|
||||
Stream_Read_UINT32(s, status);
|
||||
break;
|
||||
case PAKID_CORE_USER_LOGGEDON:
|
||||
if ((error = rdpdr_send_device_list_announce_request(rdpdr, TRUE)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpdr_send_device_list_announce_request failed with error %lu", error);
|
||||
return error;
|
||||
}
|
||||
break;
|
||||
|
||||
case PAKID_CORE_DEVICE_IOREQUEST:
|
||||
if ((error = rdpdr_process_irp(rdpdr, s)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpdr_process_irp failed with error %lu", error);
|
||||
return error;
|
||||
}
|
||||
s = NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_ERR(TAG, "RDPDR_CTYP_CORE unknown PacketId: 0x%04X", packetId);
|
||||
case PAKID_CORE_DEVICE_REPLY:
|
||||
/* connect to a specific resource */
|
||||
if (Stream_GetRemainingLength(s) < 8)
|
||||
return ERROR_INVALID_DATA;
|
||||
break;
|
||||
|
||||
Stream_Read_UINT32(s, deviceId);
|
||||
Stream_Read_UINT32(s, status);
|
||||
break;
|
||||
|
||||
case PAKID_CORE_DEVICE_IOREQUEST:
|
||||
if ((error = rdpdr_process_irp(rdpdr, s)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpdr_process_irp failed with error %lu", error);
|
||||
return error;
|
||||
}
|
||||
s = NULL;
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_ERR(TAG, "RDPDR_CTYP_CORE unknown PacketId: 0x%04X", packetId);
|
||||
return ERROR_INVALID_DATA;
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
@ -997,21 +1021,24 @@ static UINT rdpdr_process_receive(rdpdrPlugin* rdpdr, wStream* s)
|
||||
{
|
||||
switch (packetId)
|
||||
{
|
||||
case PAKID_PRN_CACHE_DATA:
|
||||
{
|
||||
UINT32 eventID;
|
||||
Stream_Read_UINT32(s, eventID);
|
||||
WLog_ERR(TAG, "Ignoring unhandled message PAKID_PRN_CACHE_DATA (EventID: 0x%04X)", eventID);
|
||||
}
|
||||
break;
|
||||
|
||||
case PAKID_PRN_USING_XPS:
|
||||
WLog_ERR(TAG, "Ignoring unhandled message PAKID_PRN_USING_XPS");
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_ERR(TAG, "Unknown printing component packetID: 0x%04X", packetId);
|
||||
case PAKID_PRN_CACHE_DATA:
|
||||
{
|
||||
UINT32 eventID;
|
||||
if (Stream_GetRemainingLength(s) < 4)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
Stream_Read_UINT32(s, eventID);
|
||||
WLog_ERR(TAG, "Ignoring unhandled message PAKID_PRN_CACHE_DATA (EventID: 0x%04X)", eventID);
|
||||
}
|
||||
break;
|
||||
|
||||
case PAKID_PRN_USING_XPS:
|
||||
WLog_ERR(TAG, "Ignoring unhandled message PAKID_PRN_USING_XPS");
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_ERR(TAG, "Unknown printing component packetID: 0x%04X", packetId);
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -1130,14 +1157,15 @@ UINT rdpdr_send(rdpdrPlugin* rdpdr, wStream* s)
|
||||
UINT status;
|
||||
rdpdrPlugin* plugin = (rdpdrPlugin*) rdpdr;
|
||||
|
||||
if (!rdpdr || !s)
|
||||
return CHANNEL_RC_NULL_DATA;
|
||||
|
||||
if (!plugin)
|
||||
{
|
||||
status = CHANNEL_RC_BAD_INIT_HANDLE;
|
||||
}
|
||||
else
|
||||
{
|
||||
status = plugin->channelEntryPoints.pVirtualChannelWrite(plugin->OpenHandle,
|
||||
Stream_Buffer(s), (UINT32) Stream_GetPosition(s), s);
|
||||
Stream_Buffer(s), (UINT32) Stream_GetPosition(s), s);
|
||||
}
|
||||
|
||||
if (status != CHANNEL_RC_OK)
|
||||
@ -1156,7 +1184,7 @@ UINT rdpdr_send(rdpdrPlugin* rdpdr, wStream* s)
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpdr_virtual_channel_event_data_received(rdpdrPlugin* rdpdr,
|
||||
void* pData, UINT32 dataLength, UINT32 totalLength, UINT32 dataFlags)
|
||||
void* pData, UINT32 dataLength, UINT32 totalLength, UINT32 dataFlags)
|
||||
{
|
||||
wStream* data_in;
|
||||
|
||||
@ -1188,7 +1216,7 @@ static UINT rdpdr_virtual_channel_event_data_received(rdpdrPlugin* rdpdr,
|
||||
if (!Stream_EnsureRemainingCapacity(data_in, (int) dataLength))
|
||||
{
|
||||
WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
|
||||
return CHANNEL_RC_NO_MEMORY;
|
||||
return ERROR_INVALID_DATA;
|
||||
}
|
||||
Stream_Write(data_in, pData, dataLength);
|
||||
|
||||
@ -1214,14 +1242,14 @@ static UINT rdpdr_virtual_channel_event_data_received(rdpdrPlugin* rdpdr,
|
||||
}
|
||||
|
||||
static VOID VCAPITYPE rdpdr_virtual_channel_open_event(DWORD openHandle, UINT event,
|
||||
LPVOID pData, UINT32 dataLength, UINT32 totalLength, UINT32 dataFlags)
|
||||
LPVOID pData, UINT32 dataLength, UINT32 totalLength, UINT32 dataFlags)
|
||||
{
|
||||
rdpdrPlugin* rdpdr;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
|
||||
rdpdr = (rdpdrPlugin*) rdpdr_get_open_handle_data(openHandle);
|
||||
|
||||
if (!rdpdr)
|
||||
if (!rdpdr || !pData)
|
||||
{
|
||||
WLog_ERR(TAG, "rdpdr_virtual_channel_open_event: error no match");
|
||||
return;
|
||||
@ -1229,17 +1257,17 @@ static VOID VCAPITYPE rdpdr_virtual_channel_open_event(DWORD openHandle, UINT ev
|
||||
|
||||
switch (event)
|
||||
{
|
||||
case CHANNEL_EVENT_DATA_RECEIVED:
|
||||
if ((error = rdpdr_virtual_channel_event_data_received(rdpdr, pData, dataLength, totalLength, dataFlags)))
|
||||
WLog_ERR(TAG, "rdpdr_virtual_channel_event_data_received failed with error %lu!", error );
|
||||
break;
|
||||
case CHANNEL_EVENT_DATA_RECEIVED:
|
||||
if ((error = rdpdr_virtual_channel_event_data_received(rdpdr, pData, dataLength, totalLength, dataFlags)))
|
||||
WLog_ERR(TAG, "rdpdr_virtual_channel_event_data_received failed with error %lu!", error );
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_WRITE_COMPLETE:
|
||||
Stream_Free((wStream*) pData, TRUE);
|
||||
break;
|
||||
case CHANNEL_EVENT_WRITE_COMPLETE:
|
||||
Stream_Free((wStream*) pData, TRUE);
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_USER:
|
||||
break;
|
||||
case CHANNEL_EVENT_USER:
|
||||
break;
|
||||
}
|
||||
if (error && rdpdr->rdpcontext)
|
||||
setChannelError(rdpdr->rdpcontext, error, "rdpdr_virtual_channel_open_event reported an error");
|
||||
@ -1254,6 +1282,12 @@ static void* rdpdr_virtual_channel_client_thread(void* arg)
|
||||
rdpdrPlugin* rdpdr = (rdpdrPlugin*) arg;
|
||||
UINT error;
|
||||
|
||||
if (!rdpdr)
|
||||
{
|
||||
ExitThread((DWORD) CHANNEL_RC_NULL_DATA);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((error = rdpdr_process_connect(rdpdr)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpdr_process_connect failed with error %lu!", error);
|
||||
@ -1303,16 +1337,16 @@ static UINT rdpdr_virtual_channel_event_connected(rdpdrPlugin* rdpdr, LPVOID pDa
|
||||
UINT error;
|
||||
|
||||
status = rdpdr->channelEntryPoints.pVirtualChannelOpen(rdpdr->InitHandle,
|
||||
&rdpdr->OpenHandle, rdpdr->channelDef.name, rdpdr_virtual_channel_open_event);
|
||||
&rdpdr->OpenHandle, rdpdr->channelDef.name, rdpdr_virtual_channel_open_event);
|
||||
|
||||
if (status != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_ERR(TAG, "pVirtualChannelOpen failed with %s [%08X]",
|
||||
WTSErrorToString(status), status);
|
||||
return status;
|
||||
}
|
||||
if (status != CHANNEL_RC_OK)
|
||||
{
|
||||
WLog_ERR(TAG, "pVirtualChannelOpen failed with %s [%08X]",
|
||||
WTSErrorToString(status), status);
|
||||
return status;
|
||||
}
|
||||
|
||||
if ((error = rdpdr_add_open_handle_data(rdpdr->OpenHandle, rdpdr)))
|
||||
if ((error = rdpdr_add_open_handle_data(rdpdr->OpenHandle, rdpdr)))
|
||||
{
|
||||
WLog_ERR(TAG, "rdpdr_add_open_handle_data failed with error %lu!", error);
|
||||
return error;
|
||||
@ -1344,11 +1378,11 @@ static UINT rdpdr_virtual_channel_event_disconnected(rdpdrPlugin* rdpdr)
|
||||
UINT error;
|
||||
|
||||
if (MessageQueue_PostQuit(rdpdr->queue, 0) && (WaitForSingleObject(rdpdr->thread, INFINITE) == WAIT_FAILED))
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
MessageQueue_Free(rdpdr->queue);
|
||||
CloseHandle(rdpdr->thread);
|
||||
@ -1357,10 +1391,10 @@ static UINT rdpdr_virtual_channel_event_disconnected(rdpdrPlugin* rdpdr)
|
||||
rdpdr->thread = NULL;
|
||||
|
||||
if ((error = drive_hotplug_thread_terminate(rdpdr)))
|
||||
{
|
||||
WLog_ERR(TAG, "drive_hotplug_thread_terminate failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
{
|
||||
WLog_ERR(TAG, "drive_hotplug_thread_terminate failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = rdpdr->channelEntryPoints.pVirtualChannelClose(rdpdr->OpenHandle);
|
||||
if (CHANNEL_RC_OK != error)
|
||||
@ -1407,25 +1441,25 @@ static VOID VCAPITYPE rdpdr_virtual_channel_init_event(LPVOID pInitHandle, UINT
|
||||
|
||||
switch (event)
|
||||
{
|
||||
case CHANNEL_EVENT_INITIALIZED:
|
||||
break;
|
||||
case CHANNEL_EVENT_CONNECTED:
|
||||
if ((error = rdpdr_virtual_channel_event_connected(rdpdr, pData, dataLength)))
|
||||
WLog_ERR(TAG, "rdpdr_virtual_channel_event_connected failed with error %lu!", error);
|
||||
break;
|
||||
case CHANNEL_EVENT_INITIALIZED:
|
||||
break;
|
||||
case CHANNEL_EVENT_CONNECTED:
|
||||
if ((error = rdpdr_virtual_channel_event_connected(rdpdr, pData, dataLength)))
|
||||
WLog_ERR(TAG, "rdpdr_virtual_channel_event_connected failed with error %lu!", error);
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_DISCONNECTED:
|
||||
if ((error = rdpdr_virtual_channel_event_disconnected(rdpdr)))
|
||||
WLog_ERR(TAG, "rdpdr_virtual_channel_event_disconnected failed with error %lu!", error);
|
||||
break;
|
||||
case CHANNEL_EVENT_DISCONNECTED:
|
||||
if ((error = rdpdr_virtual_channel_event_disconnected(rdpdr)))
|
||||
WLog_ERR(TAG, "rdpdr_virtual_channel_event_disconnected failed with error %lu!", error);
|
||||
break;
|
||||
|
||||
case CHANNEL_EVENT_TERMINATED:
|
||||
rdpdr_virtual_channel_event_terminated(rdpdr);
|
||||
break;
|
||||
default:
|
||||
WLog_ERR(TAG, "unknown event %d!", event);
|
||||
error = ERROR_INVALID_DATA;
|
||||
break;
|
||||
case CHANNEL_EVENT_TERMINATED:
|
||||
rdpdr_virtual_channel_event_terminated(rdpdr);
|
||||
break;
|
||||
default:
|
||||
WLog_ERR(TAG, "unknown event %d!", event);
|
||||
error = ERROR_INVALID_DATA;
|
||||
break;
|
||||
}
|
||||
|
||||
if (error && rdpdr->rdpcontext)
|
||||
@ -1442,7 +1476,6 @@ BOOL VCAPITYPE VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints)
|
||||
rdpdrPlugin* rdpdr;
|
||||
CHANNEL_ENTRY_POINTS_FREERDP* pEntryPointsEx;
|
||||
|
||||
|
||||
rdpdr = (rdpdrPlugin*) calloc(1, sizeof(rdpdrPlugin));
|
||||
|
||||
if (!rdpdr)
|
||||
@ -1471,7 +1504,6 @@ BOOL VCAPITYPE VirtualChannelEntry(PCHANNEL_ENTRY_POINTS pEntryPoints)
|
||||
|
||||
CopyMemory(&(rdpdr->channelEntryPoints), pEntryPoints, sizeof(CHANNEL_ENTRY_POINTS_FREERDP));
|
||||
|
||||
|
||||
rc = rdpdr->channelEntryPoints.pVirtualChannelInit(&rdpdr->InitHandle,
|
||||
&rdpdr->channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000, rdpdr_virtual_channel_init_event);
|
||||
|
||||
|
@ -27,6 +27,5 @@ add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE
|
||||
|
||||
target_link_libraries(${MODULE_NAME} freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")
|
||||
|
@ -30,6 +30,9 @@ add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE
|
||||
|
||||
target_link_libraries(${MODULE_NAME} winpr freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT STATIC_CHANNELS AND BUILD_SHARED_LIBS)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
@ -596,7 +596,7 @@ static UINT rdpei_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpei_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
||||
IWTSVirtualChannel* pChannel, BYTE* Data, int* pbAccept,
|
||||
IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept,
|
||||
IWTSVirtualChannelCallback** ppCallback)
|
||||
{
|
||||
RDPEI_CHANNEL_CALLBACK* callback;
|
||||
|
@ -33,6 +33,5 @@ set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")
|
||||
|
@ -33,7 +33,9 @@ add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE
|
||||
|
||||
target_link_libraries(${MODULE_NAME} winpr freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT STATIC_CHANNELS AND BUILD_SHARED_LIBS)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
||||
|
@ -1265,7 +1265,7 @@ static UINT rdpgfx_on_close(IWTSVirtualChannelCallback* pChannelCallback)
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT rdpgfx_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
||||
IWTSVirtualChannel* pChannel, BYTE* Data, int* pbAccept,
|
||||
IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept,
|
||||
IWTSVirtualChannelCallback** ppCallback)
|
||||
{
|
||||
RDPGFX_CHANNEL_CALLBACK* callback;
|
||||
|
@ -27,7 +27,6 @@ add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE
|
||||
|
||||
target_link_libraries(${MODULE_NAME} winpr freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
||||
|
@ -33,6 +33,4 @@ set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${ALSA_LIBRARIES})
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client/ALSA")
|
||||
|
@ -38,10 +38,8 @@ set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS}
|
||||
${CORE_AUDIO}
|
||||
${CORE_FOUNDATION})
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp}
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client/ios")
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <winpr/wlog.h>
|
||||
|
||||
#include "TPCircularBuffer.h"
|
||||
#include "rdpsnd_main.h"
|
||||
|
||||
#include <mach/mach.h>
|
||||
#include <stdio.h>
|
||||
|
@ -42,6 +42,4 @@ set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client/Mac")
|
||||
|
@ -5,6 +5,8 @@
|
||||
* Copyright 2012 Laxmikant Rashinkar <LK.Rashinkar@gmail.com>
|
||||
* Copyright 2015 Thincast Technologies GmbH
|
||||
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
|
||||
* Copyright 2016 Inuvika Inc.
|
||||
* Copyright 2016 David PHAM-VAN <d.phamvan@inuvika.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -54,6 +56,12 @@ struct rdpsnd_mac_plugin
|
||||
AudioQueueRef audioQueue;
|
||||
AudioStreamBasicDescription audioFormat;
|
||||
AudioQueueBufferRef audioBuffers[MAC_AUDIO_QUEUE_NUM_BUFFERS];
|
||||
|
||||
Float64 lastStartTime;
|
||||
|
||||
int wformat;
|
||||
int block_size;
|
||||
FREERDP_DSP_CONTEXT* dsp_context;
|
||||
};
|
||||
typedef struct rdpsnd_mac_plugin rdpsndMacPlugin;
|
||||
|
||||
@ -69,6 +77,15 @@ static BOOL rdpsnd_mac_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* form
|
||||
mac->latency = (UINT32) latency;
|
||||
CopyMemory(&(mac->format), format, sizeof(AUDIO_FORMAT));
|
||||
|
||||
mac->audioFormat.mSampleRate = format->nSamplesPerSec;
|
||||
mac->audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
|
||||
mac->audioFormat.mFramesPerPacket = 1;
|
||||
mac->audioFormat.mChannelsPerFrame = format->nChannels;
|
||||
mac->audioFormat.mBitsPerChannel = format->wBitsPerSample;
|
||||
mac->audioFormat.mBytesPerFrame = (format->wBitsPerSample * format->nChannels) / 8;
|
||||
mac->audioFormat.mBytesPerPacket = format->nBlockAlign;
|
||||
mac->audioFormat.mReserved = 0;
|
||||
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_ALAW:
|
||||
@ -83,6 +100,14 @@ static BOOL rdpsnd_mac_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* form
|
||||
mac->audioFormat.mFormatID = kAudioFormatLinearPCM;
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
mac->audioFormat.mFormatID = kAudioFormatLinearPCM;
|
||||
mac->audioFormat.mBitsPerChannel = 16;
|
||||
mac->audioFormat.mBytesPerFrame = (16 * format->nChannels) / 8;
|
||||
mac->audioFormat.mBytesPerPacket = mac->audioFormat.mFramesPerPacket * mac->audioFormat.mBytesPerFrame;
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_GSM610:
|
||||
mac->audioFormat.mFormatID = kAudioFormatMicrosoftGSM;
|
||||
break;
|
||||
@ -91,14 +116,8 @@ static BOOL rdpsnd_mac_set_format(rdpsndDevicePlugin* device, AUDIO_FORMAT* form
|
||||
break;
|
||||
}
|
||||
|
||||
mac->audioFormat.mSampleRate = format->nSamplesPerSec;
|
||||
mac->audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
|
||||
mac->audioFormat.mFramesPerPacket = 1;
|
||||
mac->audioFormat.mChannelsPerFrame = format->nChannels;
|
||||
mac->audioFormat.mBitsPerChannel = format->wBitsPerSample;
|
||||
mac->audioFormat.mBytesPerFrame = (format->wBitsPerSample * format->nChannels) / 8;
|
||||
mac->audioFormat.mBytesPerPacket = format->nBlockAlign;
|
||||
mac->audioFormat.mReserved = 0;
|
||||
mac->wformat = format->wFormatTag;
|
||||
mac->block_size = format->nBlockAlign;
|
||||
|
||||
rdpsnd_print_audio_format(format);
|
||||
return TRUE;
|
||||
@ -122,6 +141,8 @@ static BOOL rdpsnd_mac_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, in
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
freerdp_dsp_context_reset_adpcm(mac->dsp_context);
|
||||
|
||||
status = AudioQueueNewOutput(&(mac->audioFormat),
|
||||
mac_audio_queue_output_cb, mac,
|
||||
NULL, NULL, 0, &(mac->audioQueue));
|
||||
@ -156,7 +177,9 @@ static BOOL rdpsnd_mac_open(rdpsndDevicePlugin* device, AUDIO_FORMAT* format, in
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mac->lastStartTime = 0;
|
||||
|
||||
mac->isOpen = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
@ -184,27 +207,24 @@ static void rdpsnd_mac_free(rdpsndDevicePlugin* device)
|
||||
|
||||
device->Close(device);
|
||||
|
||||
freerdp_dsp_context_free(mac->dsp_context);
|
||||
|
||||
free(mac);
|
||||
}
|
||||
|
||||
static BOOL rdpsnd_mac_format_supported(rdpsndDevicePlugin* device, AUDIO_FORMAT* format)
|
||||
{
|
||||
if (format->wFormatTag == WAVE_FORMAT_PCM)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
else if (format->wFormatTag == WAVE_FORMAT_ALAW)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
else if (format->wFormatTag == WAVE_FORMAT_MULAW)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
else if (format->wFormatTag == WAVE_FORMAT_GSM610)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
case WAVE_FORMAT_ALAW:
|
||||
case WAVE_FORMAT_MULAW:
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
return TRUE;
|
||||
case WAVE_FORMAT_GSM610:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
@ -258,10 +278,42 @@ static void rdpsnd_mac_start(rdpsndDevicePlugin* device)
|
||||
}
|
||||
}
|
||||
|
||||
static void rdpsnd_mac_play(rdpsndDevicePlugin* device, BYTE* data, int size)
|
||||
static BOOL rdpsnd_mac_wave_decode(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
|
||||
{
|
||||
int length;
|
||||
BYTE* data;
|
||||
rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device;
|
||||
|
||||
if (mac->wformat == WAVE_FORMAT_ADPCM)
|
||||
{
|
||||
mac->dsp_context->decode_ms_adpcm(mac->dsp_context, wave->data, wave->length, mac->format.nChannels, mac->block_size);
|
||||
length = mac->dsp_context->adpcm_size;
|
||||
data = mac->dsp_context->adpcm_buffer;
|
||||
}
|
||||
else if (mac->wformat == WAVE_FORMAT_DVI_ADPCM)
|
||||
{
|
||||
mac->dsp_context->decode_ima_adpcm(mac->dsp_context, wave->data, wave->length, mac->format.nChannels, mac->block_size);
|
||||
length = mac->dsp_context->adpcm_size;
|
||||
data = mac->dsp_context->adpcm_buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
length = wave->length;
|
||||
data = wave->data;
|
||||
}
|
||||
|
||||
wave->data = (BYTE*) malloc(length);
|
||||
CopyMemory(wave->data, data, length);
|
||||
wave->length = length;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void rdpsnd_mac_waveplay(rdpsndDevicePlugin* device, RDPSND_WAVE* wave)
|
||||
{
|
||||
int length;
|
||||
AudioQueueBufferRef audioBuffer;
|
||||
AudioTimeStamp outActualStartTime;
|
||||
rdpsndMacPlugin* mac = (rdpsndMacPlugin*) device;
|
||||
|
||||
if (!mac->isOpen)
|
||||
@ -269,13 +321,18 @@ static void rdpsnd_mac_play(rdpsndDevicePlugin* device, BYTE* data, int size)
|
||||
|
||||
audioBuffer = mac->audioBuffers[mac->audioBufferIndex];
|
||||
|
||||
length = size > audioBuffer->mAudioDataBytesCapacity ? audioBuffer->mAudioDataBytesCapacity : size;
|
||||
length = wave->length > audioBuffer->mAudioDataBytesCapacity ? audioBuffer->mAudioDataBytesCapacity : wave->length;
|
||||
|
||||
CopyMemory(audioBuffer->mAudioData, data, length);
|
||||
CopyMemory(audioBuffer->mAudioData, wave->data, length);
|
||||
audioBuffer->mAudioDataByteSize = length;
|
||||
|
||||
AudioQueueEnqueueBuffer(mac->audioQueue, audioBuffer, 0, 0);
|
||||
|
||||
audioBuffer->mUserData = wave;
|
||||
|
||||
AudioQueueEnqueueBufferWithParameters(mac->audioQueue, audioBuffer, 0, 0, 0, 0, 0, NULL, NULL, &outActualStartTime);
|
||||
UInt64 startTimeDelta = (outActualStartTime.mSampleTime - mac->lastStartTime) / 100.0;
|
||||
wave->wLocalTimeB = wave->wLocalTimeA + startTimeDelta + wave->wAudioLength;
|
||||
wave->wTimeStampB = wave->wTimeStampA + wave->wLocalTimeB - wave->wLocalTimeA;
|
||||
mac->lastStartTime = outActualStartTime.mSampleTime;
|
||||
|
||||
mac->audioBufferIndex++;
|
||||
|
||||
if (mac->audioBufferIndex >= MAC_AUDIO_QUEUE_NUM_BUFFERS)
|
||||
@ -308,10 +365,13 @@ UINT freerdp_rdpsnd_client_subsystem_entry(PFREERDP_RDPSND_DEVICE_ENTRY_POINTS p
|
||||
mac->device.FormatSupported = rdpsnd_mac_format_supported;
|
||||
mac->device.SetFormat = rdpsnd_mac_set_format;
|
||||
mac->device.SetVolume = rdpsnd_mac_set_volume;
|
||||
mac->device.Play = rdpsnd_mac_play;
|
||||
mac->device.WaveDecode = rdpsnd_mac_wave_decode;
|
||||
mac->device.WavePlay = rdpsnd_mac_waveplay;
|
||||
mac->device.Start = rdpsnd_mac_start;
|
||||
mac->device.Close = rdpsnd_mac_close;
|
||||
mac->device.Free = rdpsnd_mac_free;
|
||||
|
||||
mac->dsp_context = freerdp_dsp_context_new();
|
||||
|
||||
pEntryPoints->pRegisterRdpsndDevice(pEntryPoints->rdpsnd, (rdpsndDevicePlugin*) mac);
|
||||
|
||||
|
@ -31,5 +31,3 @@ add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_N
|
||||
set(${MODULE_PREFIX}_LIBS freerdp ${OPENSLES_LIBRARIES})
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
@ -306,6 +306,7 @@ int android_AudioOut(OPENSL_STREAM *p, const short *buffer,int size)
|
||||
return -1;
|
||||
}
|
||||
memcpy(data, buffer, size * sizeof(short));
|
||||
Queue_Enqueue(p->queue, data);
|
||||
(*p->bqPlayerBufferQueue)->Enqueue(p->bqPlayerBufferQueue,
|
||||
data, sizeof(short) * size);
|
||||
|
||||
|
@ -138,32 +138,7 @@ static BOOL rdpsnd_opensles_set_format(rdpsndDevicePlugin* device,
|
||||
|
||||
opensles->rate = format->nSamplesPerSec;
|
||||
opensles->channels = format->nChannels;
|
||||
|
||||
switch (format->wFormatTag)
|
||||
{
|
||||
case WAVE_FORMAT_PCM:
|
||||
switch (format->wBitsPerSample)
|
||||
{
|
||||
case 4:
|
||||
opensles->format = WAVE_FORMAT_ADPCM;
|
||||
break;
|
||||
|
||||
case 8:
|
||||
opensles->format = WAVE_FORMAT_PCM;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
opensles->format = WAVE_FORMAT_ADPCM;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case WAVE_FORMAT_ADPCM:
|
||||
case WAVE_FORMAT_DVI_ADPCM:
|
||||
opensles->format = format->wFormatTag;
|
||||
break;
|
||||
}
|
||||
|
||||
opensles->format = format->wFormatTag;
|
||||
opensles->wformat = format->wFormatTag;
|
||||
opensles->block_size = format->nBlockAlign;
|
||||
}
|
||||
|
@ -33,6 +33,4 @@ set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} ${OSS_LIBRARIES})
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client/OSS")
|
||||
|
@ -36,6 +36,4 @@ endif()
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client/Pulse")
|
||||
|
@ -32,6 +32,8 @@ set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT STATIC_CHANNELS AND BUILD_SHARED_LIBS)
|
||||
install(FILES ${CMAKE_PDB_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client/WinMM")
|
||||
|
@ -27,6 +27,5 @@ add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE
|
||||
|
||||
target_link_libraries(${MODULE_NAME} freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")
|
||||
|
@ -27,6 +27,5 @@ add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
@ -23,12 +23,9 @@ set(${MODULE_PREFIX}_SRCS
|
||||
|
||||
add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "VirtualChannelEntry")
|
||||
|
||||
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server")
|
||||
|
@ -26,6 +26,9 @@ add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE
|
||||
|
||||
target_link_libraries(${MODULE_NAME} winpr freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT STATIC_CHANNELS AND BUILD_SHARED_LIBS)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
@ -47,6 +47,8 @@
|
||||
#include <freerdp/server/rdpdr.h>
|
||||
#include <freerdp/server/rdpei.h>
|
||||
#include <freerdp/server/drdynvc.h>
|
||||
#include <freerdp/server/remdesk.h>
|
||||
#include <freerdp/server/encomsp.h>
|
||||
|
||||
void freerdp_channels_dummy()
|
||||
{
|
||||
@ -70,6 +72,13 @@ void freerdp_channels_dummy()
|
||||
|
||||
rdpei_server_context_new(NULL);
|
||||
rdpei_server_context_free(NULL);
|
||||
|
||||
remdesk_server_context_new(NULL);
|
||||
remdesk_server_context_free(NULL);
|
||||
|
||||
encomsp_server_context_new(NULL);
|
||||
encomsp_server_context_free(NULL);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -30,6 +30,5 @@ add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE
|
||||
|
||||
target_link_libraries(${MODULE_NAME} winpr freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
@ -42,7 +42,9 @@ add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE
|
||||
|
||||
target_link_libraries(${MODULE_NAME} freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT STATIC_CHANNELS AND BUILD_SHARED_LIBS)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
||||
|
@ -28,5 +28,3 @@ add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_N
|
||||
|
||||
|
||||
target_link_libraries(${MODULE_NAME} freerdp ${ALSA_LIBRARIES})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
@ -39,4 +39,7 @@ else()
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
endif()
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT STATIC_CHANNELS AND BUILD_SHARED_LIBS)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
||||
endif()
|
||||
|
||||
|
@ -48,9 +48,7 @@ else()
|
||||
tsmf_X11.c)
|
||||
set(LIBS ${LIBS} ${X11_LIBRARIES} ${XEXT_LIBRARIES})
|
||||
|
||||
if(NOT XEXT_FOUND)
|
||||
message(FATAL_ERROR "Xext library not found, but required for TSMF module.")
|
||||
else()
|
||||
if(XEXT_FOUND)
|
||||
add_definitions(-DWITH_XEXT=1)
|
||||
endif()
|
||||
|
||||
@ -65,5 +63,3 @@ add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_N
|
||||
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${LIBS} freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
@ -61,6 +61,16 @@ struct X11Handle
|
||||
#endif
|
||||
Display *disp;
|
||||
Window subwin;
|
||||
BOOL subwinMapped;
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
GstVideoOverlay *overlay;
|
||||
#else
|
||||
GstXOverlay *overlay;
|
||||
#endif
|
||||
int subwinWidth;
|
||||
int subwinHeight;
|
||||
int subwinX;
|
||||
int subwinY;
|
||||
};
|
||||
|
||||
static const char* get_shm_id()
|
||||
@ -70,9 +80,71 @@ static const char* get_shm_id()
|
||||
return shm_id;
|
||||
}
|
||||
|
||||
static GstBusSyncReply tsmf_platform_bus_sync_handler(GstBus *bus, GstMessage *message, gpointer user_data)
|
||||
{
|
||||
struct X11Handle* hdl;
|
||||
|
||||
TSMFGstreamerDecoder* decoder = user_data;
|
||||
|
||||
if (GST_MESSAGE_TYPE (message) != GST_MESSAGE_ELEMENT)
|
||||
return GST_BUS_PASS;
|
||||
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
if (!gst_is_video_overlay_prepare_window_handle_message (message))
|
||||
return GST_BUS_PASS;
|
||||
#else
|
||||
if (!gst_structure_has_name (message->structure, "prepare-xwindow-id"))
|
||||
return GST_BUS_PASS;
|
||||
#endif
|
||||
|
||||
hdl = (struct X11Handle*) decoder->platform;
|
||||
|
||||
if (hdl->subwin)
|
||||
{
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
hdl->overlay = GST_VIDEO_OVERLAY (GST_MESSAGE_SRC (message));
|
||||
gst_video_overlay_set_window_handle(hdl->overlay, hdl->subwin);
|
||||
gst_video_overlay_handle_events(hdl->overlay, TRUE);
|
||||
#else
|
||||
hdl->overlay = GST_X_OVERLAY (GST_MESSAGE_SRC (message));
|
||||
gst_x_overlay_set_window_handle(hdl->overlay, hdl->subwin);
|
||||
gst_x_overlay_handle_events(hdl->overlay, TRUE);
|
||||
#endif
|
||||
|
||||
if (hdl->subwinWidth != -1 && hdl->subwinHeight != -1 && hdl->subwinX != -1 && hdl->subwinY != -1)
|
||||
{
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
if (!gst_video_overlay_set_render_rectangle(hdl->overlay, 0, 0, hdl->subwinWidth, hdl->subwinHeight))
|
||||
{
|
||||
WLog_ERR(TAG, "Could not resize overlay!");
|
||||
}
|
||||
|
||||
gst_video_overlay_expose(hdl->overlay);
|
||||
#else
|
||||
if (!gst_x_overlay_set_render_rectangle(hdl->overlay, 0, 0, hdl->subwinWidth, hdl->subwinHeight))
|
||||
{
|
||||
WLog_ERR(TAG, "Could not resize overlay!");
|
||||
}
|
||||
|
||||
gst_x_overlay_expose(hdl->overlay);
|
||||
#endif
|
||||
XLockDisplay(hdl->disp);
|
||||
XMoveResizeWindow(hdl->disp, hdl->subwin, hdl->subwinX, hdl->subwinY, hdl->subwinWidth, hdl->subwinHeight);
|
||||
XSync(hdl->disp, FALSE);
|
||||
XUnlockDisplay(hdl->disp);
|
||||
}
|
||||
} else {
|
||||
g_warning ("Window was not available before retrieving the overlay!");
|
||||
}
|
||||
|
||||
gst_message_unref (message);
|
||||
|
||||
return GST_BUS_DROP;
|
||||
}
|
||||
|
||||
const char* tsmf_platform_get_video_sink(void)
|
||||
{
|
||||
return "xvimagesink";
|
||||
return "autovideosink";
|
||||
}
|
||||
|
||||
const char* tsmf_platform_get_audio_sink(void)
|
||||
@ -119,6 +191,12 @@ int tsmf_platform_create(TSMFGstreamerDecoder* decoder)
|
||||
return -4;
|
||||
}
|
||||
|
||||
hdl->subwinMapped = FALSE;
|
||||
hdl->subwinX = -1;
|
||||
hdl->subwinY = -1;
|
||||
hdl->subwinWidth = -1;
|
||||
hdl->subwinHeight = -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -147,12 +225,20 @@ int tsmf_platform_register_handler(TSMFGstreamerDecoder* decoder)
|
||||
|
||||
bus = gst_pipeline_get_bus(GST_PIPELINE(decoder->pipe));
|
||||
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
gst_bus_set_sync_handler (bus, (GstBusSyncHandler) tsmf_platform_bus_sync_handler, decoder, NULL);
|
||||
#else
|
||||
gst_bus_set_sync_handler (bus, (GstBusSyncHandler) tsmf_platform_bus_sync_handler, decoder);
|
||||
#endif
|
||||
|
||||
if (!bus)
|
||||
{
|
||||
WLog_ERR(TAG, "gst_pipeline_get_bus failed!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
gst_object_unref (bus);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -189,12 +275,6 @@ int tsmf_window_create(TSMFGstreamerDecoder* decoder)
|
||||
}
|
||||
else
|
||||
{
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
GstVideoOverlay *overlay = GST_VIDEO_OVERLAY(decoder->outsink);
|
||||
#else
|
||||
GstXOverlay *overlay = GST_X_OVERLAY(decoder->outsink);
|
||||
#endif
|
||||
|
||||
if (!decoder)
|
||||
return -1;
|
||||
|
||||
@ -205,38 +285,32 @@ int tsmf_window_create(TSMFGstreamerDecoder* decoder)
|
||||
|
||||
if (!hdl->subwin)
|
||||
{
|
||||
int event, error;
|
||||
XLockDisplay(hdl->disp);
|
||||
hdl->subwin = XCreateSimpleWindow(hdl->disp, *(int *)hdl->xfwin, 0, 0, 1, 1, 0, 0, 0);
|
||||
XUnlockDisplay(hdl->disp);
|
||||
|
||||
if (!hdl->subwin)
|
||||
{
|
||||
WLog_ERR(TAG, "Could not create subwindow!");
|
||||
}
|
||||
|
||||
XMapWindow(hdl->disp, hdl->subwin);
|
||||
XSync(hdl->disp, FALSE);
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
gst_video_overlay_set_window_handle(overlay, hdl->subwin);
|
||||
#else
|
||||
gst_x_overlay_set_window_handle(overlay, hdl->subwin);
|
||||
#endif
|
||||
decoder->ready = TRUE;
|
||||
#if defined(WITH_XEXT)
|
||||
hdl->has_shape = XShapeQueryExtension(hdl->disp, &event, &error);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
gst_video_overlay_handle_events(overlay, TRUE);
|
||||
#else
|
||||
gst_x_overlay_handle_events(overlay, TRUE);
|
||||
tsmf_window_map(decoder);
|
||||
|
||||
decoder->ready = TRUE;
|
||||
#if defined(WITH_XEXT)
|
||||
int event, error;
|
||||
XLockDisplay(hdl->disp);
|
||||
hdl->has_shape = XShapeQueryExtension(hdl->disp, &event, &error);
|
||||
XUnlockDisplay(hdl->disp);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tsmf_window_resize(TSMFGstreamerDecoder* decoder, int x, int y, int width,
|
||||
int height, int nr_rects, RDP_RECT *rects)
|
||||
int height, int nr_rects, RDP_RECT *rects)
|
||||
{
|
||||
struct X11Handle* hdl;
|
||||
|
||||
@ -245,11 +319,6 @@ int tsmf_window_resize(TSMFGstreamerDecoder* decoder, int x, int y, int width,
|
||||
return -3;
|
||||
}
|
||||
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
GstVideoOverlay *overlay = GST_VIDEO_OVERLAY(decoder->outsink);
|
||||
#else
|
||||
GstXOverlay *overlay = GST_X_OVERLAY(decoder->outsink);
|
||||
#endif
|
||||
if (!decoder)
|
||||
return -1;
|
||||
|
||||
@ -259,49 +328,78 @@ int tsmf_window_resize(TSMFGstreamerDecoder* decoder, int x, int y, int width,
|
||||
hdl = (struct X11Handle*) decoder->platform;
|
||||
DEBUG_TSMF("resize: x=%d, y=%d, w=%d, h=%d", x, y, width, height);
|
||||
|
||||
if (hdl->overlay)
|
||||
{
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
|
||||
if (!gst_video_overlay_set_render_rectangle(overlay, 0, 0, width, height))
|
||||
{
|
||||
WLog_ERR(TAG, "Could not resize overlay!");
|
||||
}
|
||||
if (!gst_video_overlay_set_render_rectangle(hdl->overlay, 0, 0, width, height))
|
||||
{
|
||||
WLog_ERR(TAG, "Could not resize overlay!");
|
||||
}
|
||||
|
||||
gst_video_overlay_expose(overlay);
|
||||
gst_video_overlay_expose(hdl->overlay);
|
||||
#else
|
||||
if (!gst_x_overlay_set_render_rectangle(overlay, 0, 0, width, height))
|
||||
{
|
||||
WLog_ERR(TAG, "Could not resize overlay!");
|
||||
}
|
||||
if (!gst_x_overlay_set_render_rectangle(hdl->overlay, 0, 0, width, height))
|
||||
{
|
||||
WLog_ERR(TAG, "Could not resize overlay!");
|
||||
}
|
||||
|
||||
gst_x_overlay_expose(overlay);
|
||||
gst_x_overlay_expose(hdl->overlay);
|
||||
#endif
|
||||
}
|
||||
|
||||
if (hdl->subwin)
|
||||
{
|
||||
XMoveResizeWindow(hdl->disp, hdl->subwin, x, y, width, height);
|
||||
#if defined(WITH_XEXT)
|
||||
hdl->subwinX = x;
|
||||
hdl->subwinY = y;
|
||||
hdl->subwinWidth = width;
|
||||
hdl->subwinHeight = height;
|
||||
|
||||
XLockDisplay(hdl->disp);
|
||||
XMoveResizeWindow(hdl->disp, hdl->subwin, hdl->subwinX, hdl->subwinY, hdl->subwinWidth, hdl->subwinHeight);
|
||||
|
||||
/* Unmap the window if there are no visibility rects */
|
||||
if (nr_rects == 0)
|
||||
tsmf_window_unmap(decoder);
|
||||
else
|
||||
tsmf_window_map(decoder);
|
||||
|
||||
#if defined(WITH_XEXT)
|
||||
if (hdl->has_shape)
|
||||
{
|
||||
int i;
|
||||
XRectangle *xrects = calloc(nr_rects, sizeof(XRectangle));
|
||||
if (!xrects)
|
||||
return -1;
|
||||
XRectangle *xrects = NULL;
|
||||
|
||||
for (i = 0; i < nr_rects; i++)
|
||||
if (nr_rects == 0)
|
||||
{
|
||||
xrects[i].x = rects[i].x - x;
|
||||
xrects[i].y = rects[i].y - y;
|
||||
xrects[i].width = rects[i].width;
|
||||
xrects[i].height = rects[i].height;
|
||||
xrects = calloc(1, sizeof(XRectangle));
|
||||
xrects->x = x;
|
||||
xrects->y = y;
|
||||
xrects->width = width;
|
||||
xrects->height = height;
|
||||
}
|
||||
else
|
||||
{
|
||||
xrects = calloc(nr_rects, sizeof(XRectangle));
|
||||
}
|
||||
|
||||
if (xrects)
|
||||
{
|
||||
for (i = 0; i < nr_rects; i++)
|
||||
{
|
||||
xrects[i].x = rects[i].x - x;
|
||||
xrects[i].y = rects[i].y - y;
|
||||
xrects[i].width = rects[i].width;
|
||||
xrects[i].height = rects[i].height;
|
||||
}
|
||||
|
||||
XShapeCombineRectangles(hdl->disp, hdl->subwin, ShapeBounding, x, y, xrects, nr_rects, ShapeSet, 0);
|
||||
free(xrects);
|
||||
XShapeCombineRectangles(hdl->disp, hdl->subwin, ShapeBounding, x, y, xrects, nr_rects, ShapeSet, 0);
|
||||
free(xrects);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
XSync(hdl->disp, FALSE);
|
||||
XUnlockDisplay(hdl->disp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -323,6 +421,49 @@ int tsmf_window_resume(TSMFGstreamerDecoder* decoder)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tsmf_window_map(TSMFGstreamerDecoder* decoder)
|
||||
{
|
||||
struct X11Handle* hdl;
|
||||
if (!decoder)
|
||||
return -1;
|
||||
|
||||
hdl = (struct X11Handle*) decoder->platform;
|
||||
|
||||
/* Only need to map the window if it is not currently mapped */
|
||||
if ((hdl->subwin) && (!hdl->subwinMapped))
|
||||
{
|
||||
XLockDisplay(hdl->disp);
|
||||
XMapWindow(hdl->disp, hdl->subwin);
|
||||
hdl->subwinMapped = TRUE;
|
||||
XSync(hdl->disp, FALSE);
|
||||
XUnlockDisplay(hdl->disp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int tsmf_window_unmap(TSMFGstreamerDecoder* decoder)
|
||||
{
|
||||
struct X11Handle* hdl;
|
||||
if (!decoder)
|
||||
return -1;
|
||||
|
||||
hdl = (struct X11Handle*) decoder->platform;
|
||||
|
||||
/* only need to unmap window if it is currently mapped */
|
||||
if ((hdl->subwin) && (hdl->subwinMapped))
|
||||
{
|
||||
XLockDisplay(hdl->disp);
|
||||
XUnmapWindow(hdl->disp, hdl->subwin);
|
||||
hdl->subwinMapped = FALSE;
|
||||
XSync(hdl->disp, FALSE);
|
||||
XUnlockDisplay(hdl->disp);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int tsmf_window_destroy(TSMFGstreamerDecoder* decoder)
|
||||
{
|
||||
struct X11Handle* hdl;
|
||||
@ -341,11 +482,19 @@ int tsmf_window_destroy(TSMFGstreamerDecoder* decoder)
|
||||
|
||||
if (hdl->subwin)
|
||||
{
|
||||
XLockDisplay(hdl->disp);
|
||||
XDestroyWindow(hdl->disp, hdl->subwin);
|
||||
XSync(hdl->disp, FALSE);
|
||||
XUnlockDisplay(hdl->disp);
|
||||
}
|
||||
|
||||
hdl->overlay = NULL;
|
||||
hdl->subwin = 0;
|
||||
hdl->subwinMapped = FALSE;
|
||||
hdl->subwinX = -1;
|
||||
hdl->subwinY = -1;
|
||||
hdl->subwinWidth = -1;
|
||||
hdl->subwinHeight = -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -45,20 +45,52 @@
|
||||
#include <inttypes.h>
|
||||
#endif
|
||||
|
||||
/* 1 second = 10,000,000 100ns units*/
|
||||
#define SEEK_TOLERANCE 10*1000*1000
|
||||
|
||||
static BOOL tsmf_gstreamer_pipeline_build(TSMFGstreamerDecoder* mdecoder);
|
||||
static void tsmf_gstreamer_clean_up(TSMFGstreamerDecoder* mdecoder);
|
||||
static int tsmf_gstreamer_pipeline_set_state(TSMFGstreamerDecoder* mdecoder,
|
||||
GstState desired_state);
|
||||
static BOOL tsmf_gstreamer_buffer_level(ITSMFDecoder* decoder);
|
||||
|
||||
const char* get_type(TSMFGstreamerDecoder* mdecoder)
|
||||
{
|
||||
if (!mdecoder)
|
||||
return NULL;
|
||||
|
||||
if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
|
||||
return "VIDEO";
|
||||
else
|
||||
return "AUDIO";
|
||||
switch (mdecoder->media_type)
|
||||
{
|
||||
case TSMF_MAJOR_TYPE_VIDEO:
|
||||
return "VIDEO";
|
||||
case TSMF_MAJOR_TYPE_AUDIO:
|
||||
return "AUDIO";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static void cb_child_added(GstChildProxy *child_proxy, GObject *object, TSMFGstreamerDecoder* mdecoder)
|
||||
{
|
||||
DEBUG_TSMF("NAME: %s", G_OBJECT_TYPE_NAME(object));
|
||||
|
||||
if (!g_strcmp0(G_OBJECT_TYPE_NAME(object), "GstXvImageSink") || !g_strcmp0(G_OBJECT_TYPE_NAME(object), "GstXImageSink") || !g_strcmp0(G_OBJECT_TYPE_NAME(object), "GstFluVAAutoSink"))
|
||||
{
|
||||
gst_base_sink_set_max_lateness((GstBaseSink *) object, 10000000); /* nanoseconds */
|
||||
g_object_set(G_OBJECT(object), "sync", TRUE, NULL); /* synchronize on the clock */
|
||||
g_object_set(G_OBJECT(object), "async", TRUE, NULL); /* no async state changes */
|
||||
}
|
||||
|
||||
else if (!g_strcmp0(G_OBJECT_TYPE_NAME(object), "GstAlsaSink") || !g_strcmp0(G_OBJECT_TYPE_NAME(object), "GstPulseSink"))
|
||||
{
|
||||
gst_base_sink_set_max_lateness((GstBaseSink *) object, 10000000); /* nanoseconds */
|
||||
g_object_set(G_OBJECT(object), "slave-method", 1, NULL);
|
||||
g_object_set(G_OBJECT(object), "buffer-time", (gint64) 20000, NULL); /* microseconds */
|
||||
g_object_set(G_OBJECT(object), "drift-tolerance", (gint64) 20000, NULL); /* microseconds */
|
||||
g_object_set(G_OBJECT(object), "latency-time", (gint64) 10000, NULL); /* microseconds */
|
||||
g_object_set(G_OBJECT(object), "sync", TRUE, NULL); /* synchronize on the clock */
|
||||
g_object_set(G_OBJECT(object), "async", TRUE, NULL); /* no async state changes */
|
||||
}
|
||||
}
|
||||
|
||||
static void tsmf_gstreamer_enough_data(GstAppSrc *src, gpointer user_data)
|
||||
@ -81,16 +113,32 @@ static gboolean tsmf_gstreamer_seek_data(GstAppSrc *src, guint64 offset, gpointe
|
||||
(void) mdecoder;
|
||||
DEBUG_TSMF("%s offset=%llu", get_type(mdecoder), offset);
|
||||
|
||||
if (!mdecoder->paused)
|
||||
tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PAUSED);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gst_app_src_end_of_stream((GstAppSrc*) mdecoder->src);
|
||||
static BOOL tsmf_gstreamer_change_volume(ITSMFDecoder* decoder, UINT32 newVolume, UINT32 muted)
|
||||
{
|
||||
TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder *) decoder;
|
||||
|
||||
if (!mdecoder->paused)
|
||||
tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PLAYING);
|
||||
if (!mdecoder || !mdecoder->pipe)
|
||||
return TRUE;
|
||||
|
||||
if (mdecoder->sync_cb)
|
||||
mdecoder->sync_cb(mdecoder->stream);
|
||||
if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
|
||||
return TRUE;
|
||||
|
||||
mdecoder->gstMuted = (BOOL) muted;
|
||||
DEBUG_TSMF("mute=[%d]", mdecoder->gstMuted);
|
||||
mdecoder->gstVolume = (double) newVolume / (double) 10000;
|
||||
DEBUG_TSMF("gst_new_vol=[%f]", mdecoder->gstVolume);
|
||||
|
||||
if (!mdecoder->volume)
|
||||
return TRUE;
|
||||
|
||||
if (!G_IS_OBJECT(mdecoder->volume))
|
||||
return TRUE;
|
||||
|
||||
g_object_set(mdecoder->volume, "mute", mdecoder->gstMuted, NULL);
|
||||
g_object_set(mdecoder->volume, "volume", mdecoder->gstVolume, NULL);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@ -209,10 +257,17 @@ static BOOL tsmf_gstreamer_set_format(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* m
|
||||
{
|
||||
case TSMF_SUB_TYPE_WVC1:
|
||||
mdecoder->gst_caps = gst_caps_new_simple("video/x-wmv",
|
||||
"bitrate", G_TYPE_UINT, media_type->BitRate,
|
||||
"width", G_TYPE_INT, media_type->Width,
|
||||
"height", G_TYPE_INT, media_type->Height,
|
||||
"wmvversion", G_TYPE_INT, 3,
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
"format", G_TYPE_STRING, "WVC1",
|
||||
#else
|
||||
"format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('W', 'V', 'C', '1'),
|
||||
#endif
|
||||
"framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator, media_type->SamplesPerSecond.Denominator,
|
||||
"pixel-aspect-ratio", GST_TYPE_FRACTION, 1 , 1,
|
||||
NULL);
|
||||
break;
|
||||
case TSMF_SUB_TYPE_MP4S:
|
||||
@ -221,6 +276,12 @@ static BOOL tsmf_gstreamer_set_format(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* m
|
||||
"bitrate", G_TYPE_UINT, media_type->BitRate,
|
||||
"width", G_TYPE_INT, media_type->Width,
|
||||
"height", G_TYPE_INT, media_type->Height,
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
"format", G_TYPE_STRING, "MP42",
|
||||
#else
|
||||
"format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('M', 'P', '4', '2'),
|
||||
#endif
|
||||
"framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator, media_type->SamplesPerSecond.Denominator,
|
||||
NULL);
|
||||
break;
|
||||
case TSMF_SUB_TYPE_MP42:
|
||||
@ -229,15 +290,41 @@ static BOOL tsmf_gstreamer_set_format(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* m
|
||||
"bitrate", G_TYPE_UINT, media_type->BitRate,
|
||||
"width", G_TYPE_INT, media_type->Width,
|
||||
"height", G_TYPE_INT, media_type->Height,
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
"format", G_TYPE_STRING, "MP42",
|
||||
#else
|
||||
"format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('M', 'P', '4', '2'),
|
||||
#endif
|
||||
"framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator, media_type->SamplesPerSecond.Denominator,
|
||||
NULL);
|
||||
break;
|
||||
case TSMF_SUB_TYPE_MP43:
|
||||
mdecoder->gst_caps = gst_caps_new_simple("video/x-msmpeg",
|
||||
"msmpegversion", G_TYPE_INT, 43,
|
||||
"bitrate", G_TYPE_UINT, media_type->BitRate,
|
||||
"width", G_TYPE_INT, media_type->Width,
|
||||
"height", G_TYPE_INT, media_type->Height,
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
"format", G_TYPE_STRING, "MP43",
|
||||
#else
|
||||
"format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('M', 'P', '4', '3'),
|
||||
#endif
|
||||
"framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator, media_type->SamplesPerSecond.Denominator,
|
||||
NULL);
|
||||
break;
|
||||
case TSMF_SUB_TYPE_M4S2:
|
||||
mdecoder->gst_caps = gst_caps_new_simple ("video/mpeg",
|
||||
"mpegversion", G_TYPE_INT, 4,
|
||||
"width", G_TYPE_INT, media_type->Width,
|
||||
"height", G_TYPE_INT, media_type->Height,
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
"format", G_TYPE_STRING, "M4S2",
|
||||
#else
|
||||
"format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('M', '4', 'S', '2'),
|
||||
#endif
|
||||
"framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator, media_type->SamplesPerSecond.Denominator,
|
||||
NULL);
|
||||
break;
|
||||
case TSMF_SUB_TYPE_WMA9:
|
||||
mdecoder->gst_caps = gst_caps_new_simple("audio/x-wma",
|
||||
"wmaversion", G_TYPE_INT, 3,
|
||||
@ -249,6 +336,17 @@ static BOOL tsmf_gstreamer_set_format(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* m
|
||||
"block_align", G_TYPE_INT, media_type->BlockAlign,
|
||||
NULL);
|
||||
break;
|
||||
case TSMF_SUB_TYPE_WMA1:
|
||||
mdecoder->gst_caps = gst_caps_new_simple ("audio/x-wma",
|
||||
"wmaversion", G_TYPE_INT, 1,
|
||||
"rate", G_TYPE_INT, media_type->SamplesPerSecond.Numerator,
|
||||
"channels", G_TYPE_INT, media_type->Channels,
|
||||
"bitrate", G_TYPE_INT, media_type->BitRate,
|
||||
"depth", G_TYPE_INT, media_type->BitsPerSample,
|
||||
"width", G_TYPE_INT, media_type->BitsPerSample,
|
||||
"block_align", G_TYPE_INT, media_type->BlockAlign,
|
||||
NULL);
|
||||
break;
|
||||
case TSMF_SUB_TYPE_WMA2:
|
||||
mdecoder->gst_caps = gst_caps_new_simple("audio/x-wma",
|
||||
"wmaversion", G_TYPE_INT, 2,
|
||||
@ -274,6 +372,12 @@ static BOOL tsmf_gstreamer_set_format(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* m
|
||||
"width", G_TYPE_INT, media_type->Width,
|
||||
"height", G_TYPE_INT, media_type->Height,
|
||||
"wmvversion", G_TYPE_INT, 1,
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
"format", G_TYPE_STRING, "WMV1",
|
||||
#else
|
||||
"format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('W', 'M', 'V', '1'),
|
||||
#endif
|
||||
"framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator, media_type->SamplesPerSecond.Denominator,
|
||||
NULL);
|
||||
break;
|
||||
case TSMF_SUB_TYPE_WMV2:
|
||||
@ -281,6 +385,13 @@ static BOOL tsmf_gstreamer_set_format(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* m
|
||||
"width", G_TYPE_INT, media_type->Width,
|
||||
"height", G_TYPE_INT, media_type->Height,
|
||||
"wmvversion", G_TYPE_INT, 2,
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
"format", G_TYPE_STRING, "WMV2",
|
||||
#else
|
||||
"format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('W', 'M', 'V', '2'),
|
||||
#endif
|
||||
"framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator, media_type->SamplesPerSecond.Denominator,
|
||||
"pixel-aspect-ratio", GST_TYPE_FRACTION, 1 , 1,
|
||||
NULL);
|
||||
break;
|
||||
case TSMF_SUB_TYPE_WMV3:
|
||||
@ -289,6 +400,13 @@ static BOOL tsmf_gstreamer_set_format(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* m
|
||||
"width", G_TYPE_INT, media_type->Width,
|
||||
"height", G_TYPE_INT, media_type->Height,
|
||||
"wmvversion", G_TYPE_INT, 3,
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
"format", G_TYPE_STRING, "WMV3",
|
||||
#else
|
||||
"format", GST_TYPE_FOURCC, GST_MAKE_FOURCC ('W', 'M', 'V', '3'),
|
||||
#endif
|
||||
"framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator, media_type->SamplesPerSecond.Denominator,
|
||||
"pixel-aspect-ratio", GST_TYPE_FRACTION, 1 , 1,
|
||||
NULL);
|
||||
break;
|
||||
case TSMF_SUB_TYPE_AVC1:
|
||||
@ -296,6 +414,10 @@ static BOOL tsmf_gstreamer_set_format(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* m
|
||||
mdecoder->gst_caps = gst_caps_new_simple("video/x-h264",
|
||||
"width", G_TYPE_INT, media_type->Width,
|
||||
"height", G_TYPE_INT, media_type->Height,
|
||||
"framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator, media_type->SamplesPerSecond.Denominator,
|
||||
"pixel-aspect-ratio", GST_TYPE_FRACTION, 1 , 1,
|
||||
"stream-format", G_TYPE_STRING, "byte-stream",
|
||||
"alignment", G_TYPE_STRING, "nal",
|
||||
NULL);
|
||||
break;
|
||||
case TSMF_SUB_TYPE_AC3:
|
||||
@ -319,6 +441,8 @@ static BOOL tsmf_gstreamer_set_format(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* m
|
||||
"rate", G_TYPE_INT, media_type->SamplesPerSecond.Numerator,
|
||||
"channels", G_TYPE_INT, media_type->Channels,
|
||||
"mpegversion", G_TYPE_INT, 4,
|
||||
"framed", G_TYPE_BOOLEAN, TRUE,
|
||||
"stream-format", G_TYPE_STRING, "raw",
|
||||
NULL);
|
||||
break;
|
||||
case TSMF_SUB_TYPE_MP1A:
|
||||
@ -347,6 +471,7 @@ static BOOL tsmf_gstreamer_set_format(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* m
|
||||
"format", G_TYPE_STRING, "YUY2",
|
||||
"width", G_TYPE_INT, media_type->Width,
|
||||
"height", G_TYPE_INT, media_type->Height,
|
||||
"framerate", GST_TYPE_FRACTION, media_type->SamplesPerSecond.Numerator, media_type->SamplesPerSecond.Denominator,
|
||||
NULL);
|
||||
#endif
|
||||
break;
|
||||
@ -358,11 +483,14 @@ static BOOL tsmf_gstreamer_set_format(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* m
|
||||
break;
|
||||
case TSMF_SUB_TYPE_MP2A:
|
||||
mdecoder->gst_caps = gst_caps_new_simple("audio/mpeg",
|
||||
"mpegversion", G_TYPE_INT, 2,
|
||||
"mpegversion", G_TYPE_INT, 1,
|
||||
"rate", G_TYPE_INT, media_type->SamplesPerSecond.Numerator,
|
||||
"channels", G_TYPE_INT, media_type->Channels,
|
||||
NULL);
|
||||
break;
|
||||
case TSMF_SUB_TYPE_FLAC:
|
||||
mdecoder->gst_caps = gst_caps_new_simple("audio/x-flac", "", NULL);
|
||||
break;
|
||||
default:
|
||||
WLog_ERR(TAG, "unknown format:(%d).", media_type->SubType);
|
||||
return FALSE;
|
||||
@ -404,17 +532,23 @@ void tsmf_gstreamer_clean_up(TSMFGstreamerDecoder* mdecoder)
|
||||
gst_object_unref(mdecoder->pipe);
|
||||
}
|
||||
|
||||
tsmf_window_destroy(mdecoder);
|
||||
mdecoder->ready = FALSE;
|
||||
mdecoder->paused = FALSE;
|
||||
|
||||
mdecoder->pipe = NULL;
|
||||
mdecoder->src = NULL;
|
||||
mdecoder->queue = NULL;
|
||||
}
|
||||
|
||||
BOOL tsmf_gstreamer_pipeline_build(TSMFGstreamerDecoder* mdecoder)
|
||||
{
|
||||
const char* appsrc = "appsrc name=source ! decodebin name=decoder !";
|
||||
const char* video = "autovideoconvert ! videoscale !";
|
||||
const char* audio = "audioconvert ! audiorate ! audioresample ! volume name=audiovolume !";
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
const char* video = "appsrc name=videosource ! queue2 name=videoqueue ! decodebin name=videodecoder !";
|
||||
const char* audio = "appsrc name=audiosource ! queue2 name=audioqueue ! decodebin name=audiodecoder ! audioconvert ! audiorate ! audioresample ! volume name=audiovolume !";
|
||||
#else
|
||||
const char* video = "appsrc name=videosource ! queue2 name=videoqueue ! decodebin2 name=videodecoder !";
|
||||
const char* audio = "appsrc name=audiosource ! queue2 name=audioqueue ! decodebin2 name=audiodecoder ! audioconvert ! audiorate ! audioresample ! volume name=audiovolume !";
|
||||
#endif
|
||||
char pipeline[1024];
|
||||
|
||||
if (!mdecoder)
|
||||
@ -424,9 +558,9 @@ BOOL tsmf_gstreamer_pipeline_build(TSMFGstreamerDecoder* mdecoder)
|
||||
* The only fixed elements necessary are appsrc and the volume element for audio streams.
|
||||
* The rest could easily be provided in gstreamer pipeline notation from command line. */
|
||||
if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
|
||||
sprintf_s(pipeline, sizeof(pipeline), "%s %s %s name=outsink", appsrc, video, tsmf_platform_get_video_sink());
|
||||
sprintf_s(pipeline, sizeof(pipeline), "%s %s name=videosink", video, tsmf_platform_get_video_sink());
|
||||
else
|
||||
sprintf_s(pipeline, sizeof(pipeline), "%s %s %s name=outsink", appsrc, audio, tsmf_platform_get_audio_sink());
|
||||
sprintf_s(pipeline, sizeof(pipeline), "%s %s name=audiosink", audio, tsmf_platform_get_audio_sink());
|
||||
|
||||
DEBUG_TSMF("pipeline=%s", pipeline);
|
||||
mdecoder->pipe = gst_parse_launch(pipeline, NULL);
|
||||
@ -437,7 +571,10 @@ BOOL tsmf_gstreamer_pipeline_build(TSMFGstreamerDecoder* mdecoder)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
mdecoder->src = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "source");
|
||||
if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
|
||||
mdecoder->src = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "videosource");
|
||||
else
|
||||
mdecoder->src = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "audiosource");
|
||||
|
||||
if (!mdecoder->src)
|
||||
{
|
||||
@ -445,7 +582,21 @@ BOOL tsmf_gstreamer_pipeline_build(TSMFGstreamerDecoder* mdecoder)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
mdecoder->outsink = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "outsink");
|
||||
if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
|
||||
mdecoder->queue = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "videoqueue");
|
||||
else
|
||||
mdecoder->queue = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "audioqueue");
|
||||
|
||||
if (!mdecoder->queue)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to get queue");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
|
||||
mdecoder->outsink = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "videosink");
|
||||
else
|
||||
mdecoder->outsink = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "audiosink");
|
||||
|
||||
if (!mdecoder->outsink)
|
||||
{
|
||||
@ -453,7 +604,9 @@ BOOL tsmf_gstreamer_pipeline_build(TSMFGstreamerDecoder* mdecoder)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (mdecoder->media_type != TSMF_MAJOR_TYPE_VIDEO)
|
||||
g_signal_connect(mdecoder->outsink, "child-added", G_CALLBACK(cb_child_added), mdecoder);
|
||||
|
||||
if (mdecoder->media_type == TSMF_MAJOR_TYPE_AUDIO)
|
||||
{
|
||||
mdecoder->volume = gst_bin_get_by_name(GST_BIN(mdecoder->pipe), "audiovolume");
|
||||
|
||||
@ -462,6 +615,8 @@ BOOL tsmf_gstreamer_pipeline_build(TSMFGstreamerDecoder* mdecoder)
|
||||
WLog_ERR(TAG, "Failed to get volume");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
tsmf_gstreamer_change_volume((ITSMFDecoder*)mdecoder, mdecoder->gstVolume*((double) 10000), mdecoder->gstMuted);
|
||||
}
|
||||
|
||||
tsmf_platform_register_handler(mdecoder);
|
||||
@ -473,16 +628,45 @@ BOOL tsmf_gstreamer_pipeline_build(TSMFGstreamerDecoder* mdecoder)
|
||||
tsmf_gstreamer_seek_data
|
||||
};
|
||||
g_object_set(mdecoder->src, "format", GST_FORMAT_TIME, NULL);
|
||||
g_object_set(mdecoder->src, "is-live", TRUE, NULL);
|
||||
g_object_set(mdecoder->src, "block", TRUE, NULL);
|
||||
g_object_set(mdecoder->src, "is-live", FALSE, NULL);
|
||||
g_object_set(mdecoder->src, "block", FALSE, NULL);
|
||||
g_object_set(mdecoder->src, "blocksize", 1024, NULL);
|
||||
gst_app_src_set_caps((GstAppSrc *) mdecoder->src, mdecoder->gst_caps);
|
||||
gst_app_src_set_callbacks((GstAppSrc *)mdecoder->src, &callbacks, mdecoder, NULL);
|
||||
gst_app_src_set_stream_type((GstAppSrc *) mdecoder->src, GST_APP_STREAM_TYPE_SEEKABLE);
|
||||
gst_app_src_set_latency((GstAppSrc *) mdecoder->src, 0, -1);
|
||||
gst_app_src_set_max_bytes((GstAppSrc *) mdecoder->src, (guint64) 0);//unlimited
|
||||
g_object_set(G_OBJECT(mdecoder->queue), "use-buffering", FALSE, NULL);
|
||||
g_object_set(G_OBJECT(mdecoder->queue), "use-rate-estimate", FALSE, NULL);
|
||||
g_object_set(G_OBJECT(mdecoder->queue), "max-size-buffers", 0, NULL);
|
||||
g_object_set(G_OBJECT(mdecoder->queue), "max-size-bytes", 0, NULL);
|
||||
g_object_set(G_OBJECT(mdecoder->queue), "max-size-time", (guint64) 0, NULL);
|
||||
|
||||
/* Only set these properties if not an autosink, otherwise we will set properties when real sinks are added */
|
||||
if (!g_strcmp0(G_OBJECT_TYPE_NAME(mdecoder->outsink), "GstAutoVideoSink") && !g_strcmp0(G_OBJECT_TYPE_NAME(mdecoder->outsink), "GstAutoAudioSink"))
|
||||
{
|
||||
if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
|
||||
{
|
||||
gst_base_sink_set_max_lateness((GstBaseSink *) mdecoder->outsink, 10000000); /* nanoseconds */
|
||||
}
|
||||
else
|
||||
{
|
||||
gst_base_sink_set_max_lateness((GstBaseSink *) mdecoder->outsink, 10000000); /* nanoseconds */
|
||||
g_object_set(G_OBJECT(mdecoder->outsink), "buffer-time", (gint64) 20000, NULL); /* microseconds */
|
||||
g_object_set(G_OBJECT(mdecoder->outsink), "drift-tolerance", (gint64) 20000, NULL); /* microseconds */
|
||||
g_object_set(G_OBJECT(mdecoder->outsink), "latency-time", (gint64) 10000, NULL); /* microseconds */
|
||||
g_object_set(G_OBJECT(mdecoder->outsink), "slave-method", 1, NULL);
|
||||
}
|
||||
g_object_set(G_OBJECT(mdecoder->outsink), "sync", TRUE, NULL); /* synchronize on the clock */
|
||||
g_object_set(G_OBJECT(mdecoder->outsink), "async", TRUE, NULL); /* no async state changes */
|
||||
}
|
||||
|
||||
tsmf_window_create(mdecoder);
|
||||
tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_READY);
|
||||
tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PLAYING);
|
||||
mdecoder->pipeline_start_time_valid = 0;
|
||||
mdecoder->shutdown = 0;
|
||||
mdecoder->paused = FALSE;
|
||||
|
||||
GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(mdecoder->pipe), GST_DEBUG_GRAPH_SHOW_ALL, get_type(mdecoder));
|
||||
|
||||
@ -495,7 +679,7 @@ static BOOL tsmf_gstreamer_decodeEx(ITSMFDecoder* decoder, const BYTE *data, UIN
|
||||
GstBuffer *gst_buf;
|
||||
TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder *) decoder;
|
||||
UINT64 sample_time = tsmf_gstreamer_timestamp_ms_to_gst(start_time);
|
||||
UINT64 sample_duration = tsmf_gstreamer_timestamp_ms_to_gst(duration);
|
||||
BOOL useTimestamps = TRUE;
|
||||
|
||||
if (!mdecoder)
|
||||
{
|
||||
@ -509,9 +693,15 @@ static BOOL tsmf_gstreamer_decodeEx(ITSMFDecoder* decoder, const BYTE *data, UIN
|
||||
* We don't expect to block here often, since the pipeline should
|
||||
* have more than enough buffering.
|
||||
*/
|
||||
DEBUG_TSMF("%s. Start:(%llu) End:(%llu) Duration:(%llu) Last End:(%llu)",
|
||||
get_type(mdecoder), start_time, end_time, duration,
|
||||
mdecoder->last_sample_end_time);
|
||||
DEBUG_TSMF("%s. Start:(%lu) End:(%lu) Duration:(%d) Last Start:(%lu)",
|
||||
get_type(mdecoder), start_time, end_time, (int)duration,
|
||||
mdecoder->last_sample_start_time);
|
||||
|
||||
if (mdecoder->shutdown)
|
||||
{
|
||||
WLog_ERR(TAG, "decodeEx called on shutdown decoder");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (mdecoder->gst_caps == NULL)
|
||||
{
|
||||
@ -519,6 +709,9 @@ static BOOL tsmf_gstreamer_decodeEx(ITSMFDecoder* decoder, const BYTE *data, UIN
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!mdecoder->pipe)
|
||||
tsmf_gstreamer_pipeline_build(mdecoder);
|
||||
|
||||
if (!mdecoder->src)
|
||||
{
|
||||
WLog_ERR(TAG, "failed to construct pipeline correctly. Unable to push buffer to source element.");
|
||||
@ -533,53 +726,115 @@ static BOOL tsmf_gstreamer_decodeEx(ITSMFDecoder* decoder, const BYTE *data, UIN
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Relative timestamping will sometimes be set to 0
|
||||
* so we ignore these timestamps just to be safe(bit 8)
|
||||
*/
|
||||
if (extensions & 0x00000080)
|
||||
{
|
||||
DEBUG_TSMF("Ignoring the timestamps - relative - bit 8");
|
||||
useTimestamps = FALSE;
|
||||
}
|
||||
|
||||
/* If no timestamps exist then we dont want to look at the timestamp values (bit 7) */
|
||||
if (extensions & 0x00000040)
|
||||
{
|
||||
DEBUG_TSMF("Ignoring the timestamps - none - bit 7");
|
||||
useTimestamps = FALSE;
|
||||
}
|
||||
|
||||
/* If performing a seek */
|
||||
if (mdecoder->seeking)
|
||||
{
|
||||
mdecoder->seeking = FALSE;
|
||||
tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PAUSED);
|
||||
mdecoder->pipeline_start_time_valid = 0;
|
||||
}
|
||||
|
||||
if (mdecoder->pipeline_start_time_valid)
|
||||
{
|
||||
long long diff = start_time;
|
||||
diff -= mdecoder->last_sample_end_time;
|
||||
DEBUG_TSMF("%s start time %lu", get_type(mdecoder), start_time);
|
||||
|
||||
if (diff < 0)
|
||||
diff *= -1;
|
||||
/* Adjusted the condition for a seek to be based on start time only
|
||||
* WMV1 and WMV2 files in particular have bad end time and duration values
|
||||
* there seems to be no real side effects of just using the start time instead
|
||||
*/
|
||||
UINT64 minTime = mdecoder->last_sample_start_time - (UINT64) SEEK_TOLERANCE;
|
||||
UINT64 maxTime = mdecoder->last_sample_start_time + (UINT64) SEEK_TOLERANCE;
|
||||
|
||||
/* The pipe is initialized, but there is a discontinuity.
|
||||
* Seek to the start position... */
|
||||
if (diff > 50)
|
||||
/* Make sure the minTime stops at 0 , should we be at the beginning of the stream */
|
||||
if (mdecoder->last_sample_start_time < (UINT64) SEEK_TOLERANCE)
|
||||
minTime = 0;
|
||||
|
||||
/* If the start_time is valid and different from the previous start time by more than the seek tolerance, then we have a seek condition */
|
||||
if (((start_time > maxTime) || (start_time < minTime)) && useTimestamps)
|
||||
{
|
||||
DEBUG_TSMF("%s seeking to %lld", get_type(mdecoder), start_time);
|
||||
DEBUG_TSMF("tsmf_gstreamer_decodeEx: start_time=[%lu] > last_sample_start_time=[%lu] OR ", start_time, mdecoder->last_sample_start_time);
|
||||
DEBUG_TSMF("tsmf_gstreamer_decodeEx: start_time=[%lu] < last_sample_start_time=[%lu] with", start_time, mdecoder->last_sample_start_time);
|
||||
DEBUG_TSMF("tsmf_gstreamer_decodeEX: a tolerance of more than [%lu] from the last sample", SEEK_TOLERANCE);
|
||||
DEBUG_TSMF("tsmf_gstreamer_decodeEX: minTime=[%lu] maxTime=[%lu]", minTime, maxTime);
|
||||
|
||||
if (!gst_element_seek(mdecoder->pipe, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
|
||||
GST_SEEK_TYPE_SET, sample_time,
|
||||
GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
|
||||
{
|
||||
WLog_ERR(TAG, "seek failed");
|
||||
}
|
||||
mdecoder->seeking = TRUE;
|
||||
|
||||
mdecoder->pipeline_start_time_valid = 0;
|
||||
/* since we cant make the gstreamer pipeline jump to the new start time after a seek - we just maintain
|
||||
* a offset between realtime and gstreamer time
|
||||
*/
|
||||
mdecoder->seek_offset = start_time;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DEBUG_TSMF("%s start time %llu", get_type(mdecoder), sample_time);
|
||||
DEBUG_TSMF("%s start time %lu", get_type(mdecoder), start_time);
|
||||
/* Always set base/start time to 0. Will use seek offset to translate real buffer times
|
||||
* back to 0. This allows the video to be started from anywhere and the ability to handle seeks
|
||||
* without rebuilding the pipeline, etc. since that is costly
|
||||
*/
|
||||
gst_element_set_base_time(mdecoder->pipe, tsmf_gstreamer_timestamp_ms_to_gst(0));
|
||||
gst_element_set_start_time(mdecoder->pipe, tsmf_gstreamer_timestamp_ms_to_gst(0));
|
||||
mdecoder->pipeline_start_time_valid = 1;
|
||||
|
||||
/* Set the seek offset if buffer has valid timestamps. */
|
||||
if (useTimestamps)
|
||||
mdecoder->seek_offset = start_time;
|
||||
|
||||
if (!gst_element_seek(mdecoder->pipe, 1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
|
||||
GST_SEEK_TYPE_SET, 0,
|
||||
GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE))
|
||||
{
|
||||
WLog_ERR(TAG, "seek failed");
|
||||
}
|
||||
}
|
||||
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
GST_BUFFER_PTS(gst_buf) = sample_time;
|
||||
if (useTimestamps)
|
||||
GST_BUFFER_PTS(gst_buf) = sample_time - tsmf_gstreamer_timestamp_ms_to_gst(mdecoder->seek_offset);
|
||||
else
|
||||
GST_BUFFER_PTS(gst_buf) = GST_CLOCK_TIME_NONE;
|
||||
#else
|
||||
GST_BUFFER_TIMESTAMP(gst_buf) = sample_time;
|
||||
if (useTimestamps)
|
||||
GST_BUFFER_TIMESTAMP(gst_buf) = sample_time - tsmf_gstreamer_timestamp_ms_to_gst(mdecoder->seek_offset);
|
||||
else
|
||||
GST_BUFFER_TIMESTAMP(gst_buf) = GST_CLOCK_TIME_NONE;
|
||||
#endif
|
||||
GST_BUFFER_DURATION(gst_buf) = GST_CLOCK_TIME_NONE;
|
||||
GST_BUFFER_OFFSET(gst_buf) = GST_BUFFER_OFFSET_NONE;
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
#else
|
||||
gst_buffer_set_caps(gst_buf, mdecoder->gst_caps);
|
||||
#endif
|
||||
GST_BUFFER_DURATION(gst_buf) = sample_duration;
|
||||
gst_app_src_push_buffer(GST_APP_SRC(mdecoder->src), gst_buf);
|
||||
|
||||
if (mdecoder->ack_cb)
|
||||
mdecoder->ack_cb(mdecoder->stream, TRUE);
|
||||
|
||||
mdecoder->last_sample_end_time = end_time;
|
||||
|
||||
if (GST_STATE(mdecoder->pipe) != GST_STATE_PLAYING)
|
||||
/* Should only update the last timestamps if the current ones are valid */
|
||||
if (useTimestamps)
|
||||
{
|
||||
DEBUG_TSMF("%s: state=%s", get_type(mdecoder), gst_element_state_get_name(GST_STATE(mdecoder->pipe)));
|
||||
mdecoder->last_sample_start_time = start_time;
|
||||
mdecoder->last_sample_end_time = end_time;
|
||||
}
|
||||
|
||||
if (mdecoder->pipe && (GST_STATE(mdecoder->pipe) != GST_STATE_PLAYING))
|
||||
{
|
||||
DEBUG_TSMF("%s: state=%s", get_type(mdecoder), gst_element_state_get_name(GST_STATE(mdecoder->pipe)));
|
||||
|
||||
DEBUG_TSMF("%s Paused: %i Shutdown: %i Ready: %i", get_type(mdecoder), mdecoder->paused, mdecoder->shutdown, mdecoder->ready);
|
||||
if (!mdecoder->paused && !mdecoder->shutdown && mdecoder->ready)
|
||||
tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PLAYING);
|
||||
}
|
||||
@ -587,38 +842,15 @@ static BOOL tsmf_gstreamer_decodeEx(ITSMFDecoder* decoder, const BYTE *data, UIN
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL tsmf_gstreamer_change_volume(ITSMFDecoder* decoder, UINT32 newVolume, UINT32 muted)
|
||||
{
|
||||
TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder *) decoder;
|
||||
|
||||
if (!mdecoder || !mdecoder->pipe)
|
||||
return FALSE;
|
||||
|
||||
if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
|
||||
return TRUE;
|
||||
|
||||
mdecoder->gstMuted = (BOOL) muted;
|
||||
DEBUG_TSMF("mute=[%d]", mdecoder->gstMuted);
|
||||
mdecoder->gstVolume = (double) newVolume / (double) 10000;
|
||||
DEBUG_TSMF("gst_new_vol=[%f]", mdecoder->gstVolume);
|
||||
|
||||
if (!mdecoder->volume)
|
||||
return FALSE;
|
||||
|
||||
if (!G_IS_OBJECT(mdecoder->volume))
|
||||
return FALSE;
|
||||
|
||||
g_object_set(mdecoder->volume, "mute", mdecoder->gstMuted, NULL);
|
||||
g_object_set(mdecoder->volume, "volume", mdecoder->gstVolume, NULL);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL tsmf_gstreamer_control(ITSMFDecoder* decoder, ITSMFControlMsg control_msg, UINT32 *arg)
|
||||
{
|
||||
TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder *) decoder;
|
||||
|
||||
if (!mdecoder)
|
||||
return FALSE;
|
||||
{
|
||||
WLog_ERR(TAG, "Control called with no decoder!");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (control_msg == Control_Pause)
|
||||
{
|
||||
@ -626,15 +858,13 @@ static BOOL tsmf_gstreamer_control(ITSMFDecoder* decoder, ITSMFControlMsg contro
|
||||
|
||||
if (mdecoder->paused)
|
||||
{
|
||||
WLog_ERR(TAG, "%s: Ignoring control PAUSE, already received!", get_type(mdecoder));
|
||||
WLog_ERR(TAG, "%s: Ignoring Control_Pause, already received!", get_type(mdecoder));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PAUSED);
|
||||
mdecoder->shutdown = 0;
|
||||
mdecoder->paused = TRUE;
|
||||
|
||||
if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
|
||||
tsmf_window_pause(mdecoder);
|
||||
}
|
||||
else if (control_msg == Control_Resume)
|
||||
{
|
||||
@ -642,17 +872,12 @@ static BOOL tsmf_gstreamer_control(ITSMFDecoder* decoder, ITSMFControlMsg contro
|
||||
|
||||
if (!mdecoder->paused && !mdecoder->shutdown)
|
||||
{
|
||||
WLog_ERR(TAG, "%s: Ignoring control RESUME, already received!", get_type(mdecoder));
|
||||
WLog_ERR(TAG, "%s: Ignoring Control_Resume, already received!", get_type(mdecoder));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
mdecoder->shutdown = 0;
|
||||
mdecoder->paused = FALSE;
|
||||
mdecoder->shutdown = FALSE;
|
||||
|
||||
if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
|
||||
tsmf_window_resume(mdecoder);
|
||||
|
||||
tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PLAYING);
|
||||
}
|
||||
else if (control_msg == Control_Stop)
|
||||
{
|
||||
@ -660,18 +885,29 @@ static BOOL tsmf_gstreamer_control(ITSMFDecoder* decoder, ITSMFControlMsg contro
|
||||
|
||||
if (mdecoder->shutdown)
|
||||
{
|
||||
WLog_ERR(TAG, "%s: Ignoring control STOP, already received!", get_type(mdecoder));
|
||||
WLog_ERR(TAG, "%s: Ignoring Control_Stop, already received!", get_type(mdecoder));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
mdecoder->shutdown = TRUE;
|
||||
/* Reset stamps, flush buffers, etc */
|
||||
tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PAUSED);
|
||||
if (mdecoder->pipe)
|
||||
{
|
||||
tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_NULL);
|
||||
tsmf_window_destroy(mdecoder);
|
||||
tsmf_gstreamer_clean_up(mdecoder);
|
||||
}
|
||||
mdecoder->seek_offset = 0;
|
||||
mdecoder->pipeline_start_time_valid = 0;
|
||||
mdecoder->shutdown = 1;
|
||||
}
|
||||
else if (control_msg == Control_Restart)
|
||||
{
|
||||
DEBUG_TSMF("Control_Restart %s", get_type(mdecoder));
|
||||
mdecoder->shutdown = 0;
|
||||
mdecoder->paused = FALSE;
|
||||
|
||||
if (mdecoder->media_type == TSMF_MAJOR_TYPE_VIDEO)
|
||||
tsmf_window_pause(mdecoder);
|
||||
|
||||
gst_app_src_end_of_stream((GstAppSrc *)mdecoder->src);
|
||||
if (mdecoder->pipeline_start_time_valid)
|
||||
tsmf_gstreamer_pipeline_set_state(mdecoder, GST_STATE_PLAYING);
|
||||
}
|
||||
else
|
||||
WLog_ERR(TAG, "Unknown control message %08x", control_msg);
|
||||
@ -679,7 +915,7 @@ static BOOL tsmf_gstreamer_control(ITSMFDecoder* decoder, ITSMFControlMsg contro
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL tsmf_gstreamer_buffer_filled(ITSMFDecoder* decoder)
|
||||
static BOOL tsmf_gstreamer_buffer_level(ITSMFDecoder* decoder)
|
||||
{
|
||||
TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder *) decoder;
|
||||
DEBUG_TSMF("");
|
||||
@ -687,10 +923,13 @@ static BOOL tsmf_gstreamer_buffer_filled(ITSMFDecoder* decoder)
|
||||
if (!mdecoder)
|
||||
return FALSE;
|
||||
|
||||
guint buff_max = 0;
|
||||
guint clbuff = 0;
|
||||
DEBUG_TSMF("%s buffer fill %u/%u", get_type(mdecoder), clbuff, buff_max);
|
||||
return clbuff >= buff_max ? TRUE : FALSE;
|
||||
|
||||
if (G_IS_OBJECT(mdecoder->queue))
|
||||
g_object_get(mdecoder->queue, "current-level-buffers", &clbuff, NULL);
|
||||
|
||||
DEBUG_TSMF("%s buffer level %u", get_type(mdecoder), clbuff);
|
||||
return clbuff;
|
||||
}
|
||||
|
||||
static void tsmf_gstreamer_free(ITSMFDecoder* decoder)
|
||||
@ -700,7 +939,7 @@ static void tsmf_gstreamer_free(ITSMFDecoder* decoder)
|
||||
|
||||
if (mdecoder)
|
||||
{
|
||||
mdecoder->shutdown = 1;
|
||||
tsmf_window_destroy(mdecoder);
|
||||
tsmf_gstreamer_clean_up(mdecoder);
|
||||
|
||||
if (mdecoder->gst_caps)
|
||||
@ -721,19 +960,19 @@ static UINT64 tsmf_gstreamer_get_running_time(ITSMFDecoder* decoder)
|
||||
return 0;
|
||||
|
||||
if (!mdecoder->outsink)
|
||||
return mdecoder->last_sample_end_time;
|
||||
return mdecoder->last_sample_start_time;
|
||||
|
||||
if (GST_STATE(mdecoder->pipe) != GST_STATE_PLAYING)
|
||||
if (!mdecoder->pipe)
|
||||
return 0;
|
||||
|
||||
GstFormat fmt = GST_FORMAT_TIME;
|
||||
gint64 pos = 0;
|
||||
#if GST_VERSION_MAJOR > 0
|
||||
gst_element_query_position(mdecoder->outsink, fmt, &pos);
|
||||
gst_element_query_position(mdecoder->pipe, fmt, &pos);
|
||||
#else
|
||||
gst_element_query_position(mdecoder->outsink, &fmt, &pos);
|
||||
gst_element_query_position(mdecoder->pipe, &fmt, &pos);
|
||||
#endif
|
||||
return pos/100;
|
||||
return (UINT64) (pos/100 + mdecoder->seek_offset);
|
||||
}
|
||||
|
||||
static BOOL tsmf_gstreamer_update_rendering_area(ITSMFDecoder* decoder,
|
||||
@ -757,7 +996,7 @@ BOOL tsmf_gstreamer_ack(ITSMFDecoder* decoder, BOOL (*cb)(void *, BOOL), void *s
|
||||
{
|
||||
TSMFGstreamerDecoder* mdecoder = (TSMFGstreamerDecoder *) decoder;
|
||||
DEBUG_TSMF("");
|
||||
mdecoder->ack_cb = cb;
|
||||
mdecoder->ack_cb = NULL;
|
||||
mdecoder->stream = stream;
|
||||
return TRUE;
|
||||
}
|
||||
@ -800,13 +1039,17 @@ ITSMFDecoder* freerdp_tsmf_client_subsystem_entry(void)
|
||||
decoder->iface.Control = tsmf_gstreamer_control;
|
||||
decoder->iface.DecodeEx = tsmf_gstreamer_decodeEx;
|
||||
decoder->iface.ChangeVolume = tsmf_gstreamer_change_volume;
|
||||
decoder->iface.BufferFilled = tsmf_gstreamer_buffer_filled;
|
||||
decoder->iface.BufferLevel = tsmf_gstreamer_buffer_level;
|
||||
decoder->iface.SetAckFunc = tsmf_gstreamer_ack;
|
||||
decoder->iface.SetSyncFunc = tsmf_gstreamer_sync;
|
||||
decoder->paused = FALSE;
|
||||
decoder->gstVolume = 0.5;
|
||||
decoder->gstMuted = FALSE;
|
||||
decoder->state = GST_STATE_VOID_PENDING; /* No real state yet */
|
||||
decoder->last_sample_start_time = 0;
|
||||
decoder->last_sample_end_time = 0;
|
||||
decoder->seek_offset = 0;
|
||||
decoder->seeking = FALSE;
|
||||
|
||||
if (tsmf_platform_create(decoder) < 0)
|
||||
{
|
||||
|
@ -38,12 +38,16 @@ typedef struct _TSMFGstreamerDecoder
|
||||
|
||||
GstElement *pipe;
|
||||
GstElement *src;
|
||||
GstElement *queue;
|
||||
GstElement *outsink;
|
||||
GstElement *volume;
|
||||
|
||||
BOOL ready;
|
||||
BOOL paused;
|
||||
UINT64 last_sample_start_time;
|
||||
UINT64 last_sample_end_time;
|
||||
BOOL seeking;
|
||||
UINT64 seek_offset;
|
||||
|
||||
double gstVolume;
|
||||
BOOL gstMuted;
|
||||
@ -74,8 +78,8 @@ int tsmf_window_resize(TSMFGstreamerDecoder* decoder, int x, int y,
|
||||
int width, int height, int nr_rect, RDP_RECT *visible);
|
||||
int tsmf_window_destroy(TSMFGstreamerDecoder* decoder);
|
||||
|
||||
int tsmf_window_pause(TSMFGstreamerDecoder* decoder);
|
||||
int tsmf_window_resume(TSMFGstreamerDecoder* decoder);
|
||||
int tsmf_window_map(TSMFGstreamerDecoder* decoder);
|
||||
int tsmf_window_unmap(TSMFGstreamerDecoder* decoder);
|
||||
|
||||
BOOL tsmf_gstreamer_add_pad(TSMFGstreamerDecoder* mdecoder);
|
||||
void tsmf_gstreamer_remove_pad(TSMFGstreamerDecoder* mdecoder);
|
||||
|
@ -28,5 +28,3 @@ add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_N
|
||||
|
||||
|
||||
target_link_libraries(${MODULE_NAME} freerdp ${OSS_LIBRARIES})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
@ -28,5 +28,3 @@ add_channel_client_subsystem_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_N
|
||||
|
||||
|
||||
target_link_libraries(${MODULE_NAME} freerdp)
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
|
@ -75,6 +75,13 @@ static const TSMFMediaTypeMap tsmf_sub_type_map[] =
|
||||
TSMF_SUB_TYPE_WVC1
|
||||
},
|
||||
|
||||
/* 00000160-0000-0010-8000-00AA00389B71 */
|
||||
{
|
||||
{ 0x60, 0x01, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 },
|
||||
"MEDIASUBTYPE_WMAudioV1", /* V7, V8 has the same GUID */
|
||||
TSMF_SUB_TYPE_WMA1
|
||||
},
|
||||
|
||||
/* 00000161-0000-0010-8000-00AA00389B71 */
|
||||
{
|
||||
{ 0x61, 0x01, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 },
|
||||
@ -173,6 +180,13 @@ static const TSMFMediaTypeMap tsmf_sub_type_map[] =
|
||||
TSMF_SUB_TYPE_MP42
|
||||
},
|
||||
|
||||
/* 3253344D-0000-0010-8000-00AA00389B71 */
|
||||
{
|
||||
{ 0x4D, 0x34, 0x53, 0x32, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 },
|
||||
"MEDIASUBTYPE_MP42",
|
||||
TSMF_SUB_TYPE_M4S2
|
||||
},
|
||||
|
||||
/* E436EB81-524F-11CE-9F53-0020AF0BA770 */
|
||||
{
|
||||
{ 0x81, 0xEB, 0x36, 0xE4, 0x4F, 0x52, 0xCE, 0x11, 0x9F, 0x53, 0x00, 0x20, 0xAF, 0x0B, 0xA7, 0x70 },
|
||||
@ -605,11 +619,22 @@ BOOL tsmf_codec_parse_media_type(TS_AM_MEDIA_TYPE* mediatype, wStream* s)
|
||||
BOOL tsmf_codec_check_media_type(const char* decoder_name, wStream* s)
|
||||
{
|
||||
BYTE* m;
|
||||
BOOL ret;
|
||||
BOOL ret = FALSE;
|
||||
TS_AM_MEDIA_TYPE mediatype;
|
||||
|
||||
static BOOL decoderAvailable = FALSE;
|
||||
static BOOL firstRun = TRUE;
|
||||
|
||||
if (firstRun)
|
||||
{
|
||||
firstRun =FALSE;
|
||||
if (tsmf_check_decoder_available(decoder_name))
|
||||
decoderAvailable = TRUE;
|
||||
}
|
||||
|
||||
Stream_GetPointer(s, m);
|
||||
ret = tsmf_codec_parse_media_type(&mediatype, s);
|
||||
if (decoderAvailable)
|
||||
ret = tsmf_codec_parse_media_type(&mediatype, s);
|
||||
Stream_SetPointer(s, m);
|
||||
|
||||
if (ret)
|
||||
|
@ -125,6 +125,8 @@
|
||||
#define TSMF_SUB_TYPE_VP8 24
|
||||
#define TSMF_SUB_TYPE_VP9 25
|
||||
#define TSMF_SUB_TYPE_H263 26
|
||||
#define TSMF_SUB_TYPE_M4S2 27
|
||||
#define TSMF_SUB_TYPE_WMA1 28
|
||||
|
||||
/* FormatType */
|
||||
#define TSMF_FORMAT_TYPE_UNKNOWN 0
|
||||
|
@ -32,7 +32,7 @@
|
||||
#include "tsmf_constants.h"
|
||||
#include "tsmf_decoder.h"
|
||||
|
||||
static ITSMFDecoder* tsmf_load_decoder_by_name(const char *name, TS_AM_MEDIA_TYPE *media_type)
|
||||
static ITSMFDecoder* tsmf_load_decoder_by_name(const char *name)
|
||||
{
|
||||
ITSMFDecoder* decoder;
|
||||
TSMF_DECODER_ENTRY entry;
|
||||
@ -50,33 +50,74 @@ static ITSMFDecoder* tsmf_load_decoder_by_name(const char *name, TS_AM_MEDIA_TYP
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!decoder->SetFormat(decoder, media_type))
|
||||
{
|
||||
decoder->Free(decoder);
|
||||
decoder = NULL;
|
||||
}
|
||||
|
||||
return decoder;
|
||||
}
|
||||
|
||||
static BOOL tsmf_decoder_set_format(ITSMFDecoder *decoder, TS_AM_MEDIA_TYPE* media_type)
|
||||
{
|
||||
if (decoder->SetFormat(decoder, media_type))
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ITSMFDecoder* tsmf_load_decoder(const char* name, TS_AM_MEDIA_TYPE* media_type)
|
||||
{
|
||||
ITSMFDecoder* decoder = NULL;
|
||||
|
||||
if (name)
|
||||
{
|
||||
decoder = tsmf_load_decoder_by_name(name, media_type);
|
||||
decoder = tsmf_load_decoder_by_name(name);
|
||||
}
|
||||
|
||||
#if defined(WITH_GSTREAMER_1_0) || defined(WITH_GSTREAMER_0_10)
|
||||
if (!decoder)
|
||||
decoder = tsmf_load_decoder_by_name("gstreamer", media_type);
|
||||
decoder = tsmf_load_decoder_by_name("gstreamer");
|
||||
#endif
|
||||
|
||||
#if defined(WITH_FFMPEG)
|
||||
if (!decoder)
|
||||
decoder = tsmf_load_decoder_by_name("ffmpeg", media_type);
|
||||
decoder = tsmf_load_decoder_by_name("ffmpeg");
|
||||
#endif
|
||||
|
||||
if (decoder)
|
||||
{
|
||||
if (!tsmf_decoder_set_format(decoder, media_type))
|
||||
{
|
||||
decoder->Free(decoder);
|
||||
decoder = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return decoder;
|
||||
}
|
||||
|
||||
BOOL tsmf_check_decoder_available(const char* name)
|
||||
{
|
||||
ITSMFDecoder* decoder = NULL;
|
||||
BOOL retValue = FALSE;
|
||||
|
||||
if (name)
|
||||
{
|
||||
decoder = tsmf_load_decoder_by_name(name);
|
||||
}
|
||||
#if defined(WITH_GSTREAMER_1_0) || defined(WITH_GSTREAMER_0_10)
|
||||
if (!decoder)
|
||||
decoder = tsmf_load_decoder_by_name("gstreamer");
|
||||
#endif
|
||||
|
||||
#if defined(WITH_FFMPEG)
|
||||
if (!decoder)
|
||||
decoder = tsmf_load_decoder_by_name("ffmpeg");
|
||||
#endif
|
||||
|
||||
if (decoder)
|
||||
{
|
||||
decoder->Free(decoder);
|
||||
decoder = NULL;
|
||||
retValue = TRUE;
|
||||
}
|
||||
|
||||
return retValue;
|
||||
}
|
||||
|
||||
|
@ -27,6 +27,7 @@ typedef enum _ITSMFControlMsg
|
||||
{
|
||||
Control_Pause,
|
||||
Control_Resume,
|
||||
Control_Restart,
|
||||
Control_Stop
|
||||
} ITSMFControlMsg;
|
||||
|
||||
@ -49,7 +50,7 @@ struct _ITSMFDecoder
|
||||
/* Optional Contol function */
|
||||
BOOL (*Control)(ITSMFDecoder *decoder, ITSMFControlMsg control_msg, UINT32 *arg);
|
||||
/* Decode a sample with extended interface. */
|
||||
int (*DecodeEx)(ITSMFDecoder *decoder, const BYTE *data, UINT32 data_size, UINT32 extensions,
|
||||
BOOL (*DecodeEx)(ITSMFDecoder *decoder, const BYTE *data, UINT32 data_size, UINT32 extensions,
|
||||
UINT64 start_time, UINT64 end_time, UINT64 duration);
|
||||
/* Get current play time */
|
||||
UINT64(*GetRunningTime)(ITSMFDecoder *decoder);
|
||||
@ -58,7 +59,7 @@ struct _ITSMFDecoder
|
||||
/* Change Gstreamer Audio Volume */
|
||||
BOOL (*ChangeVolume)(ITSMFDecoder *decoder, UINT32 newVolume, UINT32 muted);
|
||||
/* Check buffer level */
|
||||
BOOL (*BufferFilled)(ITSMFDecoder *decoder);
|
||||
BOOL (*BufferLevel)(ITSMFDecoder *decoder);
|
||||
/* Register a callback for frame ack. */
|
||||
BOOL (*SetAckFunc)(ITSMFDecoder *decoder, BOOL (*cb)(void *,BOOL), void *stream);
|
||||
/* Register a callback for stream seek detection. */
|
||||
@ -69,6 +70,7 @@ struct _ITSMFDecoder
|
||||
typedef ITSMFDecoder *(*TSMF_DECODER_ENTRY)(void);
|
||||
|
||||
ITSMFDecoder *tsmf_load_decoder(const char *name, TS_AM_MEDIA_TYPE *media_type);
|
||||
BOOL tsmf_check_decoder_available(const char* name);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -224,6 +224,7 @@ UINT tsmf_ifman_add_stream(TSMF_IFMAN* ifman, rdpContext* rdpcontext)
|
||||
|
||||
if (!presentation)
|
||||
{
|
||||
WLog_ERR(TAG, "unknown presentation id");
|
||||
status = ERROR_NOT_FOUND;
|
||||
}
|
||||
else
|
||||
@ -232,10 +233,16 @@ UINT tsmf_ifman_add_stream(TSMF_IFMAN* ifman, rdpContext* rdpcontext)
|
||||
Stream_Seek_UINT32(ifman->input); /* numMediaType */
|
||||
stream = tsmf_stream_new(presentation, StreamId, rdpcontext);
|
||||
if (!stream)
|
||||
{
|
||||
WLog_ERR(TAG, "failed to create stream");
|
||||
return ERROR_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
if (!tsmf_stream_set_format(stream, ifman->decoder_name, ifman->input))
|
||||
{
|
||||
WLog_ERR(TAG, "failed to set stream format");
|
||||
return ERROR_OUTOFMEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
ifman->output_pending = TRUE;
|
||||
@ -553,6 +560,7 @@ UINT tsmf_ifman_set_allocator(TSMF_IFMAN* ifman)
|
||||
UINT tsmf_ifman_notify_preroll(TSMF_IFMAN* ifman)
|
||||
{
|
||||
DEBUG_TSMF("");
|
||||
tsmf_ifman_on_playback_paused(ifman);
|
||||
ifman->output_pending = TRUE;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
@ -589,9 +597,9 @@ UINT tsmf_ifman_on_sample(TSMF_IFMAN* ifman)
|
||||
if (Stream_GetRemainingLength(ifman->input) < cbData)
|
||||
return ERROR_INVALID_DATA;
|
||||
|
||||
DEBUG_TSMF("MessageId %d StreamId %d SampleStartTime %d SampleEndTime %d "
|
||||
DEBUG_TSMF("MessageId %d StreamId %d SampleStartTime %lu SampleEndTime %lu "
|
||||
"ThrottleDuration %d SampleExtensions %d cbData %d",
|
||||
ifman->message_id, StreamId, (int)SampleStartTime, (int)SampleEndTime,
|
||||
ifman->message_id, StreamId, SampleStartTime, SampleEndTime,
|
||||
(int)ThrottleDuration, SampleExtensions, cbData);
|
||||
|
||||
presentation = tsmf_presentation_find_by_id(ifman->presentation_id);
|
||||
@ -637,6 +645,7 @@ UINT tsmf_ifman_on_flush(TSMF_IFMAN* ifman)
|
||||
{
|
||||
UINT32 StreamId;
|
||||
TSMF_PRESENTATION* presentation;
|
||||
TSMF_STREAM* stream;
|
||||
|
||||
if (Stream_GetRemainingLength(ifman->input) < 20)
|
||||
return ERROR_INVALID_DATA;
|
||||
@ -653,8 +662,18 @@ UINT tsmf_ifman_on_flush(TSMF_IFMAN* ifman)
|
||||
return ERROR_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (!tsmf_presentation_flush(presentation))
|
||||
return ERROR_INVALID_OPERATION;
|
||||
/* Flush message is for a stream, not the entire presentation
|
||||
* therefore we only flush the stream as intended per the MS-RDPEV spec
|
||||
*/
|
||||
stream = tsmf_stream_find_by_id(presentation, StreamId);
|
||||
if (stream)
|
||||
{
|
||||
if (!tsmf_stream_flush(stream))
|
||||
return ERROR_INVALID_OPERATION;
|
||||
}
|
||||
else
|
||||
WLog_ERR(TAG, "unknown stream id");
|
||||
|
||||
ifman->output_pending = TRUE;
|
||||
|
||||
return CHANNEL_RC_OK;
|
||||
@ -668,7 +687,7 @@ UINT tsmf_ifman_on_flush(TSMF_IFMAN* ifman)
|
||||
UINT tsmf_ifman_on_end_of_stream(TSMF_IFMAN* ifman)
|
||||
{
|
||||
UINT32 StreamId;
|
||||
TSMF_STREAM* stream;
|
||||
TSMF_STREAM* stream = NULL;
|
||||
TSMF_PRESENTATION* presentation;
|
||||
|
||||
if (Stream_GetRemainingLength(ifman->input) < 20)
|
||||
@ -684,17 +703,12 @@ UINT tsmf_ifman_on_end_of_stream(TSMF_IFMAN* ifman)
|
||||
stream = tsmf_stream_find_by_id(presentation, StreamId);
|
||||
|
||||
if (stream)
|
||||
tsmf_stream_end(stream);
|
||||
tsmf_stream_end(stream, ifman->message_id, ifman->channel_callback);
|
||||
}
|
||||
|
||||
DEBUG_TSMF("StreamId %d", StreamId);
|
||||
if (!Stream_EnsureRemainingCapacity(ifman->output, 16))
|
||||
return ERROR_OUTOFMEMORY;
|
||||
|
||||
Stream_Write_UINT32(ifman->output, CLIENT_EVENT_NOTIFICATION); /* FunctionId */
|
||||
Stream_Write_UINT32(ifman->output, StreamId); /* StreamId */
|
||||
Stream_Write_UINT32(ifman->output, TSMM_CLIENT_EVENT_ENDOFSTREAM); /* EventId */
|
||||
Stream_Write_UINT32(ifman->output, 0); /* cbData */
|
||||
ifman->output_pending = TRUE;
|
||||
|
||||
ifman->output_interface_id = TSMF_INTERFACE_CLIENT_NOTIFICATIONS | STREAM_ID_PROXY;
|
||||
return CHANNEL_RC_OK;
|
||||
|
@ -36,10 +36,46 @@
|
||||
|
||||
#include "tsmf_main.h"
|
||||
|
||||
BOOL tsmf_send_eos_response(IWTSVirtualChannelCallback* pChannelCallback, UINT32 message_id)
|
||||
{
|
||||
wStream* s = NULL;
|
||||
int status = -1;
|
||||
TSMF_CHANNEL_CALLBACK* callback = (TSMF_CHANNEL_CALLBACK*) pChannelCallback;
|
||||
|
||||
if (!callback)
|
||||
{
|
||||
DEBUG_TSMF("No callback reference - unable to send eos response!");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (callback && callback->stream_id && callback->channel && callback->channel->Write)
|
||||
{
|
||||
s = Stream_New(NULL, 24);
|
||||
if (!s)
|
||||
return FALSE;
|
||||
Stream_Write_UINT32(s, TSMF_INTERFACE_CLIENT_NOTIFICATIONS | STREAM_ID_PROXY);
|
||||
Stream_Write_UINT32(s, message_id);
|
||||
Stream_Write_UINT32(s, CLIENT_EVENT_NOTIFICATION); /* FunctionId */
|
||||
Stream_Write_UINT32(s, callback->stream_id); /* StreamId */
|
||||
Stream_Write_UINT32(s, TSMM_CLIENT_EVENT_ENDOFSTREAM); /* EventId */
|
||||
Stream_Write_UINT32(s, 0); /* cbData */
|
||||
DEBUG_TSMF("EOS response size %i", Stream_GetPosition(s));
|
||||
|
||||
status = callback->channel->Write(callback->channel, Stream_GetPosition(s), Stream_Buffer(s), NULL);
|
||||
if (status)
|
||||
{
|
||||
WLog_ERR(TAG, "response error %d", status);
|
||||
}
|
||||
Stream_Free(s, TRUE);
|
||||
}
|
||||
|
||||
return (status == 0);
|
||||
}
|
||||
|
||||
BOOL tsmf_playback_ack(IWTSVirtualChannelCallback *pChannelCallback,
|
||||
UINT32 message_id, UINT64 duration, UINT32 data_size)
|
||||
{
|
||||
wStream *s;
|
||||
wStream *s = NULL;
|
||||
int status = -1;
|
||||
TSMF_CHANNEL_CALLBACK *callback = (TSMF_CHANNEL_CALLBACK *) pChannelCallback;
|
||||
|
||||
@ -54,7 +90,7 @@ BOOL tsmf_playback_ack(IWTSVirtualChannelCallback *pChannelCallback,
|
||||
Stream_Write_UINT64(s, duration); /* DataDuration */
|
||||
Stream_Write_UINT64(s, data_size); /* cbData */
|
||||
|
||||
DEBUG_TSMF("response size %d", (int) Stream_GetPosition(s));
|
||||
DEBUG_TSMF("ACK response size %d", (int) Stream_GetPosition(s));
|
||||
|
||||
if (!callback || !callback->channel || !callback->channel->Write)
|
||||
{
|
||||
@ -267,6 +303,11 @@ static UINT tsmf_on_data_received(IWTSVirtualChannelCallback* pChannelCallback,
|
||||
input = NULL;
|
||||
ifman.input = NULL;
|
||||
|
||||
if (error)
|
||||
{
|
||||
WLog_ERR(TAG, "ifman data received processing error %d", error);
|
||||
}
|
||||
|
||||
if (!processed)
|
||||
{
|
||||
switch (FunctionId)
|
||||
@ -351,7 +392,7 @@ static UINT tsmf_on_close(IWTSVirtualChannelCallback *pChannelCallback)
|
||||
static UINT tsmf_on_new_channel_connection(IWTSListenerCallback *pListenerCallback,
|
||||
IWTSVirtualChannel *pChannel,
|
||||
BYTE *Data,
|
||||
int *pbAccept,
|
||||
BOOL *pbAccept,
|
||||
IWTSVirtualChannelCallback **ppCallback)
|
||||
{
|
||||
TSMF_CHANNEL_CALLBACK* callback;
|
||||
|
@ -64,6 +64,7 @@ struct _TSMF_PLUGIN
|
||||
rdpContext* rdpcontext;
|
||||
};
|
||||
|
||||
BOOL tsmf_send_eos_response(IWTSVirtualChannelCallback* pChannelCallback, UINT32 message_id);
|
||||
BOOL tsmf_playback_ack(IWTSVirtualChannelCallback* pChannelCallback,
|
||||
UINT32 message_id, UINT64 duration, UINT32 data_size);
|
||||
|
||||
|
@ -56,13 +56,23 @@
|
||||
|
||||
#define AUDIO_TOLERANCE 10000000LL
|
||||
|
||||
/* 1 second = 10,000,000 100ns units*/
|
||||
#define VIDEO_ADJUST_MAX 10*1000*1000
|
||||
|
||||
#define MAX_ACK_TIME 666667
|
||||
|
||||
#define AUDIO_MIN_BUFFER_LEVEL 3
|
||||
#define AUDIO_MAX_BUFFER_LEVEL 6
|
||||
|
||||
#define VIDEO_MIN_BUFFER_LEVEL 10
|
||||
#define VIDEO_MAX_BUFFER_LEVEL 30
|
||||
|
||||
struct _TSMF_PRESENTATION
|
||||
{
|
||||
BYTE presentation_id[GUID_SIZE];
|
||||
|
||||
const char *audio_name;
|
||||
const char *audio_device;
|
||||
int eos;
|
||||
|
||||
IWTSVirtualChannelCallback *channel_callback;
|
||||
|
||||
@ -93,6 +103,9 @@ struct _TSMF_STREAM
|
||||
|
||||
int major_type;
|
||||
int eos;
|
||||
UINT32 eos_message_id;
|
||||
IWTSVirtualChannelCallback* eos_channel_callback;
|
||||
int delayed_stop;
|
||||
UINT32 width;
|
||||
UINT32 height;
|
||||
|
||||
@ -101,11 +114,17 @@ struct _TSMF_STREAM
|
||||
UINT32 channels;
|
||||
UINT32 bits_per_sample;
|
||||
|
||||
/* The start time of last played sample */
|
||||
UINT64 last_start_time;
|
||||
/* The end_time of last played sample */
|
||||
UINT64 last_end_time;
|
||||
/* Next sample should not start before this system time. */
|
||||
UINT64 next_start_time;
|
||||
|
||||
UINT32 minBufferLevel;
|
||||
UINT32 maxBufferLevel;
|
||||
UINT32 currentBufferLevel;
|
||||
|
||||
HANDLE play_thread;
|
||||
HANDLE ack_thread;
|
||||
HANDLE stopEvent;
|
||||
@ -114,6 +133,8 @@ struct _TSMF_STREAM
|
||||
wQueue *sample_list;
|
||||
wQueue *sample_ack_list;
|
||||
rdpContext* rdpcontext;
|
||||
|
||||
BOOL seeking;
|
||||
};
|
||||
|
||||
struct _TSMF_SAMPLE
|
||||
@ -128,6 +149,8 @@ struct _TSMF_SAMPLE
|
||||
UINT32 decoded_size;
|
||||
UINT32 pixfmt;
|
||||
|
||||
BOOL invalidTimestamps;
|
||||
|
||||
TSMF_STREAM* stream;
|
||||
IWTSVirtualChannelCallback *channel_callback;
|
||||
UINT64 ack_time;
|
||||
@ -170,7 +193,10 @@ static TSMF_SAMPLE* tsmf_stream_pop_sample(TSMF_STREAM* stream, int sync)
|
||||
if (stream->major_type == TSMF_MAJOR_TYPE_AUDIO)
|
||||
{
|
||||
/* Check if some other stream has earlier sample that needs to be played first */
|
||||
if (stream->last_end_time > AUDIO_TOLERANCE)
|
||||
/* Start time is more reliable than end time as some stream types seem to have incorrect
|
||||
* end times from the server
|
||||
*/
|
||||
if (stream->last_start_time > AUDIO_TOLERANCE)
|
||||
{
|
||||
ArrayList_Lock(presentation->stream_list);
|
||||
count = ArrayList_Count(presentation->stream_list);
|
||||
@ -179,9 +205,13 @@ static TSMF_SAMPLE* tsmf_stream_pop_sample(TSMF_STREAM* stream, int sync)
|
||||
{
|
||||
s = (TSMF_STREAM *) ArrayList_GetItem(presentation->stream_list, index);
|
||||
|
||||
if (s != stream && !s->eos && s->last_end_time &&
|
||||
s->last_end_time < stream->last_end_time - AUDIO_TOLERANCE)
|
||||
/* Start time is more reliable than end time as some stream types seem to have incorrect
|
||||
* end times from the server
|
||||
*/
|
||||
if (s != stream && !s->eos && s->last_start_time &&
|
||||
s->last_start_time < stream->last_start_time - AUDIO_TOLERANCE)
|
||||
{
|
||||
DEBUG_TSMF("Pending due to audio tolerance");
|
||||
pending = TRUE;
|
||||
break;
|
||||
}
|
||||
@ -192,8 +222,12 @@ static TSMF_SAMPLE* tsmf_stream_pop_sample(TSMF_STREAM* stream, int sync)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (stream->last_end_time > presentation->audio_end_time)
|
||||
/* Start time is more reliable than end time as some stream types seem to have incorrect
|
||||
* end times from the server
|
||||
*/
|
||||
if (stream->last_start_time > presentation->audio_start_time)
|
||||
{
|
||||
DEBUG_TSMF("Pending due to stream start time > audio start time");
|
||||
pending = TRUE;
|
||||
}
|
||||
}
|
||||
@ -206,9 +240,14 @@ static TSMF_SAMPLE* tsmf_stream_pop_sample(TSMF_STREAM* stream, int sync)
|
||||
|
||||
sample = (TSMF_SAMPLE *) Queue_Dequeue(stream->sample_list);
|
||||
|
||||
if (sample && (sample->end_time > stream->last_end_time))
|
||||
/* Only update stream last end time if the sample end time is valid and greater than the current stream end time */
|
||||
if (sample && (sample->end_time > stream->last_end_time) && (!sample->invalidTimestamps))
|
||||
stream->last_end_time = sample->end_time;
|
||||
|
||||
/* Only update stream last start time if the sample start time is valid and greater than the current stream start time */
|
||||
if (sample && (sample->start_time > stream->last_start_time) && (!sample->invalidTimestamps))
|
||||
stream->last_start_time = sample->start_time;
|
||||
|
||||
return sample;
|
||||
}
|
||||
|
||||
@ -242,6 +281,9 @@ static BOOL tsmf_sample_queue_ack(TSMF_SAMPLE* sample)
|
||||
return Queue_Enqueue(sample->stream->sample_ack_list, sample);
|
||||
}
|
||||
|
||||
/* Returns TRUE if no more samples are currently available
|
||||
* Returns FALSE otherwise
|
||||
*/
|
||||
static BOOL tsmf_stream_process_ack(void* arg, BOOL force)
|
||||
{
|
||||
TSMF_STREAM* stream = arg;
|
||||
@ -250,26 +292,42 @@ static BOOL tsmf_stream_process_ack(void* arg, BOOL force)
|
||||
BOOL rc = FALSE;
|
||||
|
||||
if (!stream)
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
|
||||
Queue_Lock(stream->sample_ack_list);
|
||||
sample = (TSMF_SAMPLE*) Queue_Peek(stream->sample_ack_list);
|
||||
|
||||
if (!sample)
|
||||
{
|
||||
rc = TRUE;
|
||||
goto finally;
|
||||
}
|
||||
|
||||
if (!force)
|
||||
{
|
||||
/* Do some min/max ack limiting if we have access to Buffer level information */
|
||||
if (stream->decoder->BufferLevel)
|
||||
{
|
||||
/* Try to keep buffer level below max by withholding acks */
|
||||
if (stream->currentBufferLevel > stream->maxBufferLevel)
|
||||
goto finally;
|
||||
/* Try to keep buffer level above min by pushing acks through quickly */
|
||||
else if (stream->currentBufferLevel < stream->minBufferLevel)
|
||||
goto dequeue;
|
||||
}
|
||||
|
||||
/* Time based acks only */
|
||||
ack_time = get_current_time();
|
||||
|
||||
if (sample->ack_time > ack_time)
|
||||
goto finally;
|
||||
}
|
||||
|
||||
dequeue:
|
||||
sample = Queue_Dequeue(stream->sample_ack_list);
|
||||
if (sample)
|
||||
{
|
||||
rc = tsmf_sample_ack(sample);
|
||||
tsmf_sample_ack(sample);
|
||||
tsmf_sample_free(sample);
|
||||
}
|
||||
|
||||
@ -295,6 +353,7 @@ TSMF_PRESENTATION* tsmf_presentation_new(const BYTE* guid, IWTSVirtualChannelCal
|
||||
CopyMemory(presentation->presentation_id, guid, GUID_SIZE);
|
||||
presentation->channel_callback = pChannelCallback;
|
||||
presentation->volume = 5000; /* 50% */
|
||||
presentation->muted = 0;
|
||||
if (!(presentation->stream_list = ArrayList_New(TRUE)))
|
||||
goto error_stream_list;
|
||||
|
||||
@ -372,9 +431,12 @@ static BOOL tsmf_sample_playback_video(TSMF_SAMPLE* sample)
|
||||
{
|
||||
t = get_current_time();
|
||||
|
||||
/* Start time is more reliable than end time as some stream types seem to have incorrect
|
||||
* end times from the server
|
||||
*/
|
||||
if (stream->next_start_time > t &&
|
||||
(sample->end_time >= presentation->audio_start_time ||
|
||||
sample->end_time < stream->last_end_time))
|
||||
((sample->start_time >= presentation->audio_start_time) ||
|
||||
((sample->start_time < stream->last_start_time) && (!sample->invalidTimestamps))))
|
||||
{
|
||||
USleep((stream->next_start_time - t) / 10);
|
||||
}
|
||||
@ -449,9 +511,15 @@ static BOOL tsmf_sample_playback_audio(TSMF_SAMPLE* sample)
|
||||
}
|
||||
|
||||
sample->ack_time = latency + get_current_time();
|
||||
stream->last_end_time = sample->end_time + latency;
|
||||
stream->presentation->audio_start_time = sample->start_time + latency;
|
||||
stream->presentation->audio_end_time = sample->end_time + latency;
|
||||
|
||||
/* Only update stream times if the sample timestamps are valid */
|
||||
if (!sample->invalidTimestamps)
|
||||
{
|
||||
stream->last_start_time = sample->start_time + latency;
|
||||
stream->last_end_time = sample->end_time + latency;
|
||||
stream->presentation->audio_start_time = sample->start_time + latency;
|
||||
stream->presentation->audio_end_time = sample->end_time + latency;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -467,6 +535,46 @@ static BOOL tsmf_sample_playback(TSMF_SAMPLE* sample)
|
||||
{
|
||||
if (stream->decoder->DecodeEx)
|
||||
{
|
||||
/* Try to "sync" video buffers to audio buffers by looking at the running time for each stream
|
||||
* The difference between the two running times causes an offset between audio and video actual
|
||||
* render times. So, we try to adjust timestamps on the video buffer to match those on the audio buffer.
|
||||
*/
|
||||
if (stream->major_type == TSMF_MAJOR_TYPE_VIDEO)
|
||||
{
|
||||
TSMF_STREAM* temp_stream = NULL;
|
||||
TSMF_PRESENTATION* presentation = stream->presentation;
|
||||
ArrayList_Lock(presentation->stream_list);
|
||||
int count = ArrayList_Count(presentation->stream_list);
|
||||
int index = 0;
|
||||
for (index = 0; index < count; index++)
|
||||
{
|
||||
UINT64 time_diff;
|
||||
|
||||
temp_stream = (TSMF_STREAM*) ArrayList_GetItem(presentation->stream_list, index);
|
||||
if (temp_stream->major_type == TSMF_MAJOR_TYPE_AUDIO)
|
||||
{
|
||||
UINT64 video_time = (UINT64) stream->decoder->GetRunningTime(stream->decoder);
|
||||
UINT64 audio_time = (UINT64) temp_stream->decoder->GetRunningTime(temp_stream->decoder);
|
||||
UINT64 max_adjust = VIDEO_ADJUST_MAX;
|
||||
|
||||
if (video_time < audio_time)
|
||||
max_adjust = -VIDEO_ADJUST_MAX;
|
||||
|
||||
if (video_time > audio_time)
|
||||
time_diff = video_time - audio_time;
|
||||
else
|
||||
time_diff = audio_time - video_time;
|
||||
|
||||
time_diff = time_diff < VIDEO_ADJUST_MAX ? time_diff : max_adjust;
|
||||
sample->start_time += time_diff;
|
||||
sample->end_time += time_diff;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
ArrayList_Unlock(presentation->stream_list);
|
||||
}
|
||||
|
||||
ret = stream->decoder->DecodeEx(stream->decoder, sample->data, sample->data_size, sample->extensions,
|
||||
sample->start_time, sample->end_time, sample->duration);
|
||||
}
|
||||
@ -478,9 +586,12 @@ static BOOL tsmf_sample_playback(TSMF_SAMPLE* sample)
|
||||
|
||||
if (!ret)
|
||||
{
|
||||
WLog_ERR(TAG, "decode error");
|
||||
WLog_ERR(TAG, "decode error, queue ack anyways");
|
||||
if (!tsmf_sample_queue_ack(sample))
|
||||
{
|
||||
WLog_ERR(TAG, "error queuing sample for ack");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
@ -499,7 +610,7 @@ static BOOL tsmf_sample_playback(TSMF_SAMPLE* sample)
|
||||
WLog_ERR(TAG, "unable to decode video format");
|
||||
if (!tsmf_sample_queue_ack(sample))
|
||||
{
|
||||
WLog_ERR(TAG, "error acking sample");
|
||||
WLog_ERR(TAG, "error queuing sample for ack");
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
@ -507,8 +618,6 @@ static BOOL tsmf_sample_playback(TSMF_SAMPLE* sample)
|
||||
sample->pixfmt = pixfmt;
|
||||
}
|
||||
|
||||
ret = FALSE;
|
||||
|
||||
if (stream->decoder->GetDecodedDimension)
|
||||
{
|
||||
ret = stream->decoder->GetDecodedDimension(stream->decoder, &width, &height);
|
||||
@ -543,54 +652,43 @@ static BOOL tsmf_sample_playback(TSMF_SAMPLE* sample)
|
||||
{
|
||||
TSMF_STREAM* stream = sample->stream;
|
||||
UINT64 ack_anticipation_time = get_current_time();
|
||||
UINT64 currentRunningTime = sample->start_time;
|
||||
BOOL buffer_filled = TRUE;
|
||||
|
||||
if (stream->decoder->GetRunningTime)
|
||||
/* Classify the buffer as filled once it reaches minimum level */
|
||||
if (stream->decoder->BufferLevel)
|
||||
{
|
||||
currentRunningTime = stream->decoder->GetRunningTime(stream->decoder);
|
||||
}
|
||||
|
||||
if (stream->decoder->BufferFilled)
|
||||
{
|
||||
buffer_filled = stream->decoder->BufferFilled(stream->decoder);
|
||||
if (stream->currentBufferLevel < stream->minBufferLevel)
|
||||
buffer_filled = FALSE;
|
||||
}
|
||||
|
||||
if (buffer_filled)
|
||||
{
|
||||
if (currentRunningTime > sample->start_time)
|
||||
{
|
||||
ack_anticipation_time += sample->duration;
|
||||
}
|
||||
else if (currentRunningTime == 0)
|
||||
{
|
||||
ack_anticipation_time += sample->duration;
|
||||
}
|
||||
else
|
||||
{
|
||||
ack_anticipation_time += (sample->start_time - currentRunningTime);
|
||||
}
|
||||
ack_anticipation_time += (sample->duration/2 < MAX_ACK_TIME) ? sample->duration/2 : MAX_ACK_TIME;
|
||||
}
|
||||
else
|
||||
{
|
||||
ack_anticipation_time += sample->duration / 2;
|
||||
ack_anticipation_time += (sample->duration/2 < MAX_ACK_TIME) ? sample->duration/2 : MAX_ACK_TIME;
|
||||
}
|
||||
|
||||
switch (sample->stream->major_type)
|
||||
{
|
||||
case TSMF_MAJOR_TYPE_VIDEO:
|
||||
{
|
||||
break;
|
||||
}
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
case TSMF_MAJOR_TYPE_AUDIO:
|
||||
{
|
||||
break;
|
||||
}
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sample->ack_time = ack_anticipation_time;
|
||||
ret = tsmf_sample_queue_ack(sample);
|
||||
if (!tsmf_sample_queue_ack(sample))
|
||||
{
|
||||
WLog_ERR(TAG, "error queuing sample for ack");
|
||||
ret = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
@ -608,31 +706,63 @@ static void* tsmf_stream_ack_func(void *arg)
|
||||
|
||||
while (1)
|
||||
{
|
||||
DWORD ev = WaitForMultipleObjects(2, hdl, FALSE, INFINITE);
|
||||
DWORD ev = WaitForMultipleObjects(2, hdl, FALSE, 1000);
|
||||
|
||||
if (ev == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %lu!", error);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ev == WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
if (!stream->decoder)
|
||||
continue;
|
||||
|
||||
if (stream->decoder->SetAckFunc)
|
||||
continue;
|
||||
|
||||
if (tsmf_stream_process_ack(stream, FALSE))
|
||||
if (ev == WAIT_FAILED)
|
||||
{
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
WLog_ERR(TAG, "tsmf_stream_process_ack failed!");
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %lu!", error);
|
||||
break;
|
||||
}
|
||||
|
||||
if (stream->decoder)
|
||||
if (stream->decoder->BufferLevel)
|
||||
stream->currentBufferLevel = stream->decoder->BufferLevel(stream->decoder);
|
||||
|
||||
if (stream->eos)
|
||||
{
|
||||
while ((stream->currentBufferLevel > 0) || !(tsmf_stream_process_ack(stream, TRUE)))
|
||||
{
|
||||
DEBUG_TSMF("END OF STREAM PROCESSING!");
|
||||
if (stream->decoder->BufferLevel)
|
||||
stream->currentBufferLevel = stream->decoder->BufferLevel(stream->decoder);
|
||||
else
|
||||
stream->currentBufferLevel = 1;
|
||||
|
||||
USleep(1000);
|
||||
}
|
||||
|
||||
tsmf_send_eos_response(stream->eos_channel_callback, stream->eos_message_id);
|
||||
stream->eos = 0;
|
||||
|
||||
if (stream->delayed_stop)
|
||||
{
|
||||
DEBUG_TSMF("Finishing delayed stream stop, now that eos has processed.");
|
||||
tsmf_stream_flush(stream);
|
||||
|
||||
if (stream->decoder->Control)
|
||||
stream->decoder->Control(stream->decoder, Control_Stop, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Stream stopped force all of the acks to happen */
|
||||
if (ev == WAIT_OBJECT_0)
|
||||
{
|
||||
DEBUG_TSMF("ack: Stream stopped!");
|
||||
while(1)
|
||||
{
|
||||
if (tsmf_stream_process_ack(stream, TRUE))
|
||||
break;
|
||||
USleep(1000);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (tsmf_stream_process_ack(stream, FALSE))
|
||||
continue;
|
||||
|
||||
if (stream->currentBufferLevel > stream->minBufferLevel)
|
||||
USleep(1000);
|
||||
}
|
||||
|
||||
if (error && stream->rdpcontext)
|
||||
@ -646,11 +776,11 @@ static void* tsmf_stream_ack_func(void *arg)
|
||||
static void* tsmf_stream_playback_func(void *arg)
|
||||
{
|
||||
HANDLE hdl[2];
|
||||
TSMF_SAMPLE* sample;
|
||||
TSMF_SAMPLE* sample = NULL;
|
||||
TSMF_STREAM* stream = (TSMF_STREAM *) arg;
|
||||
TSMF_PRESENTATION* presentation = stream->presentation;
|
||||
UINT error = CHANNEL_RC_OK;
|
||||
DWORD status;
|
||||
DWORD status;
|
||||
|
||||
DEBUG_TSMF("in %d", stream->stream_id);
|
||||
|
||||
@ -678,28 +808,32 @@ static void* tsmf_stream_playback_func(void *arg)
|
||||
|
||||
while (1)
|
||||
{
|
||||
status = WaitForMultipleObjects(2, hdl, FALSE, INFINITE);
|
||||
status = WaitForMultipleObjects(2, hdl, FALSE, 1000);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %lu!", error);
|
||||
break;
|
||||
}
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForMultipleObjects failed with error %lu!", error);
|
||||
break;
|
||||
}
|
||||
|
||||
status = WaitForSingleObject(stream->stopEvent, 0);
|
||||
status = WaitForSingleObject(stream->stopEvent, 0);
|
||||
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", error);
|
||||
break;
|
||||
}
|
||||
if (status == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", error);
|
||||
break;
|
||||
}
|
||||
|
||||
if (status == WAIT_OBJECT_0)
|
||||
break;
|
||||
if (status == WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
sample = tsmf_stream_pop_sample(stream, 0);
|
||||
if (stream->decoder)
|
||||
if (stream->decoder->BufferLevel)
|
||||
stream->currentBufferLevel = stream->decoder->BufferLevel(stream->decoder);
|
||||
|
||||
sample = tsmf_stream_pop_sample(stream, 0);
|
||||
|
||||
if (sample && !tsmf_sample_playback(sample))
|
||||
{
|
||||
@ -707,8 +841,12 @@ static void* tsmf_stream_playback_func(void *arg)
|
||||
error = ERROR_INTERNAL_ERROR;
|
||||
break;
|
||||
}
|
||||
|
||||
if (stream->currentBufferLevel > stream->minBufferLevel)
|
||||
USleep(1000);
|
||||
}
|
||||
|
||||
|
||||
if (stream->audio)
|
||||
{
|
||||
stream->audio->Free(stream->audio);
|
||||
@ -728,7 +866,9 @@ static BOOL tsmf_stream_start(TSMF_STREAM* stream)
|
||||
if (!stream || !stream->presentation || !stream->decoder || !stream->decoder->Control)
|
||||
return TRUE;
|
||||
|
||||
return stream->decoder->Control(stream->decoder, Control_Resume, NULL);
|
||||
stream->eos = 0;
|
||||
|
||||
return stream->decoder->Control(stream->decoder, Control_Restart, NULL);
|
||||
}
|
||||
|
||||
static BOOL tsmf_stream_stop(TSMF_STREAM* stream)
|
||||
@ -736,7 +876,24 @@ static BOOL tsmf_stream_stop(TSMF_STREAM* stream)
|
||||
if (!stream || !stream->decoder || !stream->decoder->Control)
|
||||
return TRUE;
|
||||
|
||||
return stream->decoder->Control(stream->decoder, Control_Stop, NULL);
|
||||
/* If stopping after eos - we delay until the eos has been processed
|
||||
* this allows us to process any buffers that have been acked even though
|
||||
* they have not actually been completely processes by the decoder
|
||||
*/
|
||||
if (stream->eos)
|
||||
{
|
||||
DEBUG_TSMF("Setting up a delayed stop for once the eos has been processed.");
|
||||
stream->delayed_stop = 1;
|
||||
return TRUE;
|
||||
}
|
||||
/* Otherwise force stop immediately */
|
||||
else
|
||||
{
|
||||
DEBUG_TSMF("Stop with no pending eos response, so do it immediately.");
|
||||
tsmf_stream_flush(stream);
|
||||
|
||||
return stream->decoder->Control(stream->decoder, Control_Stop, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL tsmf_stream_pause(TSMF_STREAM* stream)
|
||||
@ -752,7 +909,9 @@ static BOOL tsmf_stream_restart(TSMF_STREAM* stream)
|
||||
if (!stream || !stream->decoder || !stream->decoder->Control)
|
||||
return TRUE;
|
||||
|
||||
return stream->decoder->Control(stream->decoder, Control_Resume, NULL);
|
||||
stream->eos = 0;
|
||||
|
||||
return stream->decoder->Control(stream->decoder, Control_Restart, NULL);
|
||||
}
|
||||
|
||||
static BOOL tsmf_stream_change_volume(TSMF_STREAM* stream, UINT32 newVolume, UINT32 muted)
|
||||
@ -863,7 +1022,7 @@ UINT tsmf_presentation_sync(TSMF_PRESENTATION* presentation)
|
||||
{
|
||||
UINT32 index;
|
||||
UINT32 count;
|
||||
UINT error;
|
||||
UINT error;
|
||||
|
||||
ArrayList_Lock(presentation->stream_list);
|
||||
count = ArrayList_Count(presentation->stream_list);
|
||||
@ -872,15 +1031,15 @@ UINT tsmf_presentation_sync(TSMF_PRESENTATION* presentation)
|
||||
{
|
||||
TSMF_STREAM* stream = (TSMF_STREAM *) ArrayList_GetItem(presentation->stream_list, index);
|
||||
if (WaitForSingleObject(stream->ready, 500) == WAIT_FAILED)
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
{
|
||||
error = GetLastError();
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", error);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList_Unlock(presentation->stream_list);
|
||||
return CHANNEL_RC_OK;
|
||||
return CHANNEL_RC_OK;
|
||||
}
|
||||
|
||||
BOOL tsmf_presentation_stop(TSMF_PRESENTATION* presentation)
|
||||
@ -890,8 +1049,6 @@ BOOL tsmf_presentation_stop(TSMF_PRESENTATION* presentation)
|
||||
TSMF_STREAM* stream;
|
||||
BOOL ret = TRUE;
|
||||
|
||||
ret &= tsmf_presentation_flush(presentation);
|
||||
|
||||
ArrayList_Lock(presentation->stream_list);
|
||||
count = ArrayList_Count(presentation->stream_list);
|
||||
|
||||
@ -902,6 +1059,9 @@ BOOL tsmf_presentation_stop(TSMF_PRESENTATION* presentation)
|
||||
}
|
||||
|
||||
ArrayList_Unlock(presentation->stream_list);
|
||||
presentation->audio_start_time = 0;
|
||||
presentation->audio_end_time = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -911,33 +1071,26 @@ BOOL tsmf_presentation_set_geometry_info(TSMF_PRESENTATION* presentation,
|
||||
UINT32 index;
|
||||
UINT32 count;
|
||||
TSMF_STREAM* stream;
|
||||
void *tmp_rects;
|
||||
void *tmp_rects = NULL;
|
||||
BOOL ret = TRUE;
|
||||
|
||||
if (num_rects < 1 || !rects)
|
||||
return TRUE;
|
||||
|
||||
/* The server may send messages with invalid width / height.
|
||||
* Ignore those messages. */
|
||||
if (!width || !height)
|
||||
return TRUE;
|
||||
|
||||
if ((width == presentation->width) && (height == presentation->height) &&
|
||||
(x == presentation->x) && (y == presentation->y) &&
|
||||
(num_rects == presentation->nr_rects) &&
|
||||
(0 == memcmp(rects, presentation->rects, num_rects * sizeof(RDP_RECT))))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
/* Streams can be added/removed from the presentation and the server will resend geometry info when a new stream is
|
||||
* added to the presentation. Also, num_rects is used to indicate whether or not the window is visible.
|
||||
* So, always process a valid message with unchanged position/size and/or no visibility rects.
|
||||
*/
|
||||
|
||||
presentation->x = x;
|
||||
presentation->y = y;
|
||||
presentation->width = width;
|
||||
presentation->height = height;
|
||||
|
||||
|
||||
tmp_rects = realloc(presentation->rects, sizeof(RDP_RECT) * num_rects);
|
||||
if (!tmp_rects)
|
||||
return FALSE;
|
||||
|
||||
presentation->nr_rects = num_rects;
|
||||
presentation->rects = tmp_rects;
|
||||
|
||||
@ -969,7 +1122,7 @@ void tsmf_presentation_set_audio_device(TSMF_PRESENTATION* presentation, const c
|
||||
presentation->audio_device = device;
|
||||
}
|
||||
|
||||
static BOOL tsmf_stream_flush(TSMF_STREAM* stream)
|
||||
BOOL tsmf_stream_flush(TSMF_STREAM* stream)
|
||||
{
|
||||
BOOL ret = TRUE;
|
||||
|
||||
@ -979,6 +1132,9 @@ static BOOL tsmf_stream_flush(TSMF_STREAM* stream)
|
||||
ret = stream->audio->Flush(stream->audio);
|
||||
|
||||
stream->eos = 0;
|
||||
stream->eos_message_id = 0;
|
||||
stream->eos_channel_callback = NULL;
|
||||
stream->delayed_stop = 0;
|
||||
stream->last_end_time = 0;
|
||||
stream->next_start_time = 0;
|
||||
|
||||
@ -990,30 +1146,6 @@ static BOOL tsmf_stream_flush(TSMF_STREAM* stream)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL tsmf_presentation_flush(TSMF_PRESENTATION* presentation)
|
||||
{
|
||||
UINT32 index;
|
||||
UINT32 count;
|
||||
TSMF_STREAM* stream;
|
||||
BOOL ret = TRUE;
|
||||
|
||||
ArrayList_Lock(presentation->stream_list);
|
||||
count = ArrayList_Count(presentation->stream_list);
|
||||
|
||||
for (index = 0; index < count; index++)
|
||||
{
|
||||
stream = (TSMF_STREAM*) ArrayList_GetItem(presentation->stream_list, index);
|
||||
ret &= tsmf_stream_flush(stream);
|
||||
}
|
||||
|
||||
ArrayList_Unlock(presentation->stream_list);
|
||||
presentation->eos = 0;
|
||||
presentation->audio_start_time = 0;
|
||||
presentation->audio_end_time = 0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void _tsmf_presentation_free(TSMF_PRESENTATION* presentation)
|
||||
{
|
||||
tsmf_presentation_stop(presentation);
|
||||
@ -1049,6 +1181,14 @@ TSMF_STREAM* tsmf_stream_new(TSMF_PRESENTATION* presentation, UINT32 stream_id,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
stream->minBufferLevel = VIDEO_MIN_BUFFER_LEVEL;
|
||||
stream->maxBufferLevel = VIDEO_MAX_BUFFER_LEVEL;
|
||||
stream->currentBufferLevel = 1;
|
||||
|
||||
stream->seeking = FALSE;
|
||||
stream->eos = 0;
|
||||
stream->eos_message_id = 0;
|
||||
stream->eos_channel_callback = NULL;
|
||||
stream->stream_id = stream_id;
|
||||
stream->presentation = presentation;
|
||||
stream->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
@ -1083,11 +1223,11 @@ TSMF_STREAM* tsmf_stream_new(TSMF_PRESENTATION* presentation, UINT32 stream_id,
|
||||
error_add:
|
||||
SetEvent(stream->stopEvent);
|
||||
if (WaitForSingleObject(stream->ack_thread, INFINITE) == WAIT_FAILED)
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", GetLastError());
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", GetLastError());
|
||||
error_ack_thread:
|
||||
SetEvent(stream->stopEvent);
|
||||
if (WaitForSingleObject(stream->play_thread, INFINITE) == WAIT_FAILED)
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", GetLastError());
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", GetLastError());
|
||||
error_play_thread:
|
||||
Queue_Free(stream->sample_ack_list);
|
||||
error_sample_ack_list:
|
||||
@ -1144,7 +1284,10 @@ BOOL tsmf_stream_set_format(TSMF_STREAM* stream, const char *name, wStream *s)
|
||||
}
|
||||
|
||||
if (!tsmf_codec_parse_media_type(&mediatype, s))
|
||||
{
|
||||
WLog_ERR(TAG, "unable to parse media type");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (mediatype.MajorType == TSMF_MAJOR_TYPE_VIDEO)
|
||||
{
|
||||
@ -1152,6 +1295,9 @@ BOOL tsmf_stream_set_format(TSMF_STREAM* stream, const char *name, wStream *s)
|
||||
mediatype.Width, mediatype.Height, mediatype.BitRate,
|
||||
(double) mediatype.SamplesPerSecond.Numerator / (double) mediatype.SamplesPerSecond.Denominator,
|
||||
mediatype.ExtraDataSize);
|
||||
|
||||
stream->minBufferLevel = VIDEO_MIN_BUFFER_LEVEL;
|
||||
stream->maxBufferLevel = VIDEO_MAX_BUFFER_LEVEL;
|
||||
}
|
||||
else if (mediatype.MajorType == TSMF_MAJOR_TYPE_AUDIO)
|
||||
{
|
||||
@ -1164,6 +1310,9 @@ BOOL tsmf_stream_set_format(TSMF_STREAM* stream, const char *name, wStream *s)
|
||||
|
||||
if (stream->bits_per_sample == 0)
|
||||
stream->bits_per_sample = 16;
|
||||
|
||||
stream->minBufferLevel = AUDIO_MIN_BUFFER_LEVEL;
|
||||
stream->maxBufferLevel = AUDIO_MAX_BUFFER_LEVEL;
|
||||
}
|
||||
|
||||
stream->major_type = mediatype.MajorType;
|
||||
@ -1183,13 +1332,14 @@ BOOL tsmf_stream_set_format(TSMF_STREAM* stream, const char *name, wStream *s)
|
||||
return ret;
|
||||
}
|
||||
|
||||
void tsmf_stream_end(TSMF_STREAM* stream)
|
||||
void tsmf_stream_end(TSMF_STREAM* stream, UINT32 message_id, IWTSVirtualChannelCallback* pChannelCallback)
|
||||
{
|
||||
if (!stream)
|
||||
return;
|
||||
|
||||
stream->eos = 1;
|
||||
stream->presentation->eos = 1;
|
||||
stream->eos_message_id = message_id;
|
||||
stream->eos_channel_callback = pChannelCallback;
|
||||
}
|
||||
|
||||
void _tsmf_stream_free(TSMF_STREAM* stream)
|
||||
@ -1197,17 +1347,16 @@ void _tsmf_stream_free(TSMF_STREAM* stream)
|
||||
if (!stream)
|
||||
return;
|
||||
|
||||
if (tsmf_stream_stop(stream))
|
||||
tsmf_stream_flush(stream);
|
||||
tsmf_stream_stop(stream);
|
||||
SetEvent(stream->stopEvent);
|
||||
|
||||
if (stream->play_thread)
|
||||
{
|
||||
if (WaitForSingleObject(stream->play_thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", GetLastError());
|
||||
return;
|
||||
}
|
||||
{
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", GetLastError());
|
||||
return;
|
||||
}
|
||||
|
||||
CloseHandle(stream->play_thread);
|
||||
stream->play_thread = NULL;
|
||||
@ -1216,10 +1365,10 @@ void _tsmf_stream_free(TSMF_STREAM* stream)
|
||||
if (stream->ack_thread)
|
||||
{
|
||||
if (WaitForSingleObject(stream->ack_thread, INFINITE) == WAIT_FAILED)
|
||||
{
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", GetLastError());
|
||||
return;
|
||||
}
|
||||
{
|
||||
WLog_ERR(TAG, "WaitForSingleObject failed with error %lu!", GetLastError());
|
||||
return;
|
||||
}
|
||||
CloseHandle(stream->ack_thread);
|
||||
stream->ack_thread = NULL;
|
||||
}
|
||||
@ -1267,6 +1416,10 @@ BOOL tsmf_stream_push_sample(TSMF_STREAM* stream, IWTSVirtualChannelCallback *pC
|
||||
sample->end_time = end_time;
|
||||
sample->duration = duration;
|
||||
sample->extensions = extensions;
|
||||
if ((sample->extensions & 0x00000080) || (sample->extensions & 0x00000040))
|
||||
sample->invalidTimestamps = TRUE;
|
||||
else
|
||||
sample->invalidTimestamps = FALSE;
|
||||
sample->stream = stream;
|
||||
sample->channel_callback = pChannelCallback;
|
||||
sample->data_size = data_size;
|
||||
|
@ -49,14 +49,14 @@ BOOL tsmf_presentation_set_geometry_info(TSMF_PRESENTATION *presentation,
|
||||
int num_rects, RDP_RECT *rects);
|
||||
void tsmf_presentation_set_audio_device(TSMF_PRESENTATION *presentation,
|
||||
const char *name, const char *device);
|
||||
BOOL tsmf_presentation_flush(TSMF_PRESENTATION *presentation);
|
||||
void tsmf_presentation_free(TSMF_PRESENTATION *presentation);
|
||||
|
||||
TSMF_STREAM *tsmf_stream_new(TSMF_PRESENTATION *presentation, UINT32 stream_id, rdpContext* rdpcontext);
|
||||
TSMF_STREAM *tsmf_stream_find_by_id(TSMF_PRESENTATION *presentation, UINT32 stream_id);
|
||||
BOOL tsmf_stream_set_format(TSMF_STREAM *stream, const char *name, wStream *s);
|
||||
void tsmf_stream_end(TSMF_STREAM *stream);
|
||||
void tsmf_stream_end(TSMF_STREAM *stream, UINT32 message_id, IWTSVirtualChannelCallback* pChannelCallback);
|
||||
void tsmf_stream_free(TSMF_STREAM *stream);
|
||||
BOOL tsmf_stream_flush(TSMF_STREAM* stream);
|
||||
|
||||
BOOL tsmf_stream_push_sample(TSMF_STREAM *stream, IWTSVirtualChannelCallback *pChannelCallback,
|
||||
UINT32 sample_id, UINT64 start_time, UINT64 end_time, UINT64 duration, UINT32 extensions,
|
||||
|
@ -48,7 +48,9 @@ set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr freerdp)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT STATIC_CHANNELS AND BUILD_SHARED_LIBS)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
||||
endif()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Client")
|
||||
|
||||
|
@ -44,5 +44,7 @@ set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${FREERDP_ADDIN_PATH} EXPORT FreeRDPTargets)
|
||||
if (WITH_DEBUG_SYMBOLS AND MSVC AND NOT STATIC_CHANNELS AND BUILD_SHARED_LIBS)
|
||||
install(FILES ${CMAKE_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${FREERDP_ADDIN_PATH} COMPONENT symbols)
|
||||
endif()
|
||||
|
||||
|
@ -1335,7 +1335,7 @@ static UINT urbdrc_on_close(IWTSVirtualChannelCallback * pChannelCallback)
|
||||
* @return 0 on success, otherwise a Win32 error code
|
||||
*/
|
||||
static UINT urbdrc_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
|
||||
IWTSVirtualChannel * pChannel, BYTE* pData, int* pbAccept, IWTSVirtualChannelCallback** ppCallback)
|
||||
IWTSVirtualChannel * pChannel, BYTE* pData, BOOL* pbAccept, IWTSVirtualChannelCallback** ppCallback)
|
||||
{
|
||||
URBDRC_LISTENER_CALLBACK* listener_callback = (URBDRC_LISTENER_CALLBACK*) pListenerCallback;
|
||||
URBDRC_CHANNEL_CALLBACK* callback;
|
||||
|
1
client/.gitignore
vendored
1
client/.gitignore
vendored
@ -9,3 +9,4 @@
|
||||
!/X11
|
||||
!/Wayland
|
||||
!/CMakeLists.txt
|
||||
!*.in
|
||||
|
15
client/Android/.gitignore
vendored
15
client/Android/.gitignore
vendored
@ -1,15 +0,0 @@
|
||||
# Ignore directories
|
||||
bin/
|
||||
obj/
|
||||
gen/
|
||||
jni/external/*
|
||||
!libs
|
||||
libs/armeabi*
|
||||
AndroidManifest.xml
|
||||
local.properties
|
||||
!.project
|
||||
appcompat_v7
|
||||
|
||||
FreeRDPCore/project.properties
|
||||
FreeRDPCore/src/com/freerdp/freerdpcore/utils/BuildConfiguration.java
|
||||
aFreeRDP/project.properties
|
@ -1,6 +1,7 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# Android Client
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
# Copyright 2013 Bernhard Miklautz <bernhard.miklautz@thincast.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -15,68 +16,39 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
if (NOT ANDROID_NDK)
|
||||
message(FATAL_ERROR "ANDROID_NDK not set but required for building android native library.")
|
||||
set(MODULE_NAME "freerdp-android")
|
||||
set(MODULE_PREFIX "FREERDP_CLIENT_ANDROID")
|
||||
|
||||
include_directories(.)
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCC)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-pointer-sign")
|
||||
endif()
|
||||
|
||||
set(CMAKE_PROGRAM_PATH ${ANDROID_NDK})
|
||||
find_program(NDK_COMMAND ndk-build CMAKE_FIND_ROOT_PATH_BOTH)
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
android_event.c
|
||||
android_event.h
|
||||
android_freerdp.c
|
||||
android_freerdp.h
|
||||
android_jni_utils.c
|
||||
android_jni_utils.h
|
||||
android_jni_callback.c
|
||||
android_jni_callback.h)
|
||||
|
||||
if(NDK_COMMAND STREQUAL "NDK_COMMAND-NOTFOUND")
|
||||
message(FATAL_ERROR "ndk-build not found but required to build native lib")
|
||||
if(WITH_CLIENT_CHANNELS)
|
||||
set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS}
|
||||
android_cliprdr.c
|
||||
android_cliprdr.h)
|
||||
endif()
|
||||
|
||||
set(NDK_LIB_CFG "${CMAKE_CURRENT_BINARY_DIR}/FreeRDPCore/jni/Android.mk")
|
||||
if(ANDROID_BUILD_JAVA)
|
||||
if (NOT ANDROID_SDK)
|
||||
message(FATAL_ERROR "ANDROID_SDK not set but required for building the java gui (ANDROID_BUILD_JAVA)")
|
||||
endif()
|
||||
# And isn't shiped with the android ndk/sdk so
|
||||
# we need to find it on the local machine
|
||||
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM BOTH)
|
||||
find_program(ANT_COMMAND ant)
|
||||
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
|
||||
add_library(${MODULE_NAME} SHARED ${${MODULE_PREFIX}_SRCS})
|
||||
|
||||
if(ANT_COMMAND STREQUAL "ANT_COMMAND-NOTFOUND")
|
||||
message(FATAL_ERROR "ant not found but required to build android java")
|
||||
endif()
|
||||
endif(ANDROID_BUILD_JAVA)
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp-client)
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr freerdp)
|
||||
|
||||
set(ANDROID_COMMAND "${ANDROID_SDK}/tools/android")
|
||||
if(NOT EXISTS ${ANDROID_COMMAND})
|
||||
message(FATAL_ERROR "android not found but required to build android java")
|
||||
endif()
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} dl)
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} log)
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} jnigraphics)
|
||||
|
||||
if(ANDROID_BUILD_JAVA_DEBUG)
|
||||
set(ANDROID_BUILD_TYPE "debug")
|
||||
else()
|
||||
set(ANDROID_BUILD_TYPE "release")
|
||||
endif()
|
||||
|
||||
if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
|
||||
set(ANDROID_DEBUG_ENABLE "true")
|
||||
set(NDK_DEBUG "1")
|
||||
else()
|
||||
set(ANDROID_DEBUG_ENABLE "false")
|
||||
set(NDK_DEBUG "0")
|
||||
endif()
|
||||
|
||||
set(APPCOMPAT_DIR "${CMAKE_CURRENT_BINARY_DIR}/appcompat_v7")
|
||||
set(supportdir "${ANDROID_SDK}/extras/android/support/v7/appcompat")
|
||||
set(compatibilitydir "${ANDROID_SDK}/extras/android/compatibility/v7/appcompat")
|
||||
if(EXISTS "${supportdir}" AND IS_DIRECTORY "${supportdir}")
|
||||
add_custom_target(copy_appcompat ALL
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${supportdir}" ${APPCOMPAT_DIR}
|
||||
COMMAND ${ANDROID_COMMAND} update lib-project -p ${APPCOMPAT_DIR} -t android-${ANDROID_APP_TARGET_SDK}
|
||||
)
|
||||
elseif(EXISTS "${compatibilitydir}" AND IS_DIRECTORY "${compatibilitydir}")
|
||||
add_custom_target(copy_appcompat ALL
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${compatibilitydir}" ${APPCOMPAT_DIR}
|
||||
COMMAND ${ANDROID_COMMAND} update lib-project -p ${APPCOMPAT_DIR} -t android-${ANDROID_APP_TARGET_SDK}
|
||||
)
|
||||
else()
|
||||
message( FATAL_ERROR "${ANDROID_SDK}/extras/android/{support|compatibility}/v7/appcompat directory not found. Please install a recent version of Android Support Library, CMake will now exit." )
|
||||
endif()
|
||||
|
||||
add_subdirectory(FreeRDPCore)
|
||||
add_subdirectory(aFreeRDP)
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT libraries EXPORT AndroidTargets)
|
||||
|
@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
|
||||
<classpathentry exported="true" kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="gen"/>
|
||||
<classpathentry kind="output" path="bin/classes"/>
|
||||
</classpath>
|
4
client/Android/FreeRDPCore/.gitignore
vendored
4
client/Android/FreeRDPCore/.gitignore
vendored
@ -1,4 +0,0 @@
|
||||
ant.properties
|
||||
build.xml
|
||||
jni/Android.mk
|
||||
jni/Application.mk
|
@ -1,53 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>FreeRDPCore</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
|
||||
<triggers>full,incremental,</triggers>
|
||||
<arguments>
|
||||
<dictionary>
|
||||
<key>LaunchConfigHandle</key>
|
||||
<value><project>/.externalToolBuilders/org.eclipse.cdt.managedbuilder.core.genmakebuilder.launch</value>
|
||||
</dictionary>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
|
||||
<triggers>full,incremental,</triggers>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
<nature>org.eclipse.cdt.core.cnature</nature>
|
||||
<nature>org.eclipse.cdt.core.ccnature</nature>
|
||||
<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
|
||||
<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
@ -1,48 +0,0 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# Android Client
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
# Copyright 2013 Bernhard Miklautz <bernhard.miklautz@thincast.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set(ANDROID_PACKAGE_NAME "aFreeRDPCore")
|
||||
|
||||
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/AndroidManifest.xml.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/AndroidManifest.xml @ONLY)
|
||||
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/build.xml.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/build.xml @ONLY)
|
||||
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/project.properties.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/project.properties @ONLY)
|
||||
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/ant.properties.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/ant.properties @ONLY)
|
||||
|
||||
# Generate a Java class with static members set to the CMake
|
||||
# configuration properties.
|
||||
set(JAVA_CFG "src/com/freerdp/freerdpcore/utils/BuildConfiguration.java")
|
||||
set(JAVA_CFG_OUT "${CMAKE_CURRENT_BINARY_DIR}/${JAVA_CFG}")
|
||||
set(JAVA_CFG_IN "${CMAKE_CURRENT_SOURCE_DIR}/${JAVA_CFG}.in")
|
||||
|
||||
CONFIGURE_FILE(${JAVA_CFG_IN} ${JAVA_CFG_OUT})
|
||||
|
||||
file(COPY res DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
if (ANDROID_SDK)
|
||||
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/local.properties.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/local.properties @ONLY)
|
||||
endif()
|
||||
|
||||
add_subdirectory(jni)
|
||||
|
||||
SET_DIRECTORY_PROPERTIES(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "gen;bin;obj;libs")
|
||||
|
@ -1,20 +0,0 @@
|
||||
# This file is used to override default values used by the Ant build system.
|
||||
#
|
||||
# This file must be checked into Version Control Systems, as it is
|
||||
# integral to the build system of your project.
|
||||
|
||||
# This file is only used by the Ant script.
|
||||
|
||||
# You can use this to override default values such as
|
||||
# 'source.dir' for the location of your java source folder and
|
||||
# 'out.dir' for the location of your output folder.
|
||||
|
||||
# You can also use it define how the release builds are signed by declaring
|
||||
# the following properties:
|
||||
# 'key.store' for the location of your keystore and
|
||||
# 'key.alias' for the name of the key to use.
|
||||
# The password will be asked during the build when you use the 'release' target.
|
||||
build.dir=@CMAKE_CURRENT_BINARY_DIR@
|
||||
source.path=@CMAKE_CURRENT_SOURCE_DIR@/src:@CMAKE_CURRENT_BINARY_DIR@/src
|
||||
out.dir=@CMAKE_CURRENT_BINARY_DIR@/bin
|
||||
|
@ -1,92 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project name="." default="help">
|
||||
|
||||
<!-- The local.properties file is created and updated by the 'android' tool.
|
||||
It contains the path to the SDK. It should *NOT* be checked into
|
||||
Version Control Systems. -->
|
||||
<property file="local.properties" />
|
||||
|
||||
<!-- The ant.properties file can be created by you. It is only edited by the
|
||||
'android' tool to add properties to it.
|
||||
This is the place to change some Ant specific build properties.
|
||||
Here are some properties you may want to change/update:
|
||||
|
||||
source.dir
|
||||
The name of the source directory. Default is 'src'.
|
||||
out.dir
|
||||
The name of the output directory. Default is 'bin'.
|
||||
|
||||
For other overridable properties, look at the beginning of the rules
|
||||
files in the SDK, at tools/ant/build.xml
|
||||
|
||||
Properties related to the SDK location or the project target should
|
||||
be updated using the 'android' tool with the 'update' action.
|
||||
|
||||
This file is an integral part of the build system for your
|
||||
application and should be checked into Version Control Systems.
|
||||
|
||||
-->
|
||||
<property file="ant.properties" />
|
||||
|
||||
<!-- if sdk.dir was not set from one of the property file, then
|
||||
get it from the ANDROID_HOME env var.
|
||||
This must be done before we load project.properties since
|
||||
the proguard config can use sdk.dir -->
|
||||
<property environment="env" />
|
||||
<condition property="sdk.dir" value="${env.ANDROID_HOME}">
|
||||
<isset property="env.ANDROID_HOME" />
|
||||
</condition>
|
||||
|
||||
<!-- The project.properties file is created and updated by the 'android'
|
||||
tool, as well as ADT.
|
||||
|
||||
This contains project specific properties such as project target, and library
|
||||
dependencies. Lower level build properties are stored in ant.properties
|
||||
(or in .classpath for Eclipse projects).
|
||||
|
||||
This file is an integral part of the build system for your
|
||||
application and should be checked into Version Control Systems. -->
|
||||
<loadproperties srcFile="project.properties" />
|
||||
|
||||
<!-- quick check on sdk.dir -->
|
||||
<fail
|
||||
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
|
||||
unless="sdk.dir"
|
||||
/>
|
||||
|
||||
<!--
|
||||
Import per project custom build rules if present at the root of the project.
|
||||
This is the place to put custom intermediary targets such as:
|
||||
-pre-build
|
||||
-pre-compile
|
||||
-post-compile (This is typically used for code obfuscation.
|
||||
Compiled code location: ${out.classes.absolute.dir}
|
||||
If this is not done in place, override ${out.dex.input.absolute.dir})
|
||||
-post-package
|
||||
-post-build
|
||||
-pre-clean
|
||||
-->
|
||||
<import file="custom_rules.xml" optional="true" />
|
||||
|
||||
<!-- Import the actual build file.
|
||||
|
||||
To customize existing targets, there are two options:
|
||||
- Customize only one target:
|
||||
- copy/paste the target into this file, *before* the
|
||||
<import> task.
|
||||
- customize it to your needs.
|
||||
- Customize the whole content of build.xml
|
||||
- copy/paste the content of the rules files (minus the top node)
|
||||
into this file, replacing the <import> task.
|
||||
- customize to your needs.
|
||||
|
||||
***********************
|
||||
****** IMPORTANT ******
|
||||
***********************
|
||||
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
|
||||
in order to avoid having your file be overridden by tools such as "android update project"
|
||||
-->
|
||||
<!-- version-tag: 1 -->
|
||||
<import file="${sdk.dir}/tools/ant/build.xml" />
|
||||
|
||||
</project>
|
@ -1,8 +0,0 @@
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := freerdp-android
|
||||
LOCAL_SRC_FILES := $(TARGET_ARCH_ABI)/libfreerdp-android.so
|
||||
LOCAL_EXPORT_C_INCLUDES := ../../../../include
|
||||
include $(PREBUILT_SHARED_LIBRARY)
|
@ -1 +0,0 @@
|
||||
APP_ABI := @ANDROID_ABI@
|
@ -1,73 +0,0 @@
|
||||
# FreeRDP: A Remote Desktop Protocol Implementation
|
||||
# Android Client
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
# Copyright 2013 Bernhard Miklautz <bernhard.miklautz@thincast.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
set(MODULE_NAME "freerdp-android")
|
||||
set(MODULE_PREFIX "FREERDP_CLIENT_ANDROID")
|
||||
|
||||
include_directories(.)
|
||||
include_directories(generated)
|
||||
|
||||
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/Application.mk.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/Application.mk @ONLY)
|
||||
CONFIGURE_FILE(${CMAKE_CURRENT_SOURCE_DIR}/Android.mk.cmake
|
||||
${CMAKE_CURRENT_BINARY_DIR}/Android.mk @ONLY)
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCC)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-pointer-sign")
|
||||
endif()
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS
|
||||
android_debug.h
|
||||
android_event.c
|
||||
android_event.h
|
||||
android_freerdp.c
|
||||
android_freerdp.h
|
||||
android_jni_utils.c
|
||||
android_jni_utils.h
|
||||
android_jni_callback.c
|
||||
android_jni_callback.h)
|
||||
|
||||
set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS}
|
||||
generated/android_freerdp_jni.c
|
||||
generated/android_freerdp_jni.h)
|
||||
|
||||
if(WITH_CLIENT_CHANNELS)
|
||||
set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS}
|
||||
android_cliprdr.c
|
||||
android_cliprdr.h)
|
||||
endif()
|
||||
|
||||
add_library(${MODULE_NAME} SHARED ${${MODULE_PREFIX}_SRCS})
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp-client)
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr freerdp)
|
||||
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} dl)
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} log)
|
||||
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} jnigraphics)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${ANDROID_ABI}")
|
||||
set_target_properties(${MODULE_NAME}
|
||||
PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${ANDROID_ABI}")
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Client/Android")
|
||||
|
||||
get_property(LIB_ABSNAME TARGET ${MODULE_NAME} PROPERTY LOCATION)
|
||||
|
@ -1,31 +0,0 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* Android Debug Interface
|
||||
*
|
||||
* Copyright 2013 Thincast Technologies GmbH, Author: Martin Fleisz
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifndef FREERDP_ANDROID_DEBUG_H
|
||||
#define FREERDP_ANDROID_DEBUG_H
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <freerdp/log.h>
|
||||
|
||||
#define ANDROID_TAG CLIENT_TAG("android")
|
||||
#ifdef WITH_DEBUG_ANDROID_JNI
|
||||
#define DEBUG_ANDROID(fmt, ...) WLog_DBG(ANDROID_TAG, fmt, ## __VA_ARGS__)
|
||||
#else
|
||||
#define DEBUG_ANDROID(fmt, ...) do { } while (0)
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif /* FREERDP_ANDROID_DEBUG_H */
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user