--- /dev/null
+# CMake
+CMakeFiles/
+CMakeCache.txt
+config.h
+install_manifest.txt
+CTestTestfile.cmake
+freerdp.pc
+Makefile
+cmake_install.cmake
+CPackConfig.cmake
+CPackSourceConfig.cmake
+
+# Eclipse
+*.project
+*.cproject
+*.settings
+
+# Documentation
+docs/api
+client/X11/xfreerdp.1
+
+# Mac OS X
+.DS_Store
+
+# Windows
+*.vcxproj
+*.vcxproj.*
+*.sdf
+*.sln
+*.suo
+*.opensdf
+ipch
+Debug
+
+# Binaries
+*.a
+*.so
+*.so.*
+*.dylib
+cunit/test_freerdp
+client/X11/xfreerdp
+client/test/freerdp-test
+client/DirectFB/dfreerdp
+server/test/tfreerdp-server
+server/X11/xfreerdp-server
+
+# Other
+*~
+
--- /dev/null
+debian/patches
--- /dev/null
+# FreeRDP: A Remote Desktop Protocol Client
+# FreeRDP cmake build script
+#
+# Copyright 2011 O.S. Systems Software Ltda.
+# Copyright 2011 Otavio Salvador <otavio@ossystems.com.br>
+# Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.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.
+
+cmake_minimum_required(VERSION 2.6)
+project(FreeRDP C)
+set(CMAKE_COLOR_MAKEFILE ON)
+
+# Include cmake modules
+include(CheckIncludeFiles)
+include(CheckLibraryExists)
+include(FindPkgConfig)
+include(TestBigEndian)
+
+# Include our extra modules
+set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
+
+include(AutoVersioning)
+include(ConfigOptions)
+include(FindOptionalPackage)
+include(CheckCCompilerFlag)
+include(GNUInstallDirsWrapper)
+
+# Soname versioning
+set(FREERDP_VERSION_MAJOR "1")
+set(FREERDP_VERSION_MINOR "0")
+set(FREERDP_VERSION_REVISION "1")
+set(FREERDP_VERSION "${FREERDP_VERSION_MAJOR}.${FREERDP_VERSION_MINOR}")
+set(FREERDP_VERSION_FULL "${FREERDP_VERSION}.${FREERDP_VERSION_REVISION}")
+
+# Default to release build type
+if(NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE "Release")
+endif()
+
+# build shared libs
+if(NOT BUILD_SHARED_LIBS)
+ set(BUILD_SHARED_LIBS ON)
+endif()
+
+# Compiler-specific flags
+if(CMAKE_COMPILER_IS_GNUCC)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
+ CHECK_C_COMPILER_FLAG (-Wno-unused-result Wno-unused-result)
+ if(Wno-unused-result)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-result")
+ endif()
+ CHECK_C_COMPILER_FLAG (-Wno-unused-but-set-variable Wno-unused-but-set-variable)
+ if(Wno-unused-but-set-variable)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-but-set-variable")
+ endif()
+ if(CMAKE_BUILD_TYPE STREQUAL "Release")
+ set(CMAKE_C_FLAGS_RELEASE "-DNDEBUG")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2")
+ endif()
+ if(WITH_SSE2_TARGET)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse2")
+ endif()
+endif()
+
+if(MSVC)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Gd /MT")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /O2 /Ob2")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_X86_")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_UNICODE")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DFREERDP_EXPORTS")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DWIN32_LEAN_AND_MEAN")
+ SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR})
+ SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR})
+endif()
+
+# Include files
+check_include_files(sys/param.h HAVE_SYS_PARAM_H)
+check_include_files(sys/socket.h HAVE_SYS_SOCKET_H)
+check_include_files(netdb.h HAVE_NETDB_H)
+check_include_files(fcntl.h HAVE_FCNTL_H)
+check_include_files(unistd.h HAVE_UNISTD_H)
+check_include_files(limits.h HAVE_LIMITS_H)
+check_include_files(stdint.h HAVE_STDINT_H)
+check_include_files(stdbool.h HAVE_STDBOOL_H)
+check_include_files(inttypes.h HAVE_INTTYPES_H)
+
+# Libraries that we have a hard dependency on
+find_required_package(OpenSSL)
+
+# Mac OS X
+if(APPLE)
+ include_directories(/opt/local/include)
+ link_directories(/opt/local/lib)
+ set(CMAKE_SHARED_LINKER_FLAGS "-mmacosx-version-min=10.4")
+endif()
+
+if(NOT WIN32)
+ find_required_package(ZLIB)
+ find_optional_package(PulseAudio)
+ find_optional_package(PCSC)
+ find_suggested_package(Cups)
+
+ if(NOT APPLE)
+ find_suggested_package(FFmpeg)
+ find_suggested_package(ALSA)
+ else(NOT APPLE)
+ find_optional_package(FFmpeg)
+ endif()
+endif()
+
+# Endian
+test_big_endian(BIG_ENDIAN)
+
+# Path to put keymaps
+set(FREERDP_KEYMAP_PATH "${CMAKE_INSTALL_PREFIX}/freerdp/keymaps")
+
+# Path to put plugins
+set(FREERDP_PLUGIN_PATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/freerdp")
+
+# Include directories
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+include_directories(${CMAKE_SOURCE_DIR}/include)
+
+# Configure files
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/config.h)
+
+# Generate pkg-config
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/freerdp.pc.in ${CMAKE_CURRENT_BINARY_DIR}/freerdp.pc @ONLY)
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/freerdp.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
+
+# Build CUnit
+find_optional_package(CUnit)
+if(WITH_CUNIT)
+ enable_testing()
+ add_subdirectory(cunit)
+endif()
+
+# Sub-directories
+add_subdirectory(include)
+add_subdirectory(libfreerdp-utils)
+
+if(NOT WIN32)
+ add_subdirectory(libfreerdp-kbd)
+endif()
+
+add_subdirectory(libfreerdp-gdi)
+add_subdirectory(libfreerdp-rail)
+add_subdirectory(libfreerdp-cache)
+add_subdirectory(libfreerdp-codec)
+add_subdirectory(libfreerdp-channels)
+add_subdirectory(libfreerdp-core)
+
+if(NOT WIN32)
+ add_subdirectory(channels)
+endif()
+
+option(WITH_CLIENT "Build client binaries" ON)
+if(WITH_CLIENT)
+ add_subdirectory(client)
+endif()
+
+option(WITH_SERVER "Build server binaries" OFF)
+if(WITH_SERVER)
+ add_subdirectory(server)
+endif()
+
+add_subdirectory(keymaps)
+
+# Source package
+set(CPACK_SOURCE_IGNORE_FILES "/\\\\.git/;/\\\\.gitignore;/CMakeCache.txt")
+
+string(TOLOWER ${CMAKE_PROJECT_NAME} CMAKE_PROJECT_NAME_lower)
+set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME_lower}-${FREERDP_VERSION_FULL}")
+
+include(CPack)
--- /dev/null
+2012-02-07 Version 1.0.1
+
+FreeRDP 1.0.1 is a maintenance release to address a certain number of
+issues found in 1.0.0. This release also brings corrective measures
+to certificate validation which were required for inclusion in Ubuntu.
+
+* Certificate Validation
+ * Improved validation logic and robustness
+ * Added validation of certificate name against hostname
+
+* Token-based Server Redirection
+ * Fixed redirection logic
+ * HAProxy load-balancer support
+
+* xfreerdp-server
+ * better event handling
+ * capture performance improvements
+
+* wfreerdp
+ * Fix RemoteFX support
+ * Fix mingw64 compilation
+
+* libfreerdp-core:
+ * Fix severe TCP sending bug
+ * Added server-side Standard RDP security
+
+2012-01-16 Version 1.0.0
+
+License:
+
+FreeRDP 1.0 is the first release of FreeRDP under the Apache License 2.0.
+The FreeRDP 1.x series is a rewrite, meaning there is no continuity with
+the previous FreeRDP 0.x series which were released under GPLv2.
+
+New Features:
+
+* RemoteFX
+ * Both encoder and decoder
+ * SSE2 and NEON optimization
+* NSCodec
+* RemoteApp
+ * Working, minor glitches
+* Multimedia Redirection
+ * ffmpeg support
+* Network Level Authentication (NLA)
+ * NTLMv2
+* Certificate validation
+* FIPS-compliant RDP security
+* new build system (cmake)
+* added official logo and icon
+
+New Architecture:
+
+* libfreerdp-core
+ * core protocol
+ * highly portable
+ * both client and server
+* libfreerdp-cache
+ * caching operations
+* libfreerdp-codec
+ * bitmap decompression
+ * codec encoding/decoding
+* libfreerdp-kbd
+ * keyboard mapping
+* libfreerdp-channels
+ * virtual channel management
+ * client and server side support
+* libfreerdp-gdi
+ * extensively unit tested
+ * portable software GDI implementation
+* libfreerdp-rail
+ * RemoteApp library
+* libfreerdp-utils
+ * shared utility library
+
+FreeRDP Clients:
+
+* client/X11 (xfreerdp)
+ * official client
+ * RemoteApp support
+ * X11 GDI implementation
+* client/DirectFB (dfreerdp)
+ * DirectFB support
+ * software-based GDI (libfreerdp-gdi)
+* client/Windows (wfreerdp)
+ * Native Win32 support
+
+FreeRDP Servers (experimental):
+
+* server/X11 (xfreerdp-server)
+ * RemoteFX-only
+ * no authentication
+ * highly experimental
+ * keyboard and mouse input supported
+
+Virtual Channels:
+
+* cliprdr (Clipboard Redirection)
+* rail (RemoteApp)
+* drdynvc (Dynamic Virtual Channels)
+ * audin (Audio Input Redirection)
+ * alsa support
+ * pulse support
+ * tsmf (Multimedia Redirection)
+ * alsa support
+ * pulse support
+ * ffmpeg support
+* rdpdr (Device Redirection)
+ * disk (Disk Redirection)
+ * parallel (Parallel Port Redirection)
+ * serial (Serial Port Redirection)
+ * printer (Printer Redirection)
+ * CUPS support
+ * smartcard (Smartcard Redirection)
+* rdpsnd (Sound Redirection)
+ * alsa support
+ * pulse support
+
--- /dev/null
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
--- /dev/null
+FreeRDP: A Remote Desktop Protocol Implementation
+=================================================
+
+FreeRDP is a free implementation of the Remote Desktop Protocol (RDP), released under the Apache license.
+Enjoy the freedom of using your software wherever you want, the way you want it, in a world where
+interoperability can finally liberate your computing experience.
+
+Resources
+---------
+
+Website: http://www.freerdp.com/
+Wiki: https://github.com/FreeRDP/FreeRDP/wiki
+Sources: https://github.com/FreeRDP/FreeRDP/
+API doc: http://www.freerdp.com/api/
+
+IRC channel: #freerdp @ irc.freenode.net
+Mailing list: https://lists.sourceforge.net/lists/listinfo/freerdp-devel
+
+Microsoft Open Specifications
+-----------------------------
+
+Information regarding the Microsoft Open Specifications can be found at:
+http://www.microsoft.com/openspecifications/
+
+A list of reference documentation is maintained here:
+https://github.com/FreeRDP/FreeRDP/wiki/Reference-Documentation
+
+Compilation
+-----------
+
+Instructions on how to get started compiling FreeRDP can be found on the wiki:
+https://github.com/FreeRDP/FreeRDP/wiki/Compilation
+
+
--- /dev/null
+# FreeRDP: A Remote Desktop Protocol Client
+# FreeRDP cmake build script
+#
+# Copyright 2011 O.S. Systems Software Ltda.
+# Copyright 2011 Otavio Salvador <otavio@ossystems.com.br>
+# Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.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.
+
+add_subdirectory(cliprdr)
+add_subdirectory(drdynvc)
+add_subdirectory(rdpdbg)
+add_subdirectory(rdpdr)
+add_subdirectory(rail)
+add_subdirectory(rdpsnd)
+
--- /dev/null
+# FreeRDP: A Remote Desktop Protocol Client
+# FreeRDP cmake build script
+#
+# Copyright 2011 O.S. Systems Software Ltda.
+# Copyright 2011 Otavio Salvador <otavio@ossystems.com.br>
+# Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.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(CLIPRDR_SRCS
+ cliprdr_constants.h
+ cliprdr_format.c
+ cliprdr_format.h
+ cliprdr_main.c
+ cliprdr_main.h
+)
+
+add_library(cliprdr ${CLIPRDR_SRCS})
+set_target_properties(cliprdr PROPERTIES PREFIX "")
+
+target_link_libraries(cliprdr freerdp-utils)
+
+install(TARGETS cliprdr DESTINATION ${FREERDP_PLUGIN_PATH})
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Clipboard Virtual Channel
+ *
+ * Copyright 2009-2011 Jay Sorg
+ * Copyright 2010-2011 Vic Lee
+ *
+ * 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.
+ */
+
+#ifndef __CLIPRDR_CONSTANTS
+#define __CLIPRDR_CONSTANTS
+
+/* CLIPRDR_HEADER.msgType */
+#define CB_MONITOR_READY 0x0001
+#define CB_FORMAT_LIST 0x0002
+#define CB_FORMAT_LIST_RESPONSE 0x0003
+#define CB_FORMAT_DATA_REQUEST 0x0004
+#define CB_FORMAT_DATA_RESPONSE 0x0005
+#define CB_TEMP_DIRECTORY 0x0006
+#define CB_CLIP_CAPS 0x0007
+#define CB_FILECONTENTS_REQUEST 0x0008
+#define CB_FILECONTENTS_RESPONSE 0x0009
+#define CB_LOCK_CLIPDATA 0x000A
+#define CB_UNLOCK_CLIPDATA 0x000B
+
+/* CLIPRDR_HEADER.msgFlags */
+#define CB_RESPONSE_OK 0x0001
+#define CB_RESPONSE_FAIL 0x0002
+#define CB_ASCII_NAMES 0x0004
+
+/* CLIPRDR_CAPS_SET.capabilitySetType */
+#define CB_CAPSTYPE_GENERAL 0x0001
+
+/* CLIPRDR_GENERAL_CAPABILITY.lengthCapability */
+#define CB_CAPSTYPE_GENERAL_LEN 12
+
+/* CLIPRDR_GENERAL_CAPABILITY.version */
+#define CB_CAPS_VERSION_1 0x00000001
+#define CB_CAPS_VERSION_2 0x00000002
+
+/* CLIPRDR_GENERAL_CAPABILITY.generalFlags */
+#define CB_USE_LONG_FORMAT_NAMES 0x00000002
+#define CB_STREAM_FILECLIP_ENABLED 0x00000004
+#define CB_FILECLIP_NO_FILE_PATHS 0x00000008
+#define CB_CAN_LOCK_CLIPDATA 0x00000010
+
+#endif /* __CLIPRDR_CONSTANTS */
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Clipboard Virtual Channel
+ *
+ * Copyright 2009-2011 Jay Sorg
+ * Copyright 2010-2011 Vic Lee
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <freerdp/types.h>
+#include <freerdp/constants.h>
+#include <freerdp/utils/hexdump.h>
+#include <freerdp/utils/memory.h>
+#include <freerdp/utils/unicode.h>
+#include <freerdp/utils/svc_plugin.h>
+#include <freerdp/plugins/cliprdr.h>
+
+#include "cliprdr_constants.h"
+#include "cliprdr_main.h"
+#include "cliprdr_format.h"
+
+#define CFSTR_HTML "H\0T\0M\0L\0 \0F\0o\0r\0m\0a\0t\0\0"
+#define CFSTR_PNG "P\0N\0G\0\0"
+#define CFSTR_JPEG "J\0F\0I\0F\0\0"
+#define CFSTR_GIF "G\0I\0F\0\0"
+
+void cliprdr_process_format_list_event(cliprdrPlugin* cliprdr, RDP_CB_FORMAT_LIST_EVENT* cb_event)
+{
+ int i;
+ STREAM* s;
+
+ DEBUG_CLIPRDR("Sending Clipboard Format List");
+
+ if (cb_event->raw_format_data)
+ {
+ s = cliprdr_packet_new(CB_FORMAT_LIST, 0, cb_event->raw_format_data_size);
+ stream_write(s, cb_event->raw_format_data, cb_event->raw_format_data_size);
+ }
+ else
+ {
+ STREAM* body = stream_new(0);
+
+ for (i = 0; i < cb_event->num_formats; i++)
+ {
+ const char* name;
+ int name_length;
+
+ switch (cb_event->formats[i])
+ {
+ case CB_FORMAT_HTML:
+ name = CFSTR_HTML; name_length = sizeof(CFSTR_HTML);
+ break;
+ case CB_FORMAT_PNG:
+ name = CFSTR_PNG; name_length = sizeof(CFSTR_PNG);
+ break;
+ case CB_FORMAT_JPEG:
+ name = CFSTR_JPEG; name_length = sizeof(CFSTR_JPEG);
+ break;
+ case CB_FORMAT_GIF:
+ name = CFSTR_GIF; name_length = sizeof(CFSTR_GIF);
+ break;
+ default:
+ name = "\0\0"; name_length = 2;
+ }
+
+ if (!cliprdr->use_long_format_names)
+ name_length = 32;
+
+ stream_extend(body, stream_get_size(body) + 4 + name_length);
+
+ stream_write_uint32(body, cb_event->formats[i]);
+ stream_write(body, name, name_length);
+ }
+
+ s = cliprdr_packet_new(CB_FORMAT_LIST, 0, stream_get_size(body));
+ stream_write(s, stream_get_head(body), stream_get_size(body));
+ stream_free(body);
+ }
+
+ cliprdr_packet_send(cliprdr, s);
+}
+
+static void cliprdr_send_format_list_response(cliprdrPlugin* cliprdr)
+{
+ STREAM* s;
+ DEBUG_CLIPRDR("Sending Clipboard Format List Response");
+ s = cliprdr_packet_new(CB_FORMAT_LIST_RESPONSE, CB_RESPONSE_OK, 0);
+ cliprdr_packet_send(cliprdr, s);
+}
+
+void cliprdr_process_short_format_names(cliprdrPlugin* cliprdr, STREAM* s, uint32 length, uint16 flags)
+{
+ int i;
+ boolean ascii;
+ int num_formats;
+ CLIPRDR_FORMAT_NAME* format_name;
+
+ num_formats = length / 36;
+
+ if (num_formats <= 0)
+ {
+ cliprdr->format_names = NULL;
+ cliprdr->num_format_names = 0;
+ return;
+ }
+
+ if (num_formats * 36 != length)
+ DEBUG_WARN("dataLen %d not divided by 36!", length);
+
+ ascii = (flags & CB_ASCII_NAMES) ? true : false;
+
+ cliprdr->format_names = (CLIPRDR_FORMAT_NAME*) xmalloc(sizeof(CLIPRDR_FORMAT_NAME) * num_formats);
+ cliprdr->num_format_names = num_formats;
+
+ for (i = 0; i < num_formats; i++)
+ {
+ format_name = &cliprdr->format_names[i];
+
+ stream_read_uint32(s, format_name->id);
+
+ if (ascii)
+ {
+ format_name->name = xstrdup((char*) s->p);
+ format_name->length = strlen(format_name->name);
+ }
+ else
+ {
+ format_name->name = freerdp_uniconv_in(cliprdr->uniconv, s->p, 32);
+ format_name->length = strlen(format_name->name);
+ }
+
+ stream_seek(s, 32);
+ }
+}
+
+void cliprdr_process_long_format_names(cliprdrPlugin* cliprdr, STREAM* s, uint32 length, uint16 flags)
+{
+ int allocated_formats = 8;
+ uint8* end_mark;
+ CLIPRDR_FORMAT_NAME* format_name;
+
+ stream_get_mark(s, end_mark);
+ end_mark += length;
+
+ cliprdr->format_names = (CLIPRDR_FORMAT_NAME*) xmalloc(sizeof(CLIPRDR_FORMAT_NAME) * allocated_formats);
+ cliprdr->num_format_names = 0;
+
+ while (stream_get_left(s) >= 6)
+ {
+ uint8* p;
+ int name_len;
+
+ if (cliprdr->num_format_names >= allocated_formats)
+ {
+ allocated_formats *= 2;
+ cliprdr->format_names = (CLIPRDR_FORMAT_NAME*) xrealloc(cliprdr->format_names,
+ sizeof(CLIPRDR_FORMAT_NAME) * allocated_formats);
+ }
+
+ format_name = &cliprdr->format_names[cliprdr->num_format_names++];
+ stream_read_uint32(s, format_name->id);
+
+ format_name->name = NULL;
+ format_name->length = 0;
+
+ for (p = stream_get_tail(s), name_len = 0; p + 1 < end_mark; p += 2, name_len += 2)
+ {
+ if (*((unsigned short*) p) == 0)
+ break;
+ }
+
+ format_name->name = freerdp_uniconv_in(cliprdr->uniconv, stream_get_tail(s), name_len);
+ format_name->length = strlen(format_name->name);
+ stream_seek(s, name_len + 2);
+ }
+}
+
+void cliprdr_process_format_list(cliprdrPlugin* cliprdr, STREAM* s, uint32 dataLen, uint16 msgFlags)
+{
+ int i;
+ uint32 format;
+ boolean supported;
+ CLIPRDR_FORMAT_NAME* format_name;
+ RDP_CB_FORMAT_LIST_EVENT* cb_event;
+
+ cb_event = (RDP_CB_FORMAT_LIST_EVENT*) freerdp_event_new(RDP_EVENT_CLASS_CLIPRDR,
+ RDP_EVENT_TYPE_CB_FORMAT_LIST, NULL, NULL);
+
+ if (dataLen > 0)
+ {
+ cb_event->raw_format_data = (uint8*) xmalloc(dataLen);
+ memcpy(cb_event->raw_format_data, stream_get_tail(s), dataLen);
+ cb_event->raw_format_data_size = dataLen;
+ }
+
+ if (cliprdr->use_long_format_names)
+ cliprdr_process_long_format_names(cliprdr, s, dataLen, msgFlags);
+ else
+ cliprdr_process_short_format_names(cliprdr, s, dataLen, msgFlags);
+
+ if (cliprdr->num_format_names > 0)
+ cb_event->formats = (uint32*) xmalloc(sizeof(uint32) * cliprdr->num_format_names);
+
+ cb_event->num_formats = 0;
+
+ for (i = 0; i < cliprdr->num_format_names; i++)
+ {
+ supported = true;
+ format_name = &cliprdr->format_names[i];
+ format = format_name->id;
+
+ switch (format)
+ {
+ case CB_FORMAT_TEXT:
+ case CB_FORMAT_DIB:
+ case CB_FORMAT_UNICODETEXT:
+ break;
+
+ default:
+
+ if (format_name->length > 0)
+ {
+ DEBUG_CLIPRDR("format: %s", format_name->name);
+
+ if (strcmp(format_name->name, "HTML Format") == 0)
+ {
+ format = CB_FORMAT_HTML;
+ break;
+ }
+ if (strcmp(format_name->name, "PNG") == 0)
+ {
+ format = CB_FORMAT_PNG;
+ break;
+ }
+ if (strcmp(format_name->name, "JFIF") == 0)
+ {
+ format = CB_FORMAT_JPEG;
+ break;
+ }
+ if (strcmp(format_name->name, "GIF") == 0)
+ {
+ format = CB_FORMAT_GIF;
+ break;
+ }
+ }
+ else
+ {
+ supported = false;
+ }
+
+ break;
+ }
+
+ if (supported)
+ cb_event->formats[cb_event->num_formats++] = format;
+
+ if (format_name->length > 0)
+ xfree(format_name->name);
+ }
+
+ xfree(cliprdr->format_names);
+ cliprdr->format_names = NULL;
+
+ cliprdr->num_format_names = 0;
+
+ svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (RDP_EVENT*) cb_event);
+ cliprdr_send_format_list_response(cliprdr);
+}
+
+void cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, STREAM* s, uint32 dataLen, uint16 msgFlags)
+{
+ /* where is this documented? */
+#if 0
+ RDP_EVENT* event;
+
+ if ((msgFlags & CB_RESPONSE_FAIL) != 0)
+ {
+ event = freerdp_event_new(RDP_EVENT_CLASS_CLIPRDR, RDP_EVENT_TYPE_CB_MONITOR_READY, NULL, NULL);
+ svc_plugin_send_event((rdpSvcPlugin*) cliprdr, event);
+ }
+#endif
+}
+
+void cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, STREAM* s, uint32 dataLen, uint16 msgFlags)
+{
+ RDP_CB_DATA_REQUEST_EVENT* cb_event;
+
+ cb_event = (RDP_CB_DATA_REQUEST_EVENT*) freerdp_event_new(RDP_EVENT_CLASS_CLIPRDR,
+ RDP_EVENT_TYPE_CB_DATA_REQUEST, NULL, NULL);
+
+ stream_read_uint32(s, cb_event->format);
+ svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (RDP_EVENT*) cb_event);
+}
+
+void cliprdr_process_format_data_response_event(cliprdrPlugin* cliprdr, RDP_CB_DATA_RESPONSE_EVENT* cb_event)
+{
+ STREAM* s;
+
+ DEBUG_CLIPRDR("Sending Format Data Response");
+
+ if (cb_event->size > 0)
+ {
+ s = cliprdr_packet_new(CB_FORMAT_DATA_RESPONSE, CB_RESPONSE_OK, cb_event->size);
+ stream_write(s, cb_event->data, cb_event->size);
+ }
+ else
+ {
+ s = cliprdr_packet_new(CB_FORMAT_DATA_RESPONSE, CB_RESPONSE_FAIL, 0);
+ }
+
+ cliprdr_packet_send(cliprdr, s);
+}
+
+void cliprdr_process_format_data_request_event(cliprdrPlugin* cliprdr, RDP_CB_DATA_REQUEST_EVENT* cb_event)
+{
+ STREAM* s;
+
+ DEBUG_CLIPRDR("Sending Format Data Request");
+
+ s = cliprdr_packet_new(CB_FORMAT_DATA_REQUEST, 0, 4);
+ stream_write_uint32(s, cb_event->format);
+ cliprdr_packet_send(cliprdr, s);
+}
+
+void cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, STREAM* s, uint32 dataLen, uint16 msgFlags)
+{
+ RDP_CB_DATA_RESPONSE_EVENT* cb_event;
+
+ cb_event = (RDP_CB_DATA_RESPONSE_EVENT*) freerdp_event_new(RDP_EVENT_CLASS_CLIPRDR,
+ RDP_EVENT_TYPE_CB_DATA_RESPONSE, NULL, NULL);
+
+ if (dataLen > 0)
+ {
+ cb_event->size = dataLen;
+ cb_event->data = (uint8*) xmalloc(dataLen);
+ memcpy(cb_event->data, stream_get_tail(s), dataLen);
+ }
+
+ svc_plugin_send_event((rdpSvcPlugin*) cliprdr, (RDP_EVENT*) cb_event);
+}
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Clipboard Virtual Channel
+ *
+ * Copyright 2009-2011 Jay Sorg
+ * Copyright 2010-2011 Vic Lee
+ *
+ * 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.
+ */
+
+#ifndef __CLIPRDR_FORMAT_H
+#define __CLIPRDR_FORMAT_H
+
+void cliprdr_process_format_list_event(cliprdrPlugin* cliprdr, RDP_CB_FORMAT_LIST_EVENT* cb_event);
+void cliprdr_process_format_list(cliprdrPlugin* cliprdr, STREAM* s, uint32 dataLen, uint16 msgFlags);
+void cliprdr_process_format_list_response(cliprdrPlugin* cliprdr, STREAM* s, uint32 dataLen, uint16 msgFlags);
+
+void cliprdr_process_format_data_request(cliprdrPlugin* cliprdr, STREAM* s, uint32 dataLen, uint16 msgFlags);
+void cliprdr_process_format_data_response_event(cliprdrPlugin* cliprdr, RDP_CB_DATA_RESPONSE_EVENT* cb_event);
+
+void cliprdr_process_format_data_request_event(cliprdrPlugin* cliprdr, RDP_CB_DATA_REQUEST_EVENT* cb_event);
+void cliprdr_process_format_data_response(cliprdrPlugin* cliprdr, STREAM* s, uint32 dataLen, uint16 msgFlags);
+
+#endif /* __CLIPRDR_FORMAT_H */
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Clipboard Virtual Channel
+ *
+ * Copyright 2009-2011 Jay Sorg
+ * Copyright 2010-2011 Vic Lee
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <freerdp/types.h>
+#include <freerdp/constants.h>
+#include <freerdp/utils/memory.h>
+#include <freerdp/utils/unicode.h>
+#include <freerdp/utils/svc_plugin.h>
+#include <freerdp/plugins/cliprdr.h>
+
+#include "cliprdr_constants.h"
+#include "cliprdr_main.h"
+#include "cliprdr_format.h"
+
+static const char* const CB_MSG_TYPE_STRINGS[] =
+{
+ "",
+ "CB_MONITOR_READY",
+ "CB_FORMAT_LIST",
+ "CB_FORMAT_LIST_RESPONSE",
+ "CB_FORMAT_DATA_REQUEST",
+ "CB_FORMAT_DATA_RESPONSE",
+ "CB_TEMP_DIRECTORY",
+ "CB_CLIP_CAPS",
+ "CB_FILECONTENTS_REQUEST",
+ "CB_FILECONTENTS_RESPONSE",
+ "CB_LOCK_CLIPDATA"
+ "CB_UNLOCK_CLIPDATA"
+};
+
+STREAM* cliprdr_packet_new(uint16 msgType, uint16 msgFlags, uint32 dataLen)
+{
+ STREAM* s;
+
+ s = stream_new(dataLen + 8);
+ stream_write_uint16(s, msgType);
+ stream_write_uint16(s, msgFlags);
+ /* Write actual length after the entire packet has been constructed. */
+ stream_seek(s, 4);
+
+ return s;
+}
+
+void cliprdr_packet_send(cliprdrPlugin* cliprdr, STREAM* s)
+{
+ int pos;
+ uint32 dataLen;
+
+ pos = stream_get_pos(s);
+ dataLen = pos - 8;
+ stream_set_pos(s, 4);
+ stream_write_uint32(s, dataLen);
+ stream_set_pos(s, pos);
+
+ svc_plugin_send((rdpSvcPlugin*) cliprdr, s);
+}
+
+static void cliprdr_process_connect(rdpSvcPlugin* plugin)
+{
+ DEBUG_CLIPRDR("connecting");
+
+ ((cliprdrPlugin*) plugin)->uniconv = freerdp_uniconv_new();
+}
+
+void cliprdr_print_general_capability_flags(uint32 flags)
+{
+ printf("generalFlags (0x%08X) {\n", flags);
+
+ if (flags & CB_USE_LONG_FORMAT_NAMES)
+ printf("\tCB_USE_LONG_FORMAT_NAMES\n");
+ if (flags & CB_STREAM_FILECLIP_ENABLED)
+ printf("\tCB_STREAM_FILECLIP_ENABLED\n");
+ if (flags & CB_FILECLIP_NO_FILE_PATHS)
+ printf("\tCB_FILECLIP_NO_FILE_PATHS\n");
+ if (flags & CB_CAN_LOCK_CLIPDATA)
+ printf("\tCB_CAN_LOCK_CLIPDATA\n");
+
+ printf("}\n");
+}
+
+static void cliprdr_process_general_capability(cliprdrPlugin* cliprdr, STREAM* s)
+{
+ uint32 version;
+ uint32 generalFlags;
+
+ stream_read_uint32(s, version); /* version (4 bytes) */
+ stream_read_uint32(s, generalFlags); /* generalFlags (4 bytes) */
+
+ DEBUG_CLIPRDR("Version: %d", version);
+
+#ifdef WITH_DEBUG_CLIPRDR
+ cliprdr_print_general_capability_flags(generalFlags);
+#endif
+
+ if (generalFlags & CB_USE_LONG_FORMAT_NAMES)
+ cliprdr->use_long_format_names = true;
+
+ if (generalFlags & CB_STREAM_FILECLIP_ENABLED)
+ cliprdr->stream_fileclip_enabled = true;
+
+ if (generalFlags & CB_FILECLIP_NO_FILE_PATHS)
+ cliprdr->fileclip_no_file_paths = true;
+
+ if (generalFlags & CB_CAN_LOCK_CLIPDATA)
+ cliprdr->can_lock_clipdata = true;
+
+ cliprdr->received_caps = true;
+}
+
+static void cliprdr_process_clip_caps(cliprdrPlugin* cliprdr, STREAM* s, uint16 length, uint16 flags)
+{
+ int i;
+ uint16 lengthCapability;
+ uint16 cCapabilitiesSets;
+ uint16 capabilitySetType;
+
+ stream_read_uint16(s, cCapabilitiesSets); /* cCapabilitiesSets (2 bytes) */
+ stream_seek_uint16(s); /* pad1 (2 bytes) */
+
+ DEBUG_CLIPRDR("cCapabilitiesSets %d", cCapabilitiesSets);
+
+ for (i = 0; i < cCapabilitiesSets; i++)
+ {
+ stream_read_uint16(s, capabilitySetType); /* capabilitySetType (2 bytes) */
+ stream_read_uint16(s, lengthCapability); /* lengthCapability (2 bytes) */
+
+ switch (capabilitySetType)
+ {
+ case CB_CAPSTYPE_GENERAL:
+ cliprdr_process_general_capability(cliprdr, s);
+ break;
+
+ default:
+ DEBUG_WARN("unknown cliprdr capability set: %d", capabilitySetType);
+ break;
+ }
+ }
+}
+
+static void cliprdr_send_clip_caps(cliprdrPlugin* cliprdr)
+{
+ STREAM* s;
+ uint32 flags;
+
+ s = cliprdr_packet_new(CB_CLIP_CAPS, 0, 4 + CB_CAPSTYPE_GENERAL_LEN);
+
+ DEBUG_CLIPRDR("Sending Capabilities");
+
+ flags = CB_USE_LONG_FORMAT_NAMES;
+
+ stream_write_uint16(s, 1); /* cCapabilitiesSets */
+ stream_write_uint16(s, 0); /* pad1 */
+ stream_write_uint16(s, CB_CAPSTYPE_GENERAL); /* capabilitySetType */
+ stream_write_uint16(s, CB_CAPSTYPE_GENERAL_LEN); /* lengthCapability */
+ stream_write_uint32(s, CB_CAPS_VERSION_2); /* version */
+ stream_write_uint32(s, flags); /* generalFlags */
+
+ cliprdr_packet_send(cliprdr, s);
+}
+
+static void cliprdr_process_monitor_ready(cliprdrPlugin* cliprdr, STREAM* s, uint16 length, uint16 flags)
+{
+ RDP_EVENT* event;
+
+ if (cliprdr->received_caps)
+ cliprdr_send_clip_caps(cliprdr);
+
+ event = freerdp_event_new(RDP_EVENT_CLASS_CLIPRDR, RDP_EVENT_TYPE_CB_MONITOR_READY, NULL, NULL);
+ svc_plugin_send_event((rdpSvcPlugin*) cliprdr, event);
+}
+
+static void cliprdr_process_receive(rdpSvcPlugin* plugin, STREAM* s)
+{
+ uint16 msgType;
+ uint16 msgFlags;
+ uint32 dataLen;
+ cliprdrPlugin* cliprdr = (cliprdrPlugin*) plugin;
+
+ stream_read_uint16(s, msgType);
+ stream_read_uint16(s, msgFlags);
+ stream_read_uint32(s, dataLen);
+
+ DEBUG_CLIPRDR("msgType: %s (%d), msgFlags: %d dataLen: %d",
+ CB_MSG_TYPE_STRINGS[msgType], msgType, msgFlags, dataLen);
+
+ switch (msgType)
+ {
+ case CB_CLIP_CAPS:
+ cliprdr_process_clip_caps(cliprdr, s, dataLen, msgFlags);
+ break;
+
+ case CB_MONITOR_READY:
+ cliprdr_process_monitor_ready(cliprdr, s, dataLen, msgFlags);
+ break;
+
+ case CB_FORMAT_LIST:
+ cliprdr_process_format_list(cliprdr, s, dataLen, msgFlags);
+ break;
+
+ case CB_FORMAT_LIST_RESPONSE:
+ cliprdr_process_format_list_response(cliprdr, s, dataLen, msgFlags);
+ break;
+
+ case CB_FORMAT_DATA_REQUEST:
+ cliprdr_process_format_data_request(cliprdr, s, dataLen, msgFlags);
+ break;
+
+ case CB_FORMAT_DATA_RESPONSE:
+ cliprdr_process_format_data_response(cliprdr, s, dataLen, msgFlags);
+ break;
+
+ default:
+ DEBUG_WARN("unknown msgType %d", msgType);
+ break;
+ }
+
+ stream_free(s);
+}
+
+static void cliprdr_process_event(rdpSvcPlugin* plugin, RDP_EVENT* event)
+{
+ switch (event->event_type)
+ {
+ case RDP_EVENT_TYPE_CB_FORMAT_LIST:
+ cliprdr_process_format_list_event((cliprdrPlugin*) plugin, (RDP_CB_FORMAT_LIST_EVENT*) event);
+ break;
+
+ case RDP_EVENT_TYPE_CB_DATA_REQUEST:
+ cliprdr_process_format_data_request_event((cliprdrPlugin*) plugin, (RDP_CB_DATA_REQUEST_EVENT*) event);
+ break;
+
+ case RDP_EVENT_TYPE_CB_DATA_RESPONSE:
+ cliprdr_process_format_data_response_event((cliprdrPlugin*) plugin, (RDP_CB_DATA_RESPONSE_EVENT*) event);
+ break;
+
+ default:
+ DEBUG_WARN("unknown event type %d", event->event_type);
+ break;
+ }
+
+ freerdp_event_free(event);
+}
+
+static void cliprdr_process_terminate(rdpSvcPlugin* plugin)
+{
+ cliprdrPlugin* cliprdr_plugin = (cliprdrPlugin*) plugin;
+
+ if (cliprdr_plugin->uniconv != NULL)
+ freerdp_uniconv_free(cliprdr_plugin->uniconv);
+
+ xfree(plugin);
+}
+
+DEFINE_SVC_PLUGIN(cliprdr, "cliprdr",
+ CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP |
+ CHANNEL_OPTION_COMPRESS_RDP | CHANNEL_OPTION_SHOW_PROTOCOL)
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Clipboard Virtual Channel
+ *
+ * Copyright 2009-2011 Jay Sorg
+ * Copyright 2010-2011 Vic Lee
+ *
+ * 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.
+ */
+
+#ifndef __CLIPRDR_MAIN_H
+#define __CLIPRDR_MAIN_H
+
+#include <freerdp/utils/debug.h>
+#include <freerdp/utils/stream.h>
+
+struct _CLIPRDR_FORMAT_NAME
+{
+ uint32 id;
+ char* name;
+ int length;
+};
+typedef struct _CLIPRDR_FORMAT_NAME CLIPRDR_FORMAT_NAME;
+
+struct cliprdr_plugin
+{
+ rdpSvcPlugin plugin;
+ UNICONV* uniconv;
+ boolean received_caps;
+ boolean use_long_format_names;
+ boolean stream_fileclip_enabled;
+ boolean fileclip_no_file_paths;
+ boolean can_lock_clipdata;
+ CLIPRDR_FORMAT_NAME* format_names;
+ int num_format_names;
+};
+typedef struct cliprdr_plugin cliprdrPlugin;
+
+STREAM* cliprdr_packet_new(uint16 msgType, uint16 msgFlags, uint32 dataLen);
+void cliprdr_packet_send(cliprdrPlugin* cliprdr, STREAM* data_out);
+
+#ifdef WITH_DEBUG_CLIPRDR
+#define DEBUG_CLIPRDR(fmt, ...) DEBUG_CLASS(CLIPRDR, fmt, ## __VA_ARGS__)
+#else
+#define DEBUG_CLIPRDR(fmt, ...) DEBUG_NULL(fmt, ## __VA_ARGS__)
+#endif
+
+#endif /* __CLIPRDR_MAIN_H */
--- /dev/null
+# FreeRDP: A Remote Desktop Protocol Client
+# FreeRDP cmake build script
+#
+# Copyright 2011 O.S. Systems Software Ltda.
+# Copyright 2011 Otavio Salvador <otavio@ossystems.com.br>
+# Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.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(DRDYNVC_SRCS
+ drdynvc_main.c
+ drdynvc_main.h
+ drdynvc_types.h
+ dvcman.c
+ dvcman.h
+)
+
+add_library(drdynvc ${DRDYNVC_SRCS})
+set_target_properties(drdynvc PROPERTIES PREFIX "")
+
+target_link_libraries(drdynvc freerdp-utils)
+
+install(TARGETS drdynvc DESTINATION ${FREERDP_PLUGIN_PATH})
+
+add_subdirectory(tsmf)
+add_subdirectory(audin)
+
--- /dev/null
+# FreeRDP: A Remote Desktop Protocol Client
+# FreeRDP cmake build script
+#
+# Copyright 2011 O.S. Systems Software Ltda.
+# Copyright 2011 Otavio Salvador <otavio@ossystems.com.br>
+# Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.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(AUDIN_SRCS
+ audin_main.c
+ audin_main.h
+)
+
+include_directories(..)
+
+add_library(audin ${AUDIN_SRCS})
+set_target_properties(audin PROPERTIES PREFIX "")
+
+target_link_libraries(audin freerdp-utils)
+
+install(TARGETS audin DESTINATION ${FREERDP_PLUGIN_PATH})
+
+if(WITH_ALSA)
+ add_subdirectory(alsa)
+endif()
+
+if(WITH_PULSEAUDIO)
+ add_subdirectory(pulse)
+endif()
+
--- /dev/null
+# FreeRDP: A Remote Desktop Protocol Client
+# FreeRDP cmake build script
+#
+# Copyright 2011 O.S. Systems Software Ltda.
+# Copyright 2011 Otavio Salvador <otavio@ossystems.com.br>
+# Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.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(AUDIN_ALSA_SRCS
+ audin_alsa.c
+)
+
+include_directories(..)
+include_directories(${ALSA_INCLUDE_DIRS})
+
+add_library(audin_alsa ${AUDIN_ALSA_SRCS})
+set_target_properties(audin_alsa PROPERTIES PREFIX "")
+
+target_link_libraries(audin_alsa freerdp-utils)
+target_link_libraries(audin_alsa ${ALSA_LIBRARIES})
+
+install(TARGETS audin_alsa DESTINATION ${FREERDP_PLUGIN_PATH})
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Audio Input Redirection Virtual Channel - ALSA implementation
+ *
+ * Copyright 2010-2011 Vic Lee
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <alsa/asoundlib.h>
+#include <freerdp/utils/memory.h>
+#include <freerdp/utils/thread.h>
+#include <freerdp/utils/dsp.h>
+
+#include "audin_main.h"
+
+typedef struct _AudinALSADevice
+{
+ IAudinDevice iface;
+
+ char device_name[32];
+ uint32 frames_per_packet;
+ uint32 target_rate;
+ uint32 actual_rate;
+ snd_pcm_format_t format;
+ uint32 target_channels;
+ uint32 actual_channels;
+ int bytes_per_channel;
+ int wformat;
+ int block_size;
+ ADPCM adpcm;
+
+ freerdp_thread* thread;
+
+ uint8* buffer;
+ int buffer_frames;
+
+ AudinReceive receive;
+ void* user_data;
+} AudinALSADevice;
+
+static boolean audin_alsa_set_params(AudinALSADevice* alsa, snd_pcm_t* capture_handle)
+{
+ int error;
+ snd_pcm_hw_params_t* hw_params;
+
+ if ((error = snd_pcm_hw_params_malloc(&hw_params)) < 0)
+ {
+ DEBUG_WARN("snd_pcm_hw_params_malloc (%s)",
+ snd_strerror(error));
+ return false;
+ }
+ snd_pcm_hw_params_any(capture_handle, hw_params);
+ snd_pcm_hw_params_set_access(capture_handle, hw_params,
+ SND_PCM_ACCESS_RW_INTERLEAVED);
+ snd_pcm_hw_params_set_format(capture_handle, hw_params,
+ alsa->format);
+ snd_pcm_hw_params_set_rate_near(capture_handle, hw_params,
+ &alsa->actual_rate, NULL);
+ snd_pcm_hw_params_set_channels_near(capture_handle, hw_params,
+ &alsa->actual_channels);
+ snd_pcm_hw_params(capture_handle, hw_params);
+ snd_pcm_hw_params_free(hw_params);
+ snd_pcm_prepare(capture_handle);
+
+ if ((alsa->actual_rate != alsa->target_rate) ||
+ (alsa->actual_channels != alsa->target_channels))
+ {
+ DEBUG_DVC("actual rate %d / channel %d is "
+ "different from target rate %d / channel %d, resampling required.",
+ alsa->actual_rate, alsa->actual_channels,
+ alsa->target_rate, alsa->target_channels);
+ }
+ return true;
+}
+
+static boolean audin_alsa_thread_receive(AudinALSADevice* alsa, uint8* src, int size)
+{
+ int frames;
+ int cframes;
+ int ret = 0;
+ int encoded_size;
+ uint8* encoded_data;
+ int rbytes_per_frame;
+ int tbytes_per_frame;
+ uint8* resampled_data;
+
+ rbytes_per_frame = alsa->actual_channels * alsa->bytes_per_channel;
+ tbytes_per_frame = alsa->target_channels * alsa->bytes_per_channel;
+
+ if ((alsa->target_rate == alsa->actual_rate) &&
+ (alsa->target_channels == alsa->actual_channels))
+ {
+ resampled_data = NULL;
+ frames = size / rbytes_per_frame;
+ }
+ else
+ {
+ resampled_data = dsp_resample(src, alsa->bytes_per_channel,
+ alsa->actual_channels, alsa->actual_rate, size / rbytes_per_frame,
+ alsa->target_channels, alsa->target_rate, &frames);
+ DEBUG_DVC("resampled %d frames at %d to %d frames at %d",
+ size / rbytes_per_frame, alsa->actual_rate, frames, alsa->target_rate);
+ size = frames * tbytes_per_frame;
+ src = resampled_data;
+ }
+
+ while (frames > 0)
+ {
+ if (freerdp_thread_is_stopped(alsa->thread))
+ break;
+
+ cframes = alsa->frames_per_packet - alsa->buffer_frames;
+ if (cframes > frames)
+ cframes = frames;
+ memcpy(alsa->buffer + alsa->buffer_frames * tbytes_per_frame,
+ src, cframes * tbytes_per_frame);
+ alsa->buffer_frames += cframes;
+ if (alsa->buffer_frames >= alsa->frames_per_packet)
+ {
+ if (alsa->wformat == 0x11)
+ {
+ encoded_data = dsp_encode_ima_adpcm(&alsa->adpcm,
+ alsa->buffer, alsa->buffer_frames * tbytes_per_frame,
+ alsa->target_channels, alsa->block_size, &encoded_size);
+ DEBUG_DVC("encoded %d to %d",
+ alsa->buffer_frames * tbytes_per_frame, encoded_size);
+ }
+ else
+ {
+ encoded_data = alsa->buffer;
+ encoded_size = alsa->buffer_frames * tbytes_per_frame;
+ }
+
+ if (freerdp_thread_is_stopped(alsa->thread))
+ {
+ ret = 0;
+ frames = 0;
+ }
+ else
+ ret = alsa->receive(encoded_data, encoded_size, alsa->user_data);
+ alsa->buffer_frames = 0;
+ if (encoded_data != alsa->buffer)
+ xfree(encoded_data);
+ if (!ret)
+ break;
+ }
+ src += cframes * tbytes_per_frame;
+ frames -= cframes;
+ }
+
+ if (resampled_data)
+ xfree(resampled_data);
+
+ return ret;
+}
+
+static void* audin_alsa_thread_func(void* arg)
+{
+ int error;
+ uint8* buffer;
+ int rbytes_per_frame;
+ int tbytes_per_frame;
+ snd_pcm_t* capture_handle = NULL;
+ AudinALSADevice* alsa = (AudinALSADevice*) arg;
+
+ DEBUG_DVC("in");
+
+ rbytes_per_frame = alsa->actual_channels * alsa->bytes_per_channel;
+ tbytes_per_frame = alsa->target_channels * alsa->bytes_per_channel;
+ alsa->buffer = (uint8*) xzalloc(tbytes_per_frame * alsa->frames_per_packet);
+ alsa->buffer_frames = 0;
+ buffer = (uint8*) xzalloc(rbytes_per_frame * alsa->frames_per_packet);
+ memset(&alsa->adpcm, 0, sizeof(ADPCM));
+ do
+ {
+ if ((error = snd_pcm_open(&capture_handle, alsa->device_name, SND_PCM_STREAM_CAPTURE, 0)) < 0)
+ {
+ DEBUG_WARN("snd_pcm_open (%s)", snd_strerror(error));
+ break;
+ }
+ if (!audin_alsa_set_params(alsa, capture_handle))
+ {
+ break;
+ }
+
+ while (!freerdp_thread_is_stopped(alsa->thread))
+ {
+ error = snd_pcm_readi(capture_handle, buffer, alsa->frames_per_packet);
+ if (error == -EPIPE)
+ {
+ snd_pcm_recover(capture_handle, error, 0);
+ continue;
+ }
+ else if (error < 0)
+ {
+ DEBUG_WARN("snd_pcm_readi (%s)", snd_strerror(error));
+ break;
+ }
+ if (!audin_alsa_thread_receive(alsa, buffer, error * rbytes_per_frame))
+ break;
+ }
+ } while (0);
+
+ xfree(buffer);
+ xfree(alsa->buffer);
+ alsa->buffer = NULL;
+ if (capture_handle)
+ snd_pcm_close(capture_handle);
+
+ freerdp_thread_quit(alsa->thread);
+
+ DEBUG_DVC("out");
+
+ return NULL;
+}
+
+static void audin_alsa_free(IAudinDevice* device)
+{
+ AudinALSADevice* alsa = (AudinALSADevice*) device;
+
+ freerdp_thread_free(alsa->thread);
+ xfree(alsa);
+}
+
+static boolean audin_alsa_format_supported(IAudinDevice* device, audinFormat* format)
+{
+ switch (format->wFormatTag)
+ {
+ case 1: /* PCM */
+ if (format->cbSize == 0 &&
+ (format->nSamplesPerSec <= 48000) &&
+ (format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
+ (format->nChannels == 1 || format->nChannels == 2))
+ {
+ return true;
+ }
+ break;
+
+ case 0x11: /* IMA ADPCM */
+ if ((format->nSamplesPerSec <= 48000) &&
+ (format->wBitsPerSample == 4) &&
+ (format->nChannels == 1 || format->nChannels == 2))
+ {
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
+static void audin_alsa_set_format(IAudinDevice* device, audinFormat* format, uint32 FramesPerPacket)
+{
+ int bs;
+ AudinALSADevice* alsa = (AudinALSADevice*) device;
+
+ alsa->target_rate = format->nSamplesPerSec;
+ alsa->actual_rate = format->nSamplesPerSec;
+ alsa->target_channels = format->nChannels;
+ alsa->actual_channels = format->nChannels;
+ switch (format->wFormatTag)
+ {
+ case 1: /* PCM */
+ switch (format->wBitsPerSample)
+ {
+ case 8:
+ alsa->format = SND_PCM_FORMAT_S8;
+ alsa->bytes_per_channel = 1;
+ break;
+ case 16:
+ alsa->format = SND_PCM_FORMAT_S16_LE;
+ alsa->bytes_per_channel = 2;
+ break;
+ }
+ break;
+
+ case 0x11: /* IMA ADPCM */
+ alsa->format = SND_PCM_FORMAT_S16_LE;
+ alsa->bytes_per_channel = 2;
+ bs = (format->nBlockAlign - 4 * format->nChannels) * 4;
+ alsa->frames_per_packet = (alsa->frames_per_packet * format->nChannels * 2 /
+ bs + 1) * bs / (format->nChannels * 2);
+ DEBUG_DVC("aligned FramesPerPacket=%d",
+ alsa->frames_per_packet);
+ break;
+ }
+ alsa->wformat = format->wFormatTag;
+ alsa->block_size = format->nBlockAlign;
+}
+
+static void audin_alsa_open(IAudinDevice* device, AudinReceive receive, void* user_data)
+{
+ AudinALSADevice* alsa = (AudinALSADevice*) device;
+
+ DEBUG_DVC("");
+
+ alsa->receive = receive;
+ alsa->user_data = user_data;
+
+ freerdp_thread_start(alsa->thread, audin_alsa_thread_func, alsa);
+}
+
+static void audin_alsa_close(IAudinDevice* device)
+{
+ AudinALSADevice* alsa = (AudinALSADevice*) device;
+
+ DEBUG_DVC("");
+
+ freerdp_thread_stop(alsa->thread);
+
+ alsa->receive = NULL;
+ alsa->user_data = NULL;
+}
+
+int FreeRDPAudinDeviceEntry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
+{
+ AudinALSADevice* alsa;
+ RDP_PLUGIN_DATA* data;
+
+ alsa = xnew(AudinALSADevice);
+
+ alsa->iface.Open = audin_alsa_open;
+ alsa->iface.FormatSupported = audin_alsa_format_supported;
+ alsa->iface.SetFormat = audin_alsa_set_format;
+ alsa->iface.Close = audin_alsa_close;
+ alsa->iface.Free = audin_alsa_free;
+
+ data = pEntryPoints->plugin_data;
+ if (data && data->data[0] && strcmp(data->data[0], "audin") == 0 &&
+ data->data[1] && strcmp(data->data[1], "alsa") == 0)
+ {
+ if (data[2].size)
+ strncpy(alsa->device_name, (char*)data->data[2], sizeof(alsa->device_name));
+ }
+ if (alsa->device_name[0] == '\0')
+ {
+ strcpy(alsa->device_name, "default");
+ }
+
+ alsa->frames_per_packet = 128;
+ alsa->target_rate = 22050;
+ alsa->actual_rate = 22050;
+ alsa->format = SND_PCM_FORMAT_S16_LE;
+ alsa->target_channels = 2;
+ alsa->actual_channels = 2;
+ alsa->bytes_per_channel = 2;
+ alsa->thread = freerdp_thread_new();
+
+ pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) alsa);
+
+ return 0;
+}
+
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Audio Input Redirection Virtual Channel
+ *
+ * Copyright 2010-2011 Vic Lee
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <freerdp/utils/memory.h>
+#include <freerdp/utils/stream.h>
+#include <freerdp/utils/load_plugin.h>
+
+#include "audin_main.h"
+
+#define MSG_SNDIN_VERSION 0x01
+#define MSG_SNDIN_FORMATS 0x02
+#define MSG_SNDIN_OPEN 0x03
+#define MSG_SNDIN_OPEN_REPLY 0x04
+#define MSG_SNDIN_DATA_INCOMING 0x05
+#define MSG_SNDIN_DATA 0x06
+#define MSG_SNDIN_FORMATCHANGE 0x07
+
+typedef struct _AUDIN_LISTENER_CALLBACK AUDIN_LISTENER_CALLBACK;
+struct _AUDIN_LISTENER_CALLBACK
+{
+ IWTSListenerCallback iface;
+
+ IWTSPlugin* plugin;
+ IWTSVirtualChannelManager* channel_mgr;
+};
+
+typedef struct _AUDIN_CHANNEL_CALLBACK AUDIN_CHANNEL_CALLBACK;
+struct _AUDIN_CHANNEL_CALLBACK
+{
+ IWTSVirtualChannelCallback iface;
+
+ IWTSPlugin* plugin;
+ IWTSVirtualChannelManager* channel_mgr;
+ IWTSVirtualChannel* channel;
+
+ /**
+ * The supported format list sent back to the server, which needs to
+ * be stored as reference when the server sends the format index in
+ * Open PDU and Format Change PDU
+ */
+ audinFormat* formats;
+ int formats_count;
+};
+
+typedef struct _AUDIN_PLUGIN AUDIN_PLUGIN;
+struct _AUDIN_PLUGIN
+{
+ IWTSPlugin iface;
+
+ AUDIN_LISTENER_CALLBACK* listener_callback;
+
+ /* Parsed plugin data */
+ uint16 fixed_format;
+ uint16 fixed_channel;
+ uint32 fixed_rate;
+
+ /* Device interface */
+ IAudinDevice* device;
+};
+
+static int audin_process_version(IWTSVirtualChannelCallback* pChannelCallback, STREAM* s)
+{
+ int error;
+ STREAM* out;
+ uint32 Version;
+ AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
+
+ stream_read_uint32(s, Version);
+
+ DEBUG_DVC("Version=%d", Version);
+
+ out = stream_new(5);
+ stream_write_uint8(out, MSG_SNDIN_VERSION);
+ stream_write_uint32(out, Version);
+ error = callback->channel->Write(callback->channel, stream_get_length(s), stream_get_head(s), NULL);
+ stream_free(out);
+
+ return error;
+}
+
+static int audin_send_incoming_data_pdu(IWTSVirtualChannelCallback* pChannelCallback)
+{
+ uint8 out_data[1];
+ AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
+
+ out_data[0] = MSG_SNDIN_DATA_INCOMING;
+ return callback->channel->Write(callback->channel, 1, out_data, NULL);
+}
+
+static int audin_process_formats(IWTSVirtualChannelCallback* pChannelCallback, STREAM* s)
+{
+ AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
+ AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) callback->plugin;
+ uint32 i;
+ uint8* fm;
+ int error;
+ STREAM* out;
+ uint32 NumFormats;
+ audinFormat format;
+ uint32 cbSizeFormatsPacket;
+
+ stream_read_uint32(s, NumFormats);
+ DEBUG_DVC("NumFormats %d", NumFormats);
+ if ((NumFormats < 1) || (NumFormats > 1000))
+ {
+ DEBUG_WARN("bad NumFormats %d", NumFormats);
+ return 1;
+ }
+ stream_seek_uint32(s); /* cbSizeFormatsPacket */
+
+ callback->formats = (audinFormat*) xzalloc(NumFormats * sizeof(audinFormat));
+
+ out = stream_new(9);
+ stream_seek(out, 9);
+
+ /* SoundFormats (variable) */
+ for (i = 0; i < NumFormats; i++)
+ {
+ stream_get_mark(s, fm);
+ stream_read_uint16(s, format.wFormatTag);
+ stream_read_uint16(s, format.nChannels);
+ stream_read_uint32(s, format.nSamplesPerSec);
+ stream_seek_uint32(s); /* nAvgBytesPerSec */
+ stream_read_uint16(s, format.nBlockAlign);
+ stream_read_uint16(s, format.wBitsPerSample);
+ stream_read_uint16(s, format.cbSize);
+ format.data = stream_get_tail(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,
+ format.nBlockAlign, format.wBitsPerSample, format.cbSize);
+
+ if (audin->fixed_format > 0 && audin->fixed_format != format.wFormatTag)
+ continue;
+ if (audin->fixed_channel > 0 && audin->fixed_channel != format.nChannels)
+ continue;
+ if (audin->fixed_rate > 0 && audin->fixed_rate != format.nSamplesPerSec)
+ continue;
+ if (audin->device && audin->device->FormatSupported(audin->device, &format))
+ {
+ DEBUG_DVC("format ok");
+
+ /* Store the agreed format in the corresponding index */
+ callback->formats[callback->formats_count++] = format;
+ /* Put the format to output buffer */
+ stream_check_size(out, 18 + format.cbSize);
+ stream_write(out, fm, 18 + format.cbSize);
+ }
+ }
+
+ audin_send_incoming_data_pdu(pChannelCallback);
+
+ cbSizeFormatsPacket = stream_get_pos(out);
+ stream_set_pos(out, 0);
+
+ stream_write_uint8(out, MSG_SNDIN_FORMATS); /* Header (1 byte) */
+ stream_write_uint32(out, callback->formats_count); /* NumFormats (4 bytes) */
+ stream_write_uint32(out, cbSizeFormatsPacket); /* cbSizeFormatsPacket (4 bytes) */
+
+ error = callback->channel->Write(callback->channel, cbSizeFormatsPacket, stream_get_head(out), NULL);
+ stream_free(out);
+
+ return error;
+}
+
+static int audin_send_format_change_pdu(IWTSVirtualChannelCallback* pChannelCallback, uint32 NewFormat)
+{
+ int error;
+ STREAM* out;
+ AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
+
+ out = stream_new(5);
+ stream_write_uint8(out, MSG_SNDIN_FORMATCHANGE);
+ stream_write_uint32(out, NewFormat);
+ error = callback->channel->Write(callback->channel, 5, stream_get_head(out), NULL);
+ stream_free(out);
+
+ return error;
+}
+
+static int audin_send_open_reply_pdu(IWTSVirtualChannelCallback* pChannelCallback, uint32 Result)
+{
+ int error;
+ STREAM* out;
+ AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
+
+ out = stream_new(5);
+ stream_write_uint8(out, MSG_SNDIN_OPEN_REPLY);
+ stream_write_uint32(out, Result);
+ error = callback->channel->Write(callback->channel, 5, stream_get_head(out), NULL);
+ stream_free(out);
+
+ return error;
+}
+
+static boolean audin_receive_wave_data(uint8* data, int size, void* user_data)
+{
+ int error;
+ STREAM* out;
+ AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) user_data;
+
+ error = audin_send_incoming_data_pdu((IWTSVirtualChannelCallback*) callback);
+
+ if (error != 0)
+ return false;
+
+ out = stream_new(size + 1);
+ stream_write_uint8(out, MSG_SNDIN_DATA);
+ stream_write(out, data, size);
+ error = callback->channel->Write(callback->channel, stream_get_length(out), stream_get_head(out), NULL);
+ stream_free(out);
+
+ return (error == 0 ? true : false);
+}
+
+static int audin_process_open(IWTSVirtualChannelCallback* pChannelCallback, STREAM* s)
+{
+ AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
+ AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) callback->plugin;
+ audinFormat* format;
+ uint32 initialFormat;
+ uint32 FramesPerPacket;
+
+ stream_read_uint32(s, FramesPerPacket);
+ stream_read_uint32(s, initialFormat);
+
+ DEBUG_DVC("FramesPerPacket=%d initialFormat=%d",
+ FramesPerPacket, initialFormat);
+
+ if (initialFormat >= callback->formats_count)
+ {
+ DEBUG_WARN("invalid format index %d (total %d)",
+ initialFormat, callback->formats_count);
+ return 1;
+ }
+
+ format = &callback->formats[initialFormat];
+ if (audin->device)
+ {
+ IFCALL(audin->device->SetFormat, audin->device, format, FramesPerPacket);
+ IFCALL(audin->device->Open, audin->device, audin_receive_wave_data, callback);
+ }
+
+ audin_send_format_change_pdu(pChannelCallback, initialFormat);
+ audin_send_open_reply_pdu(pChannelCallback, 0);
+
+ return 0;
+}
+
+static int audin_process_format_change(IWTSVirtualChannelCallback* pChannelCallback, STREAM* s)
+{
+ AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
+ AUDIN_PLUGIN * audin = (AUDIN_PLUGIN*) callback->plugin;
+ uint32 NewFormat;
+ audinFormat* format;
+
+ stream_read_uint32(s, NewFormat);
+
+ DEBUG_DVC("NewFormat=%d", NewFormat);
+
+ if (NewFormat >= callback->formats_count)
+ {
+ DEBUG_WARN("invalid format index %d (total %d)",
+ NewFormat, callback->formats_count);
+ return 1;
+ }
+
+ format = &callback->formats[NewFormat];
+
+ if (audin->device)
+ {
+ IFCALL(audin->device->Close, audin->device);
+ IFCALL(audin->device->SetFormat, audin->device, format, 0);
+ IFCALL(audin->device->Open, audin->device, audin_receive_wave_data, callback);
+ }
+
+ audin_send_format_change_pdu(pChannelCallback, NewFormat);
+
+ return 0;
+}
+
+static int audin_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, uint32 cbSize, uint8* pBuffer)
+{
+ int error;
+ STREAM* s;
+ uint8 MessageId;
+
+ s = stream_new(0);
+ stream_attach(s, pBuffer, cbSize);
+
+ stream_read_uint8(s, MessageId);
+
+ DEBUG_DVC("MessageId=0x%x", MessageId);
+
+ switch (MessageId)
+ {
+ case MSG_SNDIN_VERSION:
+ error = audin_process_version(pChannelCallback, s);
+ break;
+
+ case MSG_SNDIN_FORMATS:
+ error = audin_process_formats(pChannelCallback, s);
+ break;
+
+ case MSG_SNDIN_OPEN:
+ error = audin_process_open(pChannelCallback, s);
+ break;
+
+ case MSG_SNDIN_FORMATCHANGE:
+ error = audin_process_format_change(pChannelCallback, s);
+ break;
+
+ default:
+ DEBUG_WARN("unknown MessageId=0x%x", MessageId);
+ error = 1;
+ break;
+ }
+
+ stream_detach(s);
+ stream_free(s);
+
+ return error;
+}
+
+static int audin_on_close(IWTSVirtualChannelCallback* pChannelCallback)
+{
+ AUDIN_CHANNEL_CALLBACK* callback = (AUDIN_CHANNEL_CALLBACK*) pChannelCallback;
+ AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) callback->plugin;
+
+ DEBUG_DVC("");
+
+ if (audin->device)
+ IFCALL(audin->device->Close, audin->device);
+
+ xfree(callback->formats);
+ xfree(callback);
+
+ return 0;
+}
+
+static int audin_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
+ IWTSVirtualChannel* pChannel, uint8* Data, int* pbAccept,
+ IWTSVirtualChannelCallback** ppCallback)
+{
+ AUDIN_CHANNEL_CALLBACK* callback;
+ AUDIN_LISTENER_CALLBACK* listener_callback = (AUDIN_LISTENER_CALLBACK*) pListenerCallback;
+
+ DEBUG_DVC("");
+
+ callback = xnew(AUDIN_CHANNEL_CALLBACK);
+
+ callback->iface.OnDataReceived = audin_on_data_received;
+ callback->iface.OnClose = audin_on_close;
+ callback->plugin = listener_callback->plugin;
+ callback->channel_mgr = listener_callback->channel_mgr;
+ callback->channel = pChannel;
+
+ *ppCallback = (IWTSVirtualChannelCallback*) callback;
+
+ return 0;
+}
+
+static int audin_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManager* pChannelMgr)
+{
+ AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) pPlugin;
+
+ DEBUG_DVC("");
+
+ audin->listener_callback = xnew(AUDIN_LISTENER_CALLBACK);
+
+ audin->listener_callback->iface.OnNewChannelConnection = audin_on_new_channel_connection;
+ audin->listener_callback->plugin = pPlugin;
+ audin->listener_callback->channel_mgr = pChannelMgr;
+
+ return pChannelMgr->CreateListener(pChannelMgr, "AUDIO_INPUT", 0,
+ (IWTSListenerCallback*) audin->listener_callback, NULL);
+}
+
+static int audin_plugin_terminated(IWTSPlugin* pPlugin)
+{
+ AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) pPlugin;
+
+ DEBUG_DVC("");
+
+ if (audin->device)
+ {
+ IFCALL(audin->device->Close, audin->device);
+ IFCALL(audin->device->Free, audin->device);
+ audin->device = NULL;
+ }
+
+ xfree(audin->listener_callback);
+ xfree(audin);
+
+ return 0;
+}
+
+static void audin_register_device_plugin(IWTSPlugin* pPlugin, IAudinDevice* device)
+{
+ AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) pPlugin;
+
+ if (audin->device)
+ {
+ DEBUG_WARN("existing device, abort.");
+ return;
+ }
+
+ DEBUG_DVC("device registered.");
+
+ audin->device = device;
+}
+
+static boolean audin_load_device_plugin(IWTSPlugin* pPlugin, const char* name, RDP_PLUGIN_DATA* data)
+{
+ char* fullname;
+ PFREERDP_AUDIN_DEVICE_ENTRY entry;
+ FREERDP_AUDIN_DEVICE_ENTRY_POINTS entryPoints;
+
+ if (strrchr(name, '.') != NULL)
+ {
+ entry = (PFREERDP_AUDIN_DEVICE_ENTRY) freerdp_load_plugin(name, AUDIN_DEVICE_EXPORT_FUNC_NAME);
+ }
+ else
+ {
+ fullname = xzalloc(strlen(name) + 8);
+ strcpy(fullname, "audin_");
+ strcat(fullname, name);
+ entry = (PFREERDP_AUDIN_DEVICE_ENTRY) freerdp_load_plugin(fullname, AUDIN_DEVICE_EXPORT_FUNC_NAME);
+ xfree(fullname);
+ }
+
+ if (entry == NULL)
+ return false;
+
+ entryPoints.plugin = pPlugin;
+ entryPoints.pRegisterAudinDevice = audin_register_device_plugin;
+ entryPoints.plugin_data = data;
+
+ if (entry(&entryPoints) != 0)
+ {
+ DEBUG_WARN("%s entry returns error.", name);
+ return false;
+ }
+
+ return true;
+}
+
+static boolean audin_process_plugin_data(IWTSPlugin* pPlugin, RDP_PLUGIN_DATA* data)
+{
+ boolean ret;
+ AUDIN_PLUGIN* audin = (AUDIN_PLUGIN*) pPlugin;
+ RDP_PLUGIN_DATA default_data[2] = { { 0 }, { 0 } };
+
+ if (data->data[0] && (strcmp((char*)data->data[0], "audin") == 0 || strstr((char*) data->data[0], "/audin.") != NULL))
+ {
+ if (data->data[1] && strcmp((char*)data->data[1], "format") == 0)
+ {
+ audin->fixed_format = atoi(data->data[2]);
+ return true;
+ }
+ else if (data->data[1] && strcmp((char*)data->data[1], "rate") == 0)
+ {
+ audin->fixed_rate = atoi(data->data[2]);
+ return true;
+ }
+ else if (data->data[1] && strcmp((char*)data->data[1], "channel") == 0)
+ {
+ audin->fixed_channel = atoi(data->data[2]);
+ return true;
+ }
+ else if (data->data[1] && ((char*)data->data[1])[0])
+ {
+ return audin_load_device_plugin(pPlugin, (char*) data->data[1], data);
+ }
+ else
+ {
+ default_data[0].size = sizeof(RDP_PLUGIN_DATA);
+ default_data[0].data[0] = "audin";
+ default_data[0].data[1] = "pulse";
+ default_data[0].data[2] = "";
+
+ ret = audin_load_device_plugin(pPlugin, "pulse", default_data);
+
+ if (!ret)
+ {
+ default_data[0].size = sizeof(RDP_PLUGIN_DATA);
+ default_data[0].data[0] = "audin";
+ default_data[0].data[1] = "alsa";
+ default_data[0].data[2] = "default";
+ ret = audin_load_device_plugin(pPlugin, "alsa", default_data);
+ }
+
+ return ret;
+ }
+ }
+
+ return true;
+}
+
+int DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
+{
+ int error = 0;
+ AUDIN_PLUGIN* audin;
+
+ audin = (AUDIN_PLUGIN*) pEntryPoints->GetPlugin(pEntryPoints, "audin");
+
+ if (audin == NULL)
+ {
+ audin = xnew(AUDIN_PLUGIN);
+
+ audin->iface.Initialize = audin_plugin_initialize;
+ audin->iface.Connected = NULL;
+ audin->iface.Disconnected = NULL;
+ audin->iface.Terminated = audin_plugin_terminated;
+ error = pEntryPoints->RegisterPlugin(pEntryPoints, "audin", (IWTSPlugin*) audin);
+ }
+
+ if (error == 0)
+ audin_process_plugin_data((IWTSPlugin*) audin, pEntryPoints->GetPluginData(pEntryPoints));
+
+ return error;
+}
+
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Audio Input Redirection Virtual Channel
+ *
+ * Copyright 2010-2011 Vic Lee
+ *
+ * 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.
+ */
+
+#ifndef __AUDIN_MAIN_H
+#define __AUDIN_MAIN_H
+
+#include "drdynvc_types.h"
+
+typedef boolean (*AudinReceive) (uint8* data, int size, void* user_data);
+
+typedef struct audin_format audinFormat;
+struct audin_format
+{
+ uint16 wFormatTag;
+ uint16 nChannels;
+ uint32 nSamplesPerSec;
+ uint16 nBlockAlign;
+ uint16 wBitsPerSample;
+ uint16 cbSize;
+ uint8* data;
+};
+
+typedef struct _IAudinDevice IAudinDevice;
+struct _IAudinDevice
+{
+ void (*Open) (IAudinDevice* devplugin, AudinReceive receive, void* user_data);
+ boolean (*FormatSupported) (IAudinDevice* devplugin, audinFormat* format);
+ void (*SetFormat) (IAudinDevice* devplugin, audinFormat* format, uint32 FramesPerPacket);
+ void (*Close) (IAudinDevice* devplugin);
+ void (*Free) (IAudinDevice* devplugin);
+};
+
+#define AUDIN_DEVICE_EXPORT_FUNC_NAME "FreeRDPAudinDeviceEntry"
+
+typedef void (*PREGISTERAUDINDEVICE)(IWTSPlugin* plugin, IAudinDevice* device);
+
+struct _FREERDP_AUDIN_DEVICE_ENTRY_POINTS
+{
+ IWTSPlugin* plugin;
+ PREGISTERAUDINDEVICE pRegisterAudinDevice;
+ RDP_PLUGIN_DATA* plugin_data;
+};
+typedef struct _FREERDP_AUDIN_DEVICE_ENTRY_POINTS FREERDP_AUDIN_DEVICE_ENTRY_POINTS;
+typedef FREERDP_AUDIN_DEVICE_ENTRY_POINTS* PFREERDP_AUDIN_DEVICE_ENTRY_POINTS;
+
+typedef int (*PFREERDP_AUDIN_DEVICE_ENTRY)(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints);
+
+#endif /* __AUDIN_MAIN_H */
+
--- /dev/null
+# FreeRDP: A Remote Desktop Protocol Client
+# FreeRDP cmake build script
+#
+# Copyright 2011 O.S. Systems Software Ltda.
+# Copyright 2011 Otavio Salvador <otavio@ossystems.com.br>
+# Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.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(AUDIN_PULSE_SRCS
+ audin_pulse.c
+)
+
+include_directories(..)
+include_directories(${PULSE_INCLUDE_DIRS})
+
+add_library(audin_pulse ${AUDIN_PULSE_SRCS})
+set_target_properties(audin_pulse PROPERTIES PREFIX "")
+
+target_link_libraries(audin_pulse freerdp-utils)
+target_link_libraries(audin_pulse ${PULSE_LIBRARIES})
+
+install(TARGETS audin_pulse DESTINATION ${FREERDP_PLUGIN_PATH})
+
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Audio Input Redirection Virtual Channel - PulseAudio implementation
+ *
+ * Copyright 2010-2011 Vic Lee
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pulse/pulseaudio.h>
+#include <freerdp/types.h>
+#include <freerdp/utils/memory.h>
+#include <freerdp/utils/dsp.h>
+
+#include "audin_main.h"
+
+typedef struct _AudinPulseDevice
+{
+ IAudinDevice iface;
+
+ char device_name[32];
+ uint32 frames_per_packet;
+ pa_threaded_mainloop* mainloop;
+ pa_context* context;
+ pa_sample_spec sample_spec;
+ pa_stream* stream;
+ int format;
+ int block_size;
+ ADPCM adpcm;
+
+ int bytes_per_frame;
+ uint8* buffer;
+ int buffer_frames;
+
+ AudinReceive receive;
+ void* user_data;
+} AudinPulseDevice;
+
+static void audin_pulse_context_state_callback(pa_context* context, void* userdata)
+{
+ pa_context_state_t state;
+ AudinPulseDevice* pulse = (AudinPulseDevice*) userdata;
+
+ state = pa_context_get_state(context);
+ switch (state)
+ {
+ case PA_CONTEXT_READY:
+ DEBUG_DVC("PA_CONTEXT_READY");
+ pa_threaded_mainloop_signal (pulse->mainloop, 0);
+ break;
+
+ case PA_CONTEXT_FAILED:
+ case PA_CONTEXT_TERMINATED:
+ DEBUG_DVC("state %d", (int)state);
+ pa_threaded_mainloop_signal (pulse->mainloop, 0);
+ break;
+
+ default:
+ DEBUG_DVC("state %d", (int)state);
+ break;
+ }
+}
+
+static boolean audin_pulse_connect(IAudinDevice* device)
+{
+ pa_context_state_t state;
+ AudinPulseDevice* pulse = (AudinPulseDevice*) device;
+
+ if (!pulse->context)
+ return false;
+
+ if (pa_context_connect(pulse->context, NULL, 0, NULL))
+ {
+ DEBUG_WARN("pa_context_connect failed (%d)",
+ pa_context_errno(pulse->context));
+ return false;
+ }
+ pa_threaded_mainloop_lock(pulse->mainloop);
+ if (pa_threaded_mainloop_start(pulse->mainloop) < 0)
+ {
+ pa_threaded_mainloop_unlock(pulse->mainloop);
+ DEBUG_WARN("pa_threaded_mainloop_start failed (%d)",
+ pa_context_errno(pulse->context));
+ return false;
+ }
+ for (;;)
+ {
+ state = pa_context_get_state(pulse->context);
+ if (state == PA_CONTEXT_READY)
+ break;
+ if (!PA_CONTEXT_IS_GOOD(state))
+ {
+ DEBUG_WARN("bad context state (%d)",
+ pa_context_errno(pulse->context));
+ break;
+ }
+ pa_threaded_mainloop_wait(pulse->mainloop);
+ }
+ pa_threaded_mainloop_unlock(pulse->mainloop);
+ if (state == PA_CONTEXT_READY)
+ {
+ DEBUG_DVC("connected");
+ return true;
+ }
+ else
+ {
+ pa_context_disconnect(pulse->context);
+ return false;
+ }
+}
+
+static void audin_pulse_free(IAudinDevice* device)
+{
+ AudinPulseDevice* pulse = (AudinPulseDevice*) device;
+
+ DEBUG_DVC("");
+
+ if (!pulse)
+ return;
+ if (pulse->mainloop)
+ {
+ pa_threaded_mainloop_stop(pulse->mainloop);
+ }
+ if (pulse->context)
+ {
+ pa_context_disconnect(pulse->context);
+ pa_context_unref(pulse->context);
+ pulse->context = NULL;
+ }
+ if (pulse->mainloop)
+ {
+ pa_threaded_mainloop_free(pulse->mainloop);
+ pulse->mainloop = NULL;
+ }
+ xfree(pulse);
+}
+
+static boolean audin_pulse_format_supported(IAudinDevice* device, audinFormat* format)
+{
+ AudinPulseDevice* pulse = (AudinPulseDevice*) device;
+
+ if (!pulse->context)
+ return 0;
+
+ switch (format->wFormatTag)
+ {
+ case 1: /* PCM */
+ if (format->cbSize == 0 &&
+ (format->nSamplesPerSec <= PA_RATE_MAX) &&
+ (format->wBitsPerSample == 8 || format->wBitsPerSample == 16) &&
+ (format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX))
+ {
+ return true;
+ }
+ break;
+
+ case 6: /* A-LAW */
+ case 7: /* U-LAW */
+ if (format->cbSize == 0 &&
+ (format->nSamplesPerSec <= PA_RATE_MAX) &&
+ (format->wBitsPerSample == 8) &&
+ (format->nChannels >= 1 && format->nChannels <= PA_CHANNELS_MAX))
+ {
+ return true;
+ }
+ break;
+
+ case 0x11: /* IMA ADPCM */
+ if ((format->nSamplesPerSec <= PA_RATE_MAX) &&
+ (format->wBitsPerSample == 4) &&
+ (format->nChannels == 1 || format->nChannels == 2))
+ {
+ return true;
+ }
+ break;
+ }
+ return false;
+}
+
+static void audin_pulse_set_format(IAudinDevice* device, audinFormat* format, uint32 FramesPerPacket)
+{
+ int bs;
+ pa_sample_spec sample_spec = { 0 };
+ AudinPulseDevice* pulse = (AudinPulseDevice*) device;
+
+ if (!pulse->context)
+ return;
+
+ if (FramesPerPacket > 0)
+ {
+ pulse->frames_per_packet = FramesPerPacket;
+ }
+
+ sample_spec.rate = format->nSamplesPerSec;
+ sample_spec.channels = format->nChannels;
+ switch (format->wFormatTag)
+ {
+ case 1: /* PCM */
+ switch (format->wBitsPerSample)
+ {
+ case 8:
+ sample_spec.format = PA_SAMPLE_U8;
+ break;
+ case 16:
+ sample_spec.format = PA_SAMPLE_S16LE;
+ break;
+ }
+ break;
+
+ case 6: /* A-LAW */
+ sample_spec.format = PA_SAMPLE_ALAW;
+ break;
+
+ case 7: /* U-LAW */
+ sample_spec.format = PA_SAMPLE_ULAW;
+ break;
+
+ case 0x11: /* IMA ADPCM */
+ sample_spec.format = PA_SAMPLE_S16LE;
+ bs = (format->nBlockAlign - 4 * format->nChannels) * 4;
+ pulse->frames_per_packet = (pulse->frames_per_packet * format->nChannels * 2 /
+ bs + 1) * bs / (format->nChannels * 2);
+ DEBUG_DVC("aligned FramesPerPacket=%d",
+ pulse->frames_per_packet);
+ break;
+ }
+
+ pulse->sample_spec = sample_spec;
+ pulse->format = format->wFormatTag;
+ pulse->block_size = format->nBlockAlign;
+}
+
+static void audin_pulse_stream_state_callback(pa_stream* stream, void* userdata)
+{
+ pa_stream_state_t state;
+ AudinPulseDevice* pulse = (AudinPulseDevice*) userdata;
+
+ state = pa_stream_get_state(stream);
+ switch (state)
+ {
+ case PA_STREAM_READY:
+ DEBUG_DVC("PA_STREAM_READY");
+ pa_threaded_mainloop_signal(pulse->mainloop, 0);
+ break;
+
+ case PA_STREAM_FAILED:
+ case PA_STREAM_TERMINATED:
+ DEBUG_DVC("state %d", (int)state);
+ pa_threaded_mainloop_signal(pulse->mainloop, 0);
+ break;
+
+ default:
+ DEBUG_DVC("state %d", (int)state);
+ break;
+ }
+}
+
+static void audin_pulse_stream_request_callback(pa_stream* stream, size_t length, void* userdata)
+{
+ int frames;
+ int cframes;
+ boolean ret;
+ const void* data;
+ const uint8* src;
+ int encoded_size;
+ uint8* encoded_data;
+ AudinPulseDevice* pulse = (AudinPulseDevice*) userdata;
+
+ pa_stream_peek(stream, &data, &length);
+ frames = length / pulse->bytes_per_frame;
+
+ DEBUG_DVC("length %d frames %d", (int) length, frames);
+
+ src = (const uint8*) data;
+ while (frames > 0)
+ {
+ cframes = pulse->frames_per_packet - pulse->buffer_frames;
+ if (cframes > frames)
+ cframes = frames;
+ memcpy(pulse->buffer + pulse->buffer_frames * pulse->bytes_per_frame,
+ src, cframes * pulse->bytes_per_frame);
+ pulse->buffer_frames += cframes;
+ if (pulse->buffer_frames >= pulse->frames_per_packet)
+ {
+ if (pulse->format == 0x11)
+ {
+ encoded_data = dsp_encode_ima_adpcm(&pulse->adpcm,
+ pulse->buffer, pulse->buffer_frames * pulse->bytes_per_frame,
+ pulse->sample_spec.channels, pulse->block_size, &encoded_size);
+ DEBUG_DVC("encoded %d to %d",
+ pulse->buffer_frames * pulse->bytes_per_frame, encoded_size);
+ }
+ else
+ {
+ encoded_data = pulse->buffer;
+ encoded_size = pulse->buffer_frames * pulse->bytes_per_frame;
+ }
+
+ ret = pulse->receive(encoded_data, encoded_size, pulse->user_data);
+ pulse->buffer_frames = 0;
+ if (encoded_data != pulse->buffer)
+ xfree(encoded_data);
+ if (!ret)
+ break;
+ }
+ src += cframes * pulse->bytes_per_frame;
+ frames -= cframes;
+ }
+
+ pa_stream_drop(stream);
+}
+
+
+static void audin_pulse_close(IAudinDevice* device)
+{
+ AudinPulseDevice* pulse = (AudinPulseDevice*) device;
+
+ if (!pulse->context || !pulse->stream)
+ return;
+
+ DEBUG_DVC("");
+
+ pa_threaded_mainloop_lock(pulse->mainloop);
+ pa_stream_disconnect(pulse->stream);
+ pa_stream_unref(pulse->stream);
+ pulse->stream = NULL;
+ pa_threaded_mainloop_unlock(pulse->mainloop);
+
+ pulse->receive = NULL;
+ pulse->user_data = NULL;
+ if (pulse->buffer)
+ {
+ xfree(pulse->buffer);
+ pulse->buffer = NULL;
+ pulse->buffer_frames = 0;
+ }
+}
+
+static void audin_pulse_open(IAudinDevice* device, AudinReceive receive, void* user_data)
+{
+ pa_stream_state_t state;
+ pa_buffer_attr buffer_attr = { 0 };
+ AudinPulseDevice* pulse = (AudinPulseDevice*) device;
+
+ if (!pulse->context)
+ return;
+ if (!pulse->sample_spec.rate || pulse->stream)
+ return;
+
+ DEBUG_DVC("");
+
+ pulse->receive = receive;
+ pulse->user_data = user_data;
+
+ pa_threaded_mainloop_lock(pulse->mainloop);
+ pulse->stream = pa_stream_new(pulse->context, "freerdp_audin",
+ &pulse->sample_spec, NULL);
+ if (!pulse->stream)
+ {
+ pa_threaded_mainloop_unlock(pulse->mainloop);
+ DEBUG_DVC("pa_stream_new failed (%d)",
+ pa_context_errno(pulse->context));
+ return;
+ }
+ pulse->bytes_per_frame = pa_frame_size(&pulse->sample_spec);
+ pa_stream_set_state_callback(pulse->stream,
+ audin_pulse_stream_state_callback, pulse);
+ pa_stream_set_read_callback(pulse->stream,
+ audin_pulse_stream_request_callback, pulse);
+ buffer_attr.maxlength = (uint32_t) -1;
+ buffer_attr.tlength = (uint32_t) -1;
+ buffer_attr.prebuf = (uint32_t) -1;
+ buffer_attr.minreq = (uint32_t) -1;
+ /* 500ms latency */
+ buffer_attr.fragsize = pa_usec_to_bytes(500000, &pulse->sample_spec);
+ if (pa_stream_connect_record(pulse->stream,
+ pulse->device_name[0] ? pulse->device_name : NULL,
+ &buffer_attr, PA_STREAM_ADJUST_LATENCY) < 0)
+ {
+ pa_threaded_mainloop_unlock(pulse->mainloop);
+ DEBUG_WARN("pa_stream_connect_playback failed (%d)",
+ pa_context_errno(pulse->context));
+ return;
+ }
+
+ for (;;)
+ {
+ state = pa_stream_get_state(pulse->stream);
+ if (state == PA_STREAM_READY)
+ break;
+ if (!PA_STREAM_IS_GOOD(state))
+ {
+ DEBUG_WARN("bad stream state (%d)",
+ pa_context_errno(pulse->context));
+ break;
+ }
+ pa_threaded_mainloop_wait(pulse->mainloop);
+ }
+ pa_threaded_mainloop_unlock(pulse->mainloop);
+ if (state == PA_STREAM_READY)
+ {
+ memset(&pulse->adpcm, 0, sizeof(ADPCM));
+ pulse->buffer = xzalloc(pulse->bytes_per_frame * pulse->frames_per_packet);
+ pulse->buffer_frames = 0;
+ DEBUG_DVC("connected");
+ }
+ else
+ {
+ audin_pulse_close(device);
+ }
+}
+
+int FreeRDPAudinDeviceEntry(PFREERDP_AUDIN_DEVICE_ENTRY_POINTS pEntryPoints)
+{
+ AudinPulseDevice* pulse;
+ RDP_PLUGIN_DATA * data;
+
+ pulse = xnew(AudinPulseDevice);
+
+ pulse->iface.Open = audin_pulse_open;
+ pulse->iface.FormatSupported = audin_pulse_format_supported;
+ pulse->iface.SetFormat = audin_pulse_set_format;
+ pulse->iface.Close = audin_pulse_close;
+ pulse->iface.Free = audin_pulse_free;
+
+ data = pEntryPoints->plugin_data;
+ if (data && data->data[0] && strcmp(data->data[0], "audin") == 0 &&
+ data->data[1] && strcmp(data->data[1], "pulse") == 0)
+ {
+ strncpy(pulse->device_name, (char*)data->data[2], sizeof(pulse->device_name));
+ }
+
+ pulse->mainloop = pa_threaded_mainloop_new();
+ if (!pulse->mainloop)
+ {
+ DEBUG_WARN("pa_threaded_mainloop_new failed");
+ audin_pulse_free((IAudinDevice*) pulse);
+ return 1;
+ }
+ pulse->context = pa_context_new(pa_threaded_mainloop_get_api(pulse->mainloop), "freerdp");
+ if (!pulse->context)
+ {
+ DEBUG_WARN("pa_context_new failed");
+ audin_pulse_free((IAudinDevice*) pulse);
+ return 1;
+ }
+ pa_context_set_state_callback(pulse->context, audin_pulse_context_state_callback, pulse);
+ if (!audin_pulse_connect((IAudinDevice*) pulse))
+ {
+ audin_pulse_free((IAudinDevice*) pulse);
+ return 1;
+ }
+
+ pEntryPoints->pRegisterAudinDevice(pEntryPoints->plugin, (IAudinDevice*) pulse);
+
+ return 0;
+}
+
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Dynamic Virtual Channel
+ *
+ * Copyright 2010-2011 Vic Lee
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <freerdp/constants.h>
+#include <freerdp/utils/memory.h>
+#include <freerdp/utils/stream.h>
+#include <freerdp/utils/svc_plugin.h>
+#include <freerdp/utils/wait_obj.h>
+
+#include "dvcman.h"
+#include "drdynvc_types.h"
+#include "drdynvc_main.h"
+
+#define CREATE_REQUEST_PDU 0x01
+#define DATA_FIRST_PDU 0x02
+#define DATA_PDU 0x03
+#define CLOSE_REQUEST_PDU 0x04
+#define CAPABILITY_REQUEST_PDU 0x05
+
+struct drdynvc_plugin
+{
+ rdpSvcPlugin plugin;
+
+ int version;
+ int PriorityCharge0;
+ int PriorityCharge1;
+ int PriorityCharge2;
+ int PriorityCharge3;
+
+ IWTSVirtualChannelManager* channel_mgr;
+};
+
+static int drdynvc_write_variable_uint(STREAM* stream, uint32 val)
+{
+ int cb;
+
+ if (val <= 0xFF)
+ {
+ cb = 0;
+ stream_write_uint8(stream, val);
+ }
+ else if (val <= 0xFFFF)
+ {
+ cb = 1;
+ stream_write_uint16(stream, val);
+ }
+ else
+ {
+ cb = 3;
+ stream_write_uint32(stream, val);
+ }
+ return cb;
+}
+
+int drdynvc_write_data(drdynvcPlugin* drdynvc, uint32 ChannelId, uint8* data, uint32 data_size)
+{
+ STREAM* data_out;
+ uint32 pos = 0;
+ uint32 cbChId;
+ uint32 cbLen;
+ uint32 chunk_len;
+ int error;
+
+ DEBUG_DVC("ChannelId=%d size=%d", ChannelId, data_size);
+
+ data_out = stream_new(CHANNEL_CHUNK_LENGTH);
+ stream_set_pos(data_out, 1);
+ cbChId = drdynvc_write_variable_uint(data_out, ChannelId);
+
+ if (data_size <= CHANNEL_CHUNK_LENGTH - pos)
+ {
+ pos = stream_get_pos(data_out);
+ stream_set_pos(data_out, 0);
+ stream_write_uint8(data_out, 0x30 | cbChId);
+ stream_set_pos(data_out, pos);
+ stream_write(data_out, data, data_size);
+ error = svc_plugin_send((rdpSvcPlugin*)drdynvc, data_out);
+ }
+ else
+ {
+ /* Fragment the data */
+ cbLen = drdynvc_write_variable_uint(data_out, data_size);
+ pos = stream_get_pos(data_out);
+ stream_set_pos(data_out, 0);
+ stream_write_uint8(data_out, 0x20 | cbChId | (cbLen << 2));
+ stream_set_pos(data_out, pos);
+ chunk_len = CHANNEL_CHUNK_LENGTH - pos;
+ stream_write(data_out, data, chunk_len);
+ data += chunk_len;
+ data_size -= chunk_len;
+ error = svc_plugin_send((rdpSvcPlugin*)drdynvc, data_out);
+
+ while (error == CHANNEL_RC_OK && data_size > 0)
+ {
+ data_out = stream_new(CHANNEL_CHUNK_LENGTH);
+ stream_set_pos(data_out, 1);
+ cbChId = drdynvc_write_variable_uint(data_out, ChannelId);
+
+ pos = stream_get_pos(data_out);
+ stream_set_pos(data_out, 0);
+ stream_write_uint8(data_out, 0x30 | cbChId);
+ stream_set_pos(data_out, pos);
+
+ chunk_len = data_size;
+ if (chunk_len > CHANNEL_CHUNK_LENGTH - pos)
+ chunk_len = CHANNEL_CHUNK_LENGTH - pos;
+ stream_write(data_out, data, chunk_len);
+ data += chunk_len;
+ data_size -= chunk_len;
+ error = svc_plugin_send((rdpSvcPlugin*)drdynvc, data_out);
+ }
+ }
+ if (error != CHANNEL_RC_OK)
+ {
+ DEBUG_WARN("VirtualChannelWrite failed %d", error);
+ return 1;
+ }
+ return 0;
+}
+
+int drdynvc_push_event(drdynvcPlugin* drdynvc, RDP_EVENT* event)
+{
+ int error;
+
+ error = svc_plugin_send_event((rdpSvcPlugin*)drdynvc, event);
+ if (error != CHANNEL_RC_OK)
+ {
+ DEBUG_WARN("pVirtualChannelEventPush failed %d", error);
+ return 1;
+ }
+ return 0;
+}
+
+static int drdynvc_process_capability_request(drdynvcPlugin* drdynvc, int Sp, int cbChId, STREAM* s)
+{
+ STREAM* data_out;
+ int error;
+
+ DEBUG_DVC("Sp=%d cbChId=%d", Sp, cbChId);
+ stream_seek(s, 1); /* pad */
+ stream_read_uint16(s, drdynvc->version);
+ if (drdynvc->version == 2)
+ {
+ stream_read_uint16(s, drdynvc->PriorityCharge0);
+ stream_read_uint16(s, drdynvc->PriorityCharge1);
+ stream_read_uint16(s, drdynvc->PriorityCharge2);
+ stream_read_uint16(s, drdynvc->PriorityCharge3);
+ }
+ data_out = stream_new(4);
+ stream_write_uint16(data_out, 0x0050); /* Cmd+Sp+cbChId+Pad. Note: MSTSC sends 0x005c */
+ stream_write_uint16(data_out, drdynvc->version);
+ error = svc_plugin_send((rdpSvcPlugin*)drdynvc, data_out);
+ if (error != CHANNEL_RC_OK)
+ {
+ DEBUG_WARN("VirtualChannelWrite failed %d", error);
+ return 1;
+ }
+ return 0;
+}
+
+static uint32 drdynvc_read_variable_uint(STREAM* stream, int cbLen)
+{
+ uint32 val;
+
+ switch (cbLen)
+ {
+ case 0:
+ stream_read_uint8(stream, val);
+ break;
+ case 1:
+ stream_read_uint16(stream, val);
+ break;
+ default:
+ stream_read_uint32(stream, val);
+ break;
+ }
+ return val;
+}
+
+static int drdynvc_process_create_request(drdynvcPlugin* drdynvc, int Sp, int cbChId, STREAM* s)
+{
+ STREAM* data_out;
+ int pos;
+ int error;
+ uint32 ChannelId;
+
+ ChannelId = drdynvc_read_variable_uint(s, cbChId);
+ pos = stream_get_pos(s);
+ DEBUG_DVC("ChannelId=%d ChannelName=%s", ChannelId, stream_get_tail(s));
+
+ error = dvcman_create_channel(drdynvc->channel_mgr, ChannelId, (char*)stream_get_tail(s));
+
+ data_out = stream_new(pos + 4);
+ stream_write_uint8(data_out, 0x10 | cbChId);
+ stream_set_pos(s, 1);
+ stream_copy(data_out, s, pos - 1);
+
+ if (error == 0)
+ {
+ DEBUG_DVC("channel created");
+ stream_write_uint32(data_out, 0);
+ }
+ else
+ {
+ DEBUG_DVC("no listener");
+ stream_write_uint32(data_out, (uint32)(-1));
+ }
+
+ error = svc_plugin_send((rdpSvcPlugin*)drdynvc, data_out);
+ if (error != CHANNEL_RC_OK)
+ {
+ DEBUG_WARN("VirtualChannelWrite failed %d", error);
+ return 1;
+ }
+ return 0;
+}
+
+static int drdynvc_process_data_first(drdynvcPlugin* drdynvc, int Sp, int cbChId, STREAM* s)
+{
+ uint32 ChannelId;
+ uint32 Length;
+ int error;
+
+ ChannelId = drdynvc_read_variable_uint(s, cbChId);
+ Length = drdynvc_read_variable_uint(s, Sp);
+ DEBUG_DVC("ChannelId=%d Length=%d", ChannelId, Length);
+
+ error = dvcman_receive_channel_data_first(drdynvc->channel_mgr, ChannelId, Length);
+ if (error)
+ return error;
+
+ return dvcman_receive_channel_data(drdynvc->channel_mgr, ChannelId,
+ stream_get_tail(s), stream_get_left(s));
+}
+
+static int drdynvc_process_data(drdynvcPlugin* drdynvc, int Sp, int cbChId, STREAM* s)
+{
+ uint32 ChannelId;
+
+ ChannelId = drdynvc_read_variable_uint(s, cbChId);
+ DEBUG_DVC("ChannelId=%d", ChannelId);
+
+ return dvcman_receive_channel_data(drdynvc->channel_mgr, ChannelId,
+ stream_get_tail(s), stream_get_left(s));
+}
+
+static int drdynvc_process_close_request(drdynvcPlugin* drdynvc, int Sp, int cbChId, STREAM* s)
+{
+ uint32 ChannelId;
+
+ ChannelId = drdynvc_read_variable_uint(s, cbChId);
+ DEBUG_DVC("ChannelId=%d", ChannelId);
+ dvcman_close_channel(drdynvc->channel_mgr, ChannelId);
+
+ return 0;
+}
+
+static void drdynvc_process_receive(rdpSvcPlugin* plugin, STREAM* s)
+{
+ drdynvcPlugin* drdynvc = (drdynvcPlugin*)plugin;
+ int value;
+ int Cmd;
+ int Sp;
+ int cbChId;
+
+ stream_read_uint8(s, value);
+ Cmd = (value & 0xf0) >> 4;
+ Sp = (value & 0x0c) >> 2;
+ cbChId = (value & 0x03) >> 0;
+
+ DEBUG_DVC("Cmd=0x%x", Cmd);
+
+ switch (Cmd)
+ {
+ case CAPABILITY_REQUEST_PDU:
+ drdynvc_process_capability_request(drdynvc, Sp, cbChId, s);
+ break;
+ case CREATE_REQUEST_PDU:
+ drdynvc_process_create_request(drdynvc, Sp, cbChId, s);
+ break;
+ case DATA_FIRST_PDU:
+ drdynvc_process_data_first(drdynvc, Sp, cbChId, s);
+ break;
+ case DATA_PDU:
+ drdynvc_process_data(drdynvc, Sp, cbChId, s);
+ break;
+ case CLOSE_REQUEST_PDU:
+ drdynvc_process_close_request(drdynvc, Sp, cbChId, s);
+ break;
+ default:
+ DEBUG_WARN("unknown drdynvc cmd 0x%x", Cmd);
+ break;
+ }
+
+ stream_free(s);
+}
+
+static void drdynvc_process_connect(rdpSvcPlugin* plugin)
+{
+ drdynvcPlugin* drdynvc = (drdynvcPlugin*)plugin;
+
+ DEBUG_DVC("connecting");
+
+ drdynvc->channel_mgr = dvcman_new(drdynvc);
+ dvcman_load_plugin(drdynvc->channel_mgr, svc_plugin_get_data(plugin));
+ dvcman_init(drdynvc->channel_mgr);
+}
+
+static void drdynvc_process_event(rdpSvcPlugin* plugin, RDP_EVENT* event)
+{
+ freerdp_event_free(event);
+}
+
+static void drdynvc_process_terminate(rdpSvcPlugin* plugin)
+{
+ drdynvcPlugin* drdynvc = (drdynvcPlugin*)plugin;
+
+ DEBUG_DVC("terminating");
+
+ if (drdynvc->channel_mgr != NULL)
+ dvcman_free(drdynvc->channel_mgr);
+ xfree(drdynvc);
+}
+
+DEFINE_SVC_PLUGIN(drdynvc, "drdynvc",
+ CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP |
+ CHANNEL_OPTION_COMPRESS_RDP)
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Dynamic Virtual Channel
+ *
+ * Copyright 2010-2011 Vic Lee
+ *
+ * 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.
+ */
+
+#ifndef __DRDYNVC_MAIN_H
+#define __DRDYNVC_MAIN_H
+
+#include <freerdp/types.h>
+
+typedef struct drdynvc_plugin drdynvcPlugin;
+
+int drdynvc_write_data(drdynvcPlugin* plugin, uint32 ChannelId, uint8* data, uint32 data_size);
+int drdynvc_push_event(drdynvcPlugin* plugin, RDP_EVENT* event);
+
+#endif
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Dynamic Virtual Channel
+ *
+ * Copyright 2010-2011 Vic Lee
+ *
+ * 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.
+ */
+
+#ifndef __DRDYNVC_TYPES_H
+#define __DRDYNVC_TYPES_H
+
+#include "config.h"
+#include <freerdp/dvc.h>
+#include <freerdp/types.h>
+#include <freerdp/utils/debug.h>
+
+#ifdef WITH_DEBUG_DVC
+#define DEBUG_DVC(fmt, ...) DEBUG_CLASS(DVC, fmt, ## __VA_ARGS__)
+#else
+#define DEBUG_DVC(fmt, ...) DEBUG_NULL(fmt, ## __VA_ARGS__)
+#endif
+
+#endif
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Dynamic Virtual Channel Manager
+ *
+ * Copyright 2010-2011 Vic Lee
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <freerdp/utils/memory.h>
+#include <freerdp/utils/stream.h>
+#include <freerdp/utils/list.h>
+#include <freerdp/utils/load_plugin.h>
+
+#include "drdynvc_types.h"
+#include "dvcman.h"
+
+#define MAX_PLUGINS 10
+
+typedef struct _DVCMAN DVCMAN;
+struct _DVCMAN
+{
+ IWTSVirtualChannelManager iface;
+
+ drdynvcPlugin* drdynvc;
+
+ const char* plugin_names[MAX_PLUGINS];
+ IWTSPlugin* plugins[MAX_PLUGINS];
+ int num_plugins;
+
+ IWTSListener* listeners[MAX_PLUGINS];
+ int num_listeners;
+
+ LIST* channels;
+};
+
+typedef struct _DVCMAN_LISTENER DVCMAN_LISTENER;
+struct _DVCMAN_LISTENER
+{
+ IWTSListener iface;
+
+ DVCMAN* dvcman;
+ char* channel_name;
+ uint32 flags;
+ IWTSListenerCallback* listener_callback;
+};
+
+typedef struct _DVCMAN_ENTRY_POINTS DVCMAN_ENTRY_POINTS;
+struct _DVCMAN_ENTRY_POINTS
+{
+ IDRDYNVC_ENTRY_POINTS iface;
+
+ DVCMAN* dvcman;
+ RDP_PLUGIN_DATA* plugin_data;
+};
+
+typedef struct _DVCMAN_CHANNEL DVCMAN_CHANNEL;
+struct _DVCMAN_CHANNEL
+{
+ IWTSVirtualChannel iface;
+
+ DVCMAN* dvcman;
+ DVCMAN_CHANNEL* next;
+ uint32 channel_id;
+ IWTSVirtualChannelCallback* channel_callback;
+
+ STREAM* dvc_data;
+};
+
+static int dvcman_get_configuration(IWTSListener* pListener, void** ppPropertyBag)
+{
+ *ppPropertyBag = NULL;
+ return 1;
+}
+
+static int dvcman_create_listener(IWTSVirtualChannelManager* pChannelMgr,
+ const char* pszChannelName, uint32 ulFlags,
+ IWTSListenerCallback* pListenerCallback, IWTSListener** ppListener)
+{
+ DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
+ DVCMAN_LISTENER* listener;
+
+ if (dvcman->num_listeners < MAX_PLUGINS)
+ {
+ DEBUG_DVC("%d.%s.", dvcman->num_listeners, pszChannelName);
+ listener = xnew(DVCMAN_LISTENER);
+ listener->iface.GetConfiguration = dvcman_get_configuration;
+ listener->dvcman = dvcman;
+ listener->channel_name = xstrdup(pszChannelName);
+ listener->flags = ulFlags;
+ listener->listener_callback = pListenerCallback;
+
+ if (ppListener)
+ *ppListener = (IWTSListener*)listener;
+ dvcman->listeners[dvcman->num_listeners++] = (IWTSListener*)listener;
+ return 0;
+ }
+ else
+ {
+ DEBUG_WARN("Maximum DVC listener number reached.");
+ return 1;
+ }
+}
+
+static int dvcman_push_event(IWTSVirtualChannelManager* pChannelMgr, RDP_EVENT* pEvent)
+{
+ int error;
+ DVCMAN* dvcman = (DVCMAN*) pChannelMgr;
+
+ error = drdynvc_push_event(dvcman->drdynvc, pEvent);
+
+ if (error == 0)
+ {
+ DEBUG_DVC("event_type %d pushed.", pEvent->event_type);
+ }
+ else
+ {
+ DEBUG_WARN("event_type %d push failed.", pEvent->event_type);
+ }
+
+ return error;
+}
+
+static int dvcman_register_plugin(IDRDYNVC_ENTRY_POINTS* pEntryPoints, const char* name, IWTSPlugin* pPlugin)
+{
+ DVCMAN* dvcman = ((DVCMAN_ENTRY_POINTS*) pEntryPoints)->dvcman;
+
+ if (dvcman->num_plugins < MAX_PLUGINS)
+ {
+ DEBUG_DVC("num_plugins %d", dvcman->num_plugins);
+ dvcman->plugin_names[dvcman->num_plugins] = name;
+ dvcman->plugins[dvcman->num_plugins++] = pPlugin;
+ return 0;
+ }
+ else
+ {
+ DEBUG_WARN("Maximum DVC plugin number reached.");
+ return 1;
+ }
+}
+
+IWTSPlugin* dvcman_get_plugin(IDRDYNVC_ENTRY_POINTS* pEntryPoints, const char* name)
+{
+ int i;
+ DVCMAN* dvcman = ((DVCMAN_ENTRY_POINTS*) pEntryPoints)->dvcman;
+
+ for (i = 0; i < dvcman->num_plugins; i++)
+ {
+ if (dvcman->plugin_names[i] == name ||
+ strcmp(dvcman->plugin_names[i], name) == 0)
+ {
+ return dvcman->plugins[i];
+ }
+ }
+
+ return NULL;
+}
+
+RDP_PLUGIN_DATA* dvcman_get_plugin_data(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
+{
+ return ((DVCMAN_ENTRY_POINTS*) pEntryPoints)->plugin_data;
+}
+
+IWTSVirtualChannelManager* dvcman_new(drdynvcPlugin* plugin)
+{
+ DVCMAN* dvcman;
+
+ dvcman = xnew(DVCMAN);
+ dvcman->iface.CreateListener = dvcman_create_listener;
+ dvcman->iface.PushEvent = dvcman_push_event;
+ dvcman->drdynvc = plugin;
+ dvcman->channels = list_new();
+
+ return (IWTSVirtualChannelManager*) dvcman;
+}
+
+int dvcman_load_plugin(IWTSVirtualChannelManager* pChannelMgr, RDP_PLUGIN_DATA* data)
+{
+ DVCMAN_ENTRY_POINTS entryPoints;
+ PDVC_PLUGIN_ENTRY pDVCPluginEntry = NULL;
+
+ while (data && data->size > 0)
+ {
+ pDVCPluginEntry = freerdp_load_plugin((char*) data->data[0], "DVCPluginEntry");
+
+ if (pDVCPluginEntry != NULL)
+ {
+ entryPoints.iface.RegisterPlugin = dvcman_register_plugin;
+ entryPoints.iface.GetPlugin = dvcman_get_plugin;
+ entryPoints.iface.GetPluginData = dvcman_get_plugin_data;
+ entryPoints.dvcman = (DVCMAN*) pChannelMgr;
+ entryPoints.plugin_data = data;
+ pDVCPluginEntry((IDRDYNVC_ENTRY_POINTS*) &entryPoints);
+ }
+
+ data = (RDP_PLUGIN_DATA*)(((void*) data) + data->size);
+ }
+
+ return 0;
+}
+
+static void dvcman_channel_free(DVCMAN_CHANNEL* channel)
+{
+ if (channel->channel_callback)
+ channel->channel_callback->OnClose(channel->channel_callback);
+
+ xfree(channel);
+}
+
+void dvcman_free(IWTSVirtualChannelManager* pChannelMgr)
+{
+ int i;
+ IWTSPlugin* pPlugin;
+ DVCMAN_LISTENER* listener;
+ DVCMAN_CHANNEL* channel;
+ DVCMAN* dvcman = (DVCMAN*) pChannelMgr;
+
+ while ((channel = (DVCMAN_CHANNEL*) list_dequeue(dvcman->channels)) != NULL)
+ dvcman_channel_free(channel);
+
+ list_free(dvcman->channels);
+
+ for (i = 0; i < dvcman->num_listeners; i++)
+ {
+ listener = (DVCMAN_LISTENER*) dvcman->listeners[i];
+ xfree(listener->channel_name);
+ xfree(listener);
+ }
+
+ for (i = 0; i < dvcman->num_plugins; i++)
+ {
+ pPlugin = dvcman->plugins[i];
+
+ if (pPlugin->Terminated)
+ pPlugin->Terminated(pPlugin);
+ }
+
+ xfree(dvcman);
+}
+
+int dvcman_init(IWTSVirtualChannelManager* pChannelMgr)
+{
+ int i;
+ IWTSPlugin* pPlugin;
+ DVCMAN* dvcman = (DVCMAN*) pChannelMgr;
+
+ for (i = 0; i < dvcman->num_plugins; i++)
+ {
+ pPlugin = dvcman->plugins[i];
+
+ if (pPlugin->Initialize)
+ pPlugin->Initialize(pPlugin, pChannelMgr);
+ }
+
+ return 0;
+}
+
+static int dvcman_write_channel(IWTSVirtualChannel* pChannel, uint32 cbSize, uint8* pBuffer, void* pReserved)
+{
+ DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*) pChannel;
+
+ return drdynvc_write_data(channel->dvcman->drdynvc, channel->channel_id, pBuffer, cbSize);
+}
+
+static int dvcman_close_channel_iface(IWTSVirtualChannel* pChannel)
+{
+ DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*) pChannel;
+ DVCMAN* dvcman = channel->dvcman;
+
+ DEBUG_DVC("id=%d", channel->channel_id);
+
+ if (list_remove(dvcman->channels, channel) == NULL)
+ DEBUG_WARN("channel not found");
+
+ dvcman_channel_free(channel);
+
+ return 1;
+}
+
+int dvcman_create_channel(IWTSVirtualChannelManager* pChannelMgr, uint32 ChannelId, const char* ChannelName)
+{
+ int i;
+ int bAccept;
+ DVCMAN_LISTENER* listener;
+ DVCMAN_CHANNEL* channel;
+ IWTSVirtualChannelCallback* pCallback;
+ DVCMAN* dvcman = (DVCMAN*) pChannelMgr;
+
+ for (i = 0; i < dvcman->num_listeners; i++)
+ {
+ listener = (DVCMAN_LISTENER*)dvcman->listeners[i];
+
+ if (strcmp(listener->channel_name, ChannelName) == 0)
+ {
+ channel = xnew(DVCMAN_CHANNEL);
+ channel->iface.Write = dvcman_write_channel;
+ channel->iface.Close = dvcman_close_channel_iface;
+ channel->dvcman = dvcman;
+ channel->channel_id = ChannelId;
+
+ bAccept = 1;
+ pCallback = NULL;
+
+ if (listener->listener_callback->OnNewChannelConnection(listener->listener_callback,
+ (IWTSVirtualChannel*) channel, NULL, &bAccept, &pCallback) == 0 && bAccept == 1)
+ {
+ DEBUG_DVC("listener %s created new channel %d",
+ listener->channel_name, channel->channel_id);
+ channel->channel_callback = pCallback;
+ list_add(dvcman->channels, channel);
+
+ return 0;
+ }
+ else
+ {
+ DEBUG_WARN("channel rejected by plugin");
+ dvcman_channel_free(channel);
+ return 1;
+ }
+ }
+ }
+
+ return 1;
+}
+
+static DVCMAN_CHANNEL* dvcman_find_channel_by_id(IWTSVirtualChannelManager* pChannelMgr, uint32 ChannelId)
+{
+ LIST_ITEM* curr;
+ DVCMAN* dvcman = (DVCMAN*) pChannelMgr;
+
+ for (curr = dvcman->channels->head; curr; curr = curr->next)
+ {
+ if (((DVCMAN_CHANNEL*) curr->data)->channel_id == ChannelId)
+ {
+ return (DVCMAN_CHANNEL*)curr->data;
+ }
+ }
+
+ return NULL;
+}
+
+int dvcman_close_channel(IWTSVirtualChannelManager* pChannelMgr, uint32 ChannelId)
+{
+ DVCMAN_CHANNEL* channel;
+ IWTSVirtualChannel* ichannel;
+
+ channel = dvcman_find_channel_by_id(pChannelMgr, ChannelId);
+
+ if (channel == NULL)
+ {
+ DEBUG_WARN("ChannelId %d not found!", ChannelId);
+ return 1;
+ }
+
+ if (channel->dvc_data)
+ {
+ stream_free(channel->dvc_data);
+ channel->dvc_data = NULL;
+ }
+
+ DEBUG_DVC("dvcman_close_channel: channel %d closed", ChannelId);
+ ichannel = (IWTSVirtualChannel*)channel;
+ ichannel->Close(ichannel);
+
+ return 0;
+}
+
+int dvcman_receive_channel_data_first(IWTSVirtualChannelManager* pChannelMgr, uint32 ChannelId, uint32 length)
+{
+ DVCMAN_CHANNEL* channel;
+
+ channel = dvcman_find_channel_by_id(pChannelMgr, ChannelId);
+
+ if (channel == NULL)
+ {
+ DEBUG_WARN("ChannelId %d not found!", ChannelId);
+ return 1;
+ }
+
+ if (channel->dvc_data)
+ stream_free(channel->dvc_data);
+
+ channel->dvc_data = stream_new(length);
+
+ return 0;
+}
+
+int dvcman_receive_channel_data(IWTSVirtualChannelManager* pChannelMgr, uint32 ChannelId, uint8* data, uint32 data_size)
+{
+ int error = 0;
+ DVCMAN_CHANNEL* channel;
+
+ channel = dvcman_find_channel_by_id(pChannelMgr, ChannelId);
+
+ if (channel == NULL)
+ {
+ DEBUG_WARN("ChannelId %d not found!", ChannelId);
+ return 1;
+ }
+
+ if (channel->dvc_data)
+ {
+ /* Fragmented data */
+ if (stream_get_length(channel->dvc_data) + data_size > stream_get_size(channel->dvc_data))
+ {
+ DEBUG_WARN("data exceeding declared length!");
+ stream_free(channel->dvc_data);
+ channel->dvc_data = NULL;
+ return 1;
+ }
+
+ stream_write(channel->dvc_data, data, data_size);
+
+ if (stream_get_length(channel->dvc_data) >= stream_get_size(channel->dvc_data))
+ {
+ error = channel->channel_callback->OnDataReceived(channel->channel_callback,
+ stream_get_size(channel->dvc_data), stream_get_data(channel->dvc_data));
+ stream_free(channel->dvc_data);
+ channel->dvc_data = NULL;
+ }
+ }
+ else
+ {
+ error = channel->channel_callback->OnDataReceived(channel->channel_callback, data_size, data);
+ }
+
+ return error;
+}
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Dynamic Virtual Channel Manager
+ *
+ * Copyright 2010-2011 Vic Lee
+ *
+ * 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.
+ */
+
+#ifndef __DVCMAN_H
+#define __DVCMAN_H
+
+#include <freerdp/dvc.h>
+#include "drdynvc_main.h"
+
+IWTSVirtualChannelManager* dvcman_new(drdynvcPlugin* plugin);
+int dvcman_load_plugin(IWTSVirtualChannelManager* pChannelMgr, RDP_PLUGIN_DATA* data);
+void dvcman_free(IWTSVirtualChannelManager* pChannelMgr);
+int dvcman_init(IWTSVirtualChannelManager* pChannelMgr);
+int dvcman_create_channel(IWTSVirtualChannelManager* pChannelMgr, uint32 ChannelId, const char* ChannelName);
+int dvcman_close_channel(IWTSVirtualChannelManager* pChannelMgr, uint32 ChannelId);
+int dvcman_receive_channel_data_first(IWTSVirtualChannelManager* pChannelMgr, uint32 ChannelId, uint32 length);
+int dvcman_receive_channel_data(IWTSVirtualChannelManager* pChannelMgr, uint32 ChannelId, uint8* data, uint32 data_size);
+
+#endif
--- /dev/null
+# FreeRDP: A Remote Desktop Protocol Client
+# FreeRDP cmake build script
+#
+# Copyright 2011 O.S. Systems Software Ltda.
+# Copyright 2011 Otavio Salvador <otavio@ossystems.com.br>
+# Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.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(TSMF_SRCS
+ tsmf_audio.c
+ tsmf_audio.h
+ tsmf_codec.c
+ tsmf_codec.h
+ tsmf_constants.h
+ tsmf_decoder.c
+ tsmf_decoder.h
+ tsmf_ifman.c
+ tsmf_ifman.h
+ tsmf_main.c
+ tsmf_main.h
+ tsmf_media.c
+ tsmf_media.h
+ tsmf_types.h
+)
+
+include_directories(..)
+
+add_library(tsmf ${TSMF_SRCS})
+set_target_properties(tsmf PROPERTIES PREFIX "")
+
+target_link_libraries(tsmf freerdp-utils)
+
+install(TARGETS tsmf DESTINATION ${FREERDP_PLUGIN_PATH})
+
+if(WITH_FFMPEG)
+ add_subdirectory(ffmpeg)
+endif()
+
+if(WITH_ALSA)
+ add_subdirectory(alsa)
+endif()
+
+if(WITH_PULSEAUDIO)
+ add_subdirectory(pulse)
+endif()
+
--- /dev/null
+# FreeRDP: A Remote Desktop Protocol Client
+# FreeRDP cmake build script
+#
+# Copyright 2011 O.S. Systems Software Ltda.
+# Copyright 2011 Otavio Salvador <otavio@ossystems.com.br>
+# Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.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(TSMF_ALSA_SRCS
+ tsmf_alsa.c
+)
+
+include_directories(..)
+include_directories(${ALSA_INCLUDE_DIRS})
+
+add_library(tsmf_alsa ${TSMF_ALSA_SRCS})
+set_target_properties(tsmf_alsa PROPERTIES PREFIX "")
+
+target_link_libraries(tsmf_alsa freerdp-utils)
+target_link_libraries(tsmf_alsa ${ALSA_LIBRARIES})
+
+install(TARGETS tsmf_alsa DESTINATION ${FREERDP_PLUGIN_PATH})
+
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Video Redirection Virtual Channel - ALSA Audio Device
+ *
+ * Copyright 2010-2011 Vic Lee
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <alsa/asoundlib.h>
+#include <freerdp/types.h>
+#include <freerdp/utils/memory.h>
+#include <freerdp/utils/dsp.h>
+
+#include "tsmf_audio.h"
+
+typedef struct _TSMFALSAAudioDevice
+{
+ ITSMFAudioDevice iface;
+
+ char device[32];
+ snd_pcm_t* out_handle;
+ uint32 source_rate;
+ uint32 actual_rate;
+ uint32 source_channels;
+ uint32 actual_channels;
+ uint32 bytes_per_sample;
+} TSMFALSAAudioDevice;
+
+static boolean tsmf_alsa_open_device(TSMFALSAAudioDevice* alsa)
+{
+ int error;
+
+ error = snd_pcm_open(&alsa->out_handle, alsa->device, SND_PCM_STREAM_PLAYBACK, 0);
+ if (error < 0)
+ {
+ DEBUG_WARN("failed to open device %s", alsa->device);
+ return false;
+ }
+
+ DEBUG_DVC("open device %s", alsa->device);
+ return true;
+}
+
+static boolean tsmf_alsa_open(ITSMFAudioDevice* audio, const char* device)
+{
+ TSMFALSAAudioDevice* alsa = (TSMFALSAAudioDevice*) audio;
+
+ if (!device)
+ {
+ if (!alsa->device[0])
+ strcpy(alsa->device, "default");
+ }
+ else
+ {
+ strcpy(alsa->device, device);
+ }
+
+ return tsmf_alsa_open_device(alsa);
+}
+
+static boolean tsmf_alsa_set_format(ITSMFAudioDevice* audio,
+ uint32 sample_rate, uint32 channels, uint32 bits_per_sample)
+{
+ int error;
+ snd_pcm_uframes_t frames;
+ snd_pcm_hw_params_t* hw_params;
+ snd_pcm_sw_params_t* sw_params;
+ TSMFALSAAudioDevice* alsa = (TSMFALSAAudioDevice*) audio;
+
+ if (!alsa->out_handle)
+ return false;
+
+ snd_pcm_drop(alsa->out_handle);
+
+ alsa->actual_rate = alsa->source_rate = sample_rate;
+ alsa->actual_channels = alsa->source_channels = channels;
+ alsa->bytes_per_sample = bits_per_sample / 8;
+
+ error = snd_pcm_hw_params_malloc(&hw_params);
+ if (error < 0)
+ {
+ DEBUG_WARN("snd_pcm_hw_params_malloc failed");
+ return false;
+ }
+ snd_pcm_hw_params_any(alsa->out_handle, hw_params);
+ snd_pcm_hw_params_set_access(alsa->out_handle, hw_params,
+ SND_PCM_ACCESS_RW_INTERLEAVED);
+ snd_pcm_hw_params_set_format(alsa->out_handle, hw_params,
+ SND_PCM_FORMAT_S16_LE);
+ snd_pcm_hw_params_set_rate_near(alsa->out_handle, hw_params,
+ &alsa->actual_rate, NULL);
+ snd_pcm_hw_params_set_channels_near(alsa->out_handle, hw_params,
+ &alsa->actual_channels);
+ frames = sample_rate;
+ snd_pcm_hw_params_set_buffer_size_near(alsa->out_handle, hw_params,
+ &frames);
+ snd_pcm_hw_params(alsa->out_handle, hw_params);
+ snd_pcm_hw_params_free(hw_params);
+
+ error = snd_pcm_sw_params_malloc(&sw_params);
+ if (error < 0)
+ {
+ DEBUG_WARN("snd_pcm_sw_params_malloc");
+ return false;
+ }
+ snd_pcm_sw_params_current(alsa->out_handle, sw_params);
+ snd_pcm_sw_params_set_start_threshold(alsa->out_handle, sw_params,
+ frames / 2);
+ snd_pcm_sw_params(alsa->out_handle, sw_params);
+ snd_pcm_sw_params_free(sw_params);
+
+ snd_pcm_prepare(alsa->out_handle);
+
+ DEBUG_DVC("sample_rate %d channels %d bits_per_sample %d",
+ sample_rate, channels, bits_per_sample);
+ DEBUG_DVC("hardware buffer %d frames", (int)frames);
+ if ((alsa->actual_rate != alsa->source_rate) ||
+ (alsa->actual_channels != alsa->source_channels))
+ {
+ DEBUG_DVC("actual rate %d / channel %d is different "
+ "from source rate %d / channel %d, resampling required.",
+ alsa->actual_rate, alsa->actual_channels,
+ alsa->source_rate, alsa->source_channels);
+ }
+ return true;
+}
+
+static boolean tsmf_alsa_play(ITSMFAudioDevice* audio, uint8* data, uint32 data_size)
+{
+ int len;
+ int error;
+ int frames;
+ uint8* end;
+ uint8* src;
+ uint8* pindex;
+ int rbytes_per_frame;
+ int sbytes_per_frame;
+ uint8* resampled_data;
+ TSMFALSAAudioDevice* alsa = (TSMFALSAAudioDevice*) audio;
+
+ DEBUG_DVC("data_size %d", data_size);
+
+ if (alsa->out_handle)
+ {
+ sbytes_per_frame = alsa->source_channels * alsa->bytes_per_sample;
+ rbytes_per_frame = alsa->actual_channels * alsa->bytes_per_sample;
+
+ if ((alsa->source_rate == alsa->actual_rate) &&
+ (alsa->source_channels == alsa->actual_channels))
+ {
+ resampled_data = NULL;
+ src = data;
+ }
+ else
+ {
+ resampled_data = dsp_resample(data, alsa->bytes_per_sample,
+ alsa->source_channels, alsa->source_rate, data_size / sbytes_per_frame,
+ alsa->actual_channels, alsa->actual_rate, &frames);
+ DEBUG_DVC("resampled %d frames at %d to %d frames at %d",
+ data_size / sbytes_per_frame, alsa->source_rate, frames, alsa->actual_rate);
+ data_size = frames * rbytes_per_frame;
+ src = resampled_data;
+ }
+
+ pindex = src;
+ end = pindex + data_size;
+ while (pindex < end)
+ {
+ len = end - pindex;
+ frames = len / rbytes_per_frame;
+ error = snd_pcm_writei(alsa->out_handle, pindex, frames);
+ if (error == -EPIPE)
+ {
+ snd_pcm_recover(alsa->out_handle, error, 0);
+ error = 0;
+ }
+ else if (error < 0)
+ {
+ DEBUG_DVC("error len %d", error);
+ snd_pcm_close(alsa->out_handle);
+ alsa->out_handle = 0;
+ tsmf_alsa_open_device(alsa);
+ break;
+ }
+ DEBUG_DVC("%d frames played.", error);
+ if (error == 0)
+ break;
+ pindex += error * rbytes_per_frame;
+ }
+
+ if (resampled_data)
+ xfree(resampled_data);
+ }
+ xfree(data);
+
+ return true;
+}
+
+static uint64 tsmf_alsa_get_latency(ITSMFAudioDevice* audio)
+{
+ uint64 latency = 0;
+ snd_pcm_sframes_t frames = 0;
+ TSMFALSAAudioDevice* alsa = (TSMFALSAAudioDevice*) audio;
+
+ if (alsa->out_handle && alsa->actual_rate > 0 &&
+ snd_pcm_delay(alsa->out_handle, &frames) == 0 &&
+ frames > 0)
+ {
+ latency = ((uint64)frames) * 10000000LL / (uint64)alsa->actual_rate;
+ }
+ return latency;
+}
+
+static void tsmf_alsa_flush(ITSMFAudioDevice* audio)
+{
+}
+
+static void tsmf_alsa_free(ITSMFAudioDevice* audio)
+{
+ TSMFALSAAudioDevice* alsa = (TSMFALSAAudioDevice*) audio;
+
+ DEBUG_DVC("");
+
+ if (alsa->out_handle)
+ {
+ snd_pcm_drain(alsa->out_handle);
+ snd_pcm_close(alsa->out_handle);
+ }
+ xfree(alsa);
+}
+
+ITSMFAudioDevice* TSMFAudioDeviceEntry(void)
+{
+ TSMFALSAAudioDevice* alsa;
+
+ alsa = xnew(TSMFALSAAudioDevice);
+
+ alsa->iface.Open = tsmf_alsa_open;
+ alsa->iface.SetFormat = tsmf_alsa_set_format;
+ alsa->iface.Play = tsmf_alsa_play;
+ alsa->iface.GetLatency = tsmf_alsa_get_latency;
+ alsa->iface.Flush = tsmf_alsa_flush;
+ alsa->iface.Free = tsmf_alsa_free;
+
+ return (ITSMFAudioDevice*) alsa;
+}
+
--- /dev/null
+# FreeRDP: A Remote Desktop Protocol Client
+# FreeRDP cmake build script
+#
+# Copyright 2011 O.S. Systems Software Ltda.
+# Copyright 2011 Otavio Salvador <otavio@ossystems.com.br>
+# Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.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(TSMF_FFMPEG_SRCS
+ tsmf_ffmpeg.c
+)
+
+include_directories(..)
+include_directories(${FFMPEG_INCLUDE_DIRS})
+
+add_library(tsmf_ffmpeg ${TSMF_FFMPEG_SRCS})
+set_target_properties(tsmf_ffmpeg PROPERTIES PREFIX "")
+
+target_link_libraries(tsmf_ffmpeg freerdp-utils)
+target_link_libraries(tsmf_ffmpeg ${FFMPEG_LIBRARIES})
+
+install(TARGETS tsmf_ffmpeg DESTINATION ${FREERDP_PLUGIN_PATH})
+
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Video Redirection Virtual Channel - FFmpeg Decoder
+ *
+ * Copyright 2010-2011 Vic Lee
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <freerdp/utils/memory.h>
+#include <freerdp/utils/event.h>
+#include <freerdp/plugins/tsmf.h>
+#include <libavcodec/avcodec.h>
+
+#include "tsmf_constants.h"
+#include "tsmf_decoder.h"
+
+/* Compatibility with older FFmpeg */
+#if LIBAVUTIL_VERSION_MAJOR < 50
+#define AVMEDIA_TYPE_VIDEO 0
+#define AVMEDIA_TYPE_AUDIO 1
+#endif
+
+typedef struct _TSMFFFmpegDecoder
+{
+ ITSMFDecoder iface;
+
+ int media_type;
+ enum CodecID codec_id;
+ AVCodecContext* codec_context;
+ AVCodec* codec;
+ AVFrame* frame;
+ int prepared;
+
+ uint8* decoded_data;
+ uint32 decoded_size;
+ uint32 decoded_size_max;
+} TSMFFFmpegDecoder;
+
+static boolean tsmf_ffmpeg_init_context(ITSMFDecoder* decoder)
+{
+ TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*) decoder;
+
+ mdecoder->codec_context = avcodec_alloc_context();
+ if (!mdecoder->codec_context)
+ {
+ DEBUG_WARN("avcodec_alloc_context failed.");
+ return false;
+ }
+
+ return true;
+}
+
+static boolean tsmf_ffmpeg_init_video_stream(ITSMFDecoder* decoder, const TS_AM_MEDIA_TYPE* media_type)
+{
+ TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*) decoder;
+
+ mdecoder->codec_context->width = media_type->Width;
+ mdecoder->codec_context->height = media_type->Height;
+ mdecoder->codec_context->bit_rate = media_type->BitRate;
+ mdecoder->codec_context->time_base.den = media_type->SamplesPerSecond.Numerator;
+ mdecoder->codec_context->time_base.num = media_type->SamplesPerSecond.Denominator;
+
+ mdecoder->frame = avcodec_alloc_frame();
+
+ return true;
+}
+
+static boolean tsmf_ffmpeg_init_audio_stream(ITSMFDecoder* decoder, const TS_AM_MEDIA_TYPE* media_type)
+{
+ TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*) decoder;
+
+ mdecoder->codec_context->sample_rate = media_type->SamplesPerSecond.Numerator;
+ mdecoder->codec_context->bit_rate = media_type->BitRate;
+ mdecoder->codec_context->channels = media_type->Channels;
+ mdecoder->codec_context->block_align = media_type->BlockAlign;
+
+#ifdef AV_CPU_FLAG_SSE2
+ mdecoder->codec_context->dsp_mask = AV_CPU_FLAG_SSE2 | AV_CPU_FLAG_MMX2;
+#else
+#if LIBAVCODEC_VERSION_MAJOR < 53
+ mdecoder->codec_context->dsp_mask = FF_MM_SSE2 | FF_MM_MMXEXT;
+#else
+ mdecoder->codec_context->dsp_mask = FF_MM_SSE2 | FF_MM_MMX2;
+#endif
+#endif
+
+ return true;
+}
+
+static boolean tsmf_ffmpeg_init_stream(ITSMFDecoder* decoder, const TS_AM_MEDIA_TYPE* media_type)
+{
+ TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*) decoder;
+ uint32 size;
+ const uint8* s;
+ uint8* p;
+
+ mdecoder->codec = avcodec_find_decoder(mdecoder->codec_id);
+ if (!mdecoder->codec)
+ {
+ DEBUG_WARN("avcodec_find_decoder failed.");
+ return false;
+ }
+
+ mdecoder->codec_context->codec_id = mdecoder->codec_id;
+ mdecoder->codec_context->codec_type = mdecoder->media_type;
+
+ if (mdecoder->media_type == AVMEDIA_TYPE_VIDEO)
+ {
+ if (!tsmf_ffmpeg_init_video_stream(decoder, media_type))
+ return false;
+ }
+ else if (mdecoder->media_type == AVMEDIA_TYPE_AUDIO)
+ {
+ if (!tsmf_ffmpeg_init_audio_stream(decoder, media_type))
+ return false;
+ }
+
+ if (media_type->ExtraData)
+ {
+ if (media_type->SubType == TSMF_SUB_TYPE_AVC1 &&
+ media_type->FormatType == TSMF_FORMAT_TYPE_MPEG2VIDEOINFO)
+ {
+ /* The extradata format that FFmpeg uses is following CodecPrivate in Matroska.
+ See http://haali.su/mkv/codecs.pdf */
+ mdecoder->codec_context->extradata_size = media_type->ExtraDataSize + 8;
+ mdecoder->codec_context->extradata = xzalloc(mdecoder->codec_context->extradata_size);
+ p = mdecoder->codec_context->extradata;
+ *p++ = 1; /* Reserved? */
+ *p++ = media_type->ExtraData[8]; /* Profile */
+ *p++ = 0; /* Profile */
+ *p++ = media_type->ExtraData[12]; /* Level */
+ *p++ = 0xff; /* Flag? */
+ *p++ = 0xe0 | 0x01; /* Reserved | #sps */
+ s = media_type->ExtraData + 20;
+ size = ((uint32)(*s)) * 256 + ((uint32)(*(s + 1)));
+ memcpy(p, s, size + 2);
+ s += size + 2;
+ p += size + 2;
+ *p++ = 1; /* #pps */
+ size = ((uint32)(*s)) * 256 + ((uint32)(*(s + 1)));
+ memcpy(p, s, size + 2);
+ }
+ else
+ {
+ /* Add a padding to avoid invalid memory read in some codec */
+ mdecoder->codec_context->extradata_size = media_type->ExtraDataSize + 8;
+ mdecoder->codec_context->extradata = xzalloc(mdecoder->codec_context->extradata_size);
+ memcpy(mdecoder->codec_context->extradata, media_type->ExtraData, media_type->ExtraDataSize);
+ memset(mdecoder->codec_context->extradata + media_type->ExtraDataSize, 0, 8);
+ }
+ }
+
+ if (mdecoder->codec->capabilities & CODEC_CAP_TRUNCATED)
+ mdecoder->codec_context->flags |= CODEC_FLAG_TRUNCATED;
+
+ return true;
+}
+
+static boolean tsmf_ffmpeg_prepare(ITSMFDecoder* decoder)
+{
+ TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*) decoder;
+
+ if (avcodec_open(mdecoder->codec_context, mdecoder->codec) < 0)
+ {
+ DEBUG_WARN("avcodec_open failed.");
+ return false;
+ }
+
+ mdecoder->prepared = 1;
+
+ return true;
+}
+
+static boolean tsmf_ffmpeg_set_format(ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* media_type)
+{
+ TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*) decoder;
+
+ switch (media_type->MajorType)
+ {
+ case TSMF_MAJOR_TYPE_VIDEO:
+ mdecoder->media_type = AVMEDIA_TYPE_VIDEO;
+ break;
+ case TSMF_MAJOR_TYPE_AUDIO:
+ mdecoder->media_type = AVMEDIA_TYPE_AUDIO;
+ break;
+ default:
+ return false;
+ }
+ switch (media_type->SubType)
+ {
+ case TSMF_SUB_TYPE_WVC1:
+ mdecoder->codec_id = CODEC_ID_VC1;
+ break;
+ case TSMF_SUB_TYPE_WMA2:
+ mdecoder->codec_id = CODEC_ID_WMAV2;
+ break;
+ case TSMF_SUB_TYPE_WMA9:
+ mdecoder->codec_id = CODEC_ID_WMAPRO;
+ break;
+ case TSMF_SUB_TYPE_MP3:
+ mdecoder->codec_id = CODEC_ID_MP3;
+ break;
+ case TSMF_SUB_TYPE_MP2A:
+ mdecoder->codec_id = CODEC_ID_MP2;
+ break;
+ case TSMF_SUB_TYPE_MP2V:
+ mdecoder->codec_id = CODEC_ID_MPEG2VIDEO;
+ break;
+ case TSMF_SUB_TYPE_WMV3:
+ mdecoder->codec_id = CODEC_ID_WMV3;
+ break;
+ case TSMF_SUB_TYPE_AAC:
+ mdecoder->codec_id = CODEC_ID_AAC;
+ /* For AAC the pFormat is a HEAACWAVEINFO struct, and the codec data
+ is at the end of it. See
+ http://msdn.microsoft.com/en-us/library/dd757806.aspx */
+ if (media_type->ExtraData)
+ {
+ media_type->ExtraData += 12;
+ media_type->ExtraDataSize -= 12;
+ }
+ break;
+ case TSMF_SUB_TYPE_H264:
+ case TSMF_SUB_TYPE_AVC1:
+ mdecoder->codec_id = CODEC_ID_H264;
+ break;
+ case TSMF_SUB_TYPE_AC3:
+ mdecoder->codec_id = CODEC_ID_AC3;
+ break;
+ default:
+ return false;
+ }
+
+ if (!tsmf_ffmpeg_init_context(decoder))
+ return false;
+ if (!tsmf_ffmpeg_init_stream(decoder, media_type))
+ return false;
+ if (!tsmf_ffmpeg_prepare(decoder))
+ return false;
+
+ return true;
+}
+
+static boolean tsmf_ffmpeg_decode_video(ITSMFDecoder* decoder, const uint8* data, uint32 data_size, uint32 extensions)
+{
+ TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*) decoder;
+ int decoded;
+ int len;
+ AVFrame* frame;
+ boolean ret = true;
+
+#if LIBAVCODEC_VERSION_MAJOR < 52 || (LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR <= 20)
+ len = avcodec_decode_video(mdecoder->codec_context, mdecoder->frame, &decoded, data, data_size);
+#else
+ {
+ AVPacket pkt;
+ av_init_packet(&pkt);
+ pkt.data = (uint8*) data;
+ pkt.size = data_size;
+ if (extensions & TSMM_SAMPLE_EXT_CLEANPOINT)
+ pkt.flags |= AV_PKT_FLAG_KEY;
+ len = avcodec_decode_video2(mdecoder->codec_context, mdecoder->frame, &decoded, &pkt);
+ }
+#endif
+
+ if (len < 0)
+ {
+ DEBUG_WARN("data_size %d, avcodec_decode_video failed (%d)", data_size, len);
+ ret = false;
+ }
+ else if (!decoded)
+ {
+ DEBUG_WARN("data_size %d, no frame is decoded.", data_size);
+ ret = false;
+ }
+ else
+ {
+ DEBUG_DVC("linesize[0] %d linesize[1] %d linesize[2] %d linesize[3] %d "
+ "pix_fmt %d width %d height %d",
+ mdecoder->frame->linesize[0], mdecoder->frame->linesize[1],
+ mdecoder->frame->linesize[2], mdecoder->frame->linesize[3],
+ mdecoder->codec_context->pix_fmt,
+ mdecoder->codec_context->width, mdecoder->codec_context->height);
+
+ mdecoder->decoded_size = avpicture_get_size(mdecoder->codec_context->pix_fmt,
+ mdecoder->codec_context->width, mdecoder->codec_context->height);
+ mdecoder->decoded_data = xzalloc(mdecoder->decoded_size);
+ frame = avcodec_alloc_frame();
+ avpicture_fill((AVPicture *) frame, mdecoder->decoded_data,
+ mdecoder->codec_context->pix_fmt,
+ mdecoder->codec_context->width, mdecoder->codec_context->height);
+
+ av_picture_copy((AVPicture *) frame, (AVPicture *) mdecoder->frame,
+ mdecoder->codec_context->pix_fmt,
+ mdecoder->codec_context->width, mdecoder->codec_context->height);
+
+ av_free(frame);
+ }
+
+ return ret;
+}
+
+static boolean tsmf_ffmpeg_decode_audio(ITSMFDecoder* decoder, const uint8* data, uint32 data_size, uint32 extensions)
+{
+ TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*) decoder;
+ int len;
+ int frame_size;
+ uint32 src_size;
+ const uint8* src;
+ uint8* dst;
+ int dst_offset;
+
+#if 0
+ LLOGLN(0, ("tsmf_ffmpeg_decode_audio: data_size %d", data_size));
+ int i;
+ for (i = 0; i < data_size; i++)
+ {
+ LLOG(0, ("%02X ", data[i]));
+ if (i % 16 == 15)
+ LLOG(0, ("\n"));
+ }
+ LLOG(0, ("\n"));
+#endif
+
+ if (mdecoder->decoded_size_max == 0)
+ mdecoder->decoded_size_max = AVCODEC_MAX_AUDIO_FRAME_SIZE + 16;
+ mdecoder->decoded_data = xzalloc(mdecoder->decoded_size_max);
+ /* align the memory for SSE2 needs */
+ dst = (uint8*) (((uintptr_t)mdecoder->decoded_data + 15) & ~ 0x0F);
+ dst_offset = dst - mdecoder->decoded_data;
+ src = data;
+ src_size = data_size;
+
+ while (src_size > 0)
+ {
+ /* Ensure enough space for decoding */
+ if (mdecoder->decoded_size_max - mdecoder->decoded_size < AVCODEC_MAX_AUDIO_FRAME_SIZE)
+ {
+ mdecoder->decoded_size_max = mdecoder->decoded_size_max * 2 + 16;
+ mdecoder->decoded_data = xrealloc(mdecoder->decoded_data, mdecoder->decoded_size_max);
+ dst = (uint8*) (((uintptr_t)mdecoder->decoded_data + 15) & ~ 0x0F);
+ if (dst - mdecoder->decoded_data != dst_offset)
+ {
+ /* re-align the memory if the alignment has changed after realloc */
+ memmove(dst, mdecoder->decoded_data + dst_offset, mdecoder->decoded_size);
+ dst_offset = dst - mdecoder->decoded_data;
+ }
+ dst += mdecoder->decoded_size;
+ }
+ frame_size = mdecoder->decoded_size_max - mdecoder->decoded_size;
+#if LIBAVCODEC_VERSION_MAJOR < 52 || (LIBAVCODEC_VERSION_MAJOR == 52 && LIBAVCODEC_VERSION_MINOR <= 20)
+ len = avcodec_decode_audio2(mdecoder->codec_context,
+ (int16_t*) dst, &frame_size,
+ src, src_size);
+#else
+ {
+ AVPacket pkt;
+ av_init_packet(&pkt);
+ pkt.data = (uint8*) src;
+ pkt.size = src_size;
+ len = avcodec_decode_audio3(mdecoder->codec_context,
+ (int16_t*) dst, &frame_size, &pkt);
+ }
+#endif
+ if (len <= 0 || frame_size <= 0)
+ {
+ DEBUG_WARN("error decoding");
+ break;
+ }
+ src += len;
+ src_size -= len;
+ mdecoder->decoded_size += frame_size;
+ dst += frame_size;
+ }
+
+ if (mdecoder->decoded_size == 0)
+ {
+ xfree(mdecoder->decoded_data);
+ mdecoder->decoded_data = NULL;
+ }
+ else if (dst_offset)
+ {
+ /* move the aligned decoded data to original place */
+ memmove(mdecoder->decoded_data, mdecoder->decoded_data + dst_offset, mdecoder->decoded_size);
+ }
+
+ DEBUG_DVC("data_size %d decoded_size %d",
+ data_size, mdecoder->decoded_size);
+
+ return true;
+}
+
+static boolean tsmf_ffmpeg_decode(ITSMFDecoder* decoder, const uint8* data, uint32 data_size, uint32 extensions)
+{
+ TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*) decoder;
+
+ if (mdecoder->decoded_data)
+ {
+ xfree(mdecoder->decoded_data);
+ mdecoder->decoded_data = NULL;
+ }
+ mdecoder->decoded_size = 0;
+
+ switch (mdecoder->media_type)
+ {
+ case AVMEDIA_TYPE_VIDEO:
+ return tsmf_ffmpeg_decode_video(decoder, data, data_size, extensions);
+ case AVMEDIA_TYPE_AUDIO:
+ return tsmf_ffmpeg_decode_audio(decoder, data, data_size, extensions);
+ default:
+ DEBUG_WARN("unknown media type.");
+ return false;
+ }
+}
+
+static uint8* tsmf_ffmpeg_get_decoded_data(ITSMFDecoder* decoder, uint32* size)
+{
+ TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*) decoder;
+ uint8* buf;
+
+ *size = mdecoder->decoded_size;
+ buf = mdecoder->decoded_data;
+ mdecoder->decoded_data = NULL;
+ mdecoder->decoded_size = 0;
+ return buf;
+}
+
+static uint32 tsmf_ffmpeg_get_decoded_format(ITSMFDecoder* decoder)
+{
+ TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*) decoder;
+
+ switch (mdecoder->codec_context->pix_fmt)
+ {
+ case PIX_FMT_YUV420P:
+ return RDP_PIXFMT_I420;
+
+ default:
+ DEBUG_WARN("unsupported pixel format %u",
+ mdecoder->codec_context->pix_fmt);
+ return (uint32) -1;
+ }
+}
+
+static boolean tsmf_ffmpeg_get_decoded_dimension(ITSMFDecoder* decoder, uint32* width, uint32* height)
+{
+ TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*) decoder;
+
+ if (mdecoder->codec_context->width > 0 && mdecoder->codec_context->height > 0)
+ {
+ *width = mdecoder->codec_context->width;
+ *height = mdecoder->codec_context->height;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+static void tsmf_ffmpeg_free(ITSMFDecoder* decoder)
+{
+ TSMFFFmpegDecoder* mdecoder = (TSMFFFmpegDecoder*) decoder;
+
+ if (mdecoder->frame)
+ av_free(mdecoder->frame);
+ if (mdecoder->decoded_data)
+ xfree(mdecoder->decoded_data);
+ if (mdecoder->codec_context)
+ {
+ if (mdecoder->prepared)
+ avcodec_close(mdecoder->codec_context);
+ if (mdecoder->codec_context->extradata)
+ xfree(mdecoder->codec_context->extradata);
+ av_free(mdecoder->codec_context);
+ }
+ xfree(decoder);
+}
+
+static boolean initialized = false;
+
+ITSMFDecoder*
+TSMFDecoderEntry(void)
+{
+ TSMFFFmpegDecoder * decoder;
+
+ if (!initialized)
+ {
+ avcodec_init();
+ avcodec_register_all();
+ initialized = true;
+ }
+
+ decoder = xnew(TSMFFFmpegDecoder);
+
+ decoder->iface.SetFormat = tsmf_ffmpeg_set_format;
+ decoder->iface.Decode = tsmf_ffmpeg_decode;
+ decoder->iface.GetDecodedData = tsmf_ffmpeg_get_decoded_data;
+ decoder->iface.GetDecodedFormat = tsmf_ffmpeg_get_decoded_format;
+ decoder->iface.GetDecodedDimension = tsmf_ffmpeg_get_decoded_dimension;
+ decoder->iface.Free = tsmf_ffmpeg_free;
+
+ return (ITSMFDecoder*) decoder;
+}
+
--- /dev/null
+# FreeRDP: A Remote Desktop Protocol Client
+# FreeRDP cmake build script
+#
+# Copyright 2011 O.S. Systems Software Ltda.
+# Copyright 2011 Otavio Salvador <otavio@ossystems.com.br>
+# Copyright 2011 Marc-Andre Moreau <marcandre.moreau@gmail.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(TSMF_PULSE_SRCS
+ tsmf_pulse.c
+)
+
+include_directories(..)
+include_directories(${PULSE_INCLUDE_DIRS})
+
+add_library(tsmf_pulse ${TSMF_PULSE_SRCS})
+set_target_properties(tsmf_pulse PROPERTIES PREFIX "")
+
+target_link_libraries(tsmf_pulse freerdp-utils)
+target_link_libraries(tsmf_pulse ${PULSE_LIBRARIES})
+
+install(TARGETS tsmf_pulse DESTINATION ${FREERDP_PLUGIN_PATH})
+
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Video Redirection Virtual Channel - PulseAudio Device
+ *
+ * Copyright 2010-2011 Vic Lee
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pulse/pulseaudio.h>
+#include <freerdp/utils/memory.h>
+
+#include "tsmf_audio.h"
+
+typedef struct _TSMFPulseAudioDevice
+{
+ ITSMFAudioDevice iface;
+
+ char device[32];
+ pa_threaded_mainloop* mainloop;
+ pa_context* context;
+ pa_sample_spec sample_spec;
+ pa_stream* stream;
+} TSMFPulseAudioDevice;
+
+static void tsmf_pulse_context_state_callback(pa_context* context, void* userdata)
+{
+ TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) userdata;
+ pa_context_state_t state;
+
+ state = pa_context_get_state(context);
+ switch (state)
+ {
+ case PA_CONTEXT_READY:
+ DEBUG_DVC("PA_CONTEXT_READY");
+ pa_threaded_mainloop_signal(pulse->mainloop, 0);
+ break;
+
+ case PA_CONTEXT_FAILED:
+ case PA_CONTEXT_TERMINATED:
+ DEBUG_DVC("state %d", (int)state);
+ pa_threaded_mainloop_signal(pulse->mainloop, 0);
+ break;
+
+ default:
+ DEBUG_DVC("state %d", (int)state);
+ break;
+ }
+}
+
+static boolean tsmf_pulse_connect(TSMFPulseAudioDevice* pulse)
+{
+ pa_context_state_t state;
+
+ if (!pulse->context)
+ return false;
+
+ if (pa_context_connect(pulse->context, NULL, 0, NULL))
+ {
+ DEBUG_WARN("pa_context_connect failed (%d)",
+ pa_context_errno(pulse->context));
+ return false;
+ }
+ pa_threaded_mainloop_lock(pulse->mainloop);
+ if (pa_threaded_mainloop_start(pulse->mainloop) < 0)
+ {
+ pa_threaded_mainloop_unlock(pulse->mainloop);
+ DEBUG_WARN("pa_threaded_mainloop_start failed (%d)",
+ pa_context_errno(pulse->context));
+ return false;
+ }
+ for (;;)
+ {
+ state = pa_context_get_state(pulse->context);
+ if (state == PA_CONTEXT_READY)
+ break;
+ if (!PA_CONTEXT_IS_GOOD(state))
+ {
+ DEBUG_DVC("bad context state (%d)",
+ pa_context_errno(pulse->context));
+ break;
+ }
+ pa_threaded_mainloop_wait(pulse->mainloop);
+ }
+ pa_threaded_mainloop_unlock(pulse->mainloop);
+ if (state == PA_CONTEXT_READY)
+ {
+ DEBUG_DVC("connected");
+ return true;
+ }
+ else
+ {
+ pa_context_disconnect(pulse->context);
+ return false;
+ }
+}
+
+static boolean tsmf_pulse_open(ITSMFAudioDevice* audio, const char* device)
+{
+ TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
+
+ if (device)
+ {
+ strcpy(pulse->device, device);
+ }
+
+ pulse->mainloop = pa_threaded_mainloop_new();
+ if (!pulse->mainloop)
+ {
+ DEBUG_WARN("pa_threaded_mainloop_new failed");
+ return false;
+ }
+ pulse->context = pa_context_new(pa_threaded_mainloop_get_api(pulse->mainloop), "freerdp");
+ if (!pulse->context)
+ {
+ DEBUG_WARN("pa_context_new failed");
+ return false;
+ }
+ pa_context_set_state_callback(pulse->context, tsmf_pulse_context_state_callback, pulse);
+ if (tsmf_pulse_connect(pulse))
+ {
+ DEBUG_WARN("tsmf_pulse_connect failed");
+ return false;
+ }
+
+ DEBUG_DVC("open device %s", pulse->device);
+ return true;
+}
+
+static void tsmf_pulse_stream_success_callback(pa_stream* stream, int success, void* userdata)
+{
+ TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) userdata;
+
+ pa_threaded_mainloop_signal(pulse->mainloop, 0);
+}
+
+static void tsmf_pulse_wait_for_operation(TSMFPulseAudioDevice* pulse, pa_operation* operation)
+{
+ if (operation == NULL)
+ return;
+ while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
+ {
+ pa_threaded_mainloop_wait(pulse->mainloop);
+ }
+ pa_operation_unref(operation);
+}
+
+static void tsmf_pulse_stream_state_callback(pa_stream* stream, void* userdata)
+{
+ TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) userdata;
+ pa_stream_state_t state;
+
+ state = pa_stream_get_state(stream);
+ switch (state)
+ {
+ case PA_STREAM_READY:
+ DEBUG_DVC("PA_STREAM_READY");
+ pa_threaded_mainloop_signal (pulse->mainloop, 0);
+ break;
+
+ case PA_STREAM_FAILED:
+ case PA_STREAM_TERMINATED:
+ DEBUG_DVC("state %d", (int)state);
+ pa_threaded_mainloop_signal (pulse->mainloop, 0);
+ break;
+
+ default:
+ DEBUG_DVC("state %d", (int)state);
+ break;
+ }
+}
+
+static void tsmf_pulse_stream_request_callback(pa_stream* stream, size_t length, void* userdata)
+{
+ TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) userdata;
+
+ DEBUG_DVC("%d", (int) length);
+
+ pa_threaded_mainloop_signal(pulse->mainloop, 0);
+}
+
+static boolean tsmf_pulse_close_stream(TSMFPulseAudioDevice* pulse)
+{
+ if (!pulse->context || !pulse->stream)
+ return false;
+
+ DEBUG_DVC("");
+
+ pa_threaded_mainloop_lock(pulse->mainloop);
+ pa_stream_set_write_callback(pulse->stream, NULL, NULL);
+ tsmf_pulse_wait_for_operation(pulse,
+ pa_stream_drain(pulse->stream, tsmf_pulse_stream_success_callback, pulse));
+ pa_stream_disconnect(pulse->stream);
+ pa_stream_unref(pulse->stream);
+ pulse->stream = NULL;
+ pa_threaded_mainloop_unlock(pulse->mainloop);
+
+ return true;
+}
+
+static boolean tsmf_pulse_open_stream(TSMFPulseAudioDevice* pulse)
+{
+ pa_stream_state_t state;
+ pa_buffer_attr buffer_attr = { 0 };
+
+ if (!pulse->context)
+ return false;
+
+ DEBUG_DVC("");
+
+ pa_threaded_mainloop_lock(pulse->mainloop);
+ pulse->stream = pa_stream_new(pulse->context, "freerdp",
+ &pulse->sample_spec, NULL);
+ if (!pulse->stream)
+ {
+ pa_threaded_mainloop_unlock(pulse->mainloop);
+ DEBUG_WARN("pa_stream_new failed (%d)",
+ pa_context_errno(pulse->context));
+ return false;
+ }
+ pa_stream_set_state_callback(pulse->stream,
+ tsmf_pulse_stream_state_callback, pulse);
+ pa_stream_set_write_callback(pulse->stream,
+ tsmf_pulse_stream_request_callback, pulse);
+ buffer_attr.maxlength = pa_usec_to_bytes(500000, &pulse->sample_spec);
+ buffer_attr.tlength = pa_usec_to_bytes(250000, &pulse->sample_spec);
+ buffer_attr.prebuf = (uint32_t) -1;
+ buffer_attr.minreq = (uint32_t) -1;
+ buffer_attr.fragsize = (uint32_t) -1;
+ if (pa_stream_connect_playback(pulse->stream,
+ pulse->device[0] ? pulse->device : NULL, &buffer_attr,
+ PA_STREAM_ADJUST_LATENCY | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_AUTO_TIMING_UPDATE,
+ NULL, NULL) < 0)
+ {
+ pa_threaded_mainloop_unlock(pulse->mainloop);
+ DEBUG_WARN("pa_stream_connect_playback failed (%d)",
+ pa_context_errno(pulse->context));
+ return false;
+ }
+
+ for (;;)
+ {
+ state = pa_stream_get_state(pulse->stream);
+ if (state == PA_STREAM_READY)
+ break;
+ if (!PA_STREAM_IS_GOOD(state))
+ {
+ DEBUG_WARN("bad stream state (%d)",
+ pa_context_errno(pulse->context));
+ break;
+ }
+ pa_threaded_mainloop_wait(pulse->mainloop);
+ }
+ pa_threaded_mainloop_unlock(pulse->mainloop);
+ if (state == PA_STREAM_READY)
+ {
+ DEBUG_DVC("connected");
+ return true;
+ }
+ else
+ {
+ tsmf_pulse_close_stream(pulse);
+ return false;
+ }
+}
+
+static boolean tsmf_pulse_set_format(ITSMFAudioDevice* audio,
+ uint32 sample_rate, uint32 channels, uint32 bits_per_sample)
+{
+ TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
+
+ DEBUG_DVC("sample_rate %d channels %d bits_per_sample %d",
+ sample_rate, channels, bits_per_sample);
+
+ pulse->sample_spec.rate = sample_rate;
+ pulse->sample_spec.channels = channels;
+ pulse->sample_spec.format = PA_SAMPLE_S16LE;
+
+ return tsmf_pulse_open_stream(pulse);
+}
+
+static boolean tsmf_pulse_play(ITSMFAudioDevice* audio, uint8* data, uint32 data_size)
+{
+ TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
+ uint8* src;
+ int len;
+ int ret;
+
+ DEBUG_DVC("data_size %d", data_size);
+
+ if (pulse->stream)
+ {
+ pa_threaded_mainloop_lock(pulse->mainloop);
+
+ src = data;
+ while (data_size > 0)
+ {
+ while ((len = pa_stream_writable_size(pulse->stream)) == 0)
+ {
+ DEBUG_DVC("waiting");
+ pa_threaded_mainloop_wait(pulse->mainloop);
+ }
+ if (len < 0)
+ break;
+ if (len > data_size)
+ len = data_size;
+ ret = pa_stream_write(pulse->stream, src, len, NULL, 0LL, PA_SEEK_RELATIVE);
+ if (ret < 0)
+ {
+ DEBUG_DVC("pa_stream_write failed (%d)",
+ pa_context_errno(pulse->context));
+ break;
+ }
+ src += len;
+ data_size -= len;
+ }
+
+ pa_threaded_mainloop_unlock(pulse->mainloop);
+ }
+ xfree(data);
+
+ return true;
+}
+
+static uint64 tsmf_pulse_get_latency(ITSMFAudioDevice* audio)
+{
+ pa_usec_t usec;
+ uint64 latency = 0;
+ TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
+
+ if (pulse->stream && pa_stream_get_latency(pulse->stream, &usec, NULL) == 0)
+ {
+ latency = ((uint64)usec) * 10LL;
+ }
+ return latency;
+}
+
+static void tsmf_pulse_flush(ITSMFAudioDevice* audio)
+{
+ TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
+
+ pa_threaded_mainloop_lock(pulse->mainloop);
+ tsmf_pulse_wait_for_operation(pulse,
+ pa_stream_flush(pulse->stream, tsmf_pulse_stream_success_callback, pulse));
+ pa_threaded_mainloop_unlock(pulse->mainloop);
+}
+
+static void tsmf_pulse_free(ITSMFAudioDevice* audio)
+{
+ TSMFPulseAudioDevice* pulse = (TSMFPulseAudioDevice*) audio;
+
+ DEBUG_DVC("");
+
+ tsmf_pulse_close_stream(pulse);
+ if (pulse->mainloop)
+ {
+ pa_threaded_mainloop_stop(pulse->mainloop);
+ }
+ if (pulse->context)
+ {
+ pa_context_disconnect(pulse->context);
+ pa_context_unref(pulse->context);
+ pulse->context = NULL;
+ }
+ if (pulse->mainloop)
+ {
+ pa_threaded_mainloop_free(pulse->mainloop);
+ pulse->mainloop = NULL;
+ }
+ xfree(pulse);
+}
+
+ITSMFAudioDevice* TSMFAudioDeviceEntry(void)
+{
+ TSMFPulseAudioDevice* pulse;
+
+ pulse = xnew(TSMFPulseAudioDevice);
+
+ pulse->iface.Open = tsmf_pulse_open;
+ pulse->iface.SetFormat = tsmf_pulse_set_format;
+ pulse->iface.Play = tsmf_pulse_play;
+ pulse->iface.GetLatency = tsmf_pulse_get_latency;
+ pulse->iface.Flush = tsmf_pulse_flush;
+ pulse->iface.Free = tsmf_pulse_free;
+
+ return (ITSMFAudioDevice*) pulse;
+}
+
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Video Redirection Virtual Channel - Audio Device Manager
+ *
+ * Copyright 2010-2011 Vic Lee
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <freerdp/utils/memory.h>
+#include <freerdp/utils/load_plugin.h>
+
+#include "tsmf_audio.h"
+
+static ITSMFAudioDevice* tsmf_load_audio_device_by_name(const char* name, const char* device)
+{
+ ITSMFAudioDevice* audio;
+ TSMF_AUDIO_DEVICE_ENTRY entry;
+ char* fullname;
+
+ if (strrchr(name, '.') != NULL)
+ entry = (TSMF_AUDIO_DEVICE_ENTRY) freerdp_load_plugin(name, TSMF_AUDIO_DEVICE_EXPORT_FUNC_NAME);
+ else
+ {
+ fullname = xzalloc(strlen(name) + 6);
+ strcpy(fullname, "tsmf_");
+ strcat(fullname, name);
+ entry = (TSMF_AUDIO_DEVICE_ENTRY) freerdp_load_plugin(fullname, TSMF_AUDIO_DEVICE_EXPORT_FUNC_NAME);
+ xfree(fullname);
+ }
+ if (entry == NULL)
+ {
+ return NULL;
+ }
+
+ audio = entry();
+ if (audio == NULL)
+ {
+ DEBUG_WARN("failed to call export function in %s", name);
+ return NULL;
+ }
+ if (!audio->Open(audio, device))
+ {
+ audio->Free(audio);
+ audio = NULL;
+ }
+ return audio;
+}
+
+ITSMFAudioDevice* tsmf_load_audio_device(const char* name, const char* device)
+{
+ ITSMFAudioDevice* audio;
+
+ if (name)
+ {
+ audio = tsmf_load_audio_device_by_name(name, device);
+ }
+ else
+ {
+ audio = tsmf_load_audio_device_by_name("pulse", device);
+ if (!audio)
+ audio = tsmf_load_audio_device_by_name("alsa", device);
+ }
+
+ return audio;
+}
+
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Video Redirection Virtual Channel - Audio Device Manager
+ *
+ * Copyright 2010-2011 Vic Lee
+ *
+ * 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.
+ */
+
+#ifndef __TSMF_AUDIO_H
+#define __TSMF_AUDIO_H
+
+#include "drdynvc_types.h"
+
+typedef struct _ITSMFAudioDevice ITSMFAudioDevice;
+
+struct _ITSMFAudioDevice
+{
+ /* Open the audio device. */
+ boolean (*Open) (ITSMFAudioDevice* audio, const char* device);
+ /* Set the audio data format. */
+ boolean (*SetFormat) (ITSMFAudioDevice* audio, uint32 sample_rate, uint32 channels, uint32 bits_per_sample);
+ /* Play audio data. */
+ boolean (*Play) (ITSMFAudioDevice* audio, uint8* data, uint32 data_size);
+ /* Get the latency of the last written sample, in 100ns */
+ uint64 (*GetLatency) (ITSMFAudioDevice* audio);
+ /* Flush queued audio data */
+ void (*Flush) (ITSMFAudioDevice* audio);
+ /* Free the audio device */
+ void (*Free) (ITSMFAudioDevice* audio);
+};
+
+#define TSMF_AUDIO_DEVICE_EXPORT_FUNC_NAME "TSMFAudioDeviceEntry"
+typedef ITSMFAudioDevice* (*TSMF_AUDIO_DEVICE_ENTRY) (void);
+
+ITSMFAudioDevice* tsmf_load_audio_device(const char* name, const char* device);
+
+#endif
+
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Video Redirection Virtual Channel - Codec
+ *
+ * Copyright 2010-2011 Vic Lee
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <freerdp/utils/stream.h>
+#include <freerdp/utils/hexdump.h>
+
+#include "drdynvc_types.h"
+#include "tsmf_constants.h"
+#include "tsmf_types.h"
+
+#include "tsmf_codec.h"
+
+typedef struct _TSMFMediaTypeMap
+{
+ uint8 guid[16];
+ const char* name;
+ int type;
+} TSMFMediaTypeMap;
+
+static const TSMFMediaTypeMap tsmf_major_type_map[] =
+{
+ /* 73646976-0000-0010-8000-00AA00389B71 */
+ {
+ { 0x76, 0x69, 0x64, 0x73, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 },
+ "MEDIATYPE_Video",
+ TSMF_MAJOR_TYPE_VIDEO
+ },
+
+ /* 73647561-0000-0010-8000-00AA00389B71 */
+ {
+ { 0x61, 0x75, 0x64, 0x73, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 },
+ "MEDIATYPE_Audio",
+ TSMF_MAJOR_TYPE_AUDIO
+ },
+
+ {
+ { 0 },
+ "Unknown",
+ TSMF_MAJOR_TYPE_UNKNOWN
+ }
+};
+
+static const TSMFMediaTypeMap tsmf_sub_type_map[] =
+{
+ /* 31435657-0000-0010-8000-00AA00389B71 */
+ {
+ { 0x57, 0x56, 0x43, 0x31, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 },
+ "MEDIASUBTYPE_WVC1",
+ TSMF_SUB_TYPE_WVC1
+ },
+
+ /* 00000161-0000-0010-8000-00AA00389B71 */
+ {
+ { 0x61, 0x01, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 },
+ "MEDIASUBTYPE_WMAudioV2", /* V7, V8 has the same GUID */
+ TSMF_SUB_TYPE_WMA2
+ },
+
+ /* 00000162-0000-0010-8000-00AA00389B71 */
+ {
+ { 0x62, 0x01, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 },
+ "MEDIASUBTYPE_WMAudioV9",
+ TSMF_SUB_TYPE_WMA9
+ },
+
+ /* 00000055-0000-0010-8000-00AA00389B71 */
+ {
+ { 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 },
+ "MEDIASUBTYPE_MP3",
+ TSMF_SUB_TYPE_MP3
+ },
+
+ /* E06D802B-DB46-11CF-B4D1-00805F6CBBEA */
+ {
+ { 0x2B, 0x80, 0x6D, 0xE0, 0x46, 0xDB, 0xCF, 0x11, 0xB4, 0xD1, 0x00, 0x80, 0x5F, 0x6C, 0xBB, 0xEA },
+ "MEDIASUBTYPE_MPEG2_AUDIO",
+ TSMF_SUB_TYPE_MP2A
+ },
+
+ /* E06D8026-DB46-11CF-B4D1-00805F6CBBEA */
+ {
+ { 0x26, 0x80, 0x6D, 0xE0, 0x46, 0xDB, 0xCF, 0x11, 0xB4, 0xD1, 0x00, 0x80, 0x5F, 0x6C, 0xBB, 0xEA },
+ "MEDIASUBTYPE_MPEG2_VIDEO",
+ TSMF_SUB_TYPE_MP2V
+ },
+
+ /* 33564D57-0000-0010-8000-00AA00389B71 */
+ {
+ { 0x57, 0x4D, 0x56, 0x33, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 },
+ "MEDIASUBTYPE_WMV3",
+ TSMF_SUB_TYPE_WMV3
+ },
+
+ /* 00001610-0000-0010-8000-00AA00389B71 */
+ {
+ { 0x10, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 },
+ "MEDIASUBTYPE_MPEG_HEAAC",
+ TSMF_SUB_TYPE_AAC
+ },
+
+ /* 34363248-0000-0010-8000-00AA00389B71 */
+ {
+ { 0x48, 0x32, 0x36, 0x34, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 },
+ "MEDIASUBTYPE_H264",
+ TSMF_SUB_TYPE_H264
+ },
+
+ /* 31435641-0000-0010-8000-00AA00389B71 */
+ {
+ { 0x41, 0x56, 0x43, 0x31, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71 },
+ "MEDIASUBTYPE_AVC1",
+ TSMF_SUB_TYPE_AVC1
+ },
+
+ /* E06D802C-DB46-11CF-B4D1-00805F6CBBEA */
+ {
+ { 0x2C, 0x80, 0x6D, 0xE0, 0x46, 0xDB, 0xCF, 0x11, 0xB4, 0xD1, 0x00, 0x80, 0x5F, 0x6C, 0xBB, 0xEA },
+ "MEDIASUBTYPE_DOLBY_AC3",
+ TSMF_SUB_TYPE_AC3
+ },
+
+ {
+ { 0 },
+ "Unknown",
+ TSMF_SUB_TYPE_UNKNOWN
+ }
+
+};
+
+static const TSMFMediaTypeMap tsmf_format_type_map[] =
+{
+ /* AED4AB2D-7326-43CB-9464-C879CAB9C43D */
+ {
+ { 0x2D, 0xAB, 0xD4, 0xAE, 0x26, 0x73, 0xCB, 0x43, 0x94, 0x64, 0xC8, 0x79, 0xCA, 0xB9, 0xC4, 0x3D },
+ "FORMAT_MFVideoFormat",
+ TSMF_FORMAT_TYPE_MFVIDEOFORMAT
+ },
+
+ /* 05589F81-C356-11CE-BF01-00AA0055595A */
+ {
+ { 0x81, 0x9F, 0x58, 0x05, 0x56, 0xC3, 0xCE, 0x11, 0xBF, 0x01, 0x00, 0xAA, 0x00, 0x55, 0x59, 0x5A },
+ "FORMAT_WaveFormatEx",
+ TSMF_FORMAT_TYPE_WAVEFORMATEX
+ },
+
+ /* E06D80E3-DB46-11CF-B4D1-00805F6CBBEA */
+ {
+ { 0xE3, 0x80, 0x6D, 0xE0, 0x46, 0xDB, 0xCF, 0x11, 0xB4, 0xD1, 0x00, 0x80, 0x5F, 0x6C, 0xBB, 0xEA },
+ "FORMAT_MPEG2_VIDEO",
+ TSMF_FORMAT_TYPE_MPEG2VIDEOINFO
+ },
+
+ /* F72A76A0-EB0A-11D0-ACE4-0000C0CC16BA */
+ {
+ { 0xA0, 0x76, 0x2A, 0xF7, 0x0A, 0xEB, 0xD0, 0x11, 0xAC, 0xE4, 0x00, 0x00, 0xC0, 0xCC, 0x16, 0xBA },
+ "FORMAT_VideoInfo2",
+ TSMF_FORMAT_TYPE_VIDEOINFO2
+ },
+
+ {
+ { 0 },
+ "Unknown",
+ TSMF_FORMAT_TYPE_UNKNOWN
+ }
+};
+
+static void tsmf_print_guid(const uint8* guid)
+{
+#ifdef WITH_DEBUG_DVC
+ int i;
+
+ for (i = 3; i >= 0; i--)
+ printf("%02X", guid[i]);
+ printf("-");
+ for (i = 5; i >= 4; i--)
+ printf("%02X", guid[i]);
+ printf("-");
+ for (i = 7; i >= 6; i--)
+ printf("%02X", guid[i]);
+ printf("-");
+ for (i = 8; i < 16; i++)
+ {
+ printf("%02X", guid[i]);
+ if (i == 9)
+ printf("-");
+ }
+ printf("\n");
+#endif
+}
+
+/* http://msdn.microsoft.com/en-us/library/dd318229.aspx */
+static uint32 tsmf_codec_parse_BITMAPINFOHEADER(TS_AM_MEDIA_TYPE* mediatype, STREAM* s, boolean bypass)
+{
+ uint32 biSize;
+ uint32 biWidth;
+ uint32 biHeight;
+
+ stream_read_uint32(s, biSize);
+ stream_read_uint32(s, biWidth);
+ stream_read_uint32(s, biHeight);
+ stream_seek(s, 28);
+
+ if (mediatype->Width == 0)
+ mediatype->Width = biWidth;
+ if (mediatype->Height == 0)
+ mediatype->Height = biHeight;
+ /* Assume there will be no color table for video? */
+
+ if (bypass && biSize > 40)
+ stream_seek(s, biSize - 40);
+
+ return (bypass ? biSize : 40);
+}
+
+/* http://msdn.microsoft.com/en-us/library/dd407326.aspx */
+static uint32 tsmf_codec_parse_VIDEOINFOHEADER2(TS_AM_MEDIA_TYPE* mediatype, STREAM* s)
+{
+ uint64 AvgTimePerFrame;
+
+ /* VIDEOINFOHEADER2.rcSource, RECT(LONG left, LONG top, LONG right, LONG bottom) */
+ stream_seek_uint32(s);
+ stream_seek_uint32(s);
+ stream_read_uint32(s, mediatype->Width);
+ stream_read_uint32(s, mediatype->Height);
+ /* VIDEOINFOHEADER2.rcTarget */
+ stream_seek(s, 16);
+ /* VIDEOINFOHEADER2.dwBitRate */
+ stream_read_uint32(s, mediatype->BitRate);
+ /* VIDEOINFOHEADER2.dwBitErrorRate */
+ stream_seek_uint32(s);
+ /* VIDEOINFOHEADER2.AvgTimePerFrame */
+ stream_read_uint64(s, AvgTimePerFrame);
+ mediatype->SamplesPerSecond.Numerator = 1000000;
+ mediatype->SamplesPerSecond.Denominator = (int)(AvgTimePerFrame / 10LL);
+ /* Remaining fields before bmiHeader */
+ stream_seek(s, 24);
+
+ return 72;
+}
+
+boolean tsmf_codec_parse_media_type(TS_AM_MEDIA_TYPE* mediatype, STREAM* s)
+{
+ int i;
+ uint32 cbFormat;
+ boolean ret = true;
+
+ memset(mediatype, 0, sizeof(TS_AM_MEDIA_TYPE));
+
+ /* MajorType */
+ DEBUG_DVC("MajorType:");
+ tsmf_print_guid(stream_get_tail(s));
+ for (i = 0; tsmf_major_type_map[i].type != TSMF_MAJOR_TYPE_UNKNOWN; i++)
+ {
+ if (memcmp(tsmf_major_type_map[i].guid, stream_get_tail(s), 16) == 0)
+ break;
+ }
+ mediatype->MajorType = tsmf_major_type_map[i].type;
+ if (mediatype->MajorType == TSMF_MAJOR_TYPE_UNKNOWN)
+ ret = false;
+ DEBUG_DVC("MajorType %s", tsmf_major_type_map[i].name);
+ stream_seek(s, 16);
+
+ /* SubType */
+ DEBUG_DVC("SubType:");
+ tsmf_print_guid(stream_get_tail(s));
+ for (i = 0; tsmf_sub_type_map[i].type != TSMF_SUB_TYPE_UNKNOWN; i++)
+ {
+ if (memcmp(tsmf_sub_type_map[i].guid, stream_get_tail(s), 16) == 0)
+ break;
+ }
+ mediatype->SubType = tsmf_sub_type_map[i].type;
+ if (mediatype->SubType == TSMF_SUB_TYPE_UNKNOWN)
+ ret = false;
+ DEBUG_DVC("SubType %s", tsmf_sub_type_map[i].name);
+ stream_seek(s, 16);
+
+ /* bFixedSizeSamples, bTemporalCompression, SampleSize */
+ stream_seek(s, 12);
+
+ /* FormatType */
+ DEBUG_DVC("FormatType:");
+ tsmf_print_guid(stream_get_tail(s));
+ for (i = 0; tsmf_format_type_map[i].type != TSMF_FORMAT_TYPE_UNKNOWN; i++)
+ {
+ if (memcmp(tsmf_format_type_map[i].guid, stream_get_tail(s), 16) == 0)
+ break;
+ }
+ mediatype->FormatType = tsmf_format_type_map[i].type;
+ if (mediatype->FormatType == TSMF_FORMAT_TYPE_UNKNOWN)
+ ret = false;
+ DEBUG_DVC("FormatType %s", tsmf_format_type_map[i].name);
+ stream_seek(s, 16);
+
+ /* cbFormat */
+ stream_read_uint32(s, cbFormat);
+ DEBUG_DVC("cbFormat %d", cbFormat);
+
+#ifdef WITH_DEBUG_DVC
+ freerdp_hexdump(stream_get_tail(s), cbFormat);
+#endif
+
+ switch (mediatype->FormatType)
+ {
+ case TSMF_FORMAT_TYPE_MFVIDEOFORMAT:
+ /* http://msdn.microsoft.com/en-us/library/aa473808.aspx */
+
+ stream_seek(s, 8); /* dwSize and ? */
+ stream_read_uint32(s, mediatype->Width); /* videoInfo.dwWidth */
+ stream_read_uint32(s, mediatype->Height); /* videoInfo.dwHeight */
+ stream_seek(s, 32);
+ /* videoInfo.FramesPerSecond */
+ stream_read_uint32(s, mediatype->SamplesPerSecond.Numerator);
+ stream_read_uint32(s, mediatype->SamplesPerSecond.Denominator);
+ stream_seek(s, 80);
+ stream_read_uint32(s, mediatype->BitRate); /* compressedInfo.AvgBitrate */
+ stream_seek(s, 36);
+
+ if (cbFormat > 176)
+ {
+ mediatype->ExtraDataSize = cbFormat - 176;
+ mediatype->ExtraData = stream_get_tail(s);
+ }
+ break;
+
+ case TSMF_FORMAT_TYPE_WAVEFORMATEX:
+ /* http://msdn.microsoft.com/en-us/library/dd757720.aspx */
+
+ stream_seek_uint16(s);
+ stream_read_uint16(s, mediatype->Channels);
+ stream_read_uint32(s, mediatype->SamplesPerSecond.Numerator);
+ mediatype->SamplesPerSecond.Denominator = 1;
+ stream_read_uint32(s, mediatype->BitRate);
+ mediatype->BitRate *= 8;
+ stream_read_uint16(s, mediatype->BlockAlign);
+ stream_read_uint16(s, mediatype->BitsPerSample);
+ stream_read_uint16(s, mediatype->ExtraDataSize);
+ if (mediatype->ExtraDataSize > 0)
+ mediatype->ExtraData = stream_get_tail(s);
+
+ break;
+
+ case TSMF_FORMAT_TYPE_MPEG2VIDEOINFO:
+ /* http://msdn.microsoft.com/en-us/library/dd390707.aspx */
+
+ i = tsmf_codec_parse_VIDEOINFOHEADER2(mediatype, s);
+ i += tsmf_codec_parse_BITMAPINFOHEADER(mediatype, s, true);
+ if (cbFormat > i)
+ {
+ mediatype->ExtraDataSize = cbFormat - i;
+ mediatype->ExtraData = stream_get_tail(s);
+ }
+ break;
+
+ case TSMF_FORMAT_TYPE_VIDEOINFO2:
+ i = tsmf_codec_parse_VIDEOINFOHEADER2(mediatype, s);
+ i += tsmf_codec_parse_BITMAPINFOHEADER(mediatype, s, false);
+ if (cbFormat > i)
+ {
+ mediatype->ExtraDataSize = cbFormat - i;
+ mediatype->ExtraData = stream_get_tail(s);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (mediatype->SamplesPerSecond.Numerator == 0)
+ mediatype->SamplesPerSecond.Numerator = 1;
+ if (mediatype->SamplesPerSecond.Denominator == 0)
+ mediatype->SamplesPerSecond.Denominator = 1;
+
+ return ret;
+}
+
+boolean tsmf_codec_check_media_type(STREAM* s)
+{
+ uint8* m;
+ boolean ret;
+ TS_AM_MEDIA_TYPE mediatype;
+
+ stream_get_mark(s, m);
+ ret = tsmf_codec_parse_media_type(&mediatype, s);
+ stream_set_mark(s, m);
+
+ return ret;
+}
+
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Video Redirection Virtual Channel - Codec
+ *
+ * Copyright 2010-2011 Vic Lee
+ *
+ * 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.
+ */
+
+#ifndef __TSMF_CODEC
+#define __TSMF_CODEC
+
+#include "tsmf_types.h"
+
+boolean tsmf_codec_parse_media_type(TS_AM_MEDIA_TYPE* mediatype, STREAM* s);
+boolean tsmf_codec_check_media_type(STREAM* s);
+
+#endif
+
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Video Redirection Virtual Channel - Constants
+ *
+ * Copyright 2010-2011 Vic Lee
+ *
+ * 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.
+ */
+
+#ifndef __TSMF_CONSTANTS_H
+#define __TSMF_CONSTANTS_H
+
+#define GUID_SIZE 16
+#define TSMF_BUFFER_PADDING_SIZE 8
+
+/* Interface IDs defined in [MS-RDPEV]. There's no constant names in the MS
+ documentation, so we create them on our own. */
+#define TSMF_INTERFACE_DEFAULT 0x00000000
+#define TSMF_INTERFACE_CLIENT_NOTIFICATIONS 0x00000001
+#define TSMF_INTERFACE_CAPABILITIES 0x00000002
+
+/* Interface ID Mask */
+#define STREAM_ID_STUB 0x80000000
+#define STREAM_ID_PROXY 0x40000000
+#define STREAM_ID_NONE 0x00000000
+
+/* Functon ID */
+/* Common IDs for all interfaces are as follows. */
+#define RIMCALL_RELEASE 0x00000001
+#define RIMCALL_QUERYINTERFACE 0x00000002
+/* Capabilities Negotiator Interface IDs are as follows. */
+#define RIM_EXCHANGE_CAPABILITY_REQUEST 0x00000100
+/* The Client Notifications Interface ID is as follows. */
+#define PLAYBACK_ACK 0x00000100
+#define CLIENT_EVENT_NOTIFICATION 0x00000101
+/* Server Data Interface IDs are as follows. */
+#define EXCHANGE_CAPABILITIES_REQ 0x00000100
+#define SET_CHANNEL_PARAMS 0x00000101
+#define ADD_STREAM 0x00000102
+#define ON_SAMPLE 0x00000103
+#define SET_VIDEO_WINDOW 0x00000104
+#define ON_NEW_PRESENTATION 0x00000105
+#define SHUTDOWN_PRESENTATION_REQ 0x00000106
+#define SET_TOPOLOGY_REQ 0x00000107
+#define CHECK_FORMAT_SUPPORT_REQ 0x00000108
+#define ON_PLAYBACK_STARTED 0x00000109
+#define ON_PLAYBACK_PAUSED 0x0000010a
+#define ON_PLAYBACK_STOPPED 0x0000010b
+#define ON_PLAYBACK_RESTARTED 0x0000010c
+#define ON_PLAYBACK_RATE_CHANGED 0x0000010d
+#define ON_FLUSH 0x0000010e
+#define ON_STREAM_VOLUME 0x0000010f
+#define ON_CHANNEL_VOLUME 0x00000110
+#define ON_END_OF_STREAM 0x00000111
+#define SET_ALLOCATOR 0x00000112
+#define NOTIFY_PREROLL 0x00000113
+#define UPDATE_GEOMETRY_INFO 0x00000114
+#define REMOVE_STREAM 0x00000115
+
+/* Supported platform */
+#define MMREDIR_CAPABILITY_PLATFORM_MF 0x00000001
+#define MMREDIR_CAPABILITY_PLATFORM_DSHOW 0x00000002
+#define MMREDIR_CAPABILITY_PLATFORM_OTHER 0x00000004
+
+/* TSMM_CLIENT_EVENT Constants */
+#define TSMM_CLIENT_EVENT_ENDOFSTREAM 0x0064
+#define TSMM_CLIENT_EVENT_STOP_COMPLETED 0x00C8
+#define TSMM_CLIENT_EVENT_START_COMPLETED 0x00C9
+#define TSMM_CLIENT_EVENT_MONITORCHANGED 0x012C
+
+/* TS_MM_DATA_SAMPLE.SampleExtensions */
+#define TSMM_SAMPLE_EXT_CLEANPOINT 0x00000001
+#define TSMM_SAMPLE_EXT_DISCONTINUITY 0x00000002
+#define TSMM_SAMPLE_EXT_INTERLACED 0x00000004
+#define TSMM_SAMPLE_EXT_BOTTOMFIELDFIRST 0x00000008
+#define TSMM_SAMPLE_EXT_REPEATFIELDFIRST 0x00000010
+#define TSMM_SAMPLE_EXT_SINGLEFIELD 0x00000020
+#define TSMM_SAMPLE_EXT_DERIVEDFROMTOPFIELD 0x00000040
+#define TSMM_SAMPLE_EXT_HAS_NO_TIMESTAMPS 0x00000080
+#define TSMM_SAMPLE_EXT_RELATIVE_TIMESTAMPS 0x00000100
+#define TSMM_SAMPLE_EXT_ABSOLUTE_TIMESTAMPS 0x00000200
+
+/* MajorType */
+#define TSMF_MAJOR_TYPE_UNKNOWN 0
+#define TSMF_MAJOR_TYPE_VIDEO 1
+#define TSMF_MAJOR_TYPE_AUDIO 2
+
+/* SubType */
+#define TSMF_SUB_TYPE_UNKNOWN 0
+#define TSMF_SUB_TYPE_WVC1 1
+#define TSMF_SUB_TYPE_WMA2 2
+#define TSMF_SUB_TYPE_WMA9 3
+#define TSMF_SUB_TYPE_MP3 4
+#define TSMF_SUB_TYPE_MP2A 5
+#define TSMF_SUB_TYPE_MP2V 6
+#define TSMF_SUB_TYPE_WMV3 7
+#define TSMF_SUB_TYPE_AAC 8
+#define TSMF_SUB_TYPE_H264 9
+#define TSMF_SUB_TYPE_AVC1 10
+#define TSMF_SUB_TYPE_AC3 11
+
+/* FormatType */
+#define TSMF_FORMAT_TYPE_UNKNOWN 0
+#define TSMF_FORMAT_TYPE_MFVIDEOFORMAT 1
+#define TSMF_FORMAT_TYPE_WAVEFORMATEX 2
+#define TSMF_FORMAT_TYPE_MPEG2VIDEOINFO 3
+#define TSMF_FORMAT_TYPE_VIDEOINFO2 4
+
+#endif
+
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Video Redirection Virtual Channel - Decoder
+ *
+ * Copyright 2010-2011 Vic Lee
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <freerdp/utils/memory.h>
+#include <freerdp/utils/load_plugin.h>
+
+#include "drdynvc_types.h"
+#include "tsmf_types.h"
+#include "tsmf_constants.h"
+#include "tsmf_decoder.h"
+
+static ITSMFDecoder* tsmf_load_decoder_by_name(const char* name, TS_AM_MEDIA_TYPE* media_type)
+{
+ ITSMFDecoder* decoder;
+ TSMF_DECODER_ENTRY entry;
+ char* fullname;
+
+ if (strrchr(name, '.') != NULL)
+ entry = (TSMF_DECODER_ENTRY) freerdp_load_plugin(name, TSMF_DECODER_EXPORT_FUNC_NAME);
+ else
+ {
+ fullname = xzalloc(strlen(name) + 6);
+ strcpy(fullname, "tsmf_");
+ strcat(fullname, name);
+ entry = (TSMF_DECODER_ENTRY) freerdp_load_plugin(fullname, TSMF_DECODER_EXPORT_FUNC_NAME);
+ xfree(fullname);
+ }
+ if (entry == NULL)
+ {
+ return NULL;
+ }
+
+ decoder = entry();
+ if (decoder == NULL)
+ {
+ DEBUG_WARN("failed to call export function in %s", name);
+ return NULL;
+ }
+ if (!decoder->SetFormat(decoder, media_type))
+ {
+ decoder->Free(decoder);
+ decoder = NULL;
+ }
+ return decoder;
+}
+
+ITSMFDecoder* tsmf_load_decoder(const char* name, TS_AM_MEDIA_TYPE* media_type)
+{
+ ITSMFDecoder* decoder;
+
+ if (name)
+ {
+ decoder = tsmf_load_decoder_by_name(name, media_type);
+ }
+ else
+ {
+ decoder = tsmf_load_decoder_by_name("ffmpeg", media_type);
+ }
+
+ return decoder;
+}
+
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Video Redirection Virtual Channel - Decoder
+ *
+ * Copyright 2010-2011 Vic Lee
+ *
+ * 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.
+ */
+
+#ifndef __TSMF_DECODER_H
+#define __TSMF_DECODER_H
+
+#include "drdynvc_types.h"
+#include "tsmf_types.h"
+
+typedef struct _ITSMFDecoder ITSMFDecoder;
+
+struct _ITSMFDecoder
+{
+ /* Set the decoder format. Return true if supported. */
+ boolean (*SetFormat) (ITSMFDecoder* decoder, TS_AM_MEDIA_TYPE* media_type);
+ /* Decode a sample. */
+ boolean (*Decode) (ITSMFDecoder* decoder, const uint8* data, uint32 data_size, uint32 extensions);
+ /* Get the decoded data */
+ uint8* (*GetDecodedData) (ITSMFDecoder* decoder, uint32* size);
+ /* Get the pixel format of decoded video frame */
+ uint32 (*GetDecodedFormat) (ITSMFDecoder* decoder);
+ /* Get the width and height of decoded video frame */
+ boolean (*GetDecodedDimension) (ITSMFDecoder* decoder, uint32* width, uint32* height);
+ /* Free the decoder */
+ void (*Free) (ITSMFDecoder* decoder);
+};
+
+#define TSMF_DECODER_EXPORT_FUNC_NAME "TSMFDecoderEntry"
+typedef ITSMFDecoder* (*TSMF_DECODER_ENTRY) (void);
+
+ITSMFDecoder* tsmf_load_decoder(const char* name, TS_AM_MEDIA_TYPE* media_type);
+
+#endif
+
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol client.
+ * Video Redirection Virtual Channel - Interface Manipulation
+ *
+ * Copyright 2010-2011 Vic Lee
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <freerdp/utils/memory.h>
+#include <freerdp/utils/stream.h>
+
+#include "drdynvc_types.h"
+#include "tsmf_constants.h"
+#include "tsmf_media.h"
+#include "tsmf_codec.h"
+
+#include "tsmf_ifman.h"
+
+int tsmf_ifman_rim_exchange_capability_request(TSMF_IFMAN* ifman)
+{
+ uint32 CapabilityValue;
+
+ stream_read_uint32(ifman->input, CapabilityValue);
+ DEBUG_DVC("server CapabilityValue %d", CapabilityValue);
+
+ stream_check_size(ifman->output, 8);
+ stream_write_uint32(ifman->output, 1); /* CapabilityValue */
+ stream_write_uint32(ifman->output, 0); /* Result */
+
+ return 0;
+}
+
+int tsmf_ifman_exchange_capability_request(TSMF_IFMAN* ifman)
+{
+ uint32 i;
+ uint32 v;
+ uint32 pos;
+ uint32 CapabilityType;
+ uint32 cbCapabilityLength;
+ uint32 numHostCapabilities;
+
+ pos = stream_get_pos(ifman->output);
+ stream_check_size(ifman->output, ifman->input_size + 4);
+ stream_copy(ifman->output, ifman->input, ifman->input_size);
+
+ stream_set_pos(ifman->output, pos);
+ stream_read_uint32(ifman->output, numHostCapabilities);
+ for (i = 0; i < numHostCapabilities; i++)
+ {
+ stream_read_uint32(ifman->output, CapabilityType);
+ stream_read_uint32(ifman->output, cbCapabilityLength);
+ pos = stream_get_pos(ifman->output);
+ switch (CapabilityType)
+ {
+ case 1: /* Protocol version request */
+ stream_read_uint32(ifman->output, v);
+ DEBUG_DVC("server protocol version %d", v);
+ break;
+ case 2: /* Supported platform */
+ stream_peek_uint32(ifman->output, v);
+ DEBUG_DVC("server supported platform %d", v);
+ /* Claim that we support both MF and DShow platforms. */
+ stream_write_uint32(ifman->output,
+ MMREDIR_CAPABILITY_PLATFORM_MF | MMREDIR_CAPABILITY_PLATFORM_DSHOW);
+ break;
+ default:
+ DEBUG_WARN("unknown capability type %d", CapabilityType);
+ break;
+ }
+ stream_set_pos(ifman->output, pos + cbCapabilityLength);
+ }
+ stream_write_uint32(ifman->output, 0); /* Result */
+
+ ifman->output_interface_id = TSMF_INTERFACE_DEFAULT | STREAM_ID_STUB;
+
+ return 0;
+}
+
+int tsmf_ifman_check_format_support_request(TSMF_IFMAN* ifman)
+{
+ uint32 numMediaType;
+ uint32 PlatformCookie;
+ uint32 FormatSupported = 1;
+
+ stream_read_uint32(ifman->input, PlatformCookie);
+ stream_seek_uint32(ifman->input); /* NoRolloverFlags (4 bytes) */
+ stream_read_uint32(ifman->input, numMediaType);
+
+ DEBUG_DVC("PlatformCookie %d numMediaType %d", PlatformCookie, numMediaType);
+
+ if (!tsmf_codec_check_media_type(ifman->input))
+ FormatSupported = 0;
+
+ if (FormatSupported)
+ DEBUG_DVC("format ok.");
+
+ stream_check_size(ifman->output, 12);
+ stream_write_uint32(ifman->output, FormatSupported);
+ stream_write_uint32(ifman->output, PlatformCookie);
+ stream_write_uint32(ifman->output, 0); /* Result */
+
+ ifman->output_interface_id = TSMF_INTERFACE_DEFAULT | STREAM_ID_STUB;
+
+ return 0;
+}
+
+int tsmf_ifman_on_new_presentation(TSMF_IFMAN* ifman)
+{
+ int error = 0;
+ TSMF_PRESENTATION* presentation;
+
+ DEBUG_DVC("");
+
+ presentation = tsmf_presentation_new(stream_get_tail(ifman->input), ifman->channel_callback);
+ if (presentation == NULL)
+ error = 1;
+ tsmf_presentation_set_audio_device(presentation, ifman->audio_name, ifman->audio_device);
+ ifman->output_pending = true;
+ return error;
+}
+
+int tsmf_ifman_add_stream(TSMF_IFMAN* ifman)
+{
+ uint32 StreamId;
+ int error = 0;
+ TSMF_STREAM* stream;
+ TSMF_PRESENTATION* presentation;
+
+ DEBUG_DVC("");
+
+ presentation = tsmf_presentation_find_by_id(stream_get_tail(ifman->input));
+ stream_seek(ifman->input, 16);
+
+ if (presentation == NULL)
+ error = 1;
+ else
+ {
+ stream_read_uint32(ifman->input, StreamId);
+ stream_seek_uint32(ifman->input); /* numMediaType */
+ stream = tsmf_stream_new(presentation, StreamId);
+ if (stream)
+ tsmf_stream_set_format(stream, ifman->decoder_name, ifman->input);
+ }